From 60483a312a7d884215883c6bcc839bd6f0c7c2d5 Mon Sep 17 00:00:00 2001 From: Preet Date: Fri, 8 Jul 2022 08:22:06 -0400 Subject: [PATCH 001/708] add new auto layout widget --- app/client/src/components/constants.ts | 5 + app/client/src/utils/WidgetRegistry.tsx | 4 + .../component/index.tsx | 24 +++++ .../AutoLayoutContainerWidget/constants.ts | 2 + .../AutoLayoutContainerWidget/icon.svg | 44 +++++++++ .../AutoLayoutContainerWidget/index.ts | 25 +++++ .../widget/index.tsx | 98 +++++++++++++++++++ 7 files changed, 202 insertions(+) create mode 100644 app/client/src/widgets/AutoLayoutContainerWidget/component/index.tsx create mode 100644 app/client/src/widgets/AutoLayoutContainerWidget/constants.ts create mode 100644 app/client/src/widgets/AutoLayoutContainerWidget/icon.svg create mode 100644 app/client/src/widgets/AutoLayoutContainerWidget/index.ts create mode 100644 app/client/src/widgets/AutoLayoutContainerWidget/widget/index.tsx diff --git a/app/client/src/components/constants.ts b/app/client/src/components/constants.ts index edc3525cfdda..1d5d89c52fa8 100644 --- a/app/client/src/components/constants.ts +++ b/app/client/src/components/constants.ts @@ -110,3 +110,8 @@ export const SELECT_DEFAULT_HEIGHT = "32px"; * Default margin bottom value for old select widgets */ export const LABEL_MARGIN_OLD_SELECT = "5px"; + +export enum LayoutDirection { + Horizontal = "Horizontal", + Vertical = "Vertical", +} diff --git a/app/client/src/utils/WidgetRegistry.tsx b/app/client/src/utils/WidgetRegistry.tsx index f17418cc6e78..b1ccccd4c804 100644 --- a/app/client/src/utils/WidgetRegistry.tsx +++ b/app/client/src/utils/WidgetRegistry.tsx @@ -144,6 +144,9 @@ import VideoWidget, { import ProgressWidget, { CONFIG as PROGRESS_WIDGET_CONFIG, } from "widgets/ProgressWidget"; +import AutoLayoutContainerWidget, { + CONFIG as AUTO_LAYOUT_CONFIG, +} from "widgets/AutoLayoutContainerWidget"; import { registerWidget } from "./WidgetRegisterHelpers"; import { WidgetConfiguration } from "widgets/constants"; @@ -192,6 +195,7 @@ export const ALL_WIDGETS_AND_CONFIG = [ [PhoneInputWidget, PHONE_INPUT_WIDGET_V2_CONFIG], [CurrencyInputWidget, CURRENCY_INPUT_WIDGET_V2_CONFIG], [JSONFormWidget, JSON_FORM_WIDGET_CONFIG], + [AutoLayoutContainerWidget, AUTO_LAYOUT_CONFIG], //Deprecated Widgets [InputWidget, INPUT_WIDGET_CONFIG], diff --git a/app/client/src/widgets/AutoLayoutContainerWidget/component/index.tsx b/app/client/src/widgets/AutoLayoutContainerWidget/component/index.tsx new file mode 100644 index 000000000000..e87dc4a660de --- /dev/null +++ b/app/client/src/widgets/AutoLayoutContainerWidget/component/index.tsx @@ -0,0 +1,24 @@ +import React, { ReactNode } from "react"; +import styled from "styled-components"; + +import { ComponentProps } from "widgets/BaseComponent"; + +const LayoutContainer = styled("div")` + display: flex; + flex-direction: ${({ isVertical }) => (isVertical ? "column" : "row")}; + justify-content: flex-start; + flex-wrap: wrap; +`; + +function AutoLayoutContainerComponent( + props: AutoLayoutContainerComponentProps, +) { + return {props.children}; +} + +export interface AutoLayoutContainerComponentProps extends ComponentProps { + children?: ReactNode[]; + isVertical: boolean; +} + +export default AutoLayoutContainerComponent; diff --git a/app/client/src/widgets/AutoLayoutContainerWidget/constants.ts b/app/client/src/widgets/AutoLayoutContainerWidget/constants.ts new file mode 100644 index 000000000000..857d86d0e12b --- /dev/null +++ b/app/client/src/widgets/AutoLayoutContainerWidget/constants.ts @@ -0,0 +1,2 @@ +// This file contains common constants which can be used across the widget configuration file (index.ts), widget and component folders. +export const AUTOLAYOUTCONTAINER_WIDGET_CONSTANT = ""; diff --git a/app/client/src/widgets/AutoLayoutContainerWidget/icon.svg b/app/client/src/widgets/AutoLayoutContainerWidget/icon.svg new file mode 100644 index 000000000000..43fda6a45216 --- /dev/null +++ b/app/client/src/widgets/AutoLayoutContainerWidget/icon.svg @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/client/src/widgets/AutoLayoutContainerWidget/index.ts b/app/client/src/widgets/AutoLayoutContainerWidget/index.ts new file mode 100644 index 000000000000..7463f35db0fa --- /dev/null +++ b/app/client/src/widgets/AutoLayoutContainerWidget/index.ts @@ -0,0 +1,25 @@ +import Widget from "./widget"; +import IconSVG from "./icon.svg"; + +export const CONFIG = { + type: Widget.getWidgetType(), + name: "Auto Layout Container", // The display name which will be made in uppercase and show in the widgets panel ( can have spaces ) + iconSVG: IconSVG, + needsMeta: false, // Defines if this widget adds any meta properties + isCanvas: true, // Defines if this widget has a canvas within in which we can drop other widgets + searchTags: ["auto layout", "flex", "div", "parent", "group"], + defaults: { + widgetName: "AutoLayoutContainer", + rows: 40, + columns: 24, + version: 1, + }, + properties: { + derived: Widget.getDerivedPropertiesMap(), + default: Widget.getDefaultPropertiesMap(), + meta: Widget.getMetaPropertiesMap(), + config: Widget.getPropertyPaneConfig(), + }, +}; + +export default Widget; diff --git a/app/client/src/widgets/AutoLayoutContainerWidget/widget/index.tsx b/app/client/src/widgets/AutoLayoutContainerWidget/widget/index.tsx new file mode 100644 index 000000000000..037bc830ed30 --- /dev/null +++ b/app/client/src/widgets/AutoLayoutContainerWidget/widget/index.tsx @@ -0,0 +1,98 @@ +import React from "react"; +import styled from "styled-components"; +import { compact, map, sortBy } from "lodash"; + +import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; +import WidgetFactory, { DerivedPropertiesMap } from "utils/WidgetFactory"; + +import AutoLayoutContainerComponent from "../component"; +import { ValidationTypes } from "constants/WidgetValidation"; +import { LayoutDirection } from "components/constants"; + +class AutoLayoutContainerWidget extends BaseWidget< + AutoLayoutContainerWidgetProps, + WidgetState +> { + static getPropertyPaneConfig() { + return [ + { + helpText: "Controls the visibility of the widget", + propertyName: "direction", + label: "Direction", + controlType: "DROP_DOWN", + options: [ + { label: "Horizontal", value: LayoutDirection.Horizontal }, + { label: "Vertical", value: LayoutDirection.Vertical }, + ], + isJSConvertible: false, + isBindProperty: false, + isTriggerProperty: false, + validation: { type: ValidationTypes.TEXT }, + }, + ]; + } + + static getDerivedPropertiesMap(): DerivedPropertiesMap { + return {}; + } + + static getDefaultPropertiesMap(): Record { + return {}; + } + + static getMetaPropertiesMap(): Record { + return {}; + } + + renderChildWidget(childWidgetData: WidgetProps): React.ReactNode { + // For now, isVisible prop defines whether to render a detached widget + if (childWidgetData.detachFromLayout && !childWidgetData.isVisible) { + return null; + } + + const { componentHeight, componentWidth } = this.getComponentDimensions(); + + childWidgetData.rightColumn = componentWidth; + childWidgetData.bottomRow = childWidgetData.bottomRow; + childWidgetData.minHeight = componentHeight; + childWidgetData.isVisible = this.props.isVisible; + childWidgetData.shouldScrollContents = false; + childWidgetData.canExtend = true; + + childWidgetData.parentId = this.props.widgetId; + + return WidgetFactory.createWidget(childWidgetData, this.props.renderMode); + } + + renderChildren = () => { + return map( + // sort by row so stacking context is correct + // TODO(abhinav): This is hacky. The stacking context should increase for widgets rendered top to bottom, always. + // Figure out a way in which the stacking context is consistent. + sortBy(compact(this.props.children), (child: any) => child.topRow), + this.renderChildWidget, + ); + }; + + getPageView() { + return ( + + {this.renderChildren()} + + ); + } + + static getWidgetType(): string { + return "AUTOLAYOUTCONTAINER_WIDGET"; + } +} + +export interface AutoLayoutContainerWidgetProps + extends WidgetProps { + children?: T[]; +} + +export default AutoLayoutContainerWidget; From df944d27077560c433e698cdd382c5fac137c7f0 Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 11 Jul 2022 15:23:29 -0400 Subject: [PATCH 002/708] wip --- .../appsmith/PositionedContainer.tsx | 7 +- .../appsmith/WidgetStyleContainer.tsx | 11 ++ .../component/index.tsx | 126 ++++++++++++- .../AutoLayoutContainerWidget/index.ts | 24 ++- .../widget/index.tsx | 170 +++++++++++++++--- app/client/src/widgets/BaseWidget.tsx | 15 +- app/client/src/widgets/CanvasWidget.tsx | 6 + .../ContainerWidget/component/index.tsx | 13 ++ .../widgets/ContainerWidget/widget/index.tsx | 3 + 9 files changed, 342 insertions(+), 33 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/PositionedContainer.tsx b/app/client/src/components/designSystems/appsmith/PositionedContainer.tsx index fd67df21aa51..ef569212716a 100644 --- a/app/client/src/components/designSystems/appsmith/PositionedContainer.tsx +++ b/app/client/src/components/designSystems/appsmith/PositionedContainer.tsx @@ -27,6 +27,7 @@ export type PositionedContainerProps = { selected?: boolean; focused?: boolean; resizeDisabled?: boolean; + useAutoLayout?: boolean; }; export const checkIsDropTarget = memoize(function isDropTarget( @@ -97,7 +98,11 @@ export function PositionedContainer(props: PositionedContainerProps) { } : {}; const styles: CSSProperties = { - position: "absolute", + // TODO: remove the widget type check. Add check for parent type. + position: + props?.useAutoLayout && !props.widgetType?.includes("AUTOLAYOUT") + ? "unset" + : "absolute", left: x, top: y, height: diff --git a/app/client/src/components/designSystems/appsmith/WidgetStyleContainer.tsx b/app/client/src/components/designSystems/appsmith/WidgetStyleContainer.tsx index 3671bb6545fb..6c11858ea28f 100644 --- a/app/client/src/components/designSystems/appsmith/WidgetStyleContainer.tsx +++ b/app/client/src/components/designSystems/appsmith/WidgetStyleContainer.tsx @@ -2,6 +2,7 @@ import React, { ReactNode } from "react"; import styled from "styled-components"; import { ContainerStyle } from "widgets/ContainerWidget/component"; import { Color } from "constants/Colors"; +import { LayoutDirection } from "components/constants"; export enum BoxShadowTypes { NONE = "NONE", @@ -24,6 +25,8 @@ export interface WidgetStyleContainerProps { borderRadius?: number; boxShadow?: BoxShadow; className?: string; + useAutoLayout?: boolean; + direction?: string; } const WidgetStyle = styled.div` @@ -36,6 +39,14 @@ const WidgetStyle = styled.div` border-style: solid; background-color: ${(props) => props.backgroundColor || "transparent"}; + display: ${({ useAutoLayout }) => (useAutoLayout ? "flex" : "block")}; + flex-direction: ${({ direction }) => + direction === LayoutDirection.Vertical ? "column" : "row"}; + justify-content: flex-start; + align-items: ${({ direction }) => + direction === LayoutDirection.Vertical ? "stretch" : "flex-start"}; + flex-wrap: wrap; + & > div { height: 100%; width: 100%; diff --git a/app/client/src/widgets/AutoLayoutContainerWidget/component/index.tsx b/app/client/src/widgets/AutoLayoutContainerWidget/component/index.tsx index e87dc4a660de..fb375532820b 100644 --- a/app/client/src/widgets/AutoLayoutContainerWidget/component/index.tsx +++ b/app/client/src/widgets/AutoLayoutContainerWidget/component/index.tsx @@ -1,24 +1,132 @@ -import React, { ReactNode } from "react"; -import styled from "styled-components"; - +import React, { ReactNode, useRef, useEffect, RefObject } from "react"; +import styled, { css } from "styled-components"; +import tinycolor from "tinycolor2"; +import { invisible } from "constants/DefaultTheme"; +import { Color } from "constants/Colors"; +import { generateClassName, getCanvasClassName } from "utils/generators"; +import { useCanvasMinHeightUpdateHook } from "utils/hooks/useCanvasMinHeightUpdateHook"; +import WidgetStyleContainer, { + WidgetStyleContainerProps, +} from "components/designSystems/appsmith/WidgetStyleContainer"; +import { pick } from "lodash"; import { ComponentProps } from "widgets/BaseComponent"; +import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; + +const scrollContents = css` + overflow-y: auto; +`; + +const StyledContainerComponent = styled.div< + AutoLayoutContainerComponentProps & { + ref: RefObject; + } +>` + height: 100%; + width: 100%; + background: ${(props) => props.backgroundColor}; + opacity: ${(props) => (props.resizeDisabled ? "0.8" : "1")}; + position: relative; + ${(props) => (!props.isVisible ? invisible : "")}; + box-shadow: ${(props) => + props.selected ? "inset 0px 0px 0px 3px rgba(59,130,246,0.5)" : "none"}; + border-radius: ${({ borderRadius }) => borderRadius}; -const LayoutContainer = styled("div")` display: flex; - flex-direction: ${({ isVertical }) => (isVertical ? "column" : "row")}; + flex-direction: row; justify-content: flex-start; flex-wrap: wrap; + + ${(props) => + props.shouldScrollContents === true + ? scrollContents + : props.shouldScrollContents === false + ? css` + overflow: hidden; + ` + : ""} + + &:hover { + z-index: ${(props) => (props.onClickCapture ? "2" : "1")}; + cursor: ${(props) => (props.onClickCapture ? "pointer" : "inherit")}; + background: ${(props) => { + return props.onClickCapture && props.backgroundColor + ? tinycolor(props.backgroundColor) + .darken(5) + .toString() + : props.backgroundColor; + }}; + } `; +function ContainerComponentWrapper(props: AutoLayoutContainerComponentProps) { + const containerStyle = props.containerStyle || "card"; + const containerRef: RefObject = useRef(null); + useEffect(() => { + if (!props.shouldScrollContents) { + const supportsNativeSmoothScroll = + "scrollBehavior" in document.documentElement.style; + if (supportsNativeSmoothScroll) { + containerRef.current?.scrollTo({ top: 0, behavior: "smooth" }); + } else { + if (containerRef.current) { + containerRef.current.scrollTop = 0; + } + } + } + }, [props.shouldScrollContents]); + return ( + + {props.children} + + ); +} + function AutoLayoutContainerComponent( props: AutoLayoutContainerComponentProps, ) { - return {props.children}; + useCanvasMinHeightUpdateHook(props.widgetId, props.minHeight); + return props.widgetId === MAIN_CONTAINER_WIDGET_ID ? ( + + ) : ( + + + + ); } -export interface AutoLayoutContainerComponentProps extends ComponentProps { - children?: ReactNode[]; - isVertical: boolean; +export type ContainerStyle = "border" | "card" | "rounded-border" | "none"; + +export interface AutoLayoutContainerComponentProps + extends ComponentProps, + WidgetStyleContainerProps { + children?: ReactNode; + className?: string; + backgroundColor?: Color; + shouldScrollContents?: boolean; + resizeDisabled?: boolean; + selected?: boolean; + focused?: boolean; + minHeight?: number; + useAutoLayout?: boolean; } export default AutoLayoutContainerComponent; diff --git a/app/client/src/widgets/AutoLayoutContainerWidget/index.ts b/app/client/src/widgets/AutoLayoutContainerWidget/index.ts index 7463f35db0fa..3119acc6446c 100644 --- a/app/client/src/widgets/AutoLayoutContainerWidget/index.ts +++ b/app/client/src/widgets/AutoLayoutContainerWidget/index.ts @@ -1,9 +1,10 @@ import Widget from "./widget"; import IconSVG from "./icon.svg"; +import { ButtonBoxShadowTypes } from "components/constants"; export const CONFIG = { type: Widget.getWidgetType(), - name: "Auto Layout Container", // The display name which will be made in uppercase and show in the widgets panel ( can have spaces ) + name: "AutoLayout Container", // The display name which will be made in uppercase and show in the widgets panel ( can have spaces ) iconSVG: IconSVG, needsMeta: false, // Defines if this widget adds any meta properties isCanvas: true, // Defines if this widget has a canvas within in which we can drop other widgets @@ -13,6 +14,27 @@ export const CONFIG = { rows: 40, columns: 24, version: 1, + backgroundColor: "#FFFFFF", + containerStyle: "card", + borderColor: "transparent", + borderWidth: "0", + boxShadow: ButtonBoxShadowTypes.NONE, + animateLoading: true, + children: [], + blueprint: { + view: [ + { + type: "CANVAS_WIDGET", + position: { top: 0, left: 0 }, + props: { + containerStyle: "none", + canExtend: false, + detachFromLayout: true, + children: [], + }, + }, + ], + }, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/AutoLayoutContainerWidget/widget/index.tsx b/app/client/src/widgets/AutoLayoutContainerWidget/widget/index.tsx index 037bc830ed30..b2c2bdcc6d18 100644 --- a/app/client/src/widgets/AutoLayoutContainerWidget/widget/index.tsx +++ b/app/client/src/widgets/AutoLayoutContainerWidget/widget/index.tsx @@ -1,5 +1,4 @@ import React from "react"; -import styled from "styled-components"; import { compact, map, sortBy } from "lodash"; import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; @@ -8,26 +7,143 @@ import WidgetFactory, { DerivedPropertiesMap } from "utils/WidgetFactory"; import AutoLayoutContainerComponent from "../component"; import { ValidationTypes } from "constants/WidgetValidation"; import { LayoutDirection } from "components/constants"; +import { ContainerStyle } from "widgets/ContainerWidget/component"; class AutoLayoutContainerWidget extends BaseWidget< AutoLayoutContainerWidgetProps, WidgetState > { + constructor(props: AutoLayoutContainerWidgetProps) { + super(props); + this.renderChildWidget = this.renderChildWidget.bind(this); + } + static getPropertyPaneConfig() { return [ { - helpText: "Controls the visibility of the widget", - propertyName: "direction", - label: "Direction", - controlType: "DROP_DOWN", - options: [ - { label: "Horizontal", value: LayoutDirection.Horizontal }, - { label: "Vertical", value: LayoutDirection.Vertical }, - ], - isJSConvertible: false, - isBindProperty: false, + propertyName: "useAutoLayout", + label: "Use Auto Layout", + controlType: "SWITCH", + defaultValue: true, + helpText: "Controls layout of children widgets", + isJSConvertible: true, + isBindProperty: true, isTriggerProperty: false, - validation: { type: ValidationTypes.TEXT }, + validation: { type: ValidationTypes.BOOLEAN }, + }, + { + sectionName: "Layout Controls", + hidden: (props: AutoLayoutContainerWidgetProps): boolean => + !props?.useAutoLayout, + children: [ + { + helpText: "Controls the visibility of the widget", + propertyName: "direction", + label: "Direction", + controlType: "DROP_DOWN", + defaultValue: LayoutDirection.Horizontal, + options: [ + { label: "Horizontal", value: LayoutDirection.Horizontal }, + { label: "Vertical", value: LayoutDirection.Vertical }, + ], + isJSConvertible: false, + isBindProperty: false, + isTriggerProperty: false, + validation: { type: ValidationTypes.TEXT }, + }, + ], + }, + { + sectionName: "General", + children: [ + { + helpText: "Controls the visibility of the widget", + propertyName: "isVisible", + label: "Visible", + controlType: "SWITCH", + isJSConvertible: true, + isBindProperty: true, + isTriggerProperty: false, + validation: { type: ValidationTypes.BOOLEAN }, + }, + { + propertyName: "animateLoading", + label: "Animate Loading", + controlType: "SWITCH", + helpText: "Controls the loading of the widget", + defaultValue: true, + isJSConvertible: true, + isBindProperty: true, + isTriggerProperty: false, + validation: { type: ValidationTypes.BOOLEAN }, + }, + { + helpText: "Enables scrolling for content inside the widget", + propertyName: "shouldScrollContents", + label: "Scroll Contents", + controlType: "SWITCH", + isBindProperty: false, + isTriggerProperty: false, + }, + ], + }, + { + sectionName: "Styles", + children: [ + { + helpText: "Use a html color name, HEX, RGB or RGBA value", + placeholderText: "#FFFFFF / Gray / rgb(255, 99, 71)", + propertyName: "backgroundColor", + label: "Background Color", + controlType: "COLOR_PICKER", + isJSConvertible: true, + isBindProperty: true, + isTriggerProperty: false, + validation: { type: ValidationTypes.TEXT }, + }, + { + helpText: "Use a html color name, HEX, RGB or RGBA value", + placeholderText: "#FFFFFF / Gray / rgb(255, 99, 71)", + propertyName: "borderColor", + label: "Border Color", + controlType: "COLOR_PICKER", + isBindProperty: true, + isTriggerProperty: false, + validation: { type: ValidationTypes.TEXT }, + }, + { + helpText: "Enter value for border width", + propertyName: "borderWidth", + label: "Border Width", + placeholderText: "Enter value in px", + controlType: "INPUT_TEXT", + isBindProperty: true, + isTriggerProperty: false, + validation: { type: ValidationTypes.NUMBER }, + }, + { + propertyName: "borderRadius", + label: "Border Radius", + helpText: + "Rounds the corners of the icon button's outer border edge", + controlType: "BORDER_RADIUS_OPTIONS", + isJSConvertible: true, + isBindProperty: true, + isTriggerProperty: false, + validation: { type: ValidationTypes.TEXT }, + }, + { + propertyName: "boxShadow", + label: "Box Shadow", + helpText: + "Enables you to cast a drop shadow from the frame of the widget", + controlType: "BOX_SHADOW_OPTIONS", + isJSConvertible: true, + isBindProperty: true, + isTriggerProperty: false, + validation: { type: ValidationTypes.TEXT }, + }, + ], }, ]; } @@ -53,13 +169,18 @@ class AutoLayoutContainerWidget extends BaseWidget< const { componentHeight, componentWidth } = this.getComponentDimensions(); childWidgetData.rightColumn = componentWidth; - childWidgetData.bottomRow = childWidgetData.bottomRow; + childWidgetData.bottomRow = this.props.shouldScrollContents + ? childWidgetData.bottomRow + : componentHeight; childWidgetData.minHeight = componentHeight; childWidgetData.isVisible = this.props.isVisible; childWidgetData.shouldScrollContents = false; - childWidgetData.canExtend = true; + childWidgetData.canExtend = this.props.shouldScrollContents; childWidgetData.parentId = this.props.widgetId; + // Pass layout controls to children + childWidgetData.useAutoLayout = this.props.useAutoLayout; + childWidgetData.direction = this.props.direction; return WidgetFactory.createWidget(childWidgetData, this.props.renderMode); } @@ -69,22 +190,26 @@ class AutoLayoutContainerWidget extends BaseWidget< // sort by row so stacking context is correct // TODO(abhinav): This is hacky. The stacking context should increase for widgets rendered top to bottom, always. // Figure out a way in which the stacking context is consistent. - sortBy(compact(this.props.children), (child: any) => child.topRow), + sortBy(compact(this.props.children), (child) => child.topRow), this.renderChildWidget, ); }; - getPageView() { + renderAsContainerComponent( + props: AutoLayoutContainerWidgetProps, + ) { return ( - - {this.renderChildren()} + + {/* without the wrapping div onClick events are triggered twice */} + <>{this.renderChildren()} ); } + getPageView() { + return this.renderAsContainerComponent(this.props); + } + static getWidgetType(): string { return "AUTOLAYOUTCONTAINER_WIDGET"; } @@ -93,6 +218,9 @@ class AutoLayoutContainerWidget extends BaseWidget< export interface AutoLayoutContainerWidgetProps extends WidgetProps { children?: T[]; + containerStyle?: ContainerStyle; + shouldScrollContents?: boolean; + noPad?: boolean; } export default AutoLayoutContainerWidget; diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index 7c7303aaed48..7d6efd1ed574 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -281,6 +281,7 @@ abstract class BaseWidget< resizeDisabled={this.props.resizeDisabled} selected={this.props.selected} style={style} + useAutoLayout={this.props.useAutoLayout} widgetId={this.props.widgetId} widgetType={this.props.type} > @@ -327,8 +328,13 @@ abstract class BaseWidget< ); } + addAutoLayoutWrapper(content: ReactNode) { + return
{content}
; + } + private getWidgetView(): ReactNode { let content: ReactNode; + console.log(this.props); switch (this.props.renderMode) { case RenderModes.CANVAS: content = this.getCanvasView(); @@ -341,7 +347,12 @@ abstract class BaseWidget< content = this.makeDraggable(content); content = this.makeSnipeable(content); // NOTE: In sniping mode we are not blocking onClick events from PositionWrapper. - content = this.makePositioned(content); + if ( + this.props.useAutoLayout && + !this.props.widgetName.toUpperCase().includes("AUTO") + ) { + content = this.addAutoLayoutWrapper(content); + } else content = this.makePositioned(content); } return content; @@ -414,6 +425,7 @@ abstract class BaseWidget< isDeletable: true, resizeDisabled: false, disablePropertyPane: false, + useAutoLayout: false, }; } @@ -462,6 +474,7 @@ export interface WidgetPositionProps extends WidgetRowCols { // MODAL_WIDGET is also detached from layout. detachFromLayout?: boolean; noContainerOffset?: boolean; // This won't offset the child in parent + useAutoLayout?: boolean; } export const WIDGET_STATIC_PROPS = { diff --git a/app/client/src/widgets/CanvasWidget.tsx b/app/client/src/widgets/CanvasWidget.tsx index 45f11a95804d..6e2f212676de 100644 --- a/app/client/src/widgets/CanvasWidget.tsx +++ b/app/client/src/widgets/CanvasWidget.tsx @@ -63,6 +63,12 @@ class CanvasWidget extends ContainerWidget { childWidgetData.parentRowSpace = snapSpaces.snapRowSpace; if (this.props.noPad) childWidgetData.noContainerOffset = true; childWidgetData.parentId = this.props.widgetId; + // Pass layout controls to children + // TODO: remove the hard check on widget name + if (this.props.widgetName !== "MainContainer") { + childWidgetData.useAutoLayout = this.props.useAutoLayout; + childWidgetData.direction = this.props.direction; + } return WidgetFactory.createWidget(childWidgetData, this.props.renderMode); } diff --git a/app/client/src/widgets/ContainerWidget/component/index.tsx b/app/client/src/widgets/ContainerWidget/component/index.tsx index 7ec7eabff27d..bc82468b35f8 100644 --- a/app/client/src/widgets/ContainerWidget/component/index.tsx +++ b/app/client/src/widgets/ContainerWidget/component/index.tsx @@ -11,6 +11,7 @@ import WidgetStyleContainer, { import { pick } from "lodash"; import { ComponentProps } from "widgets/BaseComponent"; import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; +import { LayoutDirection } from "components/constants"; const scrollContents = css` overflow-y: auto; @@ -31,6 +32,14 @@ const StyledContainerComponent = styled.div< props.selected ? "inset 0px 0px 0px 3px rgba(59,130,246,0.5)" : "none"}; border-radius: ${({ borderRadius }) => borderRadius}; + display: ${({ useAutoLayout }) => (useAutoLayout ? "flex" : "block")}; + flex-direction: ${({ direction }) => + direction === LayoutDirection.Vertical ? "column" : "row"}; + justify-content: flex-start; + align-items: ${({ direction }) => + direction === LayoutDirection.Vertical ? "stretch" : "flex-start"}; + flex-wrap: wrap; + ${(props) => props.shouldScrollContents === true ? scrollContents @@ -99,6 +108,8 @@ function ContainerComponent(props: ContainerComponentProps) { "borderWidth", "borderRadius", "boxShadow", + "useAutoLayout", + "direction", ])} > @@ -119,6 +130,8 @@ export interface ContainerComponentProps selected?: boolean; focused?: boolean; minHeight?: number; + useAutoLayout?: boolean; + direction?: string; } export default ContainerComponent; diff --git a/app/client/src/widgets/ContainerWidget/widget/index.tsx b/app/client/src/widgets/ContainerWidget/widget/index.tsx index 976ee9976e78..372aaf35d416 100644 --- a/app/client/src/widgets/ContainerWidget/widget/index.tsx +++ b/app/client/src/widgets/ContainerWidget/widget/index.tsx @@ -179,6 +179,9 @@ class ContainerWidget extends BaseWidget< childWidgetData.canExtend = this.props.shouldScrollContents; childWidgetData.parentId = this.props.widgetId; + // Pass layout controls to children + childWidgetData.useAutoLayout = this.props.useAutoLayout; + childWidgetData.direction = this.props.direction; return WidgetFactory.createWidget(childWidgetData, this.props.renderMode); } From 90f96d39058a38a827e9b781e233bc7739aa192c Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 11 Jul 2022 21:37:39 -0400 Subject: [PATCH 003/708] wip --- app/client/src/components/constants.ts | 8 + .../editorComponents/DropTargetComponent.tsx | 1 + .../editorComponents/ResizableComponent.tsx | 4 +- .../src/resizable/resizenreflow/index.tsx | 3 +- app/client/src/utils/WidgetRegistry.tsx | 4 + .../widget/index.tsx | 23 ++- app/client/src/widgets/BaseWidget.tsx | 29 ++- app/client/src/widgets/CanvasWidget.tsx | 2 + .../ContainerWidget/component/index.tsx | 7 +- .../widgets/ContainerWidget/widget/index.tsx | 2 + .../VerticalLayoutWidget/component/index.tsx | 20 +++ .../widgets/VerticalLayoutWidget/constants.ts | 2 + .../src/widgets/VerticalLayoutWidget/icon.svg | 1 + .../src/widgets/VerticalLayoutWidget/index.ts | 45 +++++ .../VerticalLayoutWidget/widget/index.tsx | 167 ++++++++++++++++++ 15 files changed, 307 insertions(+), 11 deletions(-) create mode 100644 app/client/src/widgets/VerticalLayoutWidget/component/index.tsx create mode 100644 app/client/src/widgets/VerticalLayoutWidget/constants.ts create mode 100644 app/client/src/widgets/VerticalLayoutWidget/icon.svg create mode 100644 app/client/src/widgets/VerticalLayoutWidget/index.ts create mode 100644 app/client/src/widgets/VerticalLayoutWidget/widget/index.tsx diff --git a/app/client/src/components/constants.ts b/app/client/src/components/constants.ts index 1d5d89c52fa8..9134aaa7d068 100644 --- a/app/client/src/components/constants.ts +++ b/app/client/src/components/constants.ts @@ -115,3 +115,11 @@ export enum LayoutDirection { Horizontal = "Horizontal", Vertical = "Vertical", } + +export enum JustifyContent { + FlexStart = "flex-start", + Center = "center", + SpaceAround = "space-around", + SpaceBetween = "space-between", + SpaceEvenly = "space-evenly", +} diff --git a/app/client/src/components/editorComponents/DropTargetComponent.tsx b/app/client/src/components/editorComponents/DropTargetComponent.tsx index f31523d42d3e..fdd6efc62720 100644 --- a/app/client/src/components/editorComponents/DropTargetComponent.tsx +++ b/app/client/src/components/editorComponents/DropTargetComponent.tsx @@ -180,6 +180,7 @@ export function DropTargetComponent(props: DropTargetComponentProps) { updateDropTargetRows, }; }, [updateDropTargetRows, occupiedSpacesByChildren]); + return ( )); - + console.log("========"); + console.log(props); const widgetWidth = reflowedPosition?.width === undefined ? newDimensions.width diff --git a/app/client/src/utils/WidgetRegistry.tsx b/app/client/src/utils/WidgetRegistry.tsx index b1ccccd4c804..1aa94bf792b3 100644 --- a/app/client/src/utils/WidgetRegistry.tsx +++ b/app/client/src/utils/WidgetRegistry.tsx @@ -147,6 +147,9 @@ import ProgressWidget, { import AutoLayoutContainerWidget, { CONFIG as AUTO_LAYOUT_CONFIG, } from "widgets/AutoLayoutContainerWidget"; +import VerticalLayoutWidget, { + CONFIG as VERTICAL_LAYOUT_CONFIG, +} from "widgets/VerticalLayoutWidget"; import { registerWidget } from "./WidgetRegisterHelpers"; import { WidgetConfiguration } from "widgets/constants"; @@ -196,6 +199,7 @@ export const ALL_WIDGETS_AND_CONFIG = [ [CurrencyInputWidget, CURRENCY_INPUT_WIDGET_V2_CONFIG], [JSONFormWidget, JSON_FORM_WIDGET_CONFIG], [AutoLayoutContainerWidget, AUTO_LAYOUT_CONFIG], + [VerticalLayoutWidget, VERTICAL_LAYOUT_CONFIG], //Deprecated Widgets [InputWidget, INPUT_WIDGET_CONFIG], diff --git a/app/client/src/widgets/AutoLayoutContainerWidget/widget/index.tsx b/app/client/src/widgets/AutoLayoutContainerWidget/widget/index.tsx index b2c2bdcc6d18..86d737bc6b22 100644 --- a/app/client/src/widgets/AutoLayoutContainerWidget/widget/index.tsx +++ b/app/client/src/widgets/AutoLayoutContainerWidget/widget/index.tsx @@ -6,7 +6,7 @@ import WidgetFactory, { DerivedPropertiesMap } from "utils/WidgetFactory"; import AutoLayoutContainerComponent from "../component"; import { ValidationTypes } from "constants/WidgetValidation"; -import { LayoutDirection } from "components/constants"; +import { JustifyContent, LayoutDirection } from "components/constants"; import { ContainerStyle } from "widgets/ContainerWidget/component"; class AutoLayoutContainerWidget extends BaseWidget< @@ -37,7 +37,7 @@ class AutoLayoutContainerWidget extends BaseWidget< !props?.useAutoLayout, children: [ { - helpText: "Controls the visibility of the widget", + helpText: "Controls the direction of layout", propertyName: "direction", label: "Direction", controlType: "DROP_DOWN", @@ -51,6 +51,24 @@ class AutoLayoutContainerWidget extends BaseWidget< isTriggerProperty: false, validation: { type: ValidationTypes.TEXT }, }, + { + helpText: "Controls alignment of the content", + propertyName: "justifyContent", + label: "Align content", + controlType: "DROP_DOWN", + defaultValue: JustifyContent.FlexStart, + options: [ + { label: "Flex start", value: JustifyContent.FlexStart }, + { label: "Center", value: JustifyContent.Center }, + { label: "Space around", value: JustifyContent.SpaceAround }, + { label: "Space between", value: JustifyContent.SpaceBetween }, + { label: "Space evently", value: JustifyContent.SpaceEvenly }, + ], + isJSConvertible: false, + isBindProperty: false, + isTriggerProperty: false, + validation: { type: ValidationTypes.TEXT }, + }, ], }, { @@ -181,6 +199,7 @@ class AutoLayoutContainerWidget extends BaseWidget< // Pass layout controls to children childWidgetData.useAutoLayout = this.props.useAutoLayout; childWidgetData.direction = this.props.direction; + childWidgetData.justifyContent = this.props.justifyContent; return WidgetFactory.createWidget(childWidgetData, this.props.renderMode); } diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index 7d6efd1ed574..5ae6738dd3f2 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -215,6 +215,10 @@ abstract class BaseWidget< * @param content */ makeResizable(content: ReactNode) { + const { componentHeight, componentWidth } = this.getComponentDimensions(); + console.log( + `widgetName: ${this.props.widgetName} & width: ${componentWidth}`, + ); return ( {content}; + let size = {}; + if (this.props.autoLayout && this.props.alignItems === "stretch") { + size = { + width: "100%", + height: "auto", + }; + } + return ( +
+ {content} +
+ ); } private getWidgetView(): ReactNode { let content: ReactNode; - console.log(this.props); switch (this.props.renderMode) { case RenderModes.CANVAS: content = this.getCanvasView(); @@ -342,7 +361,11 @@ abstract class BaseWidget< content = this.addPreventInteractionOverlay(content); content = this.addOverlayComments(content); if (!this.props.detachFromLayout) { - if (!this.props.resizeDisabled) content = this.makeResizable(content); + if ( + !this.props.resizeDisabled && + !(this.props.useAutoLayout && this.props.alignItems === "stretch") + ) + content = this.makeResizable(content); content = this.showWidgetName(content); content = this.makeDraggable(content); content = this.makeSnipeable(content); diff --git a/app/client/src/widgets/CanvasWidget.tsx b/app/client/src/widgets/CanvasWidget.tsx index 6e2f212676de..a471b0d0eaf6 100644 --- a/app/client/src/widgets/CanvasWidget.tsx +++ b/app/client/src/widgets/CanvasWidget.tsx @@ -68,6 +68,8 @@ class CanvasWidget extends ContainerWidget { if (this.props.widgetName !== "MainContainer") { childWidgetData.useAutoLayout = this.props.useAutoLayout; childWidgetData.direction = this.props.direction; + childWidgetData.justifyContent = this.props.justifyContent; + childWidgetData.alignItems = this.props.alignItems; } return WidgetFactory.createWidget(childWidgetData, this.props.renderMode); diff --git a/app/client/src/widgets/ContainerWidget/component/index.tsx b/app/client/src/widgets/ContainerWidget/component/index.tsx index bc82468b35f8..8d67dc98a484 100644 --- a/app/client/src/widgets/ContainerWidget/component/index.tsx +++ b/app/client/src/widgets/ContainerWidget/component/index.tsx @@ -35,9 +35,8 @@ const StyledContainerComponent = styled.div< display: ${({ useAutoLayout }) => (useAutoLayout ? "flex" : "block")}; flex-direction: ${({ direction }) => direction === LayoutDirection.Vertical ? "column" : "row"}; - justify-content: flex-start; - align-items: ${({ direction }) => - direction === LayoutDirection.Vertical ? "stretch" : "flex-start"}; + justify-content: ${({ justifyContent }) => justifyContent || "flex-start"}; + align-items: ${({ alignItems }) => alignItems || "flex-start"}; flex-wrap: wrap; ${(props) => @@ -132,6 +131,8 @@ export interface ContainerComponentProps minHeight?: number; useAutoLayout?: boolean; direction?: string; + justifyContent?: string; + alignItems?: string; } export default ContainerComponent; diff --git a/app/client/src/widgets/ContainerWidget/widget/index.tsx b/app/client/src/widgets/ContainerWidget/widget/index.tsx index 372aaf35d416..d4835775bf47 100644 --- a/app/client/src/widgets/ContainerWidget/widget/index.tsx +++ b/app/client/src/widgets/ContainerWidget/widget/index.tsx @@ -182,6 +182,8 @@ class ContainerWidget extends BaseWidget< // Pass layout controls to children childWidgetData.useAutoLayout = this.props.useAutoLayout; childWidgetData.direction = this.props.direction; + childWidgetData.justifyContent = this.props.justifyContent; + childWidgetData.alignItems = this.props.alignItems; return WidgetFactory.createWidget(childWidgetData, this.props.renderMode); } diff --git a/app/client/src/widgets/VerticalLayoutWidget/component/index.tsx b/app/client/src/widgets/VerticalLayoutWidget/component/index.tsx new file mode 100644 index 000000000000..b00dda1a4c96 --- /dev/null +++ b/app/client/src/widgets/VerticalLayoutWidget/component/index.tsx @@ -0,0 +1,20 @@ +import React, { ReactNode } from "react"; +import styled from "styled-components"; + +const Container = styled.div` + display: flex; + flex-direction: column; + justify-content: space-around; + align-items: center; +`; + +function VerticalLayoutComponent(props: VerticalLayoutComponentProps) { + return {props.children}; +} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface VerticalLayoutComponentProps { + children?: ReactNode; +} + +export default VerticalLayoutComponent; diff --git a/app/client/src/widgets/VerticalLayoutWidget/constants.ts b/app/client/src/widgets/VerticalLayoutWidget/constants.ts new file mode 100644 index 000000000000..2138a2fe7129 --- /dev/null +++ b/app/client/src/widgets/VerticalLayoutWidget/constants.ts @@ -0,0 +1,2 @@ +// This file contains common constants which can be used across the widget configuration file (index.ts), widget and component folders. +export const VERTICALLAYOUT_WIDGET_CONSTANT = ""; diff --git a/app/client/src/widgets/VerticalLayoutWidget/icon.svg b/app/client/src/widgets/VerticalLayoutWidget/icon.svg new file mode 100644 index 000000000000..e97d99915ab8 --- /dev/null +++ b/app/client/src/widgets/VerticalLayoutWidget/icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/client/src/widgets/VerticalLayoutWidget/index.ts b/app/client/src/widgets/VerticalLayoutWidget/index.ts new file mode 100644 index 000000000000..330b9b7d2981 --- /dev/null +++ b/app/client/src/widgets/VerticalLayoutWidget/index.ts @@ -0,0 +1,45 @@ +import Widget from "./widget"; +import IconSVG from "./icon.svg"; +import { ButtonBoxShadowTypes } from "components/constants"; + +export const CONFIG = { + type: Widget.getWidgetType(), + name: "Vertical Layout", // The display name which will be made in uppercase and show in the widgets panel ( can have spaces ) + iconSVG: IconSVG, + needsMeta: false, // Defines if this widget adds any meta properties + isCanvas: true, // Defines if this widget has a canvas within in which we can drop other widgets + defaults: { + widgetName: "VerticalLayout", + rows: 40, + columns: 24, + version: 1, + containerStyle: "card", + borderColor: "transparent", + borderWidth: "0", + boxShadow: ButtonBoxShadowTypes.NONE, + animateLoading: true, + children: [], + blueprint: { + view: [ + { + type: "CANVAS_WIDGET", + position: { top: 0, left: 0 }, + props: { + containerStyle: "none", + canExtend: false, + detachFromLayout: true, + children: [], + }, + }, + ], + }, + }, + properties: { + derived: Widget.getDerivedPropertiesMap(), + default: Widget.getDefaultPropertiesMap(), + meta: Widget.getMetaPropertiesMap(), + config: Widget.getPropertyPaneConfig(), + }, +}; + +export default Widget; diff --git a/app/client/src/widgets/VerticalLayoutWidget/widget/index.tsx b/app/client/src/widgets/VerticalLayoutWidget/widget/index.tsx new file mode 100644 index 000000000000..3084b55a66fc --- /dev/null +++ b/app/client/src/widgets/VerticalLayoutWidget/widget/index.tsx @@ -0,0 +1,167 @@ +import React from "react"; +import styled from "styled-components"; +import { compact, map, sortBy } from "lodash"; + +import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; +import WidgetFactory, { DerivedPropertiesMap } from "utils/WidgetFactory"; + +import VerticalLayoutComponent from "../component"; +import { ContainerStyle } from "widgets/ContainerWidget/component"; +import { LayoutDirection } from "components/constants"; +import { CanvasDraggingArena } from "pages/common/CanvasArenas/CanvasDraggingArena"; +import { CanvasSelectionArena } from "pages/common/CanvasArenas/CanvasSelectionArena"; +import { + CONTAINER_GRID_PADDING, + GridDefaults, + MAIN_CONTAINER_WIDGET_ID, + WIDGET_PADDING, +} from "constants/WidgetConstants"; +import WidgetsMultiSelectBox from "pages/Editor/WidgetsMultiSelectBox"; +import { getCanvasSnapRows } from "utils/WidgetPropsUtils"; + +class VerticalLayoutWidget extends BaseWidget< + VerticalLayoutWidgetProps, + WidgetState +> { + constructor(props: VerticalLayoutWidgetProps) { + super(props); + this.renderChildWidget = this.renderChildWidget.bind(this); + } + + static getPropertyPaneConfig() { + return []; + } + + static getDerivedPropertiesMap(): DerivedPropertiesMap { + return {}; + } + + static getDefaultPropertiesMap(): Record { + return {}; + } + + static getMetaPropertiesMap(): Record { + return {}; + } + + getSnapSpaces = () => { + const { componentWidth } = this.getComponentDimensions(); + // For all widgets inside a container, we remove both container padding as well as widget padding from component width + let padding = (CONTAINER_GRID_PADDING + WIDGET_PADDING) * 2; + if ( + this.props.widgetId === MAIN_CONTAINER_WIDGET_ID || + this.props.type === "CONTAINER_WIDGET" + ) { + //For MainContainer and any Container Widget padding doesn't exist coz there is already container padding. + padding = CONTAINER_GRID_PADDING * 2; + } + if (this.props.noPad) { + // Widgets like ListWidget choose to have no container padding so will only have widget padding + padding = WIDGET_PADDING * 2; + } + let width = componentWidth; + width -= padding; + return { + snapRowSpace: GridDefaults.DEFAULT_GRID_ROW_HEIGHT, + snapColumnSpace: componentWidth + ? width / GridDefaults.DEFAULT_GRID_COLUMNS + : 0, + }; + }; + + renderChildWidget(childWidgetData: WidgetProps): React.ReactNode { + // For now, isVisible prop defines whether to render a detached widget + if (childWidgetData.detachFromLayout && !childWidgetData.isVisible) { + return null; + } + + const { componentHeight, componentWidth } = this.getComponentDimensions(); + + childWidgetData.rightColumn = componentWidth; + childWidgetData.bottomRow = this.props.shouldScrollContents + ? childWidgetData.bottomRow + : componentHeight; + childWidgetData.minHeight = componentHeight; + childWidgetData.isVisible = this.props.isVisible; + childWidgetData.shouldScrollContents = false; + childWidgetData.canExtend = this.props.shouldScrollContents; + + childWidgetData.parentId = this.props.widgetId; + // Pass layout controls to children + const layoutProps = { + useAutoLayout: true, + direction: LayoutDirection.Vertical, + justifyContent: "space-around", + alignItems: "stretch", + }; + + return WidgetFactory.createWidget( + { ...childWidgetData, ...layoutProps }, + this.props.renderMode, + ); + } + + renderChildren = () => { + return map( + // sort by row so stacking context is correct + // TODO(abhinav): This is hacky. The stacking context should increase for widgets rendered top to bottom, always. + // Figure out a way in which the stacking context is consistent. + sortBy(compact(this.props.children), (child) => child.topRow), + this.renderChildWidget, + ); + }; + + getPageView() { + const snapRows = getCanvasSnapRows( + this.props.bottomRow, + this.props.canExtend, + ); + return ( + + {this.props.type === "CANVAS_WIDGET" && ( + <> + + + + )} + + {/* without the wrapping div onClick events are triggered twice */} + <>{this.renderChildren()} + + ); + } + + static getWidgetType(): string { + return "VERTICALLAYOUT_WIDGET"; + } +} + +export interface VerticalLayoutWidgetProps + extends WidgetProps { + children?: T[]; + containerStyle?: ContainerStyle; + shouldScrollContents?: boolean; + noPad?: boolean; +} + +export default VerticalLayoutWidget; From 7b02aa7a1b4f3a09026704007889923d843536ba Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 12 Jul 2022 08:01:06 -0400 Subject: [PATCH 004/708] add margins --- app/client/src/widgets/BaseWidget.tsx | 19 ++++++++++++++++++- .../VerticalLayoutWidget/component/index.tsx | 2 +- .../VerticalLayoutWidget/widget/index.tsx | 2 +- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index 5ae6738dd3f2..b7ecedcfe080 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -39,6 +39,7 @@ import PreventInteractionsOverlay from "components/editorComponents/PreventInter import AppsmithConsole from "utils/AppsmithConsole"; import { ENTITY_TYPE } from "entities/AppsmithConsole"; import PreviewModeComponent from "components/editorComponents/PreviewModeComponent"; +import { LayoutDirection } from "components/constants"; /*** * BaseWidget @@ -333,18 +334,34 @@ abstract class BaseWidget< } addAutoLayoutWrapper(content: ReactNode) { - let size = {}; + let size = {}, + margin = {}; if (this.props.autoLayout && this.props.alignItems === "stretch") { size = { width: "100%", height: "auto", }; } + if ( + this.props.useAutoLayout && + this.props.direction === LayoutDirection.Vertical + ) { + margin = { + marginTop: 8, + marginBottom: 4, + }; + } else { + margin = { + marginLeft: 8, + marginRight: 8, + }; + } return (
{content} diff --git a/app/client/src/widgets/VerticalLayoutWidget/component/index.tsx b/app/client/src/widgets/VerticalLayoutWidget/component/index.tsx index b00dda1a4c96..1e7302c4fed7 100644 --- a/app/client/src/widgets/VerticalLayoutWidget/component/index.tsx +++ b/app/client/src/widgets/VerticalLayoutWidget/component/index.tsx @@ -4,7 +4,7 @@ import styled from "styled-components"; const Container = styled.div` display: flex; flex-direction: column; - justify-content: space-around; + justify-content: flex-start; align-items: center; `; diff --git a/app/client/src/widgets/VerticalLayoutWidget/widget/index.tsx b/app/client/src/widgets/VerticalLayoutWidget/widget/index.tsx index 3084b55a66fc..e3a5001d102f 100644 --- a/app/client/src/widgets/VerticalLayoutWidget/widget/index.tsx +++ b/app/client/src/widgets/VerticalLayoutWidget/widget/index.tsx @@ -91,7 +91,7 @@ class VerticalLayoutWidget extends BaseWidget< const layoutProps = { useAutoLayout: true, direction: LayoutDirection.Vertical, - justifyContent: "space-around", + justifyContent: "flex-start", alignItems: "stretch", }; From dcc4298299a0638ab5b62fd554400f5ffeec8028 Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 19 Jul 2022 12:39:13 -0400 Subject: [PATCH 005/708] wip --- app/client/package.json | 2 +- .../src/components/AutoLayoutWrapper.tsx | 70 ++++++++++++++++ .../editorComponents/DraggableComponent.tsx | 1 + .../editorComponents/ResizableComponent.tsx | 4 +- .../CanvasArenas/CanvasDraggingArena.tsx | 30 ++++--- .../common/CanvasArenas/StickyCanvasArena.tsx | 13 ++- .../hooks/useBlocksToBeDraggedOnCanvas.ts | 16 ++-- .../CanvasArenas/hooks/useCanvasDragging.ts | 43 +++++++++- .../src/resizable/resizenreflow/index.tsx | 3 +- .../widget/index.tsx | 21 ++++- app/client/src/widgets/BaseWidget.tsx | 80 +++++++++++-------- .../ContainerWidget/component/index.tsx | 48 ++++++++++- .../widgets/ContainerWidget/widget/index.tsx | 3 + 13 files changed, 279 insertions(+), 55 deletions(-) create mode 100644 app/client/src/components/AutoLayoutWrapper.tsx diff --git a/app/client/package.json b/app/client/package.json index 30bacef72d59..9da6b48cbd76 100644 --- a/app/client/package.json +++ b/app/client/package.json @@ -165,7 +165,7 @@ }, "scripts": { "analyze": "yarn cra-bundle-analyzer", - "start": "BROWSER=none EXTEND_ESLINT=true REACT_APP_ENVIRONMENT=DEVELOPMENT REACT_APP_CLIENT_LOG_LEVEL=debug HOST=dev.appsmith.com craco start", + "start": "BROWSER=none EXTEND_ESLINT=true REACT_APP_ENVIRONMENT=DEVELOPMENT REACT_APP_CLIENT_LOG_LEVEL=debug HOST=dev.appsmith.com NODE_OPTIONS='--max-old-space-size=8192' craco start", "build": "./build.sh", "build-local": "craco --max-old-space-size=4096 build --config craco.build.config.js", "build-staging": "REACT_APP_ENVIRONMENT=STAGING craco --max-old-space-size=4096 build --config craco.build.config.js", diff --git a/app/client/src/components/AutoLayoutWrapper.tsx b/app/client/src/components/AutoLayoutWrapper.tsx new file mode 100644 index 000000000000..6ea4f85c3b75 --- /dev/null +++ b/app/client/src/components/AutoLayoutWrapper.tsx @@ -0,0 +1,70 @@ +import { WidgetType } from "constants/WidgetConstants"; +import styled, { css } from "styled-components"; +import React, { ReactNode, useCallback } from "react"; +import { useClickToSelectWidget } from "utils/hooks/useClickToSelectWidget"; +import { LayoutDirection } from "./constants"; + +export type AutoLayoutProps = { + children: ReactNode; + widgetId: string; + widgetType: WidgetType; + useAutoLayout?: boolean; + alignItems: string; + direction?: LayoutDirection; + parentId?: string; +}; + +const AutoLayout = styled("div")<{ styles: any }>` + position: unset; + margin: 8px; + ${({ styles }) => + styles.useAutoLayout && styles.alignItems === "stretch" + ? css` + width: calc(100% - 16px); + height: auto; + ` + : ""} +`; + +export function AutoLayoutWrapper(props: AutoLayoutProps) { + const clickToSelectWidget = useClickToSelectWidget(); + let size = {}, + margin = {}; + if (props.useAutoLayout && props.alignItems === "stretch") { + size = { + width: "100%", + height: "auto", + }; + } + if (props.useAutoLayout && props.direction === LayoutDirection.Vertical) { + margin = { + marginTop: 8, + marginBottom: 4, + }; + } else { + margin = { + marginLeft: 8, + marginRight: 8, + }; + } + const onClickFn = useCallback( + (e) => { + clickToSelectWidget(e, props.widgetId); + }, + [props.widgetId, clickToSelectWidget], + ); + + return ( + + {props.children} + + ); +} diff --git a/app/client/src/components/editorComponents/DraggableComponent.tsx b/app/client/src/components/editorComponents/DraggableComponent.tsx index d0867b5993fc..05ac2ecbd9f1 100644 --- a/app/client/src/components/editorComponents/DraggableComponent.tsx +++ b/app/client/src/components/editorComponents/DraggableComponent.tsx @@ -35,6 +35,7 @@ const WidgetBoundaries = styled.div` border: 1px dashed ${(props) => getColorWithOpacity(props.theme.colors.textAnchor, 0.5)}; pointer-events: none; + z-index: 1000; `; type DraggableComponentProps = WidgetProps; diff --git a/app/client/src/components/editorComponents/ResizableComponent.tsx b/app/client/src/components/editorComponents/ResizableComponent.tsx index 05979c4e904e..f23a968f3148 100644 --- a/app/client/src/components/editorComponents/ResizableComponent.tsx +++ b/app/client/src/components/editorComponents/ResizableComponent.tsx @@ -271,12 +271,14 @@ export const ResizableComponent = memo(function ResizableComponent( bottom: props.bottomRow, right: props.rightColumn, }; + // console.log(`${props.widgetName} =========`); + // console.log(originalPositions); const updateBottomRow = (bottom: number) => { if (props.parentId) { updateDropTargetRows && updateDropTargetRows([props.parentId], bottom); } }; - console.log(props); + return ( { @@ -40,17 +42,25 @@ export function CanvasDraggingArena({ const slidingArenaRef = React.useRef(null); const stickyCanvasRef = React.useRef(null); - const { showCanvas } = useCanvasDragging(slidingArenaRef, stickyCanvasRef, { - canExtend, - dropDisabled, - noPad, - parentId, - snapColumnSpace, - snapRows, - snapRowSpace, - widgetId, - }); + const dropPositionRef = React.useRef(null); + const { showCanvas } = useCanvasDragging( + dropPositionRef, + slidingArenaRef, + stickyCanvasRef, + { + canExtend, + dropDisabled, + noPad, + parentId, + snapColumnSpace, + snapRows, + snapRowSpace, + useAutoLayout, + widgetId, + }, + ); const canvasRef = React.useRef({ + dropPositionRef, stickyCanvasRef, slidingArenaRef, }); diff --git a/app/client/src/pages/common/CanvasArenas/StickyCanvasArena.tsx b/app/client/src/pages/common/CanvasArenas/StickyCanvasArena.tsx index 1da7a6fa2361..1b64fa856c33 100644 --- a/app/client/src/pages/common/CanvasArenas/StickyCanvasArena.tsx +++ b/app/client/src/pages/common/CanvasArenas/StickyCanvasArena.tsx @@ -16,6 +16,7 @@ interface StickyCanvasArenaProps { } interface StickyCanvasArenaRef { + dropPositionRef: RefObject; stickyCanvasRef: RefObject; slidingArenaRef: RefObject; } @@ -46,7 +47,7 @@ export const StickyCanvasArena = forwardRef( snapRows, snapRowSpace, } = props; - const { slidingArenaRef, stickyCanvasRef } = ref.current; + const { dropPositionRef, slidingArenaRef, stickyCanvasRef } = ref.current; const interSectionObserver = useRef( new IntersectionObserver((entries) => { @@ -130,6 +131,16 @@ export const StickyCanvasArena = forwardRef( paddingBottom={canvasPadding} ref={slidingArenaRef} /> +
); }, diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts b/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts index d0ab292d73bc..a92bbb8b71dd 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts @@ -74,6 +74,7 @@ export const useBlocksToBeDraggedOnCanvas = ({ snapColumnSpace, snapRows, snapRowSpace, + useAutoLayout, widgetId, }: CanvasDraggingArenaProps) => { const dispatch = useDispatch(); @@ -220,9 +221,11 @@ export const useBlocksToBeDraggedOnCanvas = ({ { top: 0, left: 0, - width: newWidget.columns * snapColumnSpace, + width: useAutoLayout + ? 64 * snapColumnSpace + : newWidget.columns * snapColumnSpace, height: newWidget.rows * snapRowSpace, - columnWidth: newWidget.columns, + columnWidth: useAutoLayout ? 64 : newWidget.columns, rowHeight: newWidget.rows, widgetId: newWidget.widgetId, detachFromLayout: newWidget.detachFromLayout, @@ -248,9 +251,11 @@ export const useBlocksToBeDraggedOnCanvas = ({ blocksToDraw: draggingSpaces.map((each) => ({ top: each.top * snapRowSpace + containerPadding, left: each.left * snapColumnSpace + containerPadding, - width: (each.right - each.left) * snapColumnSpace, + width: useAutoLayout + ? 64 * snapColumnSpace + : (each.right - each.left) * snapColumnSpace, height: (each.bottom - each.top) * snapRowSpace, - columnWidth: each.right - each.left, + columnWidth: useAutoLayout ? 64 : each.right - each.left, rowHeight: each.bottom - each.top, widgetId: each.id, isNotColliding: true, @@ -259,8 +264,9 @@ export const useBlocksToBeDraggedOnCanvas = ({ } }; const { blocksToDraw, draggingSpaces } = getBlocksToDraw(); - const dragCenterSpace: any = getDragCenterSpace(); + const dragCenterSpace: any = getDragCenterSpace(); + // get spaces occupied by unselected children const filteredChildOccupiedSpaces = childrenOccupiedSpaces.filter( (each) => !selectedWidgets.includes(each.id), ); diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index 849f1db654e0..e3a4385d3342 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -5,7 +5,7 @@ import { } from "constants/WidgetConstants"; import { debounce, isEmpty, throttle } from "lodash"; import { CanvasDraggingArenaProps } from "pages/common/CanvasArenas/CanvasDraggingArena"; -import { useEffect, useRef } from "react"; +import React, { useEffect, useRef } from "react"; import { useSelector } from "react-redux"; import { MovementLimitMap, @@ -29,6 +29,7 @@ import { } from "./useBlocksToBeDraggedOnCanvas"; import { useCanvasDragToScroll } from "./useCanvasDragToScroll"; import ContainerJumpMetrics from "./ContainerJumpMetric"; +import { remoteUrlInputValue } from "actions/gitSyncActions"; export interface XYCord { x: number; @@ -45,6 +46,7 @@ const containerJumpThresholdMetrics = new ContainerJumpMetrics<{ }>(); export const useCanvasDragging = ( + dropPositionRef: React.RefObject, slidingArenaRef: React.RefObject, stickyCanvasRef: React.RefObject, { @@ -54,6 +56,7 @@ export const useCanvasDragging = ( snapColumnSpace, snapRows, snapRowSpace, + useAutoLayout, widgetId, }: CanvasDraggingArenaProps, ) => { @@ -87,6 +90,7 @@ export const useCanvasDragging = ( snapColumnSpace, snapRows, snapRowSpace, + useAutoLayout, widgetId, }); const gridProps = { @@ -98,6 +102,20 @@ export const useCanvasDragging = ( const reflow = useRef(); reflow.current = useReflow(draggingSpaces, widgetId || "", gridProps); + const offsets: number[] = []; + if (useAutoLayout) { + const els = document.querySelectorAll(`.${widgetId}-auto-layout`); + if (els && els.length) { + els.forEach((el) => { + offsets.push((el as any).offsetTop); + }); + offsets.push( + (els[els.length - 1] as any).offsetTop + + els[els.length - 1].clientHeight + + 8, + ); + } + } const { setDraggingCanvas, @@ -242,6 +260,9 @@ export const useCanvasDragging = ( }; if (isDragging) { const startPoints = defaultHandlePositions; + /** + * On mouse up, calculate the top, left, bottom and right positions for each of the reflowed widgets + */ const onMouseUp = () => { if (isDragging && canvasIsDragging) { const { movementMap: reflowingWidgets } = currentReflowParams; @@ -253,6 +274,7 @@ export const useCanvasDragging = ( reflowedWidget.X !== undefined && (Math.abs(reflowedWidget.X) || reflowedWidget.width) ) { + // Could it be negative if the widget has been moved to the left? const movement = reflowedWidget.X / snapColumnSpace; const newWidth = reflowedWidget.width ? reflowedWidget.width / snapColumnSpace @@ -317,6 +339,7 @@ export const useCanvasDragging = ( acceleration, speed, } = containerJumpThresholdMetrics.getMetrics(); + logContainerJump(widgetId, speed, acceleration); containerJumpThresholdMetrics.clearMetrics(); // we can just use canvasIsDragging but this is needed to render the relative DragLayerComponent @@ -526,6 +549,7 @@ export const useCanvasDragging = ( renderNewRows(delta); } else if (!isUpdatingRows) { triggerReflow(e, firstMove); + highlightDropPosition(e); renderBlocks(); } scrollObj.lastMouseMoveEvent = { @@ -538,6 +562,21 @@ export const useCanvasDragging = ( onFirstMoveOnCanvas(e); } }; + const highlightDropPosition = (e: any) => { + if (!offsets || !offsets.length) return; + const pos = e.offsetY; + // console.log(e); + // console.log(pos); + const arr = offsets.sort((a, b) => { + return Math.abs(a - pos) - Math.abs(b - pos); + }); + console.log(`#### ref: ${dropPositionRef.current}`); + if (dropPositionRef && dropPositionRef.current) { + dropPositionRef.current.style.opacity = "1"; + dropPositionRef.current.style.top = arr[0] - 6 + "px"; + } + console.log(arr); + }; const renderNewRows = debounce((delta) => { isUpdatingRows = true; if (slidingArenaRef.current && stickyCanvasRef.current) { @@ -599,6 +638,8 @@ export const useCanvasDragging = ( canvasIsDragging && stickyCanvasRef.current ) { + // console.log(`${widgetId} =======`); + // console.log(currentDirection); const canvasCtx: any = stickyCanvasRef.current.getContext("2d"); canvasCtx.save(); canvasCtx.clearRect( diff --git a/app/client/src/resizable/resizenreflow/index.tsx b/app/client/src/resizable/resizenreflow/index.tsx index 30ea912ff144..90147f84cc28 100644 --- a/app/client/src/resizable/resizenreflow/index.tsx +++ b/app/client/src/resizable/resizenreflow/index.tsx @@ -473,8 +473,7 @@ export function ReflowResizable(props: ResizableProps) { snapGrid={props.snapGrid} /> )); - console.log("========"); - console.log(props); + const widgetWidth = reflowedPosition?.width === undefined ? newDimensions.width diff --git a/app/client/src/widgets/AutoLayoutContainerWidget/widget/index.tsx b/app/client/src/widgets/AutoLayoutContainerWidget/widget/index.tsx index 86d737bc6b22..816d130a0b85 100644 --- a/app/client/src/widgets/AutoLayoutContainerWidget/widget/index.tsx +++ b/app/client/src/widgets/AutoLayoutContainerWidget/widget/index.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { Context, createContext } from "react"; import { compact, map, sortBy } from "lodash"; import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; @@ -9,12 +9,27 @@ import { ValidationTypes } from "constants/WidgetValidation"; import { JustifyContent, LayoutDirection } from "components/constants"; import { ContainerStyle } from "widgets/ContainerWidget/component"; +export const AutoLayoutContext: Context<{ + useAutoLayout?: boolean; + direction?: keyof typeof LayoutDirection; + justifyContent?: keyof typeof JustifyContent; + overflow?: string; +}> = createContext({}); + class AutoLayoutContainerWidget extends BaseWidget< AutoLayoutContainerWidgetProps, WidgetState > { constructor(props: AutoLayoutContainerWidgetProps) { super(props); + this.state = { + contextValue: { + useAutoLayout: false, + direction: LayoutDirection.Horizontal, + JustifyContent: JustifyContent.FlexStart, + overflow: "wrap", + }, + }; this.renderChildWidget = this.renderChildWidget.bind(this); } @@ -178,6 +193,10 @@ class AutoLayoutContainerWidget extends BaseWidget< return {}; } + // componentDidUpdate(prevProps: AutoLayoutContainerWidgetProps): void { + // if (prevProps.) + // } + renderChildWidget(childWidgetData: WidgetProps): React.ReactNode { // For now, isVisible prop defines whether to render a detached widget if (childWidgetData.detachFromLayout && !childWidgetData.isVisible) { diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index b7ecedcfe080..918520fa62f0 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -40,6 +40,7 @@ import AppsmithConsole from "utils/AppsmithConsole"; import { ENTITY_TYPE } from "entities/AppsmithConsole"; import PreviewModeComponent from "components/editorComponents/PreviewModeComponent"; import { LayoutDirection } from "components/constants"; +import { AutoLayoutWrapper } from "components/AutoLayoutWrapper"; /*** * BaseWidget @@ -217,9 +218,7 @@ abstract class BaseWidget< */ makeResizable(content: ReactNode) { const { componentHeight, componentWidth } = this.getComponentDimensions(); - console.log( - `widgetName: ${this.props.widgetName} & width: ${componentWidth}`, - ); + return ( + // {content} + //
+ // ); return ( -
{content} -
+ ); } private getWidgetView(): ReactNode { let content: ReactNode; + // console.log(`${this.props.widgetName} =========`); + // console.log(this.props); switch (this.props.renderMode) { case RenderModes.CANVAS: content = this.getCanvasView(); @@ -393,6 +407,7 @@ abstract class BaseWidget< ) { content = this.addAutoLayoutWrapper(content); } else content = this.makePositioned(content); + // content = this.makePositioned(content); } return content; @@ -515,6 +530,7 @@ export interface WidgetPositionProps extends WidgetRowCols { detachFromLayout?: boolean; noContainerOffset?: boolean; // This won't offset the child in parent useAutoLayout?: boolean; + direction?: LayoutDirection; } export const WIDGET_STATIC_PROPS = { diff --git a/app/client/src/widgets/ContainerWidget/component/index.tsx b/app/client/src/widgets/ContainerWidget/component/index.tsx index 8d67dc98a484..085a9482a845 100644 --- a/app/client/src/widgets/ContainerWidget/component/index.tsx +++ b/app/client/src/widgets/ContainerWidget/component/index.tsx @@ -1,4 +1,10 @@ -import React, { ReactNode, useRef, useEffect, RefObject } from "react"; +import React, { + ReactElement, + ReactNode, + useRef, + useEffect, + RefObject, +} from "react"; import styled, { css } from "styled-components"; import tinycolor from "tinycolor2"; import { invisible } from "constants/DefaultTheme"; @@ -77,6 +83,7 @@ function ContainerComponentWrapper(props: ContainerComponentProps) { } } }, [props.shouldScrollContents]); + return ( { +// console.log("drag end"); +// }} +// > +// +// {(provided) => ( +//
+// {(items[0] as any)?.map((item: any, index: number) => { +// return ( +// +// {(provided) => { +// return ( +//
+// {item} +//
+// ); +// }} +//
+// ); +// })} +// {provided.placeholder} +//
+// )} +//
+// +// ); +// } + function ContainerComponent(props: ContainerComponentProps) { useCanvasMinHeightUpdateHook(props.widgetId, props.minHeight); return props.widgetId === MAIN_CONTAINER_WIDGET_ID ? ( diff --git a/app/client/src/widgets/ContainerWidget/widget/index.tsx b/app/client/src/widgets/ContainerWidget/widget/index.tsx index d4835775bf47..1e5aeaee29b7 100644 --- a/app/client/src/widgets/ContainerWidget/widget/index.tsx +++ b/app/client/src/widgets/ContainerWidget/widget/index.tsx @@ -199,6 +199,8 @@ class ContainerWidget extends BaseWidget< }; renderAsContainerComponent(props: ContainerWidgetProps) { + // console.log(`${props.widgetName} =======`); + // console.log(props); const snapRows = getCanvasSnapRows(props.bottomRow, props.canExtend); return ( @@ -211,6 +213,7 @@ class ContainerWidget extends BaseWidget< noPad={this.props.noPad} parentId={props.parentId} snapRows={snapRows} + useAutoLayout={props.useAutoLayout} widgetId={props.widgetId} /> Date: Wed, 20 Jul 2022 16:37:39 -0400 Subject: [PATCH 006/708] working dnd --- .../src/ce/constants/ReduxActionConstants.tsx | 3 + .../src/components/AutoLayoutWrapper.tsx | 3 +- .../common/CanvasArenas/StickyCanvasArena.tsx | 2 +- .../hooks/useBlocksToBeDraggedOnCanvas.ts | 49 ++++++++ .../CanvasArenas/hooks/useCanvasDragging.ts | 54 +++++--- .../sagas/CanvasSagas/DraggingCanvasSagas.ts | 119 +++++++++++++++++- app/client/src/widgets/CanvasWidget.tsx | 3 + .../ContainerWidget/component/index.tsx | 67 ++++------ .../widgets/ContainerWidget/widget/index.tsx | 23 +++- .../VerticalLayoutWidget/widget/index.tsx | 4 +- 10 files changed, 256 insertions(+), 71 deletions(-) diff --git a/app/client/src/ce/constants/ReduxActionConstants.tsx b/app/client/src/ce/constants/ReduxActionConstants.tsx index 86a69e36c347..a6d535cbdc74 100644 --- a/app/client/src/ce/constants/ReduxActionConstants.tsx +++ b/app/client/src/ce/constants/ReduxActionConstants.tsx @@ -731,6 +731,9 @@ export const ReduxActionTypes = { INIT_TRIGGER_VALUES: "INIT_TRIGGER_VALUES", FETCH_TRIGGER_VALUES_INIT: "FETCH_TRIGGER_VALUES_INIT", FETCH_TRIGGER_VALUES_SUCCESS: "FETCH_TRIGGER_VALUES_SUCCESS", + + AUTOLAYOUT_REORDER_WIDGETS: "AUTOLAYOUT_REORDER_WIDGETS", + AUTOLAYOUT_ADD_NEW_WIDGETS: "AUTOLAYOUT_ADD_NEW_WIDGETS", }; export type ReduxActionType = typeof ReduxActionTypes[keyof typeof ReduxActionTypes]; diff --git a/app/client/src/components/AutoLayoutWrapper.tsx b/app/client/src/components/AutoLayoutWrapper.tsx index 6ea4f85c3b75..b3838f94b75e 100644 --- a/app/client/src/components/AutoLayoutWrapper.tsx +++ b/app/client/src/components/AutoLayoutWrapper.tsx @@ -22,6 +22,7 @@ const AutoLayout = styled("div")<{ styles: any }>` ? css` width: calc(100% - 16px); height: auto; + min-height: 30px; ` : ""} `; @@ -56,7 +57,7 @@ export function AutoLayoutWrapper(props: AutoLayoutProps) { return ( { if (isReflowing) dispatch(stopReflowAction()); }; + const updateChildrenPositions = ( + index: number, + drawingBlocks: WidgetDraggingBlock[], + ): void => { + // console.log("**********"); + // console.log(index); + // console.log(allWidgets); + // console.log(selectedWidgets); + // console.log(widgetId); + if (isNewWidget) addNewWidgetToAutoLayout(index, drawingBlocks); + dispatch({ + type: ReduxActionTypes.AUTOLAYOUT_REORDER_WIDGETS, + payload: { + index, + movedWidgets: selectedWidgets, + parentId: widgetId, + }, + }); + }; + const addNewWidgetToAutoLayout = ( + index: number, + drawingBlocks: WidgetDraggingBlock[], + ) => { + logContainerJumpOnDrop(); + const blocksToUpdate = drawingBlocks.map((each) => { + const updateWidgetParams = widgetOperationParams( + newWidget, + { x: 0, y: each.top }, + { x: 0, y: 0 }, + snapColumnSpace, + snapRowSpace, + newWidget.detachFromLayout ? MAIN_CONTAINER_WIDGET_ID : widgetId, + { width: each.width, height: each.height }, + ); + return { + ...each, + updateWidgetParams, + }; + }); + dispatch({ + type: ReduxActionTypes.AUTOLAYOUT_ADD_NEW_WIDGETS, + payload: { + index, + newWidget: blocksToUpdate[0].updateWidgetParams.payload, + parentId: widgetId, + }, + }); + }; const onDrop = ( drawingBlocks: WidgetDraggingBlock[], reflowedPositionsUpdatesWidgets: OccupiedSpace[], @@ -497,6 +545,7 @@ export const useBlocksToBeDraggedOnCanvas = ({ rowRef, stopReflowing, updateBottomRow, + updateChildrenPositions, updateRelativeRows, widgetOccupiedSpace: childrenOccupiedSpaces.filter( (each) => each.id === dragCenter?.widgetId, diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index e3a4385d3342..833dd0c1cab9 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -29,7 +29,6 @@ import { } from "./useBlocksToBeDraggedOnCanvas"; import { useCanvasDragToScroll } from "./useCanvasDragToScroll"; import ContainerJumpMetrics from "./ContainerJumpMetric"; -import { remoteUrlInputValue } from "actions/gitSyncActions"; export interface XYCord { x: number; @@ -83,6 +82,7 @@ export const useCanvasDragging = ( rowRef, stopReflowing, updateBottomRow, + updateChildrenPositions, updateRelativeRows, } = useBlocksToBeDraggedOnCanvas({ canExtend, @@ -103,11 +103,16 @@ export const useCanvasDragging = ( const reflow = useRef(); reflow.current = useReflow(draggingSpaces, widgetId || "", gridProps); const offsets: number[] = []; + const siblings: { [key: string]: number } = {}; if (useAutoLayout) { const els = document.querySelectorAll(`.${widgetId}-auto-layout`); - if (els && els.length) { + if (els && els.length && offsets.length !== els.length) { + const blocks = blocksToDraw.map((block) => block.widgetId); els.forEach((el) => { + const mClass = el.className.split("auto-layout-child-")[1]; + if (blocks && blocks.length && blocks.indexOf(mClass) !== -1) return; offsets.push((el as any).offsetTop); + siblings[mClass] = (el as any).offsetTop; }); offsets.push( (els[els.length - 1] as any).offsetTop + @@ -116,7 +121,6 @@ export const useCanvasDragging = ( ); } } - const { setDraggingCanvas, setDraggingNewWidget, @@ -301,8 +305,14 @@ export const useCanvasDragging = ( } return each; }); - - onDrop(currentRectanglesToDraw, reflowedPositionsUpdatesWidgets); + // console.log(currentRectanglesToDraw); + // console.log(reflowedPositionsUpdatesWidgets); + const pos = getDropPosition(currentRectanglesToDraw[0].top); + console.log(`pos: ${pos}`); + if (pos !== undefined && useAutoLayout) + updateChildrenPositions(pos, currentRectanglesToDraw); + else + onDrop(currentRectanglesToDraw, reflowedPositionsUpdatesWidgets); } startPoints.top = defaultHandlePositions.top; startPoints.left = defaultHandlePositions.left; @@ -563,19 +573,31 @@ export const useCanvasDragging = ( } }; const highlightDropPosition = (e: any) => { - if (!offsets || !offsets.length) return; - const pos = e.offsetY; + if (!useAutoLayout) return; + const pos: number | undefined = getHighlightPosition(e); + if (!pos) return; + // console.log(`#### ref: ${dropPositionRef.current}`); + if (dropPositionRef && dropPositionRef.current) { + dropPositionRef.current.style.opacity = "1"; + dropPositionRef.current.style.top = pos - 6 + "px"; + } + }; + const getHighlightPosition = (e: any, top?: number) => { + let base: number[] = []; + if (!offsets || !offsets.length) base = [8]; + else base = offsets; + const pos = e?.offsetY || top; // console.log(e); // console.log(pos); - const arr = offsets.sort((a, b) => { + const arr = [...base].sort((a, b) => { return Math.abs(a - pos) - Math.abs(b - pos); }); - console.log(`#### ref: ${dropPositionRef.current}`); - if (dropPositionRef && dropPositionRef.current) { - dropPositionRef.current.style.opacity = "1"; - dropPositionRef.current.style.top = arr[0] - 6 + "px"; - } - console.log(arr); + return arr[0]; + }; + const getDropPosition = (top: number): number | undefined => { + const pos = getHighlightPosition(null, top); + if (!pos) return; + return offsets.indexOf(pos); }; const renderNewRows = debounce((delta) => { isUpdatingRows = true; @@ -688,7 +710,9 @@ export const useCanvasDragging = ( ); canvasCtx.fillStyle = `${ - blockDimensions.isNotColliding ? "rgb(104, 113, 239, 0.6)" : "red" + blockDimensions.isNotColliding || useAutoLayout + ? "rgb(104, 113, 239, 0.6)" + : "red" }`; canvasCtx.fillRect( blockDimensions.left - diff --git a/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts b/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts index 5bcbaa486650..be00963702e0 100644 --- a/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts +++ b/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts @@ -10,7 +10,7 @@ import { } from "reducers/entityReducers/canvasWidgetsReducer"; import { all, call, put, select, takeLatest } from "redux-saga/effects"; import log from "loglevel"; -import { cloneDeep } from "lodash"; +import { cloneDeep, isNumber } from "lodash"; import { updateAndSaveLayout, WidgetAddChild } from "actions/pageActions"; import { calculateDropTargetRows } from "components/editorComponents/DropTargetUtils"; import { GridDefaults } from "constants/WidgetConstants"; @@ -36,6 +36,8 @@ export type WidgetMoveParams = { */ newParentId: string; allWidgets: CanvasWidgetsReduxState; + position?: number; + useAutoLayout?: boolean; }; export function* getCanvasSizeAfterWidgetMove( @@ -133,6 +135,8 @@ function* addWidgetAndMoveWidgetsSaga( } } +// function* update + function* addWidgetAndMoveWidgets( newWidget: WidgetAddChild, draggedBlocksToUpdate: WidgetDraggingUpdateParams[], @@ -304,6 +308,111 @@ function moveWidget(widgetMoveParams: WidgetMoveParams) { return widgets; } +function* autolayoutReorderSaga( + actionPayload: ReduxAction<{ + movedWidgets: string[]; + index: number; + parentId: string; + }>, +) { + const start = performance.now(); + + const { index, movedWidgets, parentId } = actionPayload.payload; + try { + const allWidgets: CanvasWidgetsReduxState = yield select(getWidgets); + if (!parentId || !movedWidgets || !movedWidgets.length) return; + const updatedWidgets = reorderAutolayoutChildren({ + movedWidgets, + index, + parentId, + allWidgets, + }); + yield put(updateAndSaveLayout(updatedWidgets)); + log.debug("reorder computations took", performance.now() - start, "ms"); + } catch (e) { + console.error(e); + } +} + +function reorderAutolayoutChildren(params: { + movedWidgets: string[]; + index: number; + parentId: string; + allWidgets: CanvasWidgetsReduxState; +}) { + const { allWidgets, index, movedWidgets, parentId } = params; + const widgets = Object.assign({}, allWidgets); + const selectedWidgets = [...movedWidgets]; + // Check if parent has changed + const orphans = selectedWidgets.filter( + (item) => widgets[item].parentId !== parentId, + ); + if (orphans && orphans.length) { + //parent has changed + + // update parent for children + orphans.forEach((item) => { + const prevParentId = widgets[item].parentId; + if (prevParentId !== undefined) { + const prevParent = Object.assign({}, widgets[prevParentId]); + if (prevParent.children && Array.isArray(prevParent.children)) { + const updatedPrevParent = { + ...prevParent, + children: prevParent.children.filter((each) => each !== item), + }; + widgets[prevParentId] = updatedPrevParent; + } + } + widgets[item] = { + ...widgets[item], + parentId: parentId, + }; + }); + } + + const items = [...(widgets[parentId].children || [])]; + // remove moved widegts from children + const newItems = items.filter((item) => movedWidgets.indexOf(item) === -1); + // calculate valid position for drop + const pos = index > newItems.length ? newItems.length : index; + widgets[parentId] = { + ...widgets[parentId], + children: [ + ...newItems.slice(0, pos), + ...movedWidgets, + ...newItems.slice(pos), + ], + }; + return widgets; +} + +function* addWidgetAndReorderSaga( + actionPayload: ReduxAction<{ + newWidget: WidgetAddChild; + index: number; + parentId: string; + }>, +) { + const start = performance.now(); + const { index, newWidget, parentId } = actionPayload.payload; + try { + const updatedWidgetsOnAddition: CanvasWidgetsReduxState = yield call( + getUpdateDslAfterCreatingChild, + { ...newWidget, widgetId: parentId }, + ); + const updatedWidgetsOnMove = reorderAutolayoutChildren({ + movedWidgets: [newWidget.newWidgetId], + index, + parentId, + allWidgets: updatedWidgetsOnAddition, + }); + yield put(updateAndSaveLayout(updatedWidgetsOnMove)); + log.debug("reorder computations took", performance.now() - start, "ms"); + } catch (e) { + console.error(e); + } +} + export default function* draggingCanvasSagas() { yield all([ takeLatest(ReduxActionTypes.WIDGETS_MOVE, moveWidgetsSaga), @@ -311,5 +420,13 @@ export default function* draggingCanvasSagas() { ReduxActionTypes.WIDGETS_ADD_CHILD_AND_MOVE, addWidgetAndMoveWidgetsSaga, ), + takeLatest( + ReduxActionTypes.AUTOLAYOUT_REORDER_WIDGETS, + autolayoutReorderSaga, + ), + takeLatest( + ReduxActionTypes.AUTOLAYOUT_ADD_NEW_WIDGETS, + addWidgetAndReorderSaga, + ), ]); } diff --git a/app/client/src/widgets/CanvasWidget.tsx b/app/client/src/widgets/CanvasWidget.tsx index a471b0d0eaf6..b431f994560a 100644 --- a/app/client/src/widgets/CanvasWidget.tsx +++ b/app/client/src/widgets/CanvasWidget.tsx @@ -70,6 +70,9 @@ class CanvasWidget extends ContainerWidget { childWidgetData.direction = this.props.direction; childWidgetData.justifyContent = this.props.justifyContent; childWidgetData.alignItems = this.props.alignItems; + // console.log( + // `${childWidgetData.widgetName} : ${childWidgetData.widgetId} =======`, + // ); } return WidgetFactory.createWidget(childWidgetData, this.props.renderMode); diff --git a/app/client/src/widgets/ContainerWidget/component/index.tsx b/app/client/src/widgets/ContainerWidget/component/index.tsx index 085a9482a845..5df89e1bccbd 100644 --- a/app/client/src/widgets/ContainerWidget/component/index.tsx +++ b/app/client/src/widgets/ContainerWidget/component/index.tsx @@ -17,7 +17,7 @@ import WidgetStyleContainer, { import { pick } from "lodash"; import { ComponentProps } from "widgets/BaseComponent"; import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; -import { LayoutDirection } from "components/constants"; +import { JustifyContent, LayoutDirection } from "components/constants"; const scrollContents = css` overflow-y: auto; @@ -38,13 +38,6 @@ const StyledContainerComponent = styled.div< props.selected ? "inset 0px 0px 0px 3px rgba(59,130,246,0.5)" : "none"}; border-radius: ${({ borderRadius }) => borderRadius}; - display: ${({ useAutoLayout }) => (useAutoLayout ? "flex" : "block")}; - flex-direction: ${({ direction }) => - direction === LayoutDirection.Vertical ? "column" : "row"}; - justify-content: ${({ justifyContent }) => justifyContent || "flex-start"}; - align-items: ${({ alignItems }) => alignItems || "flex-start"}; - flex-wrap: wrap; - ${(props) => props.shouldScrollContents === true ? scrollContents @@ -67,6 +60,23 @@ const StyledContainerComponent = styled.div< } `; +export const FlexContainer = styled.div<{ + useAutoLayout?: boolean; + direction?: string; + justifyContent?: JustifyContent; + alignItems?: string; +}>` + display: ${({ useAutoLayout }) => (useAutoLayout ? "flex" : "block")}; + flex-direction: ${({ direction }) => + direction === "Vertical" ? "column" : "row"}; + justify-content: ${({ justifyContent }) => justifyContent || "flex-start"}; + align-items: ${({ alignItems }) => alignItems || "flex-start"}; + flex-wrap: wrap; + + width: 100%; + height: 100%; +`; + function ContainerComponentWrapper(props: ContainerComponentProps) { const containerStyle = props.containerStyle || "card"; const containerRef: RefObject = useRef(null); @@ -100,44 +110,9 @@ function ContainerComponentWrapper(props: ContainerComponentProps) { ); } -// function DnDWrapper(items: ReactNode[], widgetId: string) { -// return ( -// { -// console.log("drag end"); -// }} -// > -// -// {(provided) => ( -//
-// {(items[0] as any)?.map((item: any, index: number) => { -// return ( -// -// {(provided) => { -// return ( -//
-// {item} -//
-// ); -// }} -//
-// ); -// })} -// {provided.placeholder} -//
-// )} -//
-//
-// ); -// } +export function FlexBox(props: any) { + return {props.children}; +} function ContainerComponent(props: ContainerComponentProps) { useCanvasMinHeightUpdateHook(props.widgetId, props.minHeight); diff --git a/app/client/src/widgets/ContainerWidget/widget/index.tsx b/app/client/src/widgets/ContainerWidget/widget/index.tsx index 1e5aeaee29b7..651bd9e0d02d 100644 --- a/app/client/src/widgets/ContainerWidget/widget/index.tsx +++ b/app/client/src/widgets/ContainerWidget/widget/index.tsx @@ -1,6 +1,6 @@ import React from "react"; -import ContainerComponent, { ContainerStyle } from "../component"; +import ContainerComponent, { ContainerStyle, FlexBox } from "../component"; import WidgetFactory, { DerivedPropertiesMap } from "utils/WidgetFactory"; import { CONTAINER_GRID_PADDING, @@ -193,14 +193,16 @@ class ContainerWidget extends BaseWidget< // sort by row so stacking context is correct // TODO(abhinav): This is hacky. The stacking context should increase for widgets rendered top to bottom, always. // Figure out a way in which the stacking context is consistent. - sortBy(compact(this.props.children), (child) => child.topRow), + this.props.useAutoLayout + ? this.props.children + : sortBy(compact(this.props.children), (child) => child.topRow), this.renderChildWidget, ); }; renderAsContainerComponent(props: ContainerWidgetProps) { - // console.log(`${props.widgetName} =======`); - // console.log(props); + console.log(`${props.widgetName} : ${props.widgetId} =======`); + console.log(props); const snapRows = getCanvasSnapRows(props.bottomRow, props.canExtend); return ( @@ -233,7 +235,14 @@ class ContainerWidget extends BaseWidget< widgetType={this.props.type} /> {/* without the wrapping div onClick events are triggered twice */} - <>{this.renderChildren()} + + {this.renderChildren()} + ); } @@ -255,4 +264,8 @@ export interface ContainerWidgetProps noPad?: boolean; } +interface ContainerWidgetState extends WidgetState { + items?: T[]; +} + export default ContainerWidget; diff --git a/app/client/src/widgets/VerticalLayoutWidget/widget/index.tsx b/app/client/src/widgets/VerticalLayoutWidget/widget/index.tsx index e3a5001d102f..f2b59ccedca4 100644 --- a/app/client/src/widgets/VerticalLayoutWidget/widget/index.tsx +++ b/app/client/src/widgets/VerticalLayoutWidget/widget/index.tsx @@ -7,7 +7,7 @@ import WidgetFactory, { DerivedPropertiesMap } from "utils/WidgetFactory"; import VerticalLayoutComponent from "../component"; import { ContainerStyle } from "widgets/ContainerWidget/component"; -import { LayoutDirection } from "components/constants"; +import { JustifyContent, LayoutDirection } from "components/constants"; import { CanvasDraggingArena } from "pages/common/CanvasArenas/CanvasDraggingArena"; import { CanvasSelectionArena } from "pages/common/CanvasArenas/CanvasSelectionArena"; import { @@ -91,7 +91,7 @@ class VerticalLayoutWidget extends BaseWidget< const layoutProps = { useAutoLayout: true, direction: LayoutDirection.Vertical, - justifyContent: "flex-start", + justifyContent: JustifyContent.FlexStart, alignItems: "stretch", }; From 25646765d18e02549f61fa9892dd9258df41311e Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 21 Jul 2022 10:09:19 -0400 Subject: [PATCH 007/708] set dimensions based on props --- .../editorComponents/ResizableComponent.tsx | 24 ++++++++++++------- app/client/src/widgets/BaseWidget.tsx | 6 +---- .../ContainerWidget/component/index.tsx | 2 +- .../src/widgets/VerticalLayoutWidget/index.ts | 2 ++ .../VerticalLayoutWidget/widget/index.tsx | 22 ++++++++++++++++- 5 files changed, 40 insertions(+), 16 deletions(-) diff --git a/app/client/src/components/editorComponents/ResizableComponent.tsx b/app/client/src/components/editorComponents/ResizableComponent.tsx index f23a968f3148..fcef5d6fcda2 100644 --- a/app/client/src/components/editorComponents/ResizableComponent.tsx +++ b/app/client/src/components/editorComponents/ResizableComponent.tsx @@ -1,4 +1,4 @@ -import React, { useContext, memo, useMemo } from "react"; +import React, { useContext, memo, useMemo, useState } from "react"; import { WidgetOperations, WidgetRowCols, @@ -44,6 +44,7 @@ import { getParentToOpenIfAny } from "utils/hooks/useClickToSelectWidget"; import { GridDefaults } from "constants/WidgetConstants"; import { DropTargetContext } from "./DropTargetComponent"; import { XYCord } from "pages/common/CanvasArenas/hooks/useCanvasDragging"; +import { LayoutDirection } from "components/constants"; export type ResizableComponentProps = WidgetProps & { paddingOffset: number; @@ -94,11 +95,19 @@ export const ResizableComponent = memo(function ResizableComponent( // The ResizableContainer's size prop is controlled const dimensions: UIElementSize = { width: - (props.rightColumn - props.leftColumn) * props.parentColumnSpace - - 2 * props.paddingOffset, + props.useAutoLayout && + props.direction === LayoutDirection.Vertical && + props.alignItems === "stretch" + ? 64 * props.parentColumnSpace - 2 * props.paddingOffset + : (props.rightColumn - props.leftColumn) * props.parentColumnSpace - + 2 * props.paddingOffset, height: - (props.bottomRow - props.topRow) * props.parentRowSpace - - 2 * props.paddingOffset, + props.useAutoLayout && + props.direction === LayoutDirection.Horizontal && + props.alignItems === "stretch" + ? 64 * props.parentRowSpace - 2 * props.paddingOffset + : (props.bottomRow - props.topRow) * props.parentRowSpace - + 2 * props.paddingOffset, }; // onResize handler @@ -271,19 +280,16 @@ export const ResizableComponent = memo(function ResizableComponent( bottom: props.bottomRow, right: props.rightColumn, }; - // console.log(`${props.widgetName} =========`); - // console.log(originalPositions); const updateBottomRow = (bottom: number) => { if (props.parentId) { updateDropTargetRows && updateDropTargetRows([props.parentId], bottom); } }; - return ( ` display: ${({ useAutoLayout }) => (useAutoLayout ? "flex" : "block")}; flex-direction: ${({ direction }) => - direction === "Vertical" ? "column" : "row"}; + direction === LayoutDirection.Vertical ? "column" : "row"}; justify-content: ${({ justifyContent }) => justifyContent || "flex-start"}; align-items: ${({ alignItems }) => alignItems || "flex-start"}; flex-wrap: wrap; diff --git a/app/client/src/widgets/VerticalLayoutWidget/index.ts b/app/client/src/widgets/VerticalLayoutWidget/index.ts index 330b9b7d2981..a3c410ba81a3 100644 --- a/app/client/src/widgets/VerticalLayoutWidget/index.ts +++ b/app/client/src/widgets/VerticalLayoutWidget/index.ts @@ -8,12 +8,14 @@ export const CONFIG = { iconSVG: IconSVG, needsMeta: false, // Defines if this widget adds any meta properties isCanvas: true, // Defines if this widget has a canvas within in which we can drop other widgets + searchTags: ["auto layout", "flex", "column", "div", "parent", "group"], defaults: { widgetName: "VerticalLayout", rows: 40, columns: 24, version: 1, containerStyle: "card", + backgroundColor: "#FFFFFF", borderColor: "transparent", borderWidth: "0", boxShadow: ButtonBoxShadowTypes.NONE, diff --git a/app/client/src/widgets/VerticalLayoutWidget/widget/index.tsx b/app/client/src/widgets/VerticalLayoutWidget/widget/index.tsx index f2b59ccedca4..87880f35f7b2 100644 --- a/app/client/src/widgets/VerticalLayoutWidget/widget/index.tsx +++ b/app/client/src/widgets/VerticalLayoutWidget/widget/index.tsx @@ -18,6 +18,7 @@ import { } from "constants/WidgetConstants"; import WidgetsMultiSelectBox from "pages/Editor/WidgetsMultiSelectBox"; import { getCanvasSnapRows } from "utils/WidgetPropsUtils"; +import { ValidationTypes } from "constants/WidgetValidation"; class VerticalLayoutWidget extends BaseWidget< VerticalLayoutWidgetProps, @@ -29,7 +30,26 @@ class VerticalLayoutWidget extends BaseWidget< } static getPropertyPaneConfig() { - return []; + return [ + { + helpText: "Controls alignment of the content", + propertyName: "justifyContent", + label: "Align content", + controlType: "DROP_DOWN", + defaultValue: JustifyContent.FlexStart, + options: [ + { label: "Flex start", value: JustifyContent.FlexStart }, + { label: "Center", value: JustifyContent.Center }, + { label: "Space around", value: JustifyContent.SpaceAround }, + { label: "Space between", value: JustifyContent.SpaceBetween }, + { label: "Space evently", value: JustifyContent.SpaceEvenly }, + ], + isJSConvertible: false, + isBindProperty: false, + isTriggerProperty: false, + validation: { type: ValidationTypes.TEXT }, + }, + ]; } static getDerivedPropertiesMap(): DerivedPropertiesMap { From 39a777464022c621c2ce224ed70064d6be71ddaa Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 21 Jul 2022 11:27:14 -0400 Subject: [PATCH 008/708] disable resizing in perpendicular directions --- .../editorComponents/ResizableComponent.tsx | 11 ++- .../widget/index.tsx | 5 +- app/client/src/widgets/BaseWidget.tsx | 2 - .../widgets/ContainerWidget/widget/index.tsx | 2 +- .../VerticalLayoutWidget/widget/index.tsx | 81 +++++++++++-------- 5 files changed, 60 insertions(+), 41 deletions(-) diff --git a/app/client/src/components/editorComponents/ResizableComponent.tsx b/app/client/src/components/editorComponents/ResizableComponent.tsx index fcef5d6fcda2..5d818b0d894b 100644 --- a/app/client/src/components/editorComponents/ResizableComponent.tsx +++ b/app/client/src/components/editorComponents/ResizableComponent.tsx @@ -1,4 +1,4 @@ -import React, { useContext, memo, useMemo, useState } from "react"; +import React, { useContext, memo, useMemo } from "react"; import { WidgetOperations, WidgetRowCols, @@ -45,6 +45,7 @@ import { GridDefaults } from "constants/WidgetConstants"; import { DropTargetContext } from "./DropTargetComponent"; import { XYCord } from "pages/common/CanvasArenas/hooks/useCanvasDragging"; import { LayoutDirection } from "components/constants"; +import { AutoLayoutContext } from "widgets/AutoLayoutContainerWidget/widget"; export type ResizableComponentProps = WidgetProps & { paddingOffset: number; @@ -55,6 +56,7 @@ export const ResizableComponent = memo(function ResizableComponent( ) { // Fetch information from the context const { updateWidget } = useContext(EditorContext); + const layoutContext = useContext(AutoLayoutContext); const canvasWidgets = useSelector(getCanvasWidgets); const isCommentMode = useSelector(commentModeSelector); @@ -250,8 +252,11 @@ export const ResizableComponent = memo(function ResizableComponent( bottomLeft: BottomLeftHandleStyles, }; - return omit(allHandles, get(props, "disabledResizeHandles", [])); - }, [props]); + return omit( + allHandles, + get({ ...props, ...layoutContext }, "disabledResizeHandles", []), + ); + }, [props, layoutContext]); const isEnabled = !isDragging && diff --git a/app/client/src/widgets/AutoLayoutContainerWidget/widget/index.tsx b/app/client/src/widgets/AutoLayoutContainerWidget/widget/index.tsx index 816d130a0b85..54ac5b1e328f 100644 --- a/app/client/src/widgets/AutoLayoutContainerWidget/widget/index.tsx +++ b/app/client/src/widgets/AutoLayoutContainerWidget/widget/index.tsx @@ -11,9 +11,10 @@ import { ContainerStyle } from "widgets/ContainerWidget/component"; export const AutoLayoutContext: Context<{ useAutoLayout?: boolean; - direction?: keyof typeof LayoutDirection; - justifyContent?: keyof typeof JustifyContent; + direction?: LayoutDirection; + justifyContent?: JustifyContent; overflow?: string; + disabledResizeHandles?: string[]; }> = createContext({}); class AutoLayoutContainerWidget extends BaseWidget< diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index 451daec4c54d..ba04fc09d64d 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -217,8 +217,6 @@ abstract class BaseWidget< * @param content */ makeResizable(content: ReactNode) { - const { componentHeight, componentWidth } = this.getComponentDimensions(); - return ( ) { console.log(`${props.widgetName} : ${props.widgetId} =======`); - console.log(props); + // console.log(props); const snapRows = getCanvasSnapRows(props.bottomRow, props.canExtend); return ( diff --git a/app/client/src/widgets/VerticalLayoutWidget/widget/index.tsx b/app/client/src/widgets/VerticalLayoutWidget/widget/index.tsx index 87880f35f7b2..3c8e83f73a97 100644 --- a/app/client/src/widgets/VerticalLayoutWidget/widget/index.tsx +++ b/app/client/src/widgets/VerticalLayoutWidget/widget/index.tsx @@ -1,5 +1,4 @@ import React from "react"; -import styled from "styled-components"; import { compact, map, sortBy } from "lodash"; import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; @@ -19,6 +18,7 @@ import { import WidgetsMultiSelectBox from "pages/Editor/WidgetsMultiSelectBox"; import { getCanvasSnapRows } from "utils/WidgetPropsUtils"; import { ValidationTypes } from "constants/WidgetValidation"; +import { AutoLayoutContext } from "widgets/AutoLayoutContainerWidget/widget"; class VerticalLayoutWidget extends BaseWidget< VerticalLayoutWidgetProps, @@ -114,7 +114,6 @@ class VerticalLayoutWidget extends BaseWidget< justifyContent: JustifyContent.FlexStart, alignItems: "stretch", }; - return WidgetFactory.createWidget( { ...childWidgetData, ...layoutProps }, this.props.renderMode, @@ -137,37 +136,53 @@ class VerticalLayoutWidget extends BaseWidget< this.props.canExtend, ); return ( - - {this.props.type === "CANVAS_WIDGET" && ( - <> - - - - )} - - {/* without the wrapping div onClick events are triggered twice */} - <>{this.renderChildren()} - + + + {this.props.type === "CANVAS_WIDGET" && ( + <> + + + + )} + + {/* without the wrapping div onClick events are triggered twice */} + <>{this.renderChildren()} + + ); } From 2959e97b283f5c1de869fb87b7e2fa900f6f3173 Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 21 Jul 2022 15:40:30 -0400 Subject: [PATCH 009/708] working horizontal layout with restricted resizing --- .../src/components/AutoLayoutWrapper.tsx | 57 ++--- app/client/src/components/constants.ts | 6 + .../editorComponents/ResizableComponent.tsx | 82 ++++++-- .../CanvasArenas/CanvasDraggingArena.tsx | 9 + .../common/CanvasArenas/StickyCanvasArena.tsx | 31 ++- .../hooks/useBlocksToBeDraggedOnCanvas.ts | 35 +++- .../CanvasArenas/hooks/useCanvasDragging.ts | 83 +++++--- app/client/src/utils/WidgetRegistry.tsx | 4 + .../widget/index.tsx | 7 +- app/client/src/widgets/BaseWidget.tsx | 39 +--- .../widgets/ContainerWidget/widget/index.tsx | 2 + .../component/index.tsx | 14 ++ .../HorizontalLayoutWidget/constants.ts | 2 + .../widgets/HorizontalLayoutWidget/icon.svg | 1 + .../widgets/HorizontalLayoutWidget/index.ts | 47 +++++ .../HorizontalLayoutWidget/widget/index.tsx | 195 ++++++++++++++++++ .../VerticalLayoutWidget/widget/index.tsx | 21 +- 17 files changed, 492 insertions(+), 143 deletions(-) create mode 100644 app/client/src/widgets/HorizontalLayoutWidget/component/index.tsx create mode 100644 app/client/src/widgets/HorizontalLayoutWidget/constants.ts create mode 100644 app/client/src/widgets/HorizontalLayoutWidget/icon.svg create mode 100644 app/client/src/widgets/HorizontalLayoutWidget/index.ts create mode 100644 app/client/src/widgets/HorizontalLayoutWidget/widget/index.tsx diff --git a/app/client/src/components/AutoLayoutWrapper.tsx b/app/client/src/components/AutoLayoutWrapper.tsx index b3838f94b75e..17d516cccbb9 100644 --- a/app/client/src/components/AutoLayoutWrapper.tsx +++ b/app/client/src/components/AutoLayoutWrapper.tsx @@ -1,53 +1,40 @@ import { WidgetType } from "constants/WidgetConstants"; -import styled, { css } from "styled-components"; +import styled from "styled-components"; import React, { ReactNode, useCallback } from "react"; import { useClickToSelectWidget } from "utils/hooks/useClickToSelectWidget"; -import { LayoutDirection } from "./constants"; +import { AlignItems, LayoutDirection } from "./constants"; export type AutoLayoutProps = { children: ReactNode; widgetId: string; widgetType: WidgetType; useAutoLayout?: boolean; - alignItems: string; + alignItems?: AlignItems; direction?: LayoutDirection; parentId?: string; }; -const AutoLayout = styled("div")<{ styles: any }>` +const AutoLayout = styled("div")<{ + alignItems?: AlignItems; + direction?: LayoutDirection; + useAutoLayout?: boolean; +}>` position: unset; margin: 8px; - ${({ styles }) => - styles.useAutoLayout && styles.alignItems === "stretch" - ? css` - width: calc(100% - 16px); - height: auto; - min-height: 30px; - ` - : ""} + width: ${({ alignItems, direction }) => + alignItems === AlignItems.Stretch && direction === LayoutDirection.Vertical + ? "calc(100% - 16px)" + : "auto"}; + height: ${({ alignItems, direction }) => + alignItems === AlignItems.Stretch && + direction === LayoutDirection.Horizontal + ? "calc(100% - 16px)" + : "auto"}; + min-height: 30px; `; export function AutoLayoutWrapper(props: AutoLayoutProps) { const clickToSelectWidget = useClickToSelectWidget(); - let size = {}, - margin = {}; - if (props.useAutoLayout && props.alignItems === "stretch") { - size = { - width: "100%", - height: "auto", - }; - } - if (props.useAutoLayout && props.direction === LayoutDirection.Vertical) { - margin = { - marginTop: 8, - marginBottom: 4, - }; - } else { - margin = { - marginLeft: 8, - marginRight: 8, - }; - } const onClickFn = useCallback( (e) => { clickToSelectWidget(e, props.widgetId); @@ -57,13 +44,11 @@ export function AutoLayoutWrapper(props: AutoLayoutProps) { return ( {props.children} diff --git a/app/client/src/components/constants.ts b/app/client/src/components/constants.ts index 9134aaa7d068..7a2dd26db65d 100644 --- a/app/client/src/components/constants.ts +++ b/app/client/src/components/constants.ts @@ -123,3 +123,9 @@ export enum JustifyContent { SpaceBetween = "space-between", SpaceEvenly = "space-evenly", } + +export enum AlignItems { + FlexStart = "flex-start", + Center = "center", + Stretch = "stretch", +} diff --git a/app/client/src/components/editorComponents/ResizableComponent.tsx b/app/client/src/components/editorComponents/ResizableComponent.tsx index 5d818b0d894b..060ae4b23106 100644 --- a/app/client/src/components/editorComponents/ResizableComponent.tsx +++ b/app/client/src/components/editorComponents/ResizableComponent.tsx @@ -1,4 +1,4 @@ -import React, { useContext, memo, useMemo } from "react"; +import React, { useContext, useEffect, memo, useMemo, useState } from "react"; import { WidgetOperations, WidgetRowCols, @@ -44,7 +44,7 @@ import { getParentToOpenIfAny } from "utils/hooks/useClickToSelectWidget"; import { GridDefaults } from "constants/WidgetConstants"; import { DropTargetContext } from "./DropTargetComponent"; import { XYCord } from "pages/common/CanvasArenas/hooks/useCanvasDragging"; -import { LayoutDirection } from "components/constants"; +import { AlignItems, LayoutDirection } from "components/constants"; import { AutoLayoutContext } from "widgets/AutoLayoutContainerWidget/widget"; export type ResizableComponentProps = WidgetProps & { @@ -54,9 +54,16 @@ export type ResizableComponentProps = WidgetProps & { export const ResizableComponent = memo(function ResizableComponent( props: ResizableComponentProps, ) { + const [componentWidth, setComponentWidth] = useState(0); + const [componentHeight, setComponentHeight] = useState(0); // Fetch information from the context const { updateWidget } = useContext(EditorContext); - const layoutContext = useContext(AutoLayoutContext); + // const { + // alignItems, + // direction, + // disabledResizeHandles, + // useAutoLayout, + // } = useContext(AutoLayoutContext || null); const canvasWidgets = useSelector(getCanvasWidgets); const isCommentMode = useSelector(commentModeSelector); @@ -93,23 +100,66 @@ export const ResizableComponent = memo(function ResizableComponent( selectedWidget === props.widgetId || selectedWidgets.includes(props.widgetId); + useEffect(() => { + // Set initial dimensions + // console.log(`#### ${props.widgetName} : Initial dimensions`); + setComponentWidth( + props.useAutoLayout && + props.direction === LayoutDirection.Vertical && + props.alignItems === AlignItems.Stretch + ? 64 * props.parentColumnSpace - 2 * props.paddingOffset + : (props.rightColumn - props.leftColumn) * props.parentColumnSpace - + 2 * props.paddingOffset, + ); + setComponentHeight( + (props.bottomRow - props.topRow) * props.parentRowSpace - + 2 * props.paddingOffset, + ); + }, [props.useAutoLayout, props.direction, props.alignItems]); + + useEffect(() => { + // console.log(`#### ${props.widgetName} : Manual resize`); + setComponentWidth( + props.useAutoLayout && + props.direction === LayoutDirection.Vertical && + props.alignItems === AlignItems.Stretch + ? 64 * props.parentColumnSpace - 2 * props.paddingOffset + : (props.rightColumn - props.leftColumn) * props.parentColumnSpace - + 2 * props.paddingOffset, + ); + setComponentHeight( + (props.bottomRow - props.topRow) * props.parentRowSpace - + 2 * props.paddingOffset, + ); + }, [props.topRow, props.bottomRow, props.leftColumn, props.rightColumn]); + + useEffect(() => { + // console.log(`#### ${props.widgetName} : Parent resize`); + if (!props.useAutoLayout) { + setComponentWidth( + (props.rightColumn - props.leftColumn) * props.parentColumnSpace - + 2 * props.paddingOffset, + ); + setComponentHeight( + (props.bottomRow - props.topRow) * props.parentRowSpace - + 2 * props.paddingOffset, + ); + } + }, [props.parentColumnSpace, props.parentRowSpace, props.useAutoLayout]); + // Calculate the dimensions of the widget, // The ResizableContainer's size prop is controlled const dimensions: UIElementSize = { width: props.useAutoLayout && props.direction === LayoutDirection.Vertical && - props.alignItems === "stretch" + props.alignItems === AlignItems.Stretch ? 64 * props.parentColumnSpace - 2 * props.paddingOffset : (props.rightColumn - props.leftColumn) * props.parentColumnSpace - 2 * props.paddingOffset, height: - props.useAutoLayout && - props.direction === LayoutDirection.Horizontal && - props.alignItems === "stretch" - ? 64 * props.parentRowSpace - 2 * props.paddingOffset - : (props.bottomRow - props.topRow) * props.parentRowSpace - - 2 * props.paddingOffset, + (props.bottomRow - props.topRow) * props.parentRowSpace - + 2 * props.paddingOffset, }; // onResize handler @@ -175,7 +225,6 @@ export const ResizableComponent = memo(function ResizableComponent( height: newDimensions.height - dimensions.height, width: newDimensions.width - dimensions.width, }; - // Get the updated Widget rows and columns props // False, if there is collision // False, if none of the rows and cols have changed. @@ -252,11 +301,8 @@ export const ResizableComponent = memo(function ResizableComponent( bottomLeft: BottomLeftHandleStyles, }; - return omit( - allHandles, - get({ ...props, ...layoutContext }, "disabledResizeHandles", []), - ); - }, [props, layoutContext]); + return omit(allHandles, get(props, "disabledResizeHandles", [])); + }, [props]); const isEnabled = !isDragging && @@ -293,8 +339,8 @@ export const ResizableComponent = memo(function ResizableComponent( return ( ) : null; } diff --git a/app/client/src/pages/common/CanvasArenas/StickyCanvasArena.tsx b/app/client/src/pages/common/CanvasArenas/StickyCanvasArena.tsx index f836df069f45..ff4365ca92a4 100644 --- a/app/client/src/pages/common/CanvasArenas/StickyCanvasArena.tsx +++ b/app/client/src/pages/common/CanvasArenas/StickyCanvasArena.tsx @@ -1,10 +1,12 @@ import styled from "constants/DefaultTheme"; import React, { forwardRef, RefObject, useEffect, useRef } from "react"; import ResizeObserver from "resize-observer-polyfill"; +import { LayoutDirection } from "components/constants"; interface StickyCanvasArenaProps { showCanvas: boolean; canvasId: string; + direction?: LayoutDirection; id: string; canvasPadding: number; snapRows: number; @@ -13,6 +15,7 @@ interface StickyCanvasArenaProps { getRelativeScrollingParent: (child: HTMLDivElement) => Element | null; canExtend: boolean; ref: StickyCanvasArenaRef; + useAutoLayout?: boolean; } interface StickyCanvasArenaRef { @@ -34,18 +37,32 @@ const StyledCanvasSlider = styled.div<{ paddingBottom: number }>` overflow-y: auto; `; +const Highlight = styled.div<{ + direction?: LayoutDirection; +}>` + width: ${({ direction }) => + direction === LayoutDirection.Vertical ? "100%" : "4px"}; + height: ${({ direction }) => + direction === LayoutDirection.Horizontal ? "100%" : "4px"}; + background-color: rgba(217, 89, 183, 0.8); + position: absolute; + opacity: 0; +`; + export const StickyCanvasArena = forwardRef( (props: StickyCanvasArenaProps, ref: any) => { const { canExtend, canvasId, canvasPadding, + direction, getRelativeScrollingParent, id, showCanvas, snapColSpace, snapRows, snapRowSpace, + useAutoLayout, } = props; const { dropPositionRef, slidingArenaRef, stickyCanvasRef } = ref.current; @@ -118,7 +135,6 @@ export const StickyCanvasArena = forwardRef( resizeObserver.current.unobserve(slidingArenaRef.current); }; }, []); - return ( <> {/* Canvas will always be sticky to its scrollable parent's view port. i.e, @@ -131,16 +147,9 @@ export const StickyCanvasArena = forwardRef( paddingBottom={canvasPadding} ref={slidingArenaRef} /> -
+ {useAutoLayout && ( + + )} ); }, diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts b/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts index 91827d15e01b..63228d89dd28 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts @@ -30,6 +30,7 @@ import { DragDetails } from "reducers/uiReducers/dragResizeReducer"; import { getIsReflowing } from "selectors/widgetReflowSelectors"; import { XYCord } from "./useCanvasDragging"; import ContainerJumpMetrics from "./ContainerJumpMetric"; +import { AlignItems, LayoutDirection } from "components/constants"; export interface WidgetDraggingUpdateParams extends WidgetDraggingBlock { updateWidgetParams: WidgetOperationParams; @@ -70,6 +71,8 @@ const logContainerJumpOnDrop = () => { }; export const useBlocksToBeDraggedOnCanvas = ({ + alignItems, + direction, noPad, snapColumnSpace, snapRows, @@ -221,11 +224,19 @@ export const useBlocksToBeDraggedOnCanvas = ({ { top: 0, left: 0, - width: useAutoLayout - ? 64 * snapColumnSpace - : newWidget.columns * snapColumnSpace, + width: + useAutoLayout && + direction === LayoutDirection.Vertical && + alignItems === AlignItems.Stretch + ? 64 * snapColumnSpace + : newWidget.columns * snapColumnSpace, height: newWidget.rows * snapRowSpace, - columnWidth: useAutoLayout ? 64 : newWidget.columns, + columnWidth: + useAutoLayout && + direction === LayoutDirection.Vertical && + alignItems === AlignItems.Stretch + ? 64 + : newWidget.columns, rowHeight: newWidget.rows, widgetId: newWidget.widgetId, detachFromLayout: newWidget.detachFromLayout, @@ -251,11 +262,19 @@ export const useBlocksToBeDraggedOnCanvas = ({ blocksToDraw: draggingSpaces.map((each) => ({ top: each.top * snapRowSpace + containerPadding, left: each.left * snapColumnSpace + containerPadding, - width: useAutoLayout - ? 64 * snapColumnSpace - : (each.right - each.left) * snapColumnSpace, + width: + useAutoLayout && + direction === LayoutDirection.Vertical && + alignItems === AlignItems.Stretch + ? 64 * snapColumnSpace + : (each.right - each.left) * snapColumnSpace, height: (each.bottom - each.top) * snapRowSpace, - columnWidth: useAutoLayout ? 64 : each.right - each.left, + columnWidth: + useAutoLayout && + direction === LayoutDirection.Vertical && + alignItems === AlignItems.Stretch + ? 64 + : each.right - each.left, rowHeight: each.bottom - each.top, widgetId: each.id, isNotColliding: true, diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index 833dd0c1cab9..ee6ed97ea519 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -29,6 +29,7 @@ import { } from "./useBlocksToBeDraggedOnCanvas"; import { useCanvasDragToScroll } from "./useCanvasDragToScroll"; import ContainerJumpMetrics from "./ContainerJumpMetric"; +import { LayoutDirection } from "components/constants"; export interface XYCord { x: number; @@ -49,7 +50,9 @@ export const useCanvasDragging = ( slidingArenaRef: React.RefObject, stickyCanvasRef: React.RefObject, { + alignItems, canExtend, + direction, dropDisabled, noPad, snapColumnSpace, @@ -85,7 +88,9 @@ export const useCanvasDragging = ( updateChildrenPositions, updateRelativeRows, } = useBlocksToBeDraggedOnCanvas({ + alignItems, canExtend, + direction, noPad, snapColumnSpace, snapRows, @@ -104,23 +109,46 @@ export const useCanvasDragging = ( reflow.current = useReflow(draggingSpaces, widgetId || "", gridProps); const offsets: number[] = []; const siblings: { [key: string]: number } = {}; - if (useAutoLayout) { - const els = document.querySelectorAll(`.${widgetId}-auto-layout`); - if (els && els.length && offsets.length !== els.length) { - const blocks = blocksToDraw.map((block) => block.widgetId); - els.forEach((el) => { - const mClass = el.className.split("auto-layout-child-")[1]; - if (blocks && blocks.length && blocks.indexOf(mClass) !== -1) return; - offsets.push((el as any).offsetTop); - siblings[mClass] = (el as any).offsetTop; - }); - offsets.push( - (els[els.length - 1] as any).offsetTop + - els[els.length - 1].clientHeight + - 8, - ); + const calculateHighlightOffsets = () => { + if (useAutoLayout) { + // Get all children of current auto layout container + const els = document.querySelectorAll(`.${widgetId}-auto-layout`); + if (els && els.length && offsets.length !== els.length) { + // Get widget ids of all widgets being dragged + // console.log(els); + const blocks = blocksToDraw.map((block) => block.widgetId); + els.forEach((el) => { + // console.log((el as any).offsetParent); + // Extract widget id of current widget + const mClass = el.className.split("auto-layout-child-")[1]; + /** + * If the widget is also being dragged, + * then discount its presence from offset calculation. + */ + if (blocks && blocks.length && blocks.indexOf(mClass) !== -1) return; + const mOffset = + direction === LayoutDirection.Vertical + ? (el as any).offsetTop + : (el as any).offsetLeft; + offsets.push(mOffset); + siblings[mClass] = mOffset; + }); + offsets.push( + direction === LayoutDirection.Vertical + ? (els[els.length - 1] as any).offsetTop + + els[els.length - 1].clientHeight + + 8 + : (els[els.length - 1] as any).offsetLeft + + els[els.length - 1].clientWidth + + 8, + ); + } } - } + }; + calculateHighlightOffsets(); + // console.log("*********"); + // console.log(offsets); + // console.log(siblings); const { setDraggingCanvas, setDraggingNewWidget, @@ -307,8 +335,12 @@ export const useCanvasDragging = ( }); // console.log(currentRectanglesToDraw); // console.log(reflowedPositionsUpdatesWidgets); - const pos = getDropPosition(currentRectanglesToDraw[0].top); - console.log(`pos: ${pos}`); + const pos = getDropPosition( + direction === LayoutDirection.Vertical + ? currentRectanglesToDraw[0].top + : currentRectanglesToDraw[0].left, + ); + // console.log(`pos: ${pos}`); if (pos !== undefined && useAutoLayout) updateChildrenPositions(pos, currentRectanglesToDraw); else @@ -579,14 +611,19 @@ export const useCanvasDragging = ( // console.log(`#### ref: ${dropPositionRef.current}`); if (dropPositionRef && dropPositionRef.current) { dropPositionRef.current.style.opacity = "1"; - dropPositionRef.current.style.top = pos - 6 + "px"; + if (direction === LayoutDirection.Vertical) + dropPositionRef.current.style.top = pos - 6 + "px"; + else dropPositionRef.current.style.left = pos - 6 + "px"; } }; - const getHighlightPosition = (e: any, top?: number) => { + const getHighlightPosition = (e: any, val?: number) => { let base: number[] = []; if (!offsets || !offsets.length) base = [8]; else base = offsets; - const pos = e?.offsetY || top; + const pos = + (direction === LayoutDirection.Vertical + ? e?.offsetY + : e?.offsetX) || val; // console.log(e); // console.log(pos); const arr = [...base].sort((a, b) => { @@ -594,8 +631,8 @@ export const useCanvasDragging = ( }); return arr[0]; }; - const getDropPosition = (top: number): number | undefined => { - const pos = getHighlightPosition(null, top); + const getDropPosition = (val: number): number | undefined => { + const pos = getHighlightPosition(null, val); if (!pos) return; return offsets.indexOf(pos); }; diff --git a/app/client/src/utils/WidgetRegistry.tsx b/app/client/src/utils/WidgetRegistry.tsx index f59693cb1f0c..f72e9d4e1ae8 100644 --- a/app/client/src/utils/WidgetRegistry.tsx +++ b/app/client/src/utils/WidgetRegistry.tsx @@ -155,6 +155,9 @@ import { WidgetConfiguration } from "widgets/constants"; import TableWidgetV2, { CONFIG as TABLE_WIDGET_CONFIG_V2, } from "widgets/TableWidgetV2"; +import HorizontalLayoutWidget, { + CONFIG as HORIZONTAL_LAYOUT_CONFIG, +} from "widgets/HorizontalLayoutWidget"; export const ALL_WIDGETS_AND_CONFIG = [ [CanvasWidget, CANVAS_WIDGET_CONFIG], @@ -204,6 +207,7 @@ export const ALL_WIDGETS_AND_CONFIG = [ [TableWidgetV2, TABLE_WIDGET_CONFIG_V2], [AutoLayoutContainerWidget, AUTO_LAYOUT_CONFIG], [VerticalLayoutWidget, VERTICAL_LAYOUT_CONFIG], + [HorizontalLayoutWidget, HORIZONTAL_LAYOUT_CONFIG], //Deprecated Widgets [InputWidget, INPUT_WIDGET_CONFIG], diff --git a/app/client/src/widgets/AutoLayoutContainerWidget/widget/index.tsx b/app/client/src/widgets/AutoLayoutContainerWidget/widget/index.tsx index 54ac5b1e328f..1885a9285583 100644 --- a/app/client/src/widgets/AutoLayoutContainerWidget/widget/index.tsx +++ b/app/client/src/widgets/AutoLayoutContainerWidget/widget/index.tsx @@ -6,7 +6,11 @@ import WidgetFactory, { DerivedPropertiesMap } from "utils/WidgetFactory"; import AutoLayoutContainerComponent from "../component"; import { ValidationTypes } from "constants/WidgetValidation"; -import { JustifyContent, LayoutDirection } from "components/constants"; +import { + AlignItems, + JustifyContent, + LayoutDirection, +} from "components/constants"; import { ContainerStyle } from "widgets/ContainerWidget/component"; export const AutoLayoutContext: Context<{ @@ -15,6 +19,7 @@ export const AutoLayoutContext: Context<{ justifyContent?: JustifyContent; overflow?: string; disabledResizeHandles?: string[]; + alignItems?: AlignItems; }> = createContext({}); class AutoLayoutContainerWidget extends BaseWidget< diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index ba04fc09d64d..fb13605cd2d1 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -39,7 +39,7 @@ import PreventInteractionsOverlay from "components/editorComponents/PreventInter import AppsmithConsole from "utils/AppsmithConsole"; import { ENTITY_TYPE } from "entities/AppsmithConsole"; import PreviewModeComponent from "components/editorComponents/PreviewModeComponent"; -import { LayoutDirection } from "components/constants"; +import { AlignItems, LayoutDirection } from "components/constants"; import { AutoLayoutWrapper } from "components/AutoLayoutWrapper"; /*** @@ -331,43 +331,9 @@ abstract class BaseWidget< } addAutoLayoutWrapper(content: ReactNode) { - // let size = {}, - // margin = {}; - // if (this.props.autoLayout && this.props.alignItems === "stretch") { - // size = { - // width: "100%", - // height: "auto", - // }; - // } - // if ( - // this.props.useAutoLayout && - // this.props.direction === LayoutDirection.Vertical - // ) { - // margin = { - // marginTop: 8, - // marginBottom: 4, - // }; - // } else { - // margin = { - // marginLeft: 8, - // marginRight: 8, - // }; - // } - // return ( - //
- // {content} - //
- // ); return ( {props.children}; +} + +export interface HorizontalLayoutComponentProps { + children?: ReactNode; +} + +export default HorizontalLayoutComponent; diff --git a/app/client/src/widgets/HorizontalLayoutWidget/constants.ts b/app/client/src/widgets/HorizontalLayoutWidget/constants.ts new file mode 100644 index 000000000000..5f46d7c3b236 --- /dev/null +++ b/app/client/src/widgets/HorizontalLayoutWidget/constants.ts @@ -0,0 +1,2 @@ +// This file contains common constants which can be used across the widget configuration file (index.ts), widget and component folders. +export const HORIZONTALLAYOUT_WIDGET_CONSTANT = ""; diff --git a/app/client/src/widgets/HorizontalLayoutWidget/icon.svg b/app/client/src/widgets/HorizontalLayoutWidget/icon.svg new file mode 100644 index 000000000000..e97d99915ab8 --- /dev/null +++ b/app/client/src/widgets/HorizontalLayoutWidget/icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/client/src/widgets/HorizontalLayoutWidget/index.ts b/app/client/src/widgets/HorizontalLayoutWidget/index.ts new file mode 100644 index 000000000000..36dbd6bb4e8c --- /dev/null +++ b/app/client/src/widgets/HorizontalLayoutWidget/index.ts @@ -0,0 +1,47 @@ +import Widget from "./widget"; +import IconSVG from "./icon.svg"; +import { ButtonBoxShadowTypes } from "components/constants"; + +export const CONFIG = { + type: Widget.getWidgetType(), + name: "Horizontal Layout", // The display name which will be made in uppercase and show in the widgets panel ( can have spaces ) + iconSVG: IconSVG, + needsMeta: false, // Defines if this widget adds any meta properties + isCanvas: true, // Defines if this widget has a canvas within in which we can drop other widgets + searchTags: ["auto layout", "flex", "row", "div", "parent", "group"], + defaults: { + widgetName: "HorizontalLayout", + rows: 16, + columns: 30, + version: 1, + containerStyle: "card", + backgroundColor: "#FFFFFF", + borderColor: "transparent", + borderWidth: "0", + boxShadow: ButtonBoxShadowTypes.NONE, + animateLoading: true, + children: [], + blueprint: { + view: [ + { + type: "CANVAS_WIDGET", + position: { top: 0, left: 0 }, + props: { + containerStyle: "none", + canExtend: false, + detachFromLayout: true, + children: [], + }, + }, + ], + }, + }, + properties: { + derived: Widget.getDerivedPropertiesMap(), + default: Widget.getDefaultPropertiesMap(), + meta: Widget.getMetaPropertiesMap(), + config: Widget.getPropertyPaneConfig(), + }, +}; + +export default Widget; diff --git a/app/client/src/widgets/HorizontalLayoutWidget/widget/index.tsx b/app/client/src/widgets/HorizontalLayoutWidget/widget/index.tsx new file mode 100644 index 000000000000..589d48cd8997 --- /dev/null +++ b/app/client/src/widgets/HorizontalLayoutWidget/widget/index.tsx @@ -0,0 +1,195 @@ +import React from "react"; + +import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; +import WidgetFactory, { DerivedPropertiesMap } from "utils/WidgetFactory"; + +import HorizontalLayoutComponent from "../component"; +import { CanvasSelectionArena } from "pages/common/CanvasArenas/CanvasSelectionArena"; +import { CanvasDraggingArena } from "pages/common/CanvasArenas/CanvasDraggingArena"; +import { + AlignItems, + JustifyContent, + LayoutDirection, +} from "components/constants"; +import { AutoLayoutContext } from "widgets/AutoLayoutContainerWidget/widget"; +import { getCanvasSnapRows } from "utils/WidgetPropsUtils"; +import { ValidationTypes } from "constants/WidgetValidation"; +import { + CONTAINER_GRID_PADDING, + GridDefaults, + MAIN_CONTAINER_WIDGET_ID, + WIDGET_PADDING, +} from "constants/WidgetConstants"; +import { map } from "lodash"; +import { ContainerStyle } from "widgets/ContainerWidget/component"; +import WidgetsMultiSelectBox from "pages/Editor/WidgetsMultiSelectBox"; + +class HorizontalLayoutWidget extends BaseWidget< + HorizontalLayoutWidgetProps, + WidgetState +> { + constructor(props: HorizontalLayoutWidgetProps) { + super(props); + this.renderChildWidget = this.renderChildWidget.bind(this); + } + + static getPropertyPaneConfig() { + return [ + { + helpText: "Controls alignment of the content", + propertyName: "justifyContent", + label: "Align content", + controlType: "DROP_DOWN", + defaultValue: JustifyContent.FlexStart, + options: [ + { label: "Flex start", value: JustifyContent.FlexStart }, + { label: "Center", value: JustifyContent.Center }, + { label: "Space around", value: JustifyContent.SpaceAround }, + { label: "Space between", value: JustifyContent.SpaceBetween }, + { label: "Space evently", value: JustifyContent.SpaceEvenly }, + ], + isJSConvertible: false, + isBindProperty: false, + isTriggerProperty: false, + validation: { type: ValidationTypes.TEXT }, + }, + ]; + } + + static getDerivedPropertiesMap(): DerivedPropertiesMap { + return {}; + } + + static getDefaultPropertiesMap(): Record { + return {}; + } + + static getMetaPropertiesMap(): Record { + return {}; + } + + getSnapSpaces = () => { + const { componentWidth } = this.getComponentDimensions(); + // For all widgets inside a container, we remove both container padding as well as widget padding from component width + let padding = (CONTAINER_GRID_PADDING + WIDGET_PADDING) * 2; + if ( + this.props.widgetId === MAIN_CONTAINER_WIDGET_ID || + this.props.type === "CONTAINER_WIDGET" + ) { + //For MainContainer and any Container Widget padding doesn't exist coz there is already container padding. + padding = CONTAINER_GRID_PADDING * 2; + } + if (this.props.noPad) { + // Widgets like ListWidget choose to have no container padding so will only have widget padding + padding = WIDGET_PADDING * 2; + } + let width = componentWidth; + width -= padding; + return { + snapRowSpace: GridDefaults.DEFAULT_GRID_ROW_HEIGHT, + snapColumnSpace: componentWidth + ? width / GridDefaults.DEFAULT_GRID_COLUMNS + : 0, + }; + }; + + renderChildWidget(childWidgetData: WidgetProps): React.ReactNode { + // For now, isVisible prop defines whether to render a detached widget + if (childWidgetData.detachFromLayout && !childWidgetData.isVisible) { + return null; + } + + const { componentHeight, componentWidth } = this.getComponentDimensions(); + + childWidgetData.rightColumn = componentWidth; + childWidgetData.bottomRow = this.props.shouldScrollContents + ? childWidgetData.bottomRow + : componentHeight; + childWidgetData.minHeight = componentHeight; + childWidgetData.isVisible = this.props.isVisible; + childWidgetData.shouldScrollContents = false; + childWidgetData.canExtend = this.props.shouldScrollContents; + + childWidgetData.parentId = this.props.widgetId; + // Pass layout controls to children + const layoutProps = { + useAutoLayout: true, + direction: LayoutDirection.Horizontal, + justifyContent: JustifyContent.FlexStart, + alignItems: AlignItems.FlexStart, + }; + return WidgetFactory.createWidget( + { ...childWidgetData, ...layoutProps }, + this.props.renderMode, + ); + } + + renderChildren = () => { + return map(this.props.children, this.renderChildWidget); + }; + + getPageView() { + const snapRows = getCanvasSnapRows( + this.props.bottomRow, + this.props.canExtend, + ); + return ( + + + {this.props.type === "CANVAS_WIDGET" && ( + <> + + + + )} + + {/* without the wrapping div onClick events are triggered twice */} + <>{this.renderChildren()} + + + ); + } + + static getWidgetType(): string { + return "HORIZONTALLAYOUT_WIDGET"; + } +} + +export interface HorizontalLayoutWidgetProps + extends WidgetProps { + children?: T[]; + containerStyle?: ContainerStyle; + shouldScrollContents?: boolean; + noPad?: boolean; +} + +export default HorizontalLayoutWidget; diff --git a/app/client/src/widgets/VerticalLayoutWidget/widget/index.tsx b/app/client/src/widgets/VerticalLayoutWidget/widget/index.tsx index 3c8e83f73a97..c79ce5faf87d 100644 --- a/app/client/src/widgets/VerticalLayoutWidget/widget/index.tsx +++ b/app/client/src/widgets/VerticalLayoutWidget/widget/index.tsx @@ -1,12 +1,16 @@ import React from "react"; -import { compact, map, sortBy } from "lodash"; +import { map } from "lodash"; import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; import WidgetFactory, { DerivedPropertiesMap } from "utils/WidgetFactory"; import VerticalLayoutComponent from "../component"; import { ContainerStyle } from "widgets/ContainerWidget/component"; -import { JustifyContent, LayoutDirection } from "components/constants"; +import { + AlignItems, + JustifyContent, + LayoutDirection, +} from "components/constants"; import { CanvasDraggingArena } from "pages/common/CanvasArenas/CanvasDraggingArena"; import { CanvasSelectionArena } from "pages/common/CanvasArenas/CanvasSelectionArena"; import { @@ -112,7 +116,7 @@ class VerticalLayoutWidget extends BaseWidget< useAutoLayout: true, direction: LayoutDirection.Vertical, justifyContent: JustifyContent.FlexStart, - alignItems: "stretch", + alignItems: AlignItems.Stretch, }; return WidgetFactory.createWidget( { ...childWidgetData, ...layoutProps }, @@ -121,13 +125,7 @@ class VerticalLayoutWidget extends BaseWidget< } renderChildren = () => { - return map( - // sort by row so stacking context is correct - // TODO(abhinav): This is hacky. The stacking context should increase for widgets rendered top to bottom, always. - // Figure out a way in which the stacking context is consistent. - sortBy(compact(this.props.children), (child) => child.topRow), - this.renderChildWidget, - ); + return map(this.props.children, this.renderChildWidget); }; getPageView() { @@ -138,6 +136,7 @@ class VerticalLayoutWidget extends BaseWidget< return ( Date: Fri, 22 Jul 2022 11:21:58 -0400 Subject: [PATCH 010/708] add card style to container --- .../src/components/AutoLayoutWrapper.tsx | 2 +- .../editorComponents/ResizableComponent.tsx | 69 +++++++++++++------ .../component/index.tsx | 26 +++++-- .../VerticalLayoutWidget/component/index.tsx | 31 ++++++--- 4 files changed, 90 insertions(+), 38 deletions(-) diff --git a/app/client/src/components/AutoLayoutWrapper.tsx b/app/client/src/components/AutoLayoutWrapper.tsx index 17d516cccbb9..a859b2abbfb8 100644 --- a/app/client/src/components/AutoLayoutWrapper.tsx +++ b/app/client/src/components/AutoLayoutWrapper.tsx @@ -45,7 +45,7 @@ export function AutoLayoutWrapper(props: AutoLayoutProps) { return ( { // Set initial dimensions - // console.log(`#### ${props.widgetName} : Initial dimensions`); + log(`#### ${props.widgetName} : Initial dimensions`, shouldLog); setComponentWidth( props.useAutoLayout && props.direction === LayoutDirection.Vertical && @@ -118,7 +121,7 @@ export const ResizableComponent = memo(function ResizableComponent( }, [props.useAutoLayout, props.direction, props.alignItems]); useEffect(() => { - // console.log(`#### ${props.widgetName} : Manual resize`); + log(`#### ${props.widgetName} : Manual resize`, shouldLog); setComponentWidth( props.useAutoLayout && props.direction === LayoutDirection.Vertical && @@ -134,7 +137,7 @@ export const ResizableComponent = memo(function ResizableComponent( }, [props.topRow, props.bottomRow, props.leftColumn, props.rightColumn]); useEffect(() => { - // console.log(`#### ${props.widgetName} : Parent resize`); + log(`#### ${props.widgetName} : Parent resize`, shouldLog); if (!props.useAutoLayout) { setComponentWidth( (props.rightColumn - props.leftColumn) * props.parentColumnSpace - @@ -144,23 +147,40 @@ export const ResizableComponent = memo(function ResizableComponent( (props.bottomRow - props.topRow) * props.parentRowSpace - 2 * props.paddingOffset, ); + } else { + if ( + props.direction === LayoutDirection.Vertical && + props.alignItems === AlignItems.Stretch + ) { + setComponentWidth( + 64 * props.parentColumnSpace - 2 * props.paddingOffset, + ); + } else if ( + props.direction === LayoutDirection.Vertical && + props.alignItems === AlignItems.Stretch + ) { + setComponentHeight(64 * props.parentRowSpace - 2 * props.paddingOffset); + } } }, [props.parentColumnSpace, props.parentRowSpace, props.useAutoLayout]); // Calculate the dimensions of the widget, // The ResizableContainer's size prop is controlled - const dimensions: UIElementSize = { - width: - props.useAutoLayout && - props.direction === LayoutDirection.Vertical && - props.alignItems === AlignItems.Stretch - ? 64 * props.parentColumnSpace - 2 * props.paddingOffset - : (props.rightColumn - props.leftColumn) * props.parentColumnSpace - - 2 * props.paddingOffset, - height: - (props.bottomRow - props.topRow) * props.parentRowSpace - - 2 * props.paddingOffset, - }; + // const dimensions: UIElementSize = { + // width: + // props.useAutoLayout && + // props.direction === LayoutDirection.Vertical && + // props.alignItems === AlignItems.Stretch + // ? 64 * props.parentColumnSpace - 2 * props.paddingOffset + // : (props.rightColumn - props.leftColumn) * props.parentColumnSpace - + // 2 * props.paddingOffset, + // height: + // (props.bottomRow - props.topRow) * props.parentRowSpace - + // 2 * props.paddingOffset, + // }; + + log(`#### ${props.widgetName} : width - ${componentWidth}`, shouldLog); + log(`#### ${props.widgetName} : bottomRow - ${props.bottomRow}`, shouldLog); // onResize handler const getResizedPositions = ( @@ -168,9 +188,11 @@ export const ResizableComponent = memo(function ResizableComponent( position: XYCord, ) => { const delta: UIElementSize = { - height: newDimensions.height - dimensions.height, - width: newDimensions.width - dimensions.width, + height: newDimensions.height - componentHeight, + width: newDimensions.width - componentWidth, }; + log(newDimensions, shouldLog); + log(`#### ${props.widgetName} : delta - ${delta.height}`, shouldLog); const newRowCols: WidgetRowCols = computeRowCols(delta, position, props); let canResizeHorizontally = true, canResizeVertically = true; @@ -221,10 +243,12 @@ export const ResizableComponent = memo(function ResizableComponent( // Update widget, if both of the above are true. const updateSize = (newDimensions: UIElementSize, position: XYCord) => { // Get the difference in size of the widget, before and after resizing. + log(`#### ${props.widgetName} : update size`, shouldLog); const delta: UIElementSize = { - height: newDimensions.height - dimensions.height, - width: newDimensions.width - dimensions.width, + height: newDimensions.height - componentHeight, + width: newDimensions.width - componentWidth, }; + log(`#### ${props.widgetName} : delta - ${delta.height}`, shouldLog); // Get the updated Widget rows and columns props // False, if there is collision // False, if none of the rows and cols have changed. @@ -233,7 +257,8 @@ export const ResizableComponent = memo(function ResizableComponent( position, props, ); - + log("#### new row cols", shouldLog); + log(newRowCols, shouldLog); if (newRowCols) { updateWidget && updateWidget(WidgetOperations.RESIZE, props.widgetId, { @@ -270,8 +295,8 @@ export const ResizableComponent = memo(function ResizableComponent( AnalyticsUtil.logEvent("WIDGET_RESIZE_END", { widgetName: props.widgetName, widgetType: props.type, - startHeight: dimensions.height, - startWidth: dimensions.width, + startHeight: componentHeight, + startWidth: componentWidth, endHeight: newDimensions.height, endWidth: newDimensions.width, }); diff --git a/app/client/src/widgets/HorizontalLayoutWidget/component/index.tsx b/app/client/src/widgets/HorizontalLayoutWidget/component/index.tsx index d1aff6c116a6..76c48d632d2d 100644 --- a/app/client/src/widgets/HorizontalLayoutWidget/component/index.tsx +++ b/app/client/src/widgets/HorizontalLayoutWidget/component/index.tsx @@ -1,13 +1,29 @@ import React, { ReactNode } from "react"; -import styled from "styled-components"; - -const HorizontalContainer = styled.div``; +import WidgetStyleContainer, { + WidgetStyleContainerProps, +} from "components/designSystems/appsmith/WidgetStyleContainer"; +import { pick } from "lodash"; function HorizontalLayoutComponent(props: HorizontalLayoutComponentProps) { - return {props.children}; + return ( + + {props.children} + + ); } -export interface HorizontalLayoutComponentProps { +export interface HorizontalLayoutComponentProps + extends WidgetStyleContainerProps { children?: ReactNode; } diff --git a/app/client/src/widgets/VerticalLayoutWidget/component/index.tsx b/app/client/src/widgets/VerticalLayoutWidget/component/index.tsx index 1e7302c4fed7..9f8cc2f8fd67 100644 --- a/app/client/src/widgets/VerticalLayoutWidget/component/index.tsx +++ b/app/client/src/widgets/VerticalLayoutWidget/component/index.tsx @@ -1,19 +1,30 @@ +import WidgetStyleContainer, { + WidgetStyleContainerProps, +} from "components/designSystems/appsmith/WidgetStyleContainer"; import React, { ReactNode } from "react"; -import styled from "styled-components"; - -const Container = styled.div` - display: flex; - flex-direction: column; - justify-content: flex-start; - align-items: center; -`; +import { pick } from "lodash"; function VerticalLayoutComponent(props: VerticalLayoutComponentProps) { - return {props.children}; + return ( + + {props.children} + + ); } // eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface VerticalLayoutComponentProps { +export interface VerticalLayoutComponentProps + extends WidgetStyleContainerProps { children?: ReactNode; } From 4d27f42be1a32dcfa7afa558608b7fdda3558a51 Mon Sep 17 00:00:00 2001 From: Preet Date: Fri, 22 Jul 2022 12:30:02 -0400 Subject: [PATCH 011/708] clean up --- .../editorComponents/ResizableComponent.tsx | 25 ++++++++----------- .../CanvasArenas/hooks/useCanvasDragging.ts | 2 +- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/app/client/src/components/editorComponents/ResizableComponent.tsx b/app/client/src/components/editorComponents/ResizableComponent.tsx index 46b0e0c59d50..b5b4a0f9253e 100644 --- a/app/client/src/components/editorComponents/ResizableComponent.tsx +++ b/app/client/src/components/editorComponents/ResizableComponent.tsx @@ -46,14 +46,11 @@ import { DropTargetContext } from "./DropTargetComponent"; import { XYCord } from "pages/common/CanvasArenas/hooks/useCanvasDragging"; import { AlignItems, LayoutDirection } from "components/constants"; import { AutoLayoutContext } from "widgets/AutoLayoutContainerWidget/widget"; -import { log } from "utils/common"; export type ResizableComponentProps = WidgetProps & { paddingOffset: number; }; -const shouldLog = false; - export const ResizableComponent = memo(function ResizableComponent( props: ResizableComponentProps, ) { @@ -105,7 +102,7 @@ export const ResizableComponent = memo(function ResizableComponent( useEffect(() => { // Set initial dimensions - log(`#### ${props.widgetName} : Initial dimensions`, shouldLog); + // console.log(`#### ${props.widgetName} : Initial dimensions`); setComponentWidth( props.useAutoLayout && props.direction === LayoutDirection.Vertical && @@ -121,7 +118,7 @@ export const ResizableComponent = memo(function ResizableComponent( }, [props.useAutoLayout, props.direction, props.alignItems]); useEffect(() => { - log(`#### ${props.widgetName} : Manual resize`, shouldLog); + // console.log(`#### ${props.widgetName} : Manual resize`); setComponentWidth( props.useAutoLayout && props.direction === LayoutDirection.Vertical && @@ -137,7 +134,7 @@ export const ResizableComponent = memo(function ResizableComponent( }, [props.topRow, props.bottomRow, props.leftColumn, props.rightColumn]); useEffect(() => { - log(`#### ${props.widgetName} : Parent resize`, shouldLog); + // console.log(`#### ${props.widgetName} : Parent resize`); if (!props.useAutoLayout) { setComponentWidth( (props.rightColumn - props.leftColumn) * props.parentColumnSpace - @@ -179,8 +176,8 @@ export const ResizableComponent = memo(function ResizableComponent( // 2 * props.paddingOffset, // }; - log(`#### ${props.widgetName} : width - ${componentWidth}`, shouldLog); - log(`#### ${props.widgetName} : bottomRow - ${props.bottomRow}`, shouldLog); + // console.log(`#### ${props.widgetName} : width - ${componentWidth}`); + // console.log(`#### ${props.widgetName} : bottomRow - ${props.bottomRow}`); // onResize handler const getResizedPositions = ( @@ -191,8 +188,8 @@ export const ResizableComponent = memo(function ResizableComponent( height: newDimensions.height - componentHeight, width: newDimensions.width - componentWidth, }; - log(newDimensions, shouldLog); - log(`#### ${props.widgetName} : delta - ${delta.height}`, shouldLog); + // console.log(newDimensions); + // console.log(`#### ${props.widgetName} : delta - ${delta.height}`); const newRowCols: WidgetRowCols = computeRowCols(delta, position, props); let canResizeHorizontally = true, canResizeVertically = true; @@ -243,12 +240,12 @@ export const ResizableComponent = memo(function ResizableComponent( // Update widget, if both of the above are true. const updateSize = (newDimensions: UIElementSize, position: XYCord) => { // Get the difference in size of the widget, before and after resizing. - log(`#### ${props.widgetName} : update size`, shouldLog); + // console.log(`#### ${props.widgetName} : update size`); const delta: UIElementSize = { height: newDimensions.height - componentHeight, width: newDimensions.width - componentWidth, }; - log(`#### ${props.widgetName} : delta - ${delta.height}`, shouldLog); + // console.log(`#### ${props.widgetName} : delta - ${delta.height}`); // Get the updated Widget rows and columns props // False, if there is collision // False, if none of the rows and cols have changed. @@ -257,8 +254,8 @@ export const ResizableComponent = memo(function ResizableComponent( position, props, ); - log("#### new row cols", shouldLog); - log(newRowCols, shouldLog); + // console.log("#### new row cols"); + // console.log(newRowCols); if (newRowCols) { updateWidget && updateWidget(WidgetOperations.RESIZE, props.widgetId, { diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index ee6ed97ea519..8262bd190534 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -112,7 +112,7 @@ export const useCanvasDragging = ( const calculateHighlightOffsets = () => { if (useAutoLayout) { // Get all children of current auto layout container - const els = document.querySelectorAll(`.${widgetId}-auto-layout`); + const els = document.querySelectorAll(`.auto-layout-parent-${widgetId}`); if (els && els.length && offsets.length !== els.length) { // Get widget ids of all widgets being dragged // console.log(els); From c82565ee090b32d5a9888579dc1bf5e0aada95bd Mon Sep 17 00:00:00 2001 From: Preet Date: Fri, 22 Jul 2022 12:32:48 -0400 Subject: [PATCH 012/708] add isDragging check --- .../src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index 8262bd190534..4e42d1a0c839 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -110,7 +110,7 @@ export const useCanvasDragging = ( const offsets: number[] = []; const siblings: { [key: string]: number } = {}; const calculateHighlightOffsets = () => { - if (useAutoLayout) { + if (useAutoLayout && isDragging) { // Get all children of current auto layout container const els = document.querySelectorAll(`.auto-layout-parent-${widgetId}`); if (els && els.length && offsets.length !== els.length) { From bb52a8dafb13b75ad01b78bd88fb4f572b3a9042 Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 25 Jul 2022 12:59:43 -0400 Subject: [PATCH 013/708] wip --- app/client/package.json | 2 +- .../editorComponents/ResizableComponent.tsx | 40 ++++++++++++++----- .../hooks/useBlocksToBeDraggedOnCanvas.ts | 7 +++- .../CanvasArenas/hooks/useCanvasDragging.ts | 7 +++- .../src/resizable/resizenreflow/index.tsx | 1 + app/client/src/utils/WidgetPropsUtils.tsx | 11 +++-- app/client/src/widgets/BaseWidget.tsx | 4 +- .../widgets/ContainerWidget/widget/index.tsx | 2 +- 8 files changed, 52 insertions(+), 22 deletions(-) diff --git a/app/client/package.json b/app/client/package.json index 9da6b48cbd76..30bacef72d59 100644 --- a/app/client/package.json +++ b/app/client/package.json @@ -165,7 +165,7 @@ }, "scripts": { "analyze": "yarn cra-bundle-analyzer", - "start": "BROWSER=none EXTEND_ESLINT=true REACT_APP_ENVIRONMENT=DEVELOPMENT REACT_APP_CLIENT_LOG_LEVEL=debug HOST=dev.appsmith.com NODE_OPTIONS='--max-old-space-size=8192' craco start", + "start": "BROWSER=none EXTEND_ESLINT=true REACT_APP_ENVIRONMENT=DEVELOPMENT REACT_APP_CLIENT_LOG_LEVEL=debug HOST=dev.appsmith.com craco start", "build": "./build.sh", "build-local": "craco --max-old-space-size=4096 build --config craco.build.config.js", "build-staging": "REACT_APP_ENVIRONMENT=STAGING craco --max-old-space-size=4096 build --config craco.build.config.js", diff --git a/app/client/src/components/editorComponents/ResizableComponent.tsx b/app/client/src/components/editorComponents/ResizableComponent.tsx index b5b4a0f9253e..400e94c0de03 100644 --- a/app/client/src/components/editorComponents/ResizableComponent.tsx +++ b/app/client/src/components/editorComponents/ResizableComponent.tsx @@ -99,10 +99,17 @@ export const ResizableComponent = memo(function ResizableComponent( focusedWidget === props.widgetId || selectedWidget === props.widgetId || selectedWidgets.includes(props.widgetId); - + // if (props.widgetName.toLowerCase().includes("vertical")) { + // console.log(`#### ${props.widgetName}`); + // console.log(props); + // } useEffect(() => { // Set initial dimensions - // console.log(`#### ${props.widgetName} : Initial dimensions`); + if ( + props.widgetName.toLowerCase().includes("vertical") || + props.widgetName.toLowerCase().includes("button") + ) + console.log(`#### ${props.widgetName} : Initial dimensions`); setComponentWidth( props.useAutoLayout && props.direction === LayoutDirection.Vertical && @@ -118,7 +125,11 @@ export const ResizableComponent = memo(function ResizableComponent( }, [props.useAutoLayout, props.direction, props.alignItems]); useEffect(() => { - // console.log(`#### ${props.widgetName} : Manual resize`); + if ( + props.widgetName.toLowerCase().includes("vertical") || + props.widgetName.toLowerCase().includes("button") + ) + console.log(`#### ${props.widgetName} : Manual resize`); setComponentWidth( props.useAutoLayout && props.direction === LayoutDirection.Vertical && @@ -134,8 +145,13 @@ export const ResizableComponent = memo(function ResizableComponent( }, [props.topRow, props.bottomRow, props.leftColumn, props.rightColumn]); useEffect(() => { - // console.log(`#### ${props.widgetName} : Parent resize`); + if ( + props.widgetName.toLowerCase().includes("vertical") || + props.widgetName.toLowerCase().includes("button") + ) + console.log(`#### ${props.widgetName} : Parent resize`); if (!props.useAutoLayout) { + console.log(`#### ${props.widgetName} : Parent resize relayout`); setComponentWidth( (props.rightColumn - props.leftColumn) * props.parentColumnSpace - 2 * props.paddingOffset, @@ -152,11 +168,6 @@ export const ResizableComponent = memo(function ResizableComponent( setComponentWidth( 64 * props.parentColumnSpace - 2 * props.paddingOffset, ); - } else if ( - props.direction === LayoutDirection.Vertical && - props.alignItems === AlignItems.Stretch - ) { - setComponentHeight(64 * props.parentRowSpace - 2 * props.paddingOffset); } } }, [props.parentColumnSpace, props.parentRowSpace, props.useAutoLayout]); @@ -188,13 +199,20 @@ export const ResizableComponent = memo(function ResizableComponent( height: newDimensions.height - componentHeight, width: newDimensions.width - componentWidth, }; - // console.log(newDimensions); - // console.log(`#### ${props.widgetName} : delta - ${delta.height}`); + if ( + props.widgetName.toLowerCase().includes("vertical") || + props.widgetName.toLowerCase().includes("button") + ) { + console.log("#### resize position"); + console.log(newDimensions); + console.log(`#### ${props.widgetName} : delta - ${delta.height}`); + } const newRowCols: WidgetRowCols = computeRowCols(delta, position, props); let canResizeHorizontally = true, canResizeVertically = true; // this is required for list widget so that template have no collision + console.log(props.parentColumnSpace); if (props.ignoreCollision) return { canResizeHorizontally, diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts b/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts index 63228d89dd28..e4d36961d198 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts @@ -325,7 +325,12 @@ export const useBlocksToBeDraggedOnCanvas = ({ snapColumnSpace, snapRowSpace, newWidget.detachFromLayout ? MAIN_CONTAINER_WIDGET_ID : widgetId, - { width: each.width, height: each.height }, + { + width: each.width, + height: each.height, + }, + direction === LayoutDirection.Vertical && + alignItems === AlignItems.Stretch, ); return { ...each, diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index 4e42d1a0c839..f99b97ba6f48 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -110,7 +110,10 @@ export const useCanvasDragging = ( const offsets: number[] = []; const siblings: { [key: string]: number } = {}; const calculateHighlightOffsets = () => { - if (useAutoLayout && isDragging) { + if (useAutoLayout && isDragging && isCurrentDraggedCanvas) { + console.log(widgetId); + console.log(slidingArenaRef.current); + console.log(isCurrentDraggedCanvas); // Get all children of current auto layout container const els = document.querySelectorAll(`.auto-layout-parent-${widgetId}`); if (els && els.length && offsets.length !== els.length) { @@ -591,7 +594,7 @@ export const useCanvasDragging = ( renderNewRows(delta); } else if (!isUpdatingRows) { triggerReflow(e, firstMove); - highlightDropPosition(e); + isCurrentDraggedCanvas && highlightDropPosition(e); renderBlocks(); } scrollObj.lastMouseMoveEvent = { diff --git a/app/client/src/resizable/resizenreflow/index.tsx b/app/client/src/resizable/resizenreflow/index.tsx index 90147f84cc28..851f55c2d830 100644 --- a/app/client/src/resizable/resizenreflow/index.tsx +++ b/app/client/src/resizable/resizenreflow/index.tsx @@ -482,6 +482,7 @@ export function ReflowResizable(props: ResizableProps) { reflowedPosition?.height === undefined ? newDimensions.height : reflowedPosition.height - 2 * WIDGET_PADDING; + return ( { const [leftColumn, topRow] = getDropZoneOffsets( parentColumnSpace, @@ -253,9 +254,11 @@ export const widgetOperationParams = ( bottomRow: Math.round( topRow + widgetSizeUpdates.height / parentRowSpace, ), - rightColumn: Math.round( - leftColumn + widgetSizeUpdates.width / parentColumnSpace, - ), + rightColumn: fullWidth + ? 64 + : Math.round( + leftColumn + widgetSizeUpdates.width / parentColumnSpace, + ), parentId: widget.parentId, newParentId: parentWidgetId, }, @@ -264,7 +267,7 @@ export const widgetOperationParams = ( // Therefore, this is an operation to add child to this container } const widgetDimensions = { - columns: widget.columns, + columns: fullWidth ? 64 : widget.columns, rows: widget.rows, }; diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index fb13605cd2d1..a574ab3ae7f4 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -347,8 +347,8 @@ abstract class BaseWidget< private getWidgetView(): ReactNode { let content: ReactNode; - // console.log(`${this.props.widgetName} =========`); - // console.log(this.props); + console.log(`${this.props.widgetName} =========`); + console.log(this.props); switch (this.props.renderMode) { case RenderModes.CANVAS: content = this.getCanvasView(); diff --git a/app/client/src/widgets/ContainerWidget/widget/index.tsx b/app/client/src/widgets/ContainerWidget/widget/index.tsx index 476b9c49e1de..f9008dab595b 100644 --- a/app/client/src/widgets/ContainerWidget/widget/index.tsx +++ b/app/client/src/widgets/ContainerWidget/widget/index.tsx @@ -201,7 +201,7 @@ class ContainerWidget extends BaseWidget< }; renderAsContainerComponent(props: ContainerWidgetProps) { - console.log(`${props.widgetName} : ${props.widgetId} =======`); + // console.log(`${props.widgetName} : ${props.widgetId} =======`); // console.log(props); const snapRows = getCanvasSnapRows(props.bottomRow, props.canExtend); return ( From e1d0eb9319ffe29692d6f4f35d55bfcf68b3ca29 Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 25 Jul 2022 15:20:11 -0400 Subject: [PATCH 014/708] clean up --- .../editorComponents/ResizableComponent.tsx | 30 ++++--------------- .../CanvasArenas/hooks/useCanvasDragging.ts | 7 ++--- app/client/src/widgets/BaseWidget.tsx | 4 +-- 3 files changed, 9 insertions(+), 32 deletions(-) diff --git a/app/client/src/components/editorComponents/ResizableComponent.tsx b/app/client/src/components/editorComponents/ResizableComponent.tsx index 400e94c0de03..7352f97a5245 100644 --- a/app/client/src/components/editorComponents/ResizableComponent.tsx +++ b/app/client/src/components/editorComponents/ResizableComponent.tsx @@ -105,11 +105,7 @@ export const ResizableComponent = memo(function ResizableComponent( // } useEffect(() => { // Set initial dimensions - if ( - props.widgetName.toLowerCase().includes("vertical") || - props.widgetName.toLowerCase().includes("button") - ) - console.log(`#### ${props.widgetName} : Initial dimensions`); + // console.log(`#### ${props.widgetName} : Initial dimensions`); setComponentWidth( props.useAutoLayout && props.direction === LayoutDirection.Vertical && @@ -125,11 +121,7 @@ export const ResizableComponent = memo(function ResizableComponent( }, [props.useAutoLayout, props.direction, props.alignItems]); useEffect(() => { - if ( - props.widgetName.toLowerCase().includes("vertical") || - props.widgetName.toLowerCase().includes("button") - ) - console.log(`#### ${props.widgetName} : Manual resize`); + // console.log(`#### ${props.widgetName} : Manual resize`); setComponentWidth( props.useAutoLayout && props.direction === LayoutDirection.Vertical && @@ -145,13 +137,8 @@ export const ResizableComponent = memo(function ResizableComponent( }, [props.topRow, props.bottomRow, props.leftColumn, props.rightColumn]); useEffect(() => { - if ( - props.widgetName.toLowerCase().includes("vertical") || - props.widgetName.toLowerCase().includes("button") - ) - console.log(`#### ${props.widgetName} : Parent resize`); + // console.log(`#### ${props.widgetName} : Parent resize`); if (!props.useAutoLayout) { - console.log(`#### ${props.widgetName} : Parent resize relayout`); setComponentWidth( (props.rightColumn - props.leftColumn) * props.parentColumnSpace - 2 * props.paddingOffset, @@ -199,20 +186,12 @@ export const ResizableComponent = memo(function ResizableComponent( height: newDimensions.height - componentHeight, width: newDimensions.width - componentWidth, }; - if ( - props.widgetName.toLowerCase().includes("vertical") || - props.widgetName.toLowerCase().includes("button") - ) { - console.log("#### resize position"); - console.log(newDimensions); - console.log(`#### ${props.widgetName} : delta - ${delta.height}`); - } + const newRowCols: WidgetRowCols = computeRowCols(delta, position, props); let canResizeHorizontally = true, canResizeVertically = true; // this is required for list widget so that template have no collision - console.log(props.parentColumnSpace); if (props.ignoreCollision) return { canResizeHorizontally, @@ -234,6 +213,7 @@ export const ResizableComponent = memo(function ResizableComponent( ) { canResizeVertically = false; } + if (props.useAutoLayout) canResizeVertically = true; const resizedPositions = { id: props.widgetId, diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index f99b97ba6f48..4e42d1a0c839 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -110,10 +110,7 @@ export const useCanvasDragging = ( const offsets: number[] = []; const siblings: { [key: string]: number } = {}; const calculateHighlightOffsets = () => { - if (useAutoLayout && isDragging && isCurrentDraggedCanvas) { - console.log(widgetId); - console.log(slidingArenaRef.current); - console.log(isCurrentDraggedCanvas); + if (useAutoLayout && isDragging) { // Get all children of current auto layout container const els = document.querySelectorAll(`.auto-layout-parent-${widgetId}`); if (els && els.length && offsets.length !== els.length) { @@ -594,7 +591,7 @@ export const useCanvasDragging = ( renderNewRows(delta); } else if (!isUpdatingRows) { triggerReflow(e, firstMove); - isCurrentDraggedCanvas && highlightDropPosition(e); + highlightDropPosition(e); renderBlocks(); } scrollObj.lastMouseMoveEvent = { diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index a574ab3ae7f4..fb13605cd2d1 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -347,8 +347,8 @@ abstract class BaseWidget< private getWidgetView(): ReactNode { let content: ReactNode; - console.log(`${this.props.widgetName} =========`); - console.log(this.props); + // console.log(`${this.props.widgetName} =========`); + // console.log(this.props); switch (this.props.renderMode) { case RenderModes.CANVAS: content = this.getCanvasView(); From 238f0ae1a67d160372bbdc8bd92157f73fecad36 Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 25 Jul 2022 17:04:00 -0400 Subject: [PATCH 015/708] working drag translation --- .../CanvasArenas/hooks/useCanvasDragging.ts | 102 ++++++++++++++---- .../ContainerWidget/component/index.tsx | 4 + 2 files changed, 86 insertions(+), 20 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index 4e42d1a0c839..758be1fc12b3 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -3,7 +3,7 @@ import { CONTAINER_GRID_PADDING, GridDefaults, } from "constants/WidgetConstants"; -import { debounce, isEmpty, throttle } from "lodash"; +import { debounce, isEmpty, isNumber, throttle } from "lodash"; import { CanvasDraggingArenaProps } from "pages/common/CanvasArenas/CanvasDraggingArena"; import React, { useEffect, useRef } from "react"; import { useSelector } from "react-redux"; @@ -39,6 +39,8 @@ export interface XYCord { const CONTAINER_JUMP_ACC_THRESHOLD = 8000; const CONTAINER_JUMP_SPEED_THRESHOLD = 800; +let dragBlocksSize = 0; +let lastTranslatedIndex: number; //Since useCanvasDragging's Instance changes during container jump, metrics is stored outside const containerJumpThresholdMetrics = new ContainerJumpMetrics<{ speed?: number; @@ -107,48 +109,87 @@ export const useCanvasDragging = ( const reflow = useRef(); reflow.current = useReflow(draggingSpaces, widgetId || "", gridProps); + + const cleanUpTempStyles = () => { + // reset display of all dragged blocks + const els = document.querySelectorAll(`.auto-layout-parent-${widgetId}`); + if (els && els.length) { + els.forEach((el) => { + (el as any).classList.remove("auto-temp-no-display"); + (el as any).style.transform = null; + }); + } + + // reset state + dragBlocksSize = 0; + lastTranslatedIndex = -10; + }; + + if (!isDragging) { + cleanUpTempStyles(); + } + const offsets: number[] = []; - const siblings: { [key: string]: number } = {}; + // let siblings: { [key: string]: number } = {}; + const siblingElements: any[] = []; const calculateHighlightOffsets = () => { + if (isNewWidget) dragBlocksSize = blocksToDraw[0].height; if (useAutoLayout && isDragging) { + console.log(isCurrentDraggedCanvas); + console.log(isChildOfCanvas); // Get all children of current auto layout container const els = document.querySelectorAll(`.auto-layout-parent-${widgetId}`); if (els && els.length && offsets.length !== els.length) { // Get widget ids of all widgets being dragged // console.log(els); const blocks = blocksToDraw.map((block) => block.widgetId); + console.log("*********"); els.forEach((el) => { // console.log((el as any).offsetParent); // Extract widget id of current widget - const mClass = el.className.split("auto-layout-child-")[1]; + const mClass = el.className + .split("auto-layout-child-")[1] + .split(" ")[0]; + console.log(`parentId: ${widgetId}`); + console.log(`widgetID: ${mClass}`); + console.log(`blocks: ${blocks}`); + console.log(blocks); /** * If the widget is also being dragged, * then discount its presence from offset calculation. */ - if (blocks && blocks.length && blocks.indexOf(mClass) !== -1) return; - const mOffset = - direction === LayoutDirection.Vertical - ? (el as any).offsetTop - : (el as any).offsetLeft; - offsets.push(mOffset); - siblings[mClass] = mOffset; + if (blocks && blocks.length && blocks.indexOf(mClass) !== -1) { + // Temporarily hide the dragged widget + dragBlocksSize += (el as any).clientHeight; + (el as any).classList.add("auto-temp-no-display"); + return; + } else { + const mOffset = + direction === LayoutDirection.Vertical + ? (el as any).offsetTop + : (el as any).offsetLeft; + console.log(`offset: ${mOffset}`); + offsets.push(mOffset); + console.log(offsets); + // siblings[mClass] = mOffset; + siblingElements.push(el); + } }); offsets.push( direction === LayoutDirection.Vertical - ? (els[els.length - 1] as any).offsetTop + - els[els.length - 1].clientHeight + + ? (siblingElements[siblingElements.length - 1] as any).offsetTop + + siblingElements[siblingElements.length - 1].clientHeight + 8 - : (els[els.length - 1] as any).offsetLeft + - els[els.length - 1].clientWidth + + : (siblingElements[siblingElements.length - 1] as any).offsetLeft + + siblingElements[siblingElements.length - 1].clientWidth + 8, ); + console.log(offsets); } } }; calculateHighlightOffsets(); - // console.log("*********"); - // console.log(offsets); - // console.log(siblings); + const { setDraggingCanvas, setDraggingNewWidget, @@ -340,10 +381,11 @@ export const useCanvasDragging = ( ? currentRectanglesToDraw[0].top : currentRectanglesToDraw[0].left, ); - // console.log(`pos: ${pos}`); - if (pos !== undefined && useAutoLayout) + console.log(`#### pos: ${pos}`); + if (pos !== undefined && useAutoLayout) { + // cleanUpTempStyles(); updateChildrenPositions(pos, currentRectanglesToDraw); - else + } else onDrop(currentRectanglesToDraw, reflowedPositionsUpdatesWidgets); } startPoints.top = defaultHandlePositions.top; @@ -604,6 +646,25 @@ export const useCanvasDragging = ( onFirstMoveOnCanvas(e); } }; + const translateSiblings = (position: number): void => { + let dropIndex = 0; + if (isNumber(position)) dropIndex = offsets.indexOf(position); + + if (dropIndex === lastTranslatedIndex) return; + // Get all siblings after the highlighted drop position + const arr = [...siblingElements]; + + // translate each element in the appropriate direction + const x = + direction === LayoutDirection.Horizontal ? dragBlocksSize : 0; + const y = direction === LayoutDirection.Vertical ? dragBlocksSize : 0; + arr.forEach((each, index) => { + if (index < dropIndex) { + each.style.transform = null; + } else each.style.transform = `translate(${x}px, ${y}px)`; + }); + lastTranslatedIndex = dropIndex; + }; const highlightDropPosition = (e: any) => { if (!useAutoLayout) return; const pos: number | undefined = getHighlightPosition(e); @@ -615,6 +676,7 @@ export const useCanvasDragging = ( dropPositionRef.current.style.top = pos - 6 + "px"; else dropPositionRef.current.style.left = pos - 6 + "px"; } + translateSiblings(pos); }; const getHighlightPosition = (e: any, val?: number) => { let base: number[] = []; diff --git a/app/client/src/widgets/ContainerWidget/component/index.tsx b/app/client/src/widgets/ContainerWidget/component/index.tsx index b68a609d6f9a..12e76135e78c 100644 --- a/app/client/src/widgets/ContainerWidget/component/index.tsx +++ b/app/client/src/widgets/ContainerWidget/component/index.tsx @@ -58,6 +58,10 @@ const StyledContainerComponent = styled.div< : props.backgroundColor; }}; } + + .auto-temp-no-display { + display: none; + } `; export const FlexContainer = styled.div<{ From 17675570cc1b87407e4c6c9569aba006005e9152 Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 25 Jul 2022 17:07:37 -0400 Subject: [PATCH 016/708] remove logs --- .../CanvasArenas/hooks/useCanvasDragging.ts | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index 758be1fc12b3..ce07380d7b4a 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -135,25 +135,23 @@ export const useCanvasDragging = ( const calculateHighlightOffsets = () => { if (isNewWidget) dragBlocksSize = blocksToDraw[0].height; if (useAutoLayout && isDragging) { - console.log(isCurrentDraggedCanvas); - console.log(isChildOfCanvas); // Get all children of current auto layout container const els = document.querySelectorAll(`.auto-layout-parent-${widgetId}`); if (els && els.length && offsets.length !== els.length) { // Get widget ids of all widgets being dragged // console.log(els); const blocks = blocksToDraw.map((block) => block.widgetId); - console.log("*********"); + // console.log("*********"); els.forEach((el) => { // console.log((el as any).offsetParent); // Extract widget id of current widget const mClass = el.className .split("auto-layout-child-")[1] .split(" ")[0]; - console.log(`parentId: ${widgetId}`); - console.log(`widgetID: ${mClass}`); - console.log(`blocks: ${blocks}`); - console.log(blocks); + // console.log(`parentId: ${widgetId}`); + // console.log(`widgetID: ${mClass}`); + // console.log(`blocks: ${blocks}`); + // console.log(blocks); /** * If the widget is also being dragged, * then discount its presence from offset calculation. @@ -168,9 +166,9 @@ export const useCanvasDragging = ( direction === LayoutDirection.Vertical ? (el as any).offsetTop : (el as any).offsetLeft; - console.log(`offset: ${mOffset}`); + // console.log(`offset: ${mOffset}`); offsets.push(mOffset); - console.log(offsets); + // console.log(offsets); // siblings[mClass] = mOffset; siblingElements.push(el); } @@ -184,7 +182,7 @@ export const useCanvasDragging = ( siblingElements[siblingElements.length - 1].clientWidth + 8, ); - console.log(offsets); + // console.log(offsets); } } }; From 19ea7b2a9dfd3bb0aad3660122dafc3bdadd4707 Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 25 Jul 2022 21:33:35 -0400 Subject: [PATCH 017/708] add transition duration --- .../editorComponents/ResizableComponent.tsx | 1 - .../CanvasArenas/hooks/useCanvasDragging.ts | 42 ++++++++++--------- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/app/client/src/components/editorComponents/ResizableComponent.tsx b/app/client/src/components/editorComponents/ResizableComponent.tsx index 7352f97a5245..e2f71889cdff 100644 --- a/app/client/src/components/editorComponents/ResizableComponent.tsx +++ b/app/client/src/components/editorComponents/ResizableComponent.tsx @@ -213,7 +213,6 @@ export const ResizableComponent = memo(function ResizableComponent( ) { canResizeVertically = false; } - if (props.useAutoLayout) canResizeVertically = true; const resizedPositions = { id: props.widgetId, diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index ce07380d7b4a..9e4daef714b0 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -132,8 +132,12 @@ export const useCanvasDragging = ( const offsets: number[] = []; // let siblings: { [key: string]: number } = {}; const siblingElements: any[] = []; + const isVertical = direction === LayoutDirection.Vertical; const calculateHighlightOffsets = () => { - if (isNewWidget) dragBlocksSize = blocksToDraw[0].height; + if (isNewWidget) + dragBlocksSize = isVertical + ? blocksToDraw[0].height + : blocksToDraw[0].width; if (useAutoLayout && isDragging) { // Get all children of current auto layout container const els = document.querySelectorAll(`.auto-layout-parent-${widgetId}`); @@ -158,14 +162,16 @@ export const useCanvasDragging = ( */ if (blocks && blocks.length && blocks.indexOf(mClass) !== -1) { // Temporarily hide the dragged widget - dragBlocksSize += (el as any).clientHeight; + dragBlocksSize += isVertical + ? (el as any).clientHeight + : (el as any).clientWidth; + // console.log(`block size: ${dragBlocksSize}`); (el as any).classList.add("auto-temp-no-display"); return; } else { - const mOffset = - direction === LayoutDirection.Vertical - ? (el as any).offsetTop - : (el as any).offsetLeft; + const mOffset = isVertical + ? (el as any).offsetTop + : (el as any).offsetLeft; // console.log(`offset: ${mOffset}`); offsets.push(mOffset); // console.log(offsets); @@ -174,7 +180,7 @@ export const useCanvasDragging = ( } }); offsets.push( - direction === LayoutDirection.Vertical + isVertical ? (siblingElements[siblingElements.length - 1] as any).offsetTop + siblingElements[siblingElements.length - 1].clientHeight + 8 @@ -375,11 +381,11 @@ export const useCanvasDragging = ( // console.log(currentRectanglesToDraw); // console.log(reflowedPositionsUpdatesWidgets); const pos = getDropPosition( - direction === LayoutDirection.Vertical + isVertical ? currentRectanglesToDraw[0].top : currentRectanglesToDraw[0].left, ); - console.log(`#### pos: ${pos}`); + // console.log(`#### pos: ${pos}`); if (pos !== undefined && useAutoLayout) { // cleanUpTempStyles(); updateChildrenPositions(pos, currentRectanglesToDraw); @@ -653,13 +659,15 @@ export const useCanvasDragging = ( const arr = [...siblingElements]; // translate each element in the appropriate direction - const x = - direction === LayoutDirection.Horizontal ? dragBlocksSize : 0; - const y = direction === LayoutDirection.Vertical ? dragBlocksSize : 0; + const x = !isVertical ? dragBlocksSize : 0; + const y = isVertical ? dragBlocksSize : 0; arr.forEach((each, index) => { if (index < dropIndex) { each.style.transform = null; - } else each.style.transform = `translate(${x}px, ${y}px)`; + } else { + each.style.transform = `translate(${x}px, ${y}px)`; + each.style.transitionDuration = "0.2s"; + } }); lastTranslatedIndex = dropIndex; }; @@ -670,8 +678,7 @@ export const useCanvasDragging = ( // console.log(`#### ref: ${dropPositionRef.current}`); if (dropPositionRef && dropPositionRef.current) { dropPositionRef.current.style.opacity = "1"; - if (direction === LayoutDirection.Vertical) - dropPositionRef.current.style.top = pos - 6 + "px"; + if (isVertical) dropPositionRef.current.style.top = pos - 6 + "px"; else dropPositionRef.current.style.left = pos - 6 + "px"; } translateSiblings(pos); @@ -680,10 +687,7 @@ export const useCanvasDragging = ( let base: number[] = []; if (!offsets || !offsets.length) base = [8]; else base = offsets; - const pos = - (direction === LayoutDirection.Vertical - ? e?.offsetY - : e?.offsetX) || val; + const pos = (isVertical ? e?.offsetY : e?.offsetX) || val; // console.log(e); // console.log(pos); const arr = [...base].sort((a, b) => { From 9412feb9b434e586b15b6b8b8eb36a875f983748 Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 25 Jul 2022 21:48:50 -0400 Subject: [PATCH 018/708] remove unused imports --- .../components/editorComponents/ResizableComponent.tsx | 1 - app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts | 2 +- .../src/widgets/ContainerWidget/component/index.tsx | 8 +------- app/client/src/widgets/ContainerWidget/widget/index.tsx | 4 ---- 4 files changed, 2 insertions(+), 13 deletions(-) diff --git a/app/client/src/components/editorComponents/ResizableComponent.tsx b/app/client/src/components/editorComponents/ResizableComponent.tsx index e2f71889cdff..381b3dbb780c 100644 --- a/app/client/src/components/editorComponents/ResizableComponent.tsx +++ b/app/client/src/components/editorComponents/ResizableComponent.tsx @@ -45,7 +45,6 @@ import { GridDefaults } from "constants/WidgetConstants"; import { DropTargetContext } from "./DropTargetComponent"; import { XYCord } from "pages/common/CanvasArenas/hooks/useCanvasDragging"; import { AlignItems, LayoutDirection } from "components/constants"; -import { AutoLayoutContext } from "widgets/AutoLayoutContainerWidget/widget"; export type ResizableComponentProps = WidgetProps & { paddingOffset: number; diff --git a/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts b/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts index be00963702e0..689c6e3f3d91 100644 --- a/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts +++ b/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts @@ -10,7 +10,7 @@ import { } from "reducers/entityReducers/canvasWidgetsReducer"; import { all, call, put, select, takeLatest } from "redux-saga/effects"; import log from "loglevel"; -import { cloneDeep, isNumber } from "lodash"; +import { cloneDeep } from "lodash"; import { updateAndSaveLayout, WidgetAddChild } from "actions/pageActions"; import { calculateDropTargetRows } from "components/editorComponents/DropTargetUtils"; import { GridDefaults } from "constants/WidgetConstants"; diff --git a/app/client/src/widgets/ContainerWidget/component/index.tsx b/app/client/src/widgets/ContainerWidget/component/index.tsx index 12e76135e78c..7e719ef01a4d 100644 --- a/app/client/src/widgets/ContainerWidget/component/index.tsx +++ b/app/client/src/widgets/ContainerWidget/component/index.tsx @@ -1,10 +1,4 @@ -import React, { - ReactElement, - ReactNode, - useRef, - useEffect, - RefObject, -} from "react"; +import React, { ReactNode, useRef, useEffect, RefObject } from "react"; import styled, { css } from "styled-components"; import tinycolor from "tinycolor2"; import { invisible } from "constants/DefaultTheme"; diff --git a/app/client/src/widgets/ContainerWidget/widget/index.tsx b/app/client/src/widgets/ContainerWidget/widget/index.tsx index f9008dab595b..d90e8edc6a82 100644 --- a/app/client/src/widgets/ContainerWidget/widget/index.tsx +++ b/app/client/src/widgets/ContainerWidget/widget/index.tsx @@ -266,8 +266,4 @@ export interface ContainerWidgetProps noPad?: boolean; } -interface ContainerWidgetState extends WidgetState { - items?: T[]; -} - export default ContainerWidget; From e50723940dccb3e89b43a89977e6a6513d8a5778 Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 25 Jul 2022 22:19:52 -0400 Subject: [PATCH 019/708] remove logs --- app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts b/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts index 689c6e3f3d91..27289d0c9e8a 100644 --- a/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts +++ b/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts @@ -330,7 +330,7 @@ function* autolayoutReorderSaga( yield put(updateAndSaveLayout(updatedWidgets)); log.debug("reorder computations took", performance.now() - start, "ms"); } catch (e) { - console.error(e); + // console.error(e); } } @@ -409,7 +409,7 @@ function* addWidgetAndReorderSaga( yield put(updateAndSaveLayout(updatedWidgetsOnMove)); log.debug("reorder computations took", performance.now() - start, "ms"); } catch (e) { - console.error(e); + // console.error(e); } } From e5c53b4d66db24ee053cf415e19687762627ef2e Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 26 Jul 2022 16:29:25 -0400 Subject: [PATCH 020/708] restrict manual resizing based on props and prevent reflow in flex child --- .../editorComponents/ResizableComponent.tsx | 58 ++++++++++--------- .../src/resizable/resizenreflow/index.tsx | 7 ++- app/client/src/utils/autoLayoutContext.ts | 15 +++++ .../widget/index.tsx | 17 +----- .../HorizontalLayoutWidget/widget/index.tsx | 2 +- .../VerticalLayoutWidget/widget/index.tsx | 2 +- 6 files changed, 53 insertions(+), 48 deletions(-) create mode 100644 app/client/src/utils/autoLayoutContext.ts diff --git a/app/client/src/components/editorComponents/ResizableComponent.tsx b/app/client/src/components/editorComponents/ResizableComponent.tsx index 381b3dbb780c..a1c76dcb60fb 100644 --- a/app/client/src/components/editorComponents/ResizableComponent.tsx +++ b/app/client/src/components/editorComponents/ResizableComponent.tsx @@ -45,6 +45,7 @@ import { GridDefaults } from "constants/WidgetConstants"; import { DropTargetContext } from "./DropTargetComponent"; import { XYCord } from "pages/common/CanvasArenas/hooks/useCanvasDragging"; import { AlignItems, LayoutDirection } from "components/constants"; +import { AutoLayoutContext } from "utils/autoLayoutContext"; export type ResizableComponentProps = WidgetProps & { paddingOffset: number; @@ -57,12 +58,14 @@ export const ResizableComponent = memo(function ResizableComponent( const [componentHeight, setComponentHeight] = useState(0); // Fetch information from the context const { updateWidget } = useContext(EditorContext); - // const { - // alignItems, - // direction, - // disabledResizeHandles, - // useAutoLayout, - // } = useContext(AutoLayoutContext || null); + const { + alignItems, + direction, + disabledResizeHandles, + useAutoLayout, + } = useContext(AutoLayoutContext || null); + const isHorizontallyStretched = + direction === LayoutDirection.Vertical && alignItems === AlignItems.Stretch; const canvasWidgets = useSelector(getCanvasWidgets); const isCommentMode = useSelector(commentModeSelector); @@ -104,11 +107,10 @@ export const ResizableComponent = memo(function ResizableComponent( // } useEffect(() => { // Set initial dimensions - // console.log(`#### ${props.widgetName} : Initial dimensions`); + // if (props.widgetName.toLowerCase().includes("button")) + // console.log(`#### ${props.widgetName} : Initial dimensions`); setComponentWidth( - props.useAutoLayout && - props.direction === LayoutDirection.Vertical && - props.alignItems === AlignItems.Stretch + useAutoLayout && isHorizontallyStretched ? 64 * props.parentColumnSpace - 2 * props.paddingOffset : (props.rightColumn - props.leftColumn) * props.parentColumnSpace - 2 * props.paddingOffset, @@ -117,14 +119,13 @@ export const ResizableComponent = memo(function ResizableComponent( (props.bottomRow - props.topRow) * props.parentRowSpace - 2 * props.paddingOffset, ); - }, [props.useAutoLayout, props.direction, props.alignItems]); + }, [useAutoLayout, direction, alignItems]); useEffect(() => { - // console.log(`#### ${props.widgetName} : Manual resize`); + // if (props.widgetName.toLowerCase().includes("button")) + // console.log(`#### ${props.widgetName} : Manual resize`); setComponentWidth( - props.useAutoLayout && - props.direction === LayoutDirection.Vertical && - props.alignItems === AlignItems.Stretch + useAutoLayout && isHorizontallyStretched ? 64 * props.parentColumnSpace - 2 * props.paddingOffset : (props.rightColumn - props.leftColumn) * props.parentColumnSpace - 2 * props.paddingOffset, @@ -136,8 +137,9 @@ export const ResizableComponent = memo(function ResizableComponent( }, [props.topRow, props.bottomRow, props.leftColumn, props.rightColumn]); useEffect(() => { - // console.log(`#### ${props.widgetName} : Parent resize`); - if (!props.useAutoLayout) { + // if (props.widgetName.toLowerCase().includes("button")) + // console.log(`#### ${props.widgetName} : Parent resize`); + if (!useAutoLayout) { setComponentWidth( (props.rightColumn - props.leftColumn) * props.parentColumnSpace - 2 * props.paddingOffset, @@ -147,24 +149,21 @@ export const ResizableComponent = memo(function ResizableComponent( 2 * props.paddingOffset, ); } else { - if ( - props.direction === LayoutDirection.Vertical && - props.alignItems === AlignItems.Stretch - ) { + if (isHorizontallyStretched) { setComponentWidth( 64 * props.parentColumnSpace - 2 * props.paddingOffset, ); } } - }, [props.parentColumnSpace, props.parentRowSpace, props.useAutoLayout]); + }, [props.parentColumnSpace, props.parentRowSpace, useAutoLayout]); // Calculate the dimensions of the widget, // The ResizableContainer's size prop is controlled // const dimensions: UIElementSize = { // width: - // props.useAutoLayout && - // props.direction === LayoutDirection.Vertical && - // props.alignItems === AlignItems.Stretch + // useAutoLayout && + // direction === LayoutDirection.Vertical && + // alignItems === AlignItems.Stretch // ? 64 * props.parentColumnSpace - 2 * props.paddingOffset // : (props.rightColumn - props.leftColumn) * props.parentColumnSpace - // 2 * props.paddingOffset, @@ -318,9 +317,11 @@ export const ResizableComponent = memo(function ResizableComponent( topRight: TopRightHandleStyles, bottomLeft: BottomLeftHandleStyles, }; - - return omit(allHandles, get(props, "disabledResizeHandles", [])); - }, [props]); + let handlesToOmit = get(props, "disabledResizeHandles", []); + if (disabledResizeHandles && disabledResizeHandles.length) + handlesToOmit = [...handlesToOmit, ...disabledResizeHandles]; + return omit(allHandles, handlesToOmit); + }, [props, disabledResizeHandles]); const isEnabled = !isDragging && @@ -369,6 +370,7 @@ export const ResizableComponent = memo(function ResizableComponent( parentId={props.parentId} snapGrid={{ x: props.parentColumnSpace, y: props.parentRowSpace }} updateBottomRow={updateBottomRow} + useAutoLayout={useAutoLayout} widgetId={props.widgetId} // Used only for performance tracking, can be removed after optimization. zWidgetId={props.widgetId} diff --git a/app/client/src/resizable/resizenreflow/index.tsx b/app/client/src/resizable/resizenreflow/index.tsx index 851f55c2d830..403d1876b8b4 100644 --- a/app/client/src/resizable/resizenreflow/index.tsx +++ b/app/client/src/resizable/resizenreflow/index.tsx @@ -155,6 +155,7 @@ type ResizableProps = { gridProps: GridProps; zWidgetType?: string; zWidgetId?: string; + useAutoLayout?: boolean; }; export function ReflowResizable(props: ResizableProps) { @@ -473,13 +474,13 @@ export function ReflowResizable(props: ResizableProps) { snapGrid={props.snapGrid} /> )); - + // Don't alter dimensions on temp reflow in children of auto layouts const widgetWidth = - reflowedPosition?.width === undefined + reflowedPosition?.width === undefined || props?.useAutoLayout ? newDimensions.width : reflowedPosition.width - 2 * WIDGET_PADDING; const widgetHeight = - reflowedPosition?.height === undefined + reflowedPosition?.height === undefined || props?.useAutoLayout ? newDimensions.height : reflowedPosition.height - 2 * WIDGET_PADDING; diff --git a/app/client/src/utils/autoLayoutContext.ts b/app/client/src/utils/autoLayoutContext.ts new file mode 100644 index 000000000000..a2116845b04a --- /dev/null +++ b/app/client/src/utils/autoLayoutContext.ts @@ -0,0 +1,15 @@ +import { Context, createContext } from "react"; +import { + AlignItems, + JustifyContent, + LayoutDirection, +} from "components/constants"; + +export const AutoLayoutContext: Context<{ + useAutoLayout?: boolean; + direction?: LayoutDirection; + justifyContent?: JustifyContent; + overflow?: string; + disabledResizeHandles?: string[]; + alignItems?: AlignItems; +}> = createContext({}); diff --git a/app/client/src/widgets/AutoLayoutContainerWidget/widget/index.tsx b/app/client/src/widgets/AutoLayoutContainerWidget/widget/index.tsx index 1885a9285583..63c20cd66f2c 100644 --- a/app/client/src/widgets/AutoLayoutContainerWidget/widget/index.tsx +++ b/app/client/src/widgets/AutoLayoutContainerWidget/widget/index.tsx @@ -1,4 +1,4 @@ -import React, { Context, createContext } from "react"; +import React from "react"; import { compact, map, sortBy } from "lodash"; import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; @@ -6,22 +6,9 @@ import WidgetFactory, { DerivedPropertiesMap } from "utils/WidgetFactory"; import AutoLayoutContainerComponent from "../component"; import { ValidationTypes } from "constants/WidgetValidation"; -import { - AlignItems, - JustifyContent, - LayoutDirection, -} from "components/constants"; +import { JustifyContent, LayoutDirection } from "components/constants"; import { ContainerStyle } from "widgets/ContainerWidget/component"; -export const AutoLayoutContext: Context<{ - useAutoLayout?: boolean; - direction?: LayoutDirection; - justifyContent?: JustifyContent; - overflow?: string; - disabledResizeHandles?: string[]; - alignItems?: AlignItems; -}> = createContext({}); - class AutoLayoutContainerWidget extends BaseWidget< AutoLayoutContainerWidgetProps, WidgetState diff --git a/app/client/src/widgets/HorizontalLayoutWidget/widget/index.tsx b/app/client/src/widgets/HorizontalLayoutWidget/widget/index.tsx index 589d48cd8997..c4701342a0bc 100644 --- a/app/client/src/widgets/HorizontalLayoutWidget/widget/index.tsx +++ b/app/client/src/widgets/HorizontalLayoutWidget/widget/index.tsx @@ -11,7 +11,6 @@ import { JustifyContent, LayoutDirection, } from "components/constants"; -import { AutoLayoutContext } from "widgets/AutoLayoutContainerWidget/widget"; import { getCanvasSnapRows } from "utils/WidgetPropsUtils"; import { ValidationTypes } from "constants/WidgetValidation"; import { @@ -23,6 +22,7 @@ import { import { map } from "lodash"; import { ContainerStyle } from "widgets/ContainerWidget/component"; import WidgetsMultiSelectBox from "pages/Editor/WidgetsMultiSelectBox"; +import { AutoLayoutContext } from "utils/autoLayoutContext"; class HorizontalLayoutWidget extends BaseWidget< HorizontalLayoutWidgetProps, diff --git a/app/client/src/widgets/VerticalLayoutWidget/widget/index.tsx b/app/client/src/widgets/VerticalLayoutWidget/widget/index.tsx index c79ce5faf87d..cc8725d4943d 100644 --- a/app/client/src/widgets/VerticalLayoutWidget/widget/index.tsx +++ b/app/client/src/widgets/VerticalLayoutWidget/widget/index.tsx @@ -22,7 +22,7 @@ import { import WidgetsMultiSelectBox from "pages/Editor/WidgetsMultiSelectBox"; import { getCanvasSnapRows } from "utils/WidgetPropsUtils"; import { ValidationTypes } from "constants/WidgetValidation"; -import { AutoLayoutContext } from "widgets/AutoLayoutContainerWidget/widget"; +import { AutoLayoutContext } from "utils/autoLayoutContext"; class VerticalLayoutWidget extends BaseWidget< VerticalLayoutWidgetProps, From 36ada5e41d38f7451a9ebb411f7fead1a08e3d93 Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 27 Jul 2022 12:53:25 -0400 Subject: [PATCH 021/708] fix dnd in nested flex containers --- .../src/components/AutoLayoutWrapper.tsx | 25 ++++++++++++++++++- .../CanvasArenas/hooks/useCanvasDragging.ts | 19 +++++++++----- app/client/src/widgets/BaseWidget.tsx | 2 +- 3 files changed, 38 insertions(+), 8 deletions(-) diff --git a/app/client/src/components/AutoLayoutWrapper.tsx b/app/client/src/components/AutoLayoutWrapper.tsx index a859b2abbfb8..6c841588f08f 100644 --- a/app/client/src/components/AutoLayoutWrapper.tsx +++ b/app/client/src/components/AutoLayoutWrapper.tsx @@ -3,6 +3,11 @@ import styled from "styled-components"; import React, { ReactNode, useCallback } from "react"; import { useClickToSelectWidget } from "utils/hooks/useClickToSelectWidget"; import { AlignItems, LayoutDirection } from "./constants"; +import { useSelector } from "react-redux"; +import { AppState } from "reducers"; +import { checkIsDropTarget } from "./designSystems/appsmith/PositionedContainer"; +import { getSelectedWidgets } from "selectors/ui"; +import { Layers } from "constants/Layers"; export type AutoLayoutProps = { children: ReactNode; @@ -33,6 +38,11 @@ const AutoLayout = styled("div")<{ min-height: 30px; `; +const ZIndexContainer = styled.div<{ zIndex: number }>` + position: relative; + z-index: ${({ zIndex }) => zIndex || Layers.positionedWidget}; +`; + export function AutoLayoutWrapper(props: AutoLayoutProps) { const clickToSelectWidget = useClickToSelectWidget(); const onClickFn = useCallback( @@ -42,6 +52,19 @@ export function AutoLayoutWrapper(props: AutoLayoutProps) { [props.widgetId, clickToSelectWidget], ); + const isDropTarget = checkIsDropTarget(props.widgetType); + const isDragging = useSelector( + (state: AppState) => state.ui.widgetDragResize.isDragging, + ); + const selectedWidgets = useSelector(getSelectedWidgets); + const isThisWidgetDragging = + isDragging && selectedWidgets.includes(props.widgetId); + + const zIndex = + isDragging && !(!isThisWidgetDragging && isDropTarget) + ? -1 + : Layers.positionedWidget + 1; + return ( - {props.children} + {props.children} ); } diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index 9e4daef714b0..4c45522f9c30 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -128,7 +128,6 @@ export const useCanvasDragging = ( if (!isDragging) { cleanUpTempStyles(); } - const offsets: number[] = []; // let siblings: { [key: string]: number } = {}; const siblingElements: any[] = []; @@ -138,7 +137,12 @@ export const useCanvasDragging = ( dragBlocksSize = isVertical ? blocksToDraw[0].height : blocksToDraw[0].width; - if (useAutoLayout && isDragging) { + if ( + useAutoLayout && + isDragging && + isCurrentDraggedCanvas && + isChildOfCanvas + ) { // Get all children of current auto layout container const els = document.querySelectorAll(`.auto-layout-parent-${widgetId}`); if (els && els.length && offsets.length !== els.length) { @@ -180,13 +184,16 @@ export const useCanvasDragging = ( } }); offsets.push( - isVertical - ? (siblingElements[siblingElements.length - 1] as any).offsetTop + + siblingElements.length + ? isVertical + ? (siblingElements[siblingElements.length - 1] as any).offsetTop + siblingElements[siblingElements.length - 1].clientHeight + 8 - : (siblingElements[siblingElements.length - 1] as any).offsetLeft + + : (siblingElements[siblingElements.length - 1] as any) + .offsetLeft + siblingElements[siblingElements.length - 1].clientWidth + - 8, + 8 + : 8, ); // console.log(offsets); } diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index fb13605cd2d1..4387f95b7958 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -347,7 +347,7 @@ abstract class BaseWidget< private getWidgetView(): ReactNode { let content: ReactNode; - // console.log(`${this.props.widgetName} =========`); + console.log(`${this.props.widgetName} : ${this.props.widgetId} =========`); // console.log(this.props); switch (this.props.renderMode) { case RenderModes.CANVAS: From 9161fef1719885adbb4907443f0cb4568fdfdeda Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 27 Jul 2022 13:49:42 -0400 Subject: [PATCH 022/708] wip translate not working --- .../src/components/AutoLayoutWrapper.tsx | 27 +++++++++++++------ .../CanvasArenas/hooks/useCanvasDragging.ts | 25 +++++++++++------ .../ContainerWidget/component/index.tsx | 3 ++- 3 files changed, 38 insertions(+), 17 deletions(-) diff --git a/app/client/src/components/AutoLayoutWrapper.tsx b/app/client/src/components/AutoLayoutWrapper.tsx index 6c841588f08f..afad6b569a23 100644 --- a/app/client/src/components/AutoLayoutWrapper.tsx +++ b/app/client/src/components/AutoLayoutWrapper.tsx @@ -25,7 +25,16 @@ const AutoLayout = styled("div")<{ useAutoLayout?: boolean; }>` position: unset; - margin: 8px; +`; + +const ZIndexContainer = styled.div<{ + alignItems?: AlignItems; + direction?: LayoutDirection; + zIndex: number; +}>` + position: relative; + z-index: ${({ zIndex }) => zIndex || Layers.positionedWidget}; + width: ${({ alignItems, direction }) => alignItems === AlignItems.Stretch && direction === LayoutDirection.Vertical ? "calc(100% - 16px)" @@ -36,11 +45,7 @@ const AutoLayout = styled("div")<{ ? "calc(100% - 16px)" : "auto"}; min-height: 30px; -`; - -const ZIndexContainer = styled.div<{ zIndex: number }>` - position: relative; - z-index: ${({ zIndex }) => zIndex || Layers.positionedWidget}; + margin: 8px; `; export function AutoLayoutWrapper(props: AutoLayoutProps) { @@ -68,12 +73,18 @@ export function AutoLayoutWrapper(props: AutoLayoutProps) { return ( - {props.children} + + {props.children} + ); } diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index 4c45522f9c30..60f6cce50a29 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -147,29 +147,38 @@ export const useCanvasDragging = ( const els = document.querySelectorAll(`.auto-layout-parent-${widgetId}`); if (els && els.length && offsets.length !== els.length) { // Get widget ids of all widgets being dragged - // console.log(els); + console.log(els); const blocks = blocksToDraw.map((block) => block.widgetId); // console.log("*********"); - els.forEach((el) => { + els.forEach((el, index) => { // console.log((el as any).offsetParent); // Extract widget id of current widget const mClass = el.className .split("auto-layout-child-")[1] .split(" ")[0]; // console.log(`parentId: ${widgetId}`); - // console.log(`widgetID: ${mClass}`); + console.log(`widgetID: ${mClass}`); // console.log(`blocks: ${blocks}`); // console.log(blocks); /** * If the widget is also being dragged, * then discount its presence from offset calculation. */ + const width = (el as any).clientWidth || (el as any).offsetWidth; + const height = (el as any).clientHeight || (el as any).offsetHeight; if (blocks && blocks.length && blocks.indexOf(mClass) !== -1) { // Temporarily hide the dragged widget - dragBlocksSize += isVertical - ? (el as any).clientHeight - : (el as any).clientWidth; - // console.log(`block size: ${dragBlocksSize}`); + console.log(el as any); + console.log((el as any).offsetWidth); + console.log((el as any).clientWidth); + console.log((el as any).clientHeight); + console.log((el as any).offsetHeight); + console.log((el as any).getBoundingClientRect()); + console.log(els[index].clientWidth); + console.log(width); + console.log(height); + dragBlocksSize += isVertical ? height : width; + console.log(`block size: ${dragBlocksSize}`); (el as any).classList.add("auto-temp-no-display"); return; } else { @@ -644,7 +653,7 @@ export const useCanvasDragging = ( renderNewRows(delta); } else if (!isUpdatingRows) { triggerReflow(e, firstMove); - highlightDropPosition(e); + isCurrentDraggedCanvas && highlightDropPosition(e); renderBlocks(); } scrollObj.lastMouseMoveEvent = { diff --git a/app/client/src/widgets/ContainerWidget/component/index.tsx b/app/client/src/widgets/ContainerWidget/component/index.tsx index 7e719ef01a4d..982aa4c5d7f1 100644 --- a/app/client/src/widgets/ContainerWidget/component/index.tsx +++ b/app/client/src/widgets/ContainerWidget/component/index.tsx @@ -54,7 +54,8 @@ const StyledContainerComponent = styled.div< } .auto-temp-no-display { - display: none; + position: absolute; + left: -9999px; } `; From 8260550d1559a6d624db07831119c4c68eb448a5 Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 27 Jul 2022 15:18:40 -0400 Subject: [PATCH 023/708] fix widget translation --- .../CanvasArenas/hooks/useCanvasDragging.ts | 63 +++++++++---------- app/client/src/widgets/BaseWidget.tsx | 2 +- 2 files changed, 29 insertions(+), 36 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index 60f6cce50a29..c05250bce2e6 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -39,7 +39,6 @@ export interface XYCord { const CONTAINER_JUMP_ACC_THRESHOLD = 8000; const CONTAINER_JUMP_SPEED_THRESHOLD = 800; -let dragBlocksSize = 0; let lastTranslatedIndex: number; //Since useCanvasDragging's Instance changes during container jump, metrics is stored outside const containerJumpThresholdMetrics = new ContainerJumpMetrics<{ @@ -110,24 +109,7 @@ export const useCanvasDragging = ( const reflow = useRef(); reflow.current = useReflow(draggingSpaces, widgetId || "", gridProps); - const cleanUpTempStyles = () => { - // reset display of all dragged blocks - const els = document.querySelectorAll(`.auto-layout-parent-${widgetId}`); - if (els && els.length) { - els.forEach((el) => { - (el as any).classList.remove("auto-temp-no-display"); - (el as any).style.transform = null; - }); - } - - // reset state - dragBlocksSize = 0; - lastTranslatedIndex = -10; - }; - - if (!isDragging) { - cleanUpTempStyles(); - } + let dragBlocksSize = 0; const offsets: number[] = []; // let siblings: { [key: string]: number } = {}; const siblingElements: any[] = []; @@ -147,38 +129,30 @@ export const useCanvasDragging = ( const els = document.querySelectorAll(`.auto-layout-parent-${widgetId}`); if (els && els.length && offsets.length !== els.length) { // Get widget ids of all widgets being dragged - console.log(els); + // console.log(els); const blocks = blocksToDraw.map((block) => block.widgetId); // console.log("*********"); - els.forEach((el, index) => { + els.forEach((el) => { // console.log((el as any).offsetParent); // Extract widget id of current widget const mClass = el.className .split("auto-layout-child-")[1] .split(" ")[0]; // console.log(`parentId: ${widgetId}`); - console.log(`widgetID: ${mClass}`); + // console.log(`widgetID: ${mClass}`); // console.log(`blocks: ${blocks}`); // console.log(blocks); /** * If the widget is also being dragged, * then discount its presence from offset calculation. */ - const width = (el as any).clientWidth || (el as any).offsetWidth; - const height = (el as any).clientHeight || (el as any).offsetHeight; if (blocks && blocks.length && blocks.indexOf(mClass) !== -1) { // Temporarily hide the dragged widget - console.log(el as any); - console.log((el as any).offsetWidth); - console.log((el as any).clientWidth); - console.log((el as any).clientHeight); - console.log((el as any).offsetHeight); - console.log((el as any).getBoundingClientRect()); - console.log(els[index].clientWidth); - console.log(width); - console.log(height); - dragBlocksSize += isVertical ? height : width; - console.log(`block size: ${dragBlocksSize}`); + const currentBlock = blocksToDraw[blocks.indexOf(mClass)]; + dragBlocksSize += isVertical + ? currentBlock.height + : currentBlock.width; + // console.log(`block size: ${dragBlocksSize}`); (el as any).classList.add("auto-temp-no-display"); return; } else { @@ -210,6 +184,25 @@ export const useCanvasDragging = ( }; calculateHighlightOffsets(); + const cleanUpTempStyles = () => { + // reset display of all dragged blocks + const els = document.querySelectorAll(`.auto-layout-parent-${widgetId}`); + if (els && els.length) { + els.forEach((el) => { + (el as any).classList.remove("auto-temp-no-display"); + (el as any).style.transform = null; + }); + } + + // reset state + dragBlocksSize = 0; + lastTranslatedIndex = -10; + }; + + if (!isDragging) { + cleanUpTempStyles(); + } + const { setDraggingCanvas, setDraggingNewWidget, diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index 4387f95b7958..2f41603332e9 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -347,7 +347,7 @@ abstract class BaseWidget< private getWidgetView(): ReactNode { let content: ReactNode; - console.log(`${this.props.widgetName} : ${this.props.widgetId} =========`); + // console.log(`${this.props.widgetName} : ${this.props.widgetId} =========`); // console.log(this.props); switch (this.props.renderMode) { case RenderModes.CANVAS: From d95b2a6f4572e3075eec9f0b376f13028f3af0a3 Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 27 Jul 2022 15:45:06 -0400 Subject: [PATCH 024/708] fix translation for existing widgets outside the flex container --- .../CanvasArenas/hooks/useCanvasDragging.ts | 28 +++++++------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index c05250bce2e6..626d4750c9f8 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -115,23 +115,19 @@ export const useCanvasDragging = ( const siblingElements: any[] = []; const isVertical = direction === LayoutDirection.Vertical; const calculateHighlightOffsets = () => { - if (isNewWidget) - dragBlocksSize = isVertical - ? blocksToDraw[0].height - : blocksToDraw[0].width; - if ( - useAutoLayout && - isDragging && - isCurrentDraggedCanvas && - isChildOfCanvas - ) { + if (useAutoLayout && isDragging && isCurrentDraggedCanvas) { + // calculate total drag size to translate siblings by + blocksToDraw?.map((each) => { + dragBlocksSize += isVertical ? each.height : each.width; + }); // Get all children of current auto layout container const els = document.querySelectorAll(`.auto-layout-parent-${widgetId}`); if (els && els.length && offsets.length !== els.length) { // Get widget ids of all widgets being dragged - // console.log(els); + console.log(els); + console.log(blocksToDraw); const blocks = blocksToDraw.map((block) => block.widgetId); - // console.log("*********"); + console.log("*********"); els.forEach((el) => { // console.log((el as any).offsetParent); // Extract widget id of current widget @@ -148,11 +144,6 @@ export const useCanvasDragging = ( */ if (blocks && blocks.length && blocks.indexOf(mClass) !== -1) { // Temporarily hide the dragged widget - const currentBlock = blocksToDraw[blocks.indexOf(mClass)]; - dragBlocksSize += isVertical - ? currentBlock.height - : currentBlock.width; - // console.log(`block size: ${dragBlocksSize}`); (el as any).classList.add("auto-temp-no-display"); return; } else { @@ -178,7 +169,7 @@ export const useCanvasDragging = ( 8 : 8, ); - // console.log(offsets); + console.log(offsets); } } }; @@ -668,6 +659,7 @@ export const useCanvasDragging = ( const arr = [...siblingElements]; // translate each element in the appropriate direction + console.log(`dragblocksize: ${dragBlocksSize}`); const x = !isVertical ? dragBlocksSize : 0; const y = isVertical ? dragBlocksSize : 0; arr.forEach((each, index) => { From 2e546651e3aa62d2dbde31e3f4b429a12fc3c22c Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 27 Jul 2022 15:45:33 -0400 Subject: [PATCH 025/708] remove logs --- .../common/CanvasArenas/hooks/useCanvasDragging.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index 626d4750c9f8..a534e9409e9d 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -124,10 +124,10 @@ export const useCanvasDragging = ( const els = document.querySelectorAll(`.auto-layout-parent-${widgetId}`); if (els && els.length && offsets.length !== els.length) { // Get widget ids of all widgets being dragged - console.log(els); - console.log(blocksToDraw); + // console.log(els); + // console.log(blocksToDraw); const blocks = blocksToDraw.map((block) => block.widgetId); - console.log("*********"); + // console.log("*********"); els.forEach((el) => { // console.log((el as any).offsetParent); // Extract widget id of current widget @@ -169,7 +169,7 @@ export const useCanvasDragging = ( 8 : 8, ); - console.log(offsets); + // console.log(offsets); } } }; @@ -659,7 +659,7 @@ export const useCanvasDragging = ( const arr = [...siblingElements]; // translate each element in the appropriate direction - console.log(`dragblocksize: ${dragBlocksSize}`); + // console.log(`dragblocksize: ${dragBlocksSize}`); const x = !isVertical ? dragBlocksSize : 0; const y = isVertical ? dragBlocksSize : 0; arr.forEach((each, index) => { From 142bd3804f4759d77a31471227b2a422b9c5fd20 Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 9 Aug 2022 10:29:12 -0400 Subject: [PATCH 026/708] add positioned container --- app/client/src/widgets/BaseWidget.tsx | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index 2f41603332e9..8354a3dd849e 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -361,13 +361,15 @@ abstract class BaseWidget< content = this.makeDraggable(content); content = this.makeSnipeable(content); // NOTE: In sniping mode we are not blocking onClick events from PositionWrapper. - if ( - this.props.useAutoLayout && - !this.props.widgetName.toUpperCase().includes("AUTO") - ) { + // if ( + // this.props.useAutoLayout && + // !this.props.widgetName.toUpperCase().includes("AUTO") + // ) { + // content = this.addAutoLayoutWrapper(content); + // } else content = this.makePositioned(content); + content = this.makePositioned(content); + if (this.props.useAutoLayout) content = this.addAutoLayoutWrapper(content); - } else content = this.makePositioned(content); - // content = this.makePositioned(content); } return content; From e9f12d56210d697d444a8f34776f33ef7e4df121 Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 9 Aug 2022 10:32:15 -0400 Subject: [PATCH 027/708] add positioning enum --- app/client/src/components/constants.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/client/src/components/constants.ts b/app/client/src/components/constants.ts index 7a2dd26db65d..93d5ab6046c2 100644 --- a/app/client/src/components/constants.ts +++ b/app/client/src/components/constants.ts @@ -129,3 +129,9 @@ export enum AlignItems { Center = "center", Stretch = "stretch", } + +export enum Positioning { + Fixed = "fixed", + Horizontal = "horizontal", + Vertical = "vertical", +} From 6b911c8ab38661d8e6ad8f2b3b4659222fa10294 Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 9 Aug 2022 12:24:20 -0400 Subject: [PATCH 028/708] working positioning prop in container --- .../src/components/AutoLayoutWrapper.tsx | 1 + .../widgets/ContainerWidget/widget/index.tsx | 62 ++++++++++++++++--- 2 files changed, 55 insertions(+), 8 deletions(-) diff --git a/app/client/src/components/AutoLayoutWrapper.tsx b/app/client/src/components/AutoLayoutWrapper.tsx index afad6b569a23..e241bc837dea 100644 --- a/app/client/src/components/AutoLayoutWrapper.tsx +++ b/app/client/src/components/AutoLayoutWrapper.tsx @@ -25,6 +25,7 @@ const AutoLayout = styled("div")<{ useAutoLayout?: boolean; }>` position: unset; + width: fit-content; `; const ZIndexContainer = styled.div<{ diff --git a/app/client/src/widgets/ContainerWidget/widget/index.tsx b/app/client/src/widgets/ContainerWidget/widget/index.tsx index d90e8edc6a82..42883179c597 100644 --- a/app/client/src/widgets/ContainerWidget/widget/index.tsx +++ b/app/client/src/widgets/ContainerWidget/widget/index.tsx @@ -19,16 +19,39 @@ import { compact, map, sortBy } from "lodash"; import { CanvasDraggingArena } from "pages/common/CanvasArenas/CanvasDraggingArena"; import { getCanvasSnapRows } from "utils/WidgetPropsUtils"; +import { + JustifyContent, + LayoutDirection, + Positioning, +} from "components/constants"; +import { AutoLayoutContext } from "utils/autoLayoutContext"; class ContainerWidget extends BaseWidget< ContainerWidgetProps, - WidgetState + ContainerWidgetState > { constructor(props: ContainerWidgetProps) { super(props); + this.state = { + useAutoLayout: false, + direction: LayoutDirection.Horizontal, + }; this.renderChildWidget = this.renderChildWidget.bind(this); } + componentDidUpdate(prevProps: ContainerWidgetProps): void { + if (!this.props.positioning || this.props.positioning === Positioning.Fixed) + this.setState({ useAutoLayout: false }); + else + this.setState({ + useAutoLayout: true, + direction: + this.props.positioning === Positioning.Horizontal + ? LayoutDirection.Horizontal + : LayoutDirection.Vertical, + }); + } + static getPropertyPaneConfig() { return [ { @@ -63,6 +86,22 @@ class ContainerWidget extends BaseWidget< isBindProperty: false, isTriggerProperty: false, }, + { + helpText: "Position styles to be applied to the children", + propertyName: "positioning", + label: "Positioning", + controlType: "DROP_DOWN", + defaultValue: Positioning.Fixed, + options: [ + { label: "Fixed", value: Positioning.Fixed }, + { label: "Horizontal stack", value: Positioning.Horizontal }, + { label: "Vertical stack", value: Positioning.Vertical }, + ], + isJSConvertible: false, + isBindProperty: true, + isTriggerProperty: true, + validation: { type: ValidationTypes.TEXT }, + }, ], }, { @@ -180,10 +219,11 @@ class ContainerWidget extends BaseWidget< childWidgetData.parentId = this.props.widgetId; // Pass layout controls to children - childWidgetData.useAutoLayout = this.props.useAutoLayout; - childWidgetData.direction = this.props.direction; + childWidgetData.useAutoLayout = this.state.useAutoLayout; + childWidgetData.direction = this.state.direction; childWidgetData.justifyContent = this.props.justifyContent; childWidgetData.alignItems = this.props.alignItems; + childWidgetData.positioning = this.props.positioning; return WidgetFactory.createWidget(childWidgetData, this.props.renderMode); } @@ -193,7 +233,7 @@ class ContainerWidget extends BaseWidget< // sort by row so stacking context is correct // TODO(abhinav): This is hacky. The stacking context should increase for widgets rendered top to bottom, always. // Figure out a way in which the stacking context is consistent. - this.props.useAutoLayout + this.state.useAutoLayout ? this.props.children : sortBy(compact(this.props.children), (child) => child.topRow), this.renderChildWidget, @@ -212,12 +252,12 @@ class ContainerWidget extends BaseWidget< {...this.getSnapSpaces()} alignItems={props.alignItems} canExtend={props.canExtend} - direction={props.direction} + direction={this.props.direction} dropDisabled={!!props.dropDisabled} noPad={this.props.noPad} parentId={props.parentId} snapRows={snapRows} - useAutoLayout={props.useAutoLayout} + useAutoLayout={this.props.useAutoLayout} widgetId={props.widgetId} /> {this.renderChildren()} @@ -264,6 +304,12 @@ export interface ContainerWidgetProps containerStyle?: ContainerStyle; shouldScrollContents?: boolean; noPad?: boolean; + positioning?: Positioning; +} + +export interface ContainerWidgetState extends WidgetState { + useAutoLayout: boolean; + direction: LayoutDirection; } export default ContainerWidget; From 93761cd4780151faf4b774fa348e29c081a18897 Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 9 Aug 2022 13:01:23 -0400 Subject: [PATCH 029/708] add positioning to tabs widget --- app/client/src/widgets/CanvasWidget.tsx | 5 ++-- .../widgets/ContainerWidget/widget/index.tsx | 4 +-- .../src/widgets/TabsWidget/constants.ts | 2 ++ .../src/widgets/TabsWidget/widget/index.tsx | 26 +++++++++++++++++++ 4 files changed, 33 insertions(+), 4 deletions(-) diff --git a/app/client/src/widgets/CanvasWidget.tsx b/app/client/src/widgets/CanvasWidget.tsx index b431f994560a..3c6692cb0927 100644 --- a/app/client/src/widgets/CanvasWidget.tsx +++ b/app/client/src/widgets/CanvasWidget.tsx @@ -66,10 +66,11 @@ class CanvasWidget extends ContainerWidget { // Pass layout controls to children // TODO: remove the hard check on widget name if (this.props.widgetName !== "MainContainer") { - childWidgetData.useAutoLayout = this.props.useAutoLayout; - childWidgetData.direction = this.props.direction; + childWidgetData.useAutoLayout = this.state.useAutoLayout; + childWidgetData.direction = this.state.direction; childWidgetData.justifyContent = this.props.justifyContent; childWidgetData.alignItems = this.props.alignItems; + childWidgetData.positioning = this.props.positioning; // console.log( // `${childWidgetData.widgetName} : ${childWidgetData.widgetId} =======`, // ); diff --git a/app/client/src/widgets/ContainerWidget/widget/index.tsx b/app/client/src/widgets/ContainerWidget/widget/index.tsx index 42883179c597..5b8450bcab61 100644 --- a/app/client/src/widgets/ContainerWidget/widget/index.tsx +++ b/app/client/src/widgets/ContainerWidget/widget/index.tsx @@ -252,12 +252,12 @@ class ContainerWidget extends BaseWidget< {...this.getSnapSpaces()} alignItems={props.alignItems} canExtend={props.canExtend} - direction={this.props.direction} + direction={this.state.direction} dropDisabled={!!props.dropDisabled} noPad={this.props.noPad} parentId={props.parentId} snapRows={snapRows} - useAutoLayout={this.props.useAutoLayout} + useAutoLayout={this.state.useAutoLayout} widgetId={props.widgetId} /> widgetId: string; isVisible?: boolean; index: number; + positioning?: Positioning; } >; shouldShowTabs: boolean; diff --git a/app/client/src/widgets/TabsWidget/widget/index.tsx b/app/client/src/widgets/TabsWidget/widget/index.tsx index 4328e2e7240f..be81141d9154 100644 --- a/app/client/src/widgets/TabsWidget/widget/index.tsx +++ b/app/client/src/widgets/TabsWidget/widget/index.tsx @@ -13,6 +13,7 @@ import { WidgetProperties } from "selectors/propertyPaneSelectors"; import { WIDGET_PADDING } from "constants/WidgetConstants"; import derivedProperties from "./parseDerivedProperties"; import { isEqual, find } from "lodash"; +import { Positioning } from "components/constants"; export function selectedTabValidation( value: unknown, @@ -101,6 +102,28 @@ class TabsWidget extends BaseWidget< isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, + { + helpText: "Position styles to be applied to the children", + propertyName: "positioning", + label: "Positioning", + controlType: "DROP_DOWN", + defaultValue: Positioning.Fixed, + options: [ + { label: "Fixed", value: Positioning.Fixed }, + { + label: "Horizontal stack", + value: Positioning.Horizontal, + }, + { + label: "Vertical stack", + value: Positioning.Vertical, + }, + ], + isJSConvertible: false, + isBindProperty: true, + isTriggerProperty: true, + validation: { type: ValidationTypes.TEXT }, + }, ], }, ], @@ -282,6 +305,9 @@ class TabsWidget extends BaseWidget< : componentHeight - 1; childWidgetData.parentId = this.props.widgetId; childWidgetData.minHeight = componentHeight; + childWidgetData.positioning = Object.values(this.props.tabsObj)?.filter( + (item) => item.widgetId === selectedTabWidgetId, + )[0]?.positioning; return WidgetFactory.createWidget(childWidgetData, this.props.renderMode); }; From a1bc11873d4f9ab15eebd1a3cc33c938d71dc144 Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 9 Aug 2022 13:08:53 -0400 Subject: [PATCH 030/708] add positioning to modal --- .../src/widgets/ModalWidget/widget/index.tsx | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/app/client/src/widgets/ModalWidget/widget/index.tsx b/app/client/src/widgets/ModalWidget/widget/index.tsx index 65edee40a693..37cb251e9846 100644 --- a/app/client/src/widgets/ModalWidget/widget/index.tsx +++ b/app/client/src/widgets/ModalWidget/widget/index.tsx @@ -16,6 +16,7 @@ import { commentModeSelector } from "selectors/commentsSelectors"; import { getCanvasWidth, snipingModeSelector } from "selectors/editorSelectors"; import { deselectAllInitAction } from "actions/widgetSelectionActions"; import { ValidationTypes } from "constants/WidgetValidation"; +import { Positioning } from "components/constants"; const minSize = 100; @@ -25,6 +26,22 @@ export class ModalWidget extends BaseWidget { { sectionName: "General", children: [ + { + helpText: "Position styles to be applied to the children", + propertyName: "positioning", + label: "Positioning", + controlType: "DROP_DOWN", + defaultValue: Positioning.Fixed, + options: [ + { label: "Fixed", value: Positioning.Fixed }, + { label: "Horizontal stack", value: Positioning.Horizontal }, + { label: "Vertical stack", value: Positioning.Vertical }, + ], + isJSConvertible: false, + isBindProperty: true, + isTriggerProperty: true, + validation: { type: ValidationTypes.TEXT }, + }, { helpText: "Enables scrolling for content inside the widget", propertyName: "shouldScrollContents", @@ -123,6 +140,7 @@ export class ModalWidget extends BaseWidget { childWidgetData.minHeight = this.props.height; childWidgetData.rightColumn = this.getModalWidth(this.props.width) + WIDGET_PADDING * 2; + childWidgetData.positioning = this.props.positioning; return WidgetFactory.createWidget(childWidgetData, this.props.renderMode); }; @@ -271,6 +289,7 @@ export interface ModalWidgetProps extends WidgetProps { backgroundColor: string; borderRadius: string; mainCanvasWidth: number; + positioning?: Positioning; } const mapDispatchToProps = (dispatch: any) => ({ From c0914ec9a4c5f066f55f882d474318650613dd94 Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 9 Aug 2022 16:43:43 -0400 Subject: [PATCH 031/708] add positioning to main container --- .../pages/Editor/CanvasPropertyPane/index.tsx | 68 ++++++++++++++++++- app/client/src/widgets/CanvasWidget.tsx | 17 ++--- .../widgets/ContainerWidget/widget/index.tsx | 9 +-- 3 files changed, 74 insertions(+), 20 deletions(-) diff --git a/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx b/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx index c9f89ea4afab..8d6bd6438c37 100644 --- a/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx +++ b/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx @@ -1,13 +1,72 @@ -import React from "react"; +import React, { useState } from "react"; import * as Sentry from "@sentry/react"; import { MainContainerLayoutControl } from "../MainContainerLayoutControl"; import ThemeEditor from "../ThemePropertyPane/ThemeEditor"; +import { Dropdown, DropdownOption, RenderOption } from "components/ads"; +import { Positioning } from "components/constants"; +import { useDispatch } from "react-redux"; +import { batchUpdateMultipleWidgetProperties } from "actions/controlActions"; +import { useSelector } from "store"; +import { getWidgets } from "sagas/selectors"; type Props = { skipThemeEditor?: boolean; }; +const PositioningOptions = () => { + const dispatch = useDispatch(); + const widgets = useSelector(getWidgets); + const options: DropdownOption[] = [ + { label: "Fixed", value: Positioning.Fixed }, + { label: "Horizontal stack", value: Positioning.Horizontal }, + { label: "Vertical stack", value: Positioning.Vertical }, + ]; + const [selectedOption, setSelectedOption] = useState(() => { + if (widgets && widgets["0"].positioning) { + return options + .map((each) => each.value) + .indexOf(widgets["0"].positioning); + } + return 0; + }); + const renderOption: RenderOption = ({ isSelectedNode, option }) => ( +
{ + setSelectedOption(options.indexOf(option as DropdownOption)); + dispatch( + batchUpdateMultipleWidgetProperties([ + { + widgetId: "0", + updates: { + modify: { + positioning: (option as DropdownOption).value, + }, + }, + }, + ]), + ); + }} + > +
{(option as DropdownOption).label}
+
+ ); + return ( +
+ +
+ ); +}; + export function CanvasPropertyPane(props: Props) { return (
@@ -18,7 +77,12 @@ export function CanvasPropertyPane(props: Props) {

Canvas Size

- + {!props.skipThemeEditor && ( +
+

Positioning

+ +
+ )} {!props.skipThemeEditor && }
diff --git a/app/client/src/widgets/CanvasWidget.tsx b/app/client/src/widgets/CanvasWidget.tsx index 3c6692cb0927..5bc87935bdbe 100644 --- a/app/client/src/widgets/CanvasWidget.tsx +++ b/app/client/src/widgets/CanvasWidget.tsx @@ -64,17 +64,12 @@ class CanvasWidget extends ContainerWidget { if (this.props.noPad) childWidgetData.noContainerOffset = true; childWidgetData.parentId = this.props.widgetId; // Pass layout controls to children - // TODO: remove the hard check on widget name - if (this.props.widgetName !== "MainContainer") { - childWidgetData.useAutoLayout = this.state.useAutoLayout; - childWidgetData.direction = this.state.direction; - childWidgetData.justifyContent = this.props.justifyContent; - childWidgetData.alignItems = this.props.alignItems; - childWidgetData.positioning = this.props.positioning; - // console.log( - // `${childWidgetData.widgetName} : ${childWidgetData.widgetId} =======`, - // ); - } + childWidgetData.positioning = + childWidgetData?.positioning || this.props.positioning; + childWidgetData.useAutoLayout = this.state.useAutoLayout; + childWidgetData.direction = this.state.direction; + childWidgetData.justifyContent = this.props.justifyContent; + childWidgetData.alignItems = this.props.alignItems; return WidgetFactory.createWidget(childWidgetData, this.props.renderMode); } diff --git a/app/client/src/widgets/ContainerWidget/widget/index.tsx b/app/client/src/widgets/ContainerWidget/widget/index.tsx index 5b8450bcab61..1a8073585880 100644 --- a/app/client/src/widgets/ContainerWidget/widget/index.tsx +++ b/app/client/src/widgets/ContainerWidget/widget/index.tsx @@ -19,12 +19,7 @@ import { compact, map, sortBy } from "lodash"; import { CanvasDraggingArena } from "pages/common/CanvasArenas/CanvasDraggingArena"; import { getCanvasSnapRows } from "utils/WidgetPropsUtils"; -import { - JustifyContent, - LayoutDirection, - Positioning, -} from "components/constants"; -import { AutoLayoutContext } from "utils/autoLayoutContext"; +import { LayoutDirection, Positioning } from "components/constants"; class ContainerWidget extends BaseWidget< ContainerWidgetProps, @@ -39,7 +34,7 @@ class ContainerWidget extends BaseWidget< this.renderChildWidget = this.renderChildWidget.bind(this); } - componentDidUpdate(prevProps: ContainerWidgetProps): void { + componentDidUpdate(): void { if (!this.props.positioning || this.props.positioning === Positioning.Fixed) this.setState({ useAutoLayout: false }); else From 1e61c2d43d3b6b40f05bb9825cd8a94d0343c40f Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 10 Aug 2022 10:43:23 -0400 Subject: [PATCH 032/708] disable resize on flex children --- .../widgets/ContainerWidget/widget/index.tsx | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/app/client/src/widgets/ContainerWidget/widget/index.tsx b/app/client/src/widgets/ContainerWidget/widget/index.tsx index 1a8073585880..653ee5ca9523 100644 --- a/app/client/src/widgets/ContainerWidget/widget/index.tsx +++ b/app/client/src/widgets/ContainerWidget/widget/index.tsx @@ -19,7 +19,13 @@ import { compact, map, sortBy } from "lodash"; import { CanvasDraggingArena } from "pages/common/CanvasArenas/CanvasDraggingArena"; import { getCanvasSnapRows } from "utils/WidgetPropsUtils"; -import { LayoutDirection, Positioning } from "components/constants"; +import { + AlignItems, + JustifyContent, + LayoutDirection, + Positioning, +} from "components/constants"; +import { AutoLayoutContext } from "utils/autoLayoutContext"; class ContainerWidget extends BaseWidget< ContainerWidgetProps, @@ -34,7 +40,7 @@ class ContainerWidget extends BaseWidget< this.renderChildWidget = this.renderChildWidget.bind(this); } - componentDidUpdate(): void { + componentDidUpdate(prevProps: ContainerWidgetProps): void { if (!this.props.positioning || this.props.positioning === Positioning.Fixed) this.setState({ useAutoLayout: false }); else @@ -278,7 +284,16 @@ class ContainerWidget extends BaseWidget< justifyContent={this.props.justifyContent} useAutoLayout={this.state.useAutoLayout} > - {this.renderChildren()} + + {this.renderChildren()} + ); From 6abc66841f2e4b7d56ffa71ddfe11e0d56986bc0 Mon Sep 17 00:00:00 2001 From: Preet Sidhu Date: Wed, 10 Aug 2022 11:05:44 -0400 Subject: [PATCH 033/708] remove duplicate import --- app/client/src/widgets/TabsWidget/widget/index.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/app/client/src/widgets/TabsWidget/widget/index.tsx b/app/client/src/widgets/TabsWidget/widget/index.tsx index 60214d53377d..25fe430fe964 100644 --- a/app/client/src/widgets/TabsWidget/widget/index.tsx +++ b/app/client/src/widgets/TabsWidget/widget/index.tsx @@ -13,7 +13,6 @@ import { AutocompleteDataType } from "utils/autocomplete/TernServer"; import { WidgetProperties } from "selectors/propertyPaneSelectors"; import { WIDGET_PADDING } from "constants/WidgetConstants"; import derivedProperties from "./parseDerivedProperties"; -import { isEqual, find } from "lodash"; import { Positioning } from "components/constants"; export function selectedTabValidation( From 16fc035dc1ec7c734c3c93b8df18f286d6215f0c Mon Sep 17 00:00:00 2001 From: Preet Sidhu Date: Wed, 10 Aug 2022 14:11:00 -0400 Subject: [PATCH 034/708] working stretchOnMobile and flex wrap height fix --- app/client/src/widgets/CanvasWidget.tsx | 9 +++- .../ContainerWidget/component/index.tsx | 2 +- .../widgets/ContainerWidget/widget/index.tsx | 54 ++++++++++++++----- app/client/src/widgets/SelectWidget/index.ts | 1 + .../src/widgets/SelectWidget/widget/index.tsx | 1 + 5 files changed, 52 insertions(+), 15 deletions(-) diff --git a/app/client/src/widgets/CanvasWidget.tsx b/app/client/src/widgets/CanvasWidget.tsx index 76d43916c438..19df209e590c 100644 --- a/app/client/src/widgets/CanvasWidget.tsx +++ b/app/client/src/widgets/CanvasWidget.tsx @@ -17,6 +17,9 @@ class CanvasWidget extends ContainerWidget { static getWidgetType() { return "CANVAS_WIDGET"; } + componentDidMount(): void { + super.componentDidMount(); + } getCanvasProps(): ContainerWidgetProps { return { @@ -59,7 +62,6 @@ class CanvasWidget extends ContainerWidget { } const snapSpaces = this.getSnapSpaces(); - childWidgetData.parentColumnSpace = snapSpaces.snapColumnSpace; childWidgetData.parentRowSpace = snapSpaces.snapRowSpace; if (this.props.noPad) childWidgetData.noContainerOffset = true; @@ -72,6 +74,11 @@ class CanvasWidget extends ContainerWidget { childWidgetData.justifyContent = this.props.justifyContent; childWidgetData.alignItems = this.props.alignItems; + if (childWidgetData?.stretchOnMobile && this.state.isMobile) { + childWidgetData.leftColumn = 0; + childWidgetData.rightColumn = 64; + } + return WidgetFactory.createWidget(childWidgetData, this.props.renderMode); } diff --git a/app/client/src/widgets/ContainerWidget/component/index.tsx b/app/client/src/widgets/ContainerWidget/component/index.tsx index 982aa4c5d7f1..64a322319b42 100644 --- a/app/client/src/widgets/ContainerWidget/component/index.tsx +++ b/app/client/src/widgets/ContainerWidget/component/index.tsx @@ -73,7 +73,7 @@ export const FlexContainer = styled.div<{ flex-wrap: wrap; width: 100%; - height: 100%; + height: auto; `; function ContainerComponentWrapper(props: ContainerComponentProps) { diff --git a/app/client/src/widgets/ContainerWidget/widget/index.tsx b/app/client/src/widgets/ContainerWidget/widget/index.tsx index 572019d1968f..7ecef9849d6a 100644 --- a/app/client/src/widgets/ContainerWidget/widget/index.tsx +++ b/app/client/src/widgets/ContainerWidget/widget/index.tsx @@ -36,23 +36,11 @@ class ContainerWidget extends BaseWidget< this.state = { useAutoLayout: false, direction: LayoutDirection.Horizontal, + isMobile: false, }; this.renderChildWidget = this.renderChildWidget.bind(this); } - componentDidUpdate(prevProps: ContainerWidgetProps): void { - if (!this.props.positioning || this.props.positioning === Positioning.Fixed) - this.setState({ useAutoLayout: false }); - else - this.setState({ - useAutoLayout: true, - direction: - this.props.positioning === Positioning.Horizontal - ? LayoutDirection.Horizontal - : LayoutDirection.Vertical, - }); - } - static getPropertyPaneConfig() { return [ { @@ -103,6 +91,18 @@ class ContainerWidget extends BaseWidget< isTriggerProperty: true, validation: { type: ValidationTypes.TEXT }, }, + { + helpText: + "Should the children take up the complete width on mobile", + propertyName: "stretchOnMobile", + label: "Stretch On Mobile", + controlType: "SWITCH", + defaultValue: false, + isJSConvertible: false, + isBindProperty: false, + isTriggerProperty: false, + validation: { type: ValidationTypes.BOOLEAN }, + }, ], }, { @@ -283,6 +283,32 @@ class ContainerWidget extends BaseWidget< return {}; } + componentDidMount(): void { + this.updatePositioningInformation(); + this.checkIsMobile(); + } + + componentDidUpdate(prevProps: ContainerWidgetProps): void { + this.updatePositioningInformation(); + } + + checkIsMobile = (): void => { + if (window.innerWidth < 767) this.setState({ isMobile: true }); + }; + + updatePositioningInformation = (): void => { + if (!this.props.positioning || this.props.positioning === Positioning.Fixed) + this.setState({ useAutoLayout: false }); + else + this.setState({ + useAutoLayout: true, + direction: + this.props.positioning === Positioning.Horizontal + ? LayoutDirection.Horizontal + : LayoutDirection.Vertical, + }); + }; + getSnapSpaces = () => { const { componentWidth } = this.getComponentDimensions(); // For all widgets inside a container, we remove both container padding as well as widget padding from component width @@ -422,11 +448,13 @@ export interface ContainerWidgetProps shouldScrollContents?: boolean; noPad?: boolean; positioning?: Positioning; + stretchOnMobile?: boolean; } export interface ContainerWidgetState extends WidgetState { useAutoLayout: boolean; direction: LayoutDirection; + isMobile: boolean; } export default ContainerWidget; diff --git a/app/client/src/widgets/SelectWidget/index.ts b/app/client/src/widgets/SelectWidget/index.ts index 32ca54c64c9e..01f80509f831 100644 --- a/app/client/src/widgets/SelectWidget/index.ts +++ b/app/client/src/widgets/SelectWidget/index.ts @@ -31,6 +31,7 @@ export const CONFIG = { isDisabled: false, animateLoading: true, labelTextSize: "0.875rem", + stretchOnMobile: true, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/SelectWidget/widget/index.tsx b/app/client/src/widgets/SelectWidget/widget/index.tsx index 3c93804215e2..b9ccb07c86a5 100644 --- a/app/client/src/widgets/SelectWidget/widget/index.tsx +++ b/app/client/src/widgets/SelectWidget/widget/index.tsx @@ -620,6 +620,7 @@ export interface SelectWidgetProps extends WidgetProps { onFilterUpdate: string; isDirty?: boolean; filterText: string; + stretchOnMobile?: boolean; } export default SelectWidget; From 4660a20246322e2dea91c1e8b6256f5ee9efdc7a Mon Sep 17 00:00:00 2001 From: Preet Sidhu Date: Wed, 10 Aug 2022 14:50:18 -0400 Subject: [PATCH 035/708] add stretchOnMobile to widgets --- app/client/src/widgets/BaseWidget.tsx | 1 + app/client/src/widgets/ButtonGroupWidget/index.ts | 1 + app/client/src/widgets/ContainerWidget/widget/index.tsx | 2 ++ app/client/src/widgets/CurrencyInputWidget/index.ts | 1 + app/client/src/widgets/InputWidgetV2/index.ts | 1 + app/client/src/widgets/JSONFormWidget/index.ts | 1 + app/client/src/widgets/ListWidget/index.ts | 1 + app/client/src/widgets/MapWidget/index.ts | 1 + app/client/src/widgets/MultiSelectTreeWidget/index.ts | 1 + app/client/src/widgets/MultiSelectWidget/index.ts | 1 + app/client/src/widgets/MultiSelectWidgetV2/index.ts | 1 + app/client/src/widgets/PhoneInputWidget/index.ts | 1 + app/client/src/widgets/SingleSelectTreeWidget/index.ts | 1 + app/client/src/widgets/TableWidgetV2/index.ts | 1 + app/client/src/widgets/TabsWidget/index.ts | 1 + 15 files changed, 16 insertions(+) diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index dea67115aa53..9985401d7d97 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -462,6 +462,7 @@ export interface WidgetPositionProps extends WidgetRowCols { useAutoLayout?: boolean; direction?: LayoutDirection; alignItems?: AlignItems; + stretchOnMobile?: boolean; } export const WIDGET_STATIC_PROPS = { diff --git a/app/client/src/widgets/ButtonGroupWidget/index.ts b/app/client/src/widgets/ButtonGroupWidget/index.ts index d7e52e9673dd..df135b1ebf6e 100644 --- a/app/client/src/widgets/ButtonGroupWidget/index.ts +++ b/app/client/src/widgets/ButtonGroupWidget/index.ts @@ -22,6 +22,7 @@ export const CONFIG = { isVisible: true, version: 1, animateLoading: true, + stretchOnMobile: true, groupButtons: { groupButton1: { label: "Favorite", diff --git a/app/client/src/widgets/ContainerWidget/widget/index.tsx b/app/client/src/widgets/ContainerWidget/widget/index.tsx index 7ecef9849d6a..0df2727e4ec2 100644 --- a/app/client/src/widgets/ContainerWidget/widget/index.tsx +++ b/app/client/src/widgets/ContainerWidget/widget/index.tsx @@ -284,11 +284,13 @@ class ContainerWidget extends BaseWidget< } componentDidMount(): void { + super.componentDidMount(); this.updatePositioningInformation(); this.checkIsMobile(); } componentDidUpdate(prevProps: ContainerWidgetProps): void { + super.componentDidUpdate(prevProps); this.updatePositioningInformation(); } diff --git a/app/client/src/widgets/CurrencyInputWidget/index.ts b/app/client/src/widgets/CurrencyInputWidget/index.ts index c8154fef821d..c48b0ebb7fe2 100644 --- a/app/client/src/widgets/CurrencyInputWidget/index.ts +++ b/app/client/src/widgets/CurrencyInputWidget/index.ts @@ -16,6 +16,7 @@ export const CONFIG = { allowCurrencyChange: false, defaultCurrencyCode: getDefaultCurrency().currency, decimals: 0, + stretchOnMobile: true, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/InputWidgetV2/index.ts b/app/client/src/widgets/InputWidgetV2/index.ts index b345eff0c821..eb2df7e6b434 100644 --- a/app/client/src/widgets/InputWidgetV2/index.ts +++ b/app/client/src/widgets/InputWidgetV2/index.ts @@ -13,6 +13,7 @@ export const CONFIG = { inputType: "TEXT", widgetName: "Input", version: 2, + stretchOnMobile: true, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/JSONFormWidget/index.ts b/app/client/src/widgets/JSONFormWidget/index.ts index abfadfe5a2a2..399852fe0a47 100644 --- a/app/client/src/widgets/JSONFormWidget/index.ts +++ b/app/client/src/widgets/JSONFormWidget/index.ts @@ -17,6 +17,7 @@ export const CONFIG = { iconSVG: IconSVG, needsMeta: true, defaults: { + stretchOnMobile: true, animateLoading: true, backgroundColor: "#fff", columns: 25, diff --git a/app/client/src/widgets/ListWidget/index.ts b/app/client/src/widgets/ListWidget/index.ts index 57eed2ad8775..5cbf57bdb132 100644 --- a/app/client/src/widgets/ListWidget/index.ts +++ b/app/client/src/widgets/ListWidget/index.ts @@ -25,6 +25,7 @@ export const CONFIG = { animateLoading: true, gridType: "vertical", template: {}, + stretchOnMobile: true, enhancements: { child: { autocomplete: (parentProps: any) => { diff --git a/app/client/src/widgets/MapWidget/index.ts b/app/client/src/widgets/MapWidget/index.ts index d140ecfbbf94..64db71f413f2 100644 --- a/app/client/src/widgets/MapWidget/index.ts +++ b/app/client/src/widgets/MapWidget/index.ts @@ -21,6 +21,7 @@ export const CONFIG = { isClickedMarkerCentered: true, version: 1, animateLoading: true, + stretchOnMobile: true, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/MultiSelectTreeWidget/index.ts b/app/client/src/widgets/MultiSelectTreeWidget/index.ts index 2bbd7adccfaa..e4ff4c6f4a7f 100644 --- a/app/client/src/widgets/MultiSelectTreeWidget/index.ts +++ b/app/client/src/widgets/MultiSelectTreeWidget/index.ts @@ -46,6 +46,7 @@ export const CONFIG = { labelAlignment: Alignment.LEFT, labelWidth: 5, labelTextSize: "0.875rem", + stretchOnMobile: true, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/MultiSelectWidget/index.ts b/app/client/src/widgets/MultiSelectWidget/index.ts index 8dad21d3c0bf..0896088cf531 100644 --- a/app/client/src/widgets/MultiSelectWidget/index.ts +++ b/app/client/src/widgets/MultiSelectWidget/index.ts @@ -31,6 +31,7 @@ export const CONFIG = { isRequired: false, isDisabled: false, placeholderText: "Select option(s)", + stretchOnMobile: true, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/MultiSelectWidgetV2/index.ts b/app/client/src/widgets/MultiSelectWidgetV2/index.ts index a25a8fb635ce..d866b302bad0 100644 --- a/app/client/src/widgets/MultiSelectWidgetV2/index.ts +++ b/app/client/src/widgets/MultiSelectWidgetV2/index.ts @@ -31,6 +31,7 @@ export const CONFIG = { isRequired: false, isDisabled: false, placeholderText: "Select option(s)", + stretchOnMobile: true, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/PhoneInputWidget/index.ts b/app/client/src/widgets/PhoneInputWidget/index.ts index 72baef92a3c4..3f5870da5eca 100644 --- a/app/client/src/widgets/PhoneInputWidget/index.ts +++ b/app/client/src/widgets/PhoneInputWidget/index.ts @@ -16,6 +16,7 @@ export const CONFIG = { defaultDialCode: getDefaultISDCode().dial_code, allowDialCodeChange: false, allowFormatting: true, + stretchOnMobile: true, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/SingleSelectTreeWidget/index.ts b/app/client/src/widgets/SingleSelectTreeWidget/index.ts index 5e00d936e432..8c01937fe287 100644 --- a/app/client/src/widgets/SingleSelectTreeWidget/index.ts +++ b/app/client/src/widgets/SingleSelectTreeWidget/index.ts @@ -45,6 +45,7 @@ export const CONFIG = { labelAlignment: Alignment.LEFT, labelWidth: 5, labelTextSize: "0.875rem", + stretchOnMobile: true, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/TableWidgetV2/index.ts b/app/client/src/widgets/TableWidgetV2/index.ts index 01f587e15c03..409b6da2d246 100644 --- a/app/client/src/widgets/TableWidgetV2/index.ts +++ b/app/client/src/widgets/TableWidgetV2/index.ts @@ -17,6 +17,7 @@ export const CONFIG = { iconSVG: IconSVG, needsMeta: true, defaults: { + stretchOnMobile: true, rows: 28, columns: 34, animateLoading: true, diff --git a/app/client/src/widgets/TabsWidget/index.ts b/app/client/src/widgets/TabsWidget/index.ts index ce45fd4a796a..a488135349c4 100644 --- a/app/client/src/widgets/TabsWidget/index.ts +++ b/app/client/src/widgets/TabsWidget/index.ts @@ -10,6 +10,7 @@ export const CONFIG = { needsMeta: true, isCanvas: true, defaults: { + stretchOnMobile: true, rows: 40, columns: 24, shouldScrollContents: false, From 0ca9cd79ccc389fe45eb2e1e99d91f01f4bda1e2 Mon Sep 17 00:00:00 2001 From: Preet Sidhu Date: Wed, 10 Aug 2022 15:05:33 -0400 Subject: [PATCH 036/708] revert flex height to 100% --- app/client/src/widgets/ContainerWidget/component/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/client/src/widgets/ContainerWidget/component/index.tsx b/app/client/src/widgets/ContainerWidget/component/index.tsx index 64a322319b42..982aa4c5d7f1 100644 --- a/app/client/src/widgets/ContainerWidget/component/index.tsx +++ b/app/client/src/widgets/ContainerWidget/component/index.tsx @@ -73,7 +73,7 @@ export const FlexContainer = styled.div<{ flex-wrap: wrap; width: 100%; - height: auto; + height: 100%; `; function ContainerComponentWrapper(props: ContainerComponentProps) { From 925c3a07f8f28ca2d913d4040fa19c7836efe245 Mon Sep 17 00:00:00 2001 From: Preet Sidhu Date: Wed, 10 Aug 2022 16:41:03 -0400 Subject: [PATCH 037/708] fix container widget issues --- .../ContainerWidget/component/index.tsx | 9 ++++- .../src/widgets/ContainerWidget/index.ts | 3 +- .../widgets/ContainerWidget/widget/index.tsx | 37 +++++++++++-------- 3 files changed, 30 insertions(+), 19 deletions(-) diff --git a/app/client/src/widgets/ContainerWidget/component/index.tsx b/app/client/src/widgets/ContainerWidget/component/index.tsx index 982aa4c5d7f1..804c9809cf46 100644 --- a/app/client/src/widgets/ContainerWidget/component/index.tsx +++ b/app/client/src/widgets/ContainerWidget/component/index.tsx @@ -10,7 +10,10 @@ import WidgetStyleContainer, { } from "components/designSystems/appsmith/WidgetStyleContainer"; import { pick } from "lodash"; import { ComponentProps } from "widgets/BaseComponent"; -import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; +import { + MAIN_CONTAINER_WIDGET_ID, + RenderModes, +} from "constants/WidgetConstants"; import { JustifyContent, LayoutDirection } from "components/constants"; const scrollContents = css` @@ -64,6 +67,7 @@ export const FlexContainer = styled.div<{ direction?: string; justifyContent?: JustifyContent; alignItems?: string; + renderMode?: string; }>` display: ${({ useAutoLayout }) => (useAutoLayout ? "flex" : "block")}; flex-direction: ${({ direction }) => @@ -73,7 +77,8 @@ export const FlexContainer = styled.div<{ flex-wrap: wrap; width: 100%; - height: 100%; + height: ${({ renderMode }) => + renderMode === RenderModes.PAGE ? "auto" : "100%"}; `; function ContainerComponentWrapper(props: ContainerComponentProps) { diff --git a/app/client/src/widgets/ContainerWidget/index.ts b/app/client/src/widgets/ContainerWidget/index.ts index 65a99d311d0b..af004d401cdf 100644 --- a/app/client/src/widgets/ContainerWidget/index.ts +++ b/app/client/src/widgets/ContainerWidget/index.ts @@ -1,4 +1,4 @@ -import { ButtonBoxShadowTypes } from "components/constants"; +import { ButtonBoxShadowTypes, Positioning } from "components/constants"; import IconSVG from "./icon.svg"; import Widget from "./widget"; @@ -34,6 +34,7 @@ export const CONFIG = { ], }, version: 1, + positioning: Positioning.Fixed, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/ContainerWidget/widget/index.tsx b/app/client/src/widgets/ContainerWidget/widget/index.tsx index 0df2727e4ec2..7699aad65f24 100644 --- a/app/client/src/widgets/ContainerWidget/widget/index.tsx +++ b/app/client/src/widgets/ContainerWidget/widget/index.tsx @@ -413,23 +413,28 @@ class ContainerWidget extends BaseWidget< widgetType={this.props.type} /> {/* without the wrapping div onClick events are triggered twice */} - - - {this.renderChildren()} - - + + {this.renderChildren()} + + + ) : ( + <>{this.renderChildren()} + )} ); } From 7a500fa599ed66d0a6ada18da8b6150a44ac7263 Mon Sep 17 00:00:00 2001 From: Preet Sidhu Date: Thu, 11 Aug 2022 09:22:27 -0400 Subject: [PATCH 038/708] unregister auto layout widgets --- app/client/src/utils/WidgetRegistry.tsx | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/app/client/src/utils/WidgetRegistry.tsx b/app/client/src/utils/WidgetRegistry.tsx index f72e9d4e1ae8..cdeaababdccf 100644 --- a/app/client/src/utils/WidgetRegistry.tsx +++ b/app/client/src/utils/WidgetRegistry.tsx @@ -144,20 +144,11 @@ import VideoWidget, { import ProgressWidget, { CONFIG as PROGRESS_WIDGET_CONFIG, } from "widgets/ProgressWidget"; -import AutoLayoutContainerWidget, { - CONFIG as AUTO_LAYOUT_CONFIG, -} from "widgets/AutoLayoutContainerWidget"; -import VerticalLayoutWidget, { - CONFIG as VERTICAL_LAYOUT_CONFIG, -} from "widgets/VerticalLayoutWidget"; import { registerWidget } from "./WidgetRegisterHelpers"; import { WidgetConfiguration } from "widgets/constants"; import TableWidgetV2, { CONFIG as TABLE_WIDGET_CONFIG_V2, } from "widgets/TableWidgetV2"; -import HorizontalLayoutWidget, { - CONFIG as HORIZONTAL_LAYOUT_CONFIG, -} from "widgets/HorizontalLayoutWidget"; export const ALL_WIDGETS_AND_CONFIG = [ [CanvasWidget, CANVAS_WIDGET_CONFIG], @@ -205,9 +196,6 @@ export const ALL_WIDGETS_AND_CONFIG = [ [CurrencyInputWidget, CURRENCY_INPUT_WIDGET_V2_CONFIG], [JSONFormWidget, JSON_FORM_WIDGET_CONFIG], [TableWidgetV2, TABLE_WIDGET_CONFIG_V2], - [AutoLayoutContainerWidget, AUTO_LAYOUT_CONFIG], - [VerticalLayoutWidget, VERTICAL_LAYOUT_CONFIG], - [HorizontalLayoutWidget, HORIZONTAL_LAYOUT_CONFIG], //Deprecated Widgets [InputWidget, INPUT_WIDGET_CONFIG], From 2645c1549e85f2ea4e2e67179f3ebbc71dd4df13 Mon Sep 17 00:00:00 2001 From: Preet Sidhu Date: Thu, 11 Aug 2022 11:00:17 -0400 Subject: [PATCH 039/708] remove margin --- app/client/src/components/AutoLayoutWrapper.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/app/client/src/components/AutoLayoutWrapper.tsx b/app/client/src/components/AutoLayoutWrapper.tsx index e241bc837dea..e1c5f4c238c0 100644 --- a/app/client/src/components/AutoLayoutWrapper.tsx +++ b/app/client/src/components/AutoLayoutWrapper.tsx @@ -46,7 +46,6 @@ const ZIndexContainer = styled.div<{ ? "calc(100% - 16px)" : "auto"}; min-height: 30px; - margin: 8px; `; export function AutoLayoutWrapper(props: AutoLayoutProps) { From cdf81ad5b23b33f8859852c127f0c3b58c7c5777 Mon Sep 17 00:00:00 2001 From: Preet Sidhu Date: Thu, 11 Aug 2022 11:43:17 -0400 Subject: [PATCH 040/708] disable reflow --- .../pages/common/CanvasArenas/hooks/useCanvasDragging.ts | 4 +++- app/client/src/resizable/resizenreflow/index.tsx | 7 +++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index a534e9409e9d..2a1ba2e2c4c7 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -495,7 +495,9 @@ export const useCanvasDragging = ( const canReflowBasedOnMouseSpeed = canReflowForCurrentMouseMove(); const isReflowing = !isEmpty(currentReflowParams.movementMap); const canReflow = - !currentRectanglesToDraw[0].detachFromLayout && !dropDisabled; + !currentRectanglesToDraw[0].detachFromLayout && + !dropDisabled && + false; const currentBlock = currentRectanglesToDraw[0]; const [leftColumn, topRow] = getDropZoneOffsets( snapColumnSpace, diff --git a/app/client/src/resizable/resizenreflow/index.tsx b/app/client/src/resizable/resizenreflow/index.tsx index 403d1876b8b4..5aabe5425c27 100644 --- a/app/client/src/resizable/resizenreflow/index.tsx +++ b/app/client/src/resizable/resizenreflow/index.tsx @@ -249,6 +249,13 @@ export function ReflowResizable(props: ResizableProps) { bottomMostRow = 0, movementLimitMap: MovementLimitMap | undefined = {}; + if (resizedPositions) { + const isColliding = checkForCollision(resizedPositions); + if (isColliding) { + return prevState; + } + } + if (resizedPositions) { //calling reflow to update movements of reflowing widgets and get movementLimit of current resizing widget ({ bottomMostRow, movementLimitMap } = reflow( From b7dedd89db2d52964596c4cb08b90cf8233ea94e Mon Sep 17 00:00:00 2001 From: Preet Sidhu Date: Thu, 11 Aug 2022 11:53:19 -0400 Subject: [PATCH 041/708] fix flex wrap height --- .../src/widgets/ContainerWidget/component/index.tsx | 10 +++------- .../src/widgets/ContainerWidget/widget/index.tsx | 2 +- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/app/client/src/widgets/ContainerWidget/component/index.tsx b/app/client/src/widgets/ContainerWidget/component/index.tsx index 804c9809cf46..d959ab8c0fb7 100644 --- a/app/client/src/widgets/ContainerWidget/component/index.tsx +++ b/app/client/src/widgets/ContainerWidget/component/index.tsx @@ -10,10 +10,7 @@ import WidgetStyleContainer, { } from "components/designSystems/appsmith/WidgetStyleContainer"; import { pick } from "lodash"; import { ComponentProps } from "widgets/BaseComponent"; -import { - MAIN_CONTAINER_WIDGET_ID, - RenderModes, -} from "constants/WidgetConstants"; +import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; import { JustifyContent, LayoutDirection } from "components/constants"; const scrollContents = css` @@ -67,7 +64,7 @@ export const FlexContainer = styled.div<{ direction?: string; justifyContent?: JustifyContent; alignItems?: string; - renderMode?: string; + stretchHeight: boolean; }>` display: ${({ useAutoLayout }) => (useAutoLayout ? "flex" : "block")}; flex-direction: ${({ direction }) => @@ -77,8 +74,7 @@ export const FlexContainer = styled.div<{ flex-wrap: wrap; width: 100%; - height: ${({ renderMode }) => - renderMode === RenderModes.PAGE ? "auto" : "100%"}; + height: ${({ stretchHeight }) => (stretchHeight ? "100%" : "auto")}; `; function ContainerComponentWrapper(props: ContainerComponentProps) { diff --git a/app/client/src/widgets/ContainerWidget/widget/index.tsx b/app/client/src/widgets/ContainerWidget/widget/index.tsx index 7699aad65f24..22aa6e95b339 100644 --- a/app/client/src/widgets/ContainerWidget/widget/index.tsx +++ b/app/client/src/widgets/ContainerWidget/widget/index.tsx @@ -418,7 +418,7 @@ class ContainerWidget extends BaseWidget< alignItems={this.props.alignItems} direction={this.state.direction} justifyContent={this.props.justifyContent} - renderMode={this.props.renderMode} + stretchHeight={!this.props.children || !this.props.children?.length} useAutoLayout={this.state.useAutoLayout} > Date: Fri, 12 Aug 2022 18:39:03 -0400 Subject: [PATCH 042/708] add new property responsivebehavior --- app/client/src/components/constants.ts | 5 +++++ .../src/utils/ResposniveBehaviorConfig.ts | 22 +++++++++++++++++++ .../AudioRecorderWidget/widget/index.tsx | 3 +++ .../src/widgets/AudioWidget/widget/index.tsx | 3 +++ app/client/src/widgets/BaseWidget.tsx | 8 +++++-- .../src/widgets/ButtonGroupWidget/index.ts | 3 ++- .../ButtonGroupWidget/widget/index.tsx | 3 +++ app/client/src/widgets/ButtonWidget/index.ts | 2 ++ .../src/widgets/ButtonWidget/widget/index.tsx | 3 +++ app/client/src/widgets/CanvasWidget.tsx | 6 ++++- .../ChartWidget/widget/propertyConfig.ts | 3 +++ .../src/widgets/CheckboxGroupWidget/index.ts | 3 ++- .../CheckboxGroupWidget/widget/index.tsx | 3 +++ .../src/widgets/CheckboxWidget/index.ts | 3 ++- .../widgets/CheckboxWidget/widget/index.tsx | 4 +++- .../widgets/ContainerWidget/widget/index.tsx | 20 ++++++++++------- .../src/widgets/CurrencyInputWidget/index.ts | 3 ++- .../CurrencyInputWidget/widget/index.tsx | 3 +++ app/client/src/widgets/DividerWidget/index.ts | 2 ++ .../widgets/DividerWidget/widget/index.tsx | 3 +++ .../src/widgets/FilePickerWidgetV2/index.ts | 2 ++ .../FilePickerWidgetV2/widget/index.tsx | 3 +++ app/client/src/widgets/InputWidgetV2/index.ts | 3 ++- .../widgets/InputWidgetV2/widget/index.tsx | 3 +++ .../src/widgets/JSONFormWidget/index.ts | 3 ++- .../JSONFormWidget/widget/propertyConfig.ts | 8 ++++++- app/client/src/widgets/ListWidget/index.ts | 3 ++- .../ListWidget/widget/propertyConfig.ts | 3 +++ app/client/src/widgets/MapWidget/index.ts | 3 ++- .../src/widgets/MapWidget/widget/index.tsx | 3 +++ .../widgets/MultiSelectTreeWidget/index.ts | 3 ++- .../MultiSelectTreeWidget/widget/index.tsx | 4 +++- .../src/widgets/MultiSelectWidget/index.ts | 3 ++- .../MultiSelectWidget/widget/index.tsx | 4 +++- .../src/widgets/MultiSelectWidgetV2/index.ts | 3 ++- .../MultiSelectWidgetV2/widget/index.tsx | 4 +++- .../src/widgets/PhoneInputWidget/index.ts | 3 ++- .../widgets/PhoneInputWidget/widget/index.tsx | 3 +++ app/client/src/widgets/SelectWidget/index.ts | 3 ++- .../src/widgets/SelectWidget/widget/index.tsx | 5 +++-- .../widgets/SingleSelectTreeWidget/index.ts | 3 ++- .../SingleSelectTreeWidget/widget/index.tsx | 4 +++- app/client/src/widgets/TableWidgetV2/index.ts | 3 ++- .../widget/propertyConfig/General.ts | 3 +++ app/client/src/widgets/TabsWidget/index.ts | 3 ++- .../src/widgets/TabsWidget/widget/index.tsx | 19 +++++++++++++++- 46 files changed, 173 insertions(+), 36 deletions(-) create mode 100644 app/client/src/utils/ResposniveBehaviorConfig.ts diff --git a/app/client/src/components/constants.ts b/app/client/src/components/constants.ts index 93d5ab6046c2..620657be6feb 100644 --- a/app/client/src/components/constants.ts +++ b/app/client/src/components/constants.ts @@ -135,3 +135,8 @@ export enum Positioning { Horizontal = "horizontal", Vertical = "vertical", } + +export enum ResponsiveBehavior { + Fill = "fill", + Hug = "hug", +} diff --git a/app/client/src/utils/ResposniveBehaviorConfig.ts b/app/client/src/utils/ResposniveBehaviorConfig.ts new file mode 100644 index 000000000000..513895945649 --- /dev/null +++ b/app/client/src/utils/ResposniveBehaviorConfig.ts @@ -0,0 +1,22 @@ +import { ResponsiveBehavior } from "components/constants"; +import { ValidationTypes } from "constants/WidgetValidation"; + +export const generateResponsiveBehaviorConfig = ( + value: ResponsiveBehavior, +): any => { + return { + helpText: "Should the children take up the complete width on mobile", + propertyName: "responsiveBehavior", + label: "Responsive behavior", + controlType: "DROP_DOWN", + defaultValue: value, + options: [ + { label: "Fill", value: ResponsiveBehavior.Fill }, + { label: "Hug", value: ResponsiveBehavior.Hug }, + ], + isJSConvertible: true, + isBindProperty: false, + isTriggerProperty: true, + validation: { type: ValidationTypes.TEXT }, + }; +}; diff --git a/app/client/src/widgets/AudioRecorderWidget/widget/index.tsx b/app/client/src/widgets/AudioRecorderWidget/widget/index.tsx index d5f123884ae9..aa7b37b20f3c 100644 --- a/app/client/src/widgets/AudioRecorderWidget/widget/index.tsx +++ b/app/client/src/widgets/AudioRecorderWidget/widget/index.tsx @@ -8,6 +8,8 @@ import AudioRecorderComponent from "../component"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; import { createBlobUrl } from "utils/AppsmithUtils"; import { FileDataTypes } from "widgets/constants"; +import { generateResponsiveBehaviorConfig } from "utils/ResposniveBehaviorConfig"; +import { ResponsiveBehavior } from "components/constants"; export interface AudioRecorderWidgetProps extends WidgetProps { accentColor: string; @@ -66,6 +68,7 @@ class AudioRecorderWidget extends BaseWidget< isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, + { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug) }, ], }, { diff --git a/app/client/src/widgets/AudioWidget/widget/index.tsx b/app/client/src/widgets/AudioWidget/widget/index.tsx index a1d5d5520663..fe0428a63416 100644 --- a/app/client/src/widgets/AudioWidget/widget/index.tsx +++ b/app/client/src/widgets/AudioWidget/widget/index.tsx @@ -7,6 +7,8 @@ import Skeleton from "components/utils/Skeleton"; import { retryPromise } from "utils/AppsmithUtils"; import ReactPlayer from "react-player"; import { AutocompleteDataType } from "utils/autocomplete/TernServer"; +import { ResponsiveBehavior } from "components/constants"; +import { generateResponsiveBehaviorConfig } from "utils/ResposniveBehaviorConfig"; const AudioComponent = lazy(() => retryPromise(() => import("../component"))); @@ -75,6 +77,7 @@ class AudioWidget extends BaseWidget { isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, + { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug) }, ], }, { diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index 9985401d7d97..c30a78a3995e 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -37,7 +37,11 @@ import { BatchPropertyUpdatePayload } from "actions/controlActions"; import AppsmithConsole from "utils/AppsmithConsole"; import { ENTITY_TYPE } from "entities/AppsmithConsole"; import PreviewModeComponent from "components/editorComponents/PreviewModeComponent"; -import { AlignItems, LayoutDirection } from "components/constants"; +import { + AlignItems, + LayoutDirection, + ResponsiveBehavior, +} from "components/constants"; import { AutoLayoutWrapper } from "components/AutoLayoutWrapper"; /*** @@ -462,7 +466,7 @@ export interface WidgetPositionProps extends WidgetRowCols { useAutoLayout?: boolean; direction?: LayoutDirection; alignItems?: AlignItems; - stretchOnMobile?: boolean; + responsiveBehavior?: ResponsiveBehavior; } export const WIDGET_STATIC_PROPS = { diff --git a/app/client/src/widgets/ButtonGroupWidget/index.ts b/app/client/src/widgets/ButtonGroupWidget/index.ts index df135b1ebf6e..77d3c02612fa 100644 --- a/app/client/src/widgets/ButtonGroupWidget/index.ts +++ b/app/client/src/widgets/ButtonGroupWidget/index.ts @@ -5,6 +5,7 @@ import { BlueprintOperationTypes } from "widgets/constants"; import { klona as clone } from "klona/full"; import IconSVG from "./icon.svg"; import Widget from "./widget"; +import { ResponsiveBehavior } from "components/constants"; export const CONFIG = { type: Widget.getWidgetType(), @@ -22,7 +23,7 @@ export const CONFIG = { isVisible: true, version: 1, animateLoading: true, - stretchOnMobile: true, + responsiveBehavior: ResponsiveBehavior.Fill, groupButtons: { groupButton1: { label: "Favorite", diff --git a/app/client/src/widgets/ButtonGroupWidget/widget/index.tsx b/app/client/src/widgets/ButtonGroupWidget/widget/index.tsx index f20cde506161..9360191bf00e 100644 --- a/app/client/src/widgets/ButtonGroupWidget/widget/index.tsx +++ b/app/client/src/widgets/ButtonGroupWidget/widget/index.tsx @@ -10,10 +10,12 @@ import { ButtonPlacementTypes, ButtonPlacement, ButtonVariantTypes, + ResponsiveBehavior, } from "components/constants"; import ButtonGroupComponent from "../component"; import { MinimumPopupRows } from "widgets/constants"; import { getStylesheetValue } from "./helpers"; +import { generateResponsiveBehaviorConfig } from "utils/ResposniveBehaviorConfig"; class ButtonGroupWidget extends BaseWidget< ButtonGroupWidgetProps, @@ -74,6 +76,7 @@ class ButtonGroupWidget extends BaseWidget< isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, + { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, ], }, { diff --git a/app/client/src/widgets/ButtonWidget/index.ts b/app/client/src/widgets/ButtonWidget/index.ts index a5115c9ca4b6..5536546b030a 100644 --- a/app/client/src/widgets/ButtonWidget/index.ts +++ b/app/client/src/widgets/ButtonWidget/index.ts @@ -2,6 +2,7 @@ import { ButtonPlacementTypes, ButtonVariantTypes, RecaptchaTypes, + ResponsiveBehavior, } from "components/constants"; import IconSVG from "./icon.svg"; import Widget from "./widget"; @@ -27,6 +28,7 @@ export const CONFIG = { resetFormOnClick: false, recaptchaType: RecaptchaTypes.V3, version: 1, + responsiveBehavior: ResponsiveBehavior.Hug, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/ButtonWidget/widget/index.tsx b/app/client/src/widgets/ButtonWidget/widget/index.tsx index 444712d15dd6..8cd6d5749b12 100644 --- a/app/client/src/widgets/ButtonWidget/widget/index.tsx +++ b/app/client/src/widgets/ButtonWidget/widget/index.tsx @@ -17,8 +17,10 @@ import { RecaptchaType, ButtonPlacementTypes, ButtonPlacement, + ResponsiveBehavior, } from "components/constants"; import FormWidget from "widgets/FormWidget/widget"; +import { generateResponsiveBehaviorConfig } from "utils/ResposniveBehaviorConfig"; class ButtonWidget extends BaseWidget { onButtonClickBound: (event: React.MouseEvent) => void; @@ -449,6 +451,7 @@ class ButtonWidget extends BaseWidget { isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, + { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug) }, ], }, // TODO: refactor widgetParentProps implementation when we address #10659 diff --git a/app/client/src/widgets/CanvasWidget.tsx b/app/client/src/widgets/CanvasWidget.tsx index 19df209e590c..fa163d3c678d 100644 --- a/app/client/src/widgets/CanvasWidget.tsx +++ b/app/client/src/widgets/CanvasWidget.tsx @@ -9,6 +9,7 @@ import { getCanvasSnapRows } from "utils/WidgetPropsUtils"; import { getCanvasClassName } from "utils/generators"; import WidgetFactory, { DerivedPropertiesMap } from "utils/WidgetFactory"; import { CANVAS_DEFAULT_MIN_HEIGHT_PX } from "constants/AppConstants"; +import { ResponsiveBehavior } from "components/constants"; class CanvasWidget extends ContainerWidget { static getPropertyPaneConfig() { @@ -74,7 +75,10 @@ class CanvasWidget extends ContainerWidget { childWidgetData.justifyContent = this.props.justifyContent; childWidgetData.alignItems = this.props.alignItems; - if (childWidgetData?.stretchOnMobile && this.state.isMobile) { + if ( + childWidgetData?.responsiveBehavior === ResponsiveBehavior.Fill && + this.state.isMobile + ) { childWidgetData.leftColumn = 0; childWidgetData.rightColumn = 64; } diff --git a/app/client/src/widgets/ChartWidget/widget/propertyConfig.ts b/app/client/src/widgets/ChartWidget/widget/propertyConfig.ts index e4f641e3d7e1..ea8995931b82 100644 --- a/app/client/src/widgets/ChartWidget/widget/propertyConfig.ts +++ b/app/client/src/widgets/ChartWidget/widget/propertyConfig.ts @@ -3,6 +3,8 @@ import { ValidationTypes } from "constants/WidgetValidation"; import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; import { CUSTOM_CHART_TYPES, LabelOrientation } from "../constants"; import { isLabelOrientationApplicableFor } from "../component"; +import { generateResponsiveBehaviorConfig } from "utils/ResposniveBehaviorConfig"; +import { ResponsiveBehavior } from "components/constants"; export default [ { @@ -236,6 +238,7 @@ export default [ x.chartType === "CUSTOM_FUSION_CHART" || x.chartType === "PIE_CHART", dependencies: ["chartType"], }, + { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug) }, ], }, { diff --git a/app/client/src/widgets/CheckboxGroupWidget/index.ts b/app/client/src/widgets/CheckboxGroupWidget/index.ts index 482e3321dfb5..3ad4d2b82d0d 100644 --- a/app/client/src/widgets/CheckboxGroupWidget/index.ts +++ b/app/client/src/widgets/CheckboxGroupWidget/index.ts @@ -1,5 +1,5 @@ import { Alignment } from "@blueprintjs/core"; -import { LabelPosition } from "components/constants"; +import { LabelPosition, ResponsiveBehavior } from "components/constants"; import IconSVG from "./icon.svg"; import Widget from "./widget"; @@ -29,6 +29,7 @@ export const CONFIG = { labelWidth: 5, widgetName: "CheckboxGroup", version: 2, + responsiveBehavior: ResponsiveBehavior.Fill, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/CheckboxGroupWidget/widget/index.tsx b/app/client/src/widgets/CheckboxGroupWidget/widget/index.tsx index d78fa85e338a..5ebd1257dce7 100644 --- a/app/client/src/widgets/CheckboxGroupWidget/widget/index.tsx +++ b/app/client/src/widgets/CheckboxGroupWidget/widget/index.tsx @@ -15,12 +15,14 @@ import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; import { CheckboxGroupAlignmentTypes, LabelPosition, + ResponsiveBehavior, } from "components/constants"; import { Alignment } from "@blueprintjs/core"; import { GRID_DENSITY_MIGRATION_V1 } from "widgets/constants"; import CheckboxGroupComponent from "../component"; import { OptionProps, SelectAllState, SelectAllStates } from "../constants"; +import { generateResponsiveBehaviorConfig } from "utils/ResposniveBehaviorConfig"; export function defaultSelectedValuesValidation( value: unknown, @@ -192,6 +194,7 @@ class CheckboxGroupWidget extends BaseWidget< isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, + { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, ], }, { diff --git a/app/client/src/widgets/CheckboxWidget/index.ts b/app/client/src/widgets/CheckboxWidget/index.ts index f1468597aab4..708bdf180c7b 100644 --- a/app/client/src/widgets/CheckboxWidget/index.ts +++ b/app/client/src/widgets/CheckboxWidget/index.ts @@ -1,6 +1,6 @@ import Widget from "./widget"; import IconSVG from "./icon.svg"; -import { LabelPosition } from "components/constants"; +import { LabelPosition, ResponsiveBehavior } from "components/constants"; import { AlignWidgetTypes } from "widgets/constants"; export const CONFIG = { @@ -21,6 +21,7 @@ export const CONFIG = { isDisabled: false, isRequired: false, animateLoading: true, + responsiveBehavior: ResponsiveBehavior.Fill, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/CheckboxWidget/widget/index.tsx b/app/client/src/widgets/CheckboxWidget/widget/index.tsx index 22a881f84f26..10f4787a4513 100644 --- a/app/client/src/widgets/CheckboxWidget/widget/index.tsx +++ b/app/client/src/widgets/CheckboxWidget/widget/index.tsx @@ -5,8 +5,9 @@ import CheckboxComponent from "../component"; import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; import { ValidationTypes } from "constants/WidgetValidation"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; -import { LabelPosition } from "components/constants"; +import { LabelPosition, ResponsiveBehavior } from "components/constants"; import { AlignWidgetTypes } from "widgets/constants"; +import { generateResponsiveBehaviorConfig } from "utils/ResposniveBehaviorConfig"; class CheckboxWidget extends BaseWidget { static getPropertyPaneConfig() { @@ -112,6 +113,7 @@ class CheckboxWidget extends BaseWidget { isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, + { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, ], }, { diff --git a/app/client/src/widgets/ContainerWidget/widget/index.tsx b/app/client/src/widgets/ContainerWidget/widget/index.tsx index 22aa6e95b339..44516611b3e5 100644 --- a/app/client/src/widgets/ContainerWidget/widget/index.tsx +++ b/app/client/src/widgets/ContainerWidget/widget/index.tsx @@ -24,6 +24,7 @@ import { JustifyContent, LayoutDirection, Positioning, + ResponsiveBehavior, } from "components/constants"; import { AutoLayoutContext } from "utils/autoLayoutContext"; @@ -94,14 +95,18 @@ class ContainerWidget extends BaseWidget< { helpText: "Should the children take up the complete width on mobile", - propertyName: "stretchOnMobile", - label: "Stretch On Mobile", - controlType: "SWITCH", - defaultValue: false, - isJSConvertible: false, + propertyName: "responsiveBehavior", + label: "Responsive behavior", + controlType: "DROP_DOWN", + defaultValue: ResponsiveBehavior.Fill, + options: [ + { label: "Fill", value: ResponsiveBehavior.Fill }, + { label: "Hug", value: ResponsiveBehavior.Hug }, + ], + isJSConvertible: true, isBindProperty: false, - isTriggerProperty: false, - validation: { type: ValidationTypes.BOOLEAN }, + isTriggerProperty: true, + validation: { type: ValidationTypes.TEXT }, }, ], }, @@ -455,7 +460,6 @@ export interface ContainerWidgetProps shouldScrollContents?: boolean; noPad?: boolean; positioning?: Positioning; - stretchOnMobile?: boolean; } export interface ContainerWidgetState extends WidgetState { diff --git a/app/client/src/widgets/CurrencyInputWidget/index.ts b/app/client/src/widgets/CurrencyInputWidget/index.ts index c48b0ebb7fe2..ae36844e3aec 100644 --- a/app/client/src/widgets/CurrencyInputWidget/index.ts +++ b/app/client/src/widgets/CurrencyInputWidget/index.ts @@ -2,6 +2,7 @@ import Widget from "./widget"; import IconSVG from "./icon.svg"; import { CONFIG as BaseConfig } from "widgets/BaseInputWidget"; import { getDefaultCurrency } from "./component/CurrencyCodeDropdown"; +import { ResponsiveBehavior } from "components/constants"; export const CONFIG = { type: Widget.getWidgetType(), @@ -16,7 +17,7 @@ export const CONFIG = { allowCurrencyChange: false, defaultCurrencyCode: getDefaultCurrency().currency, decimals: 0, - stretchOnMobile: true, + responsiveBehavior: ResponsiveBehavior.Fill, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/CurrencyInputWidget/widget/index.tsx b/app/client/src/widgets/CurrencyInputWidget/widget/index.tsx index d536a8148d50..3c2d9d212107 100644 --- a/app/client/src/widgets/CurrencyInputWidget/widget/index.tsx +++ b/app/client/src/widgets/CurrencyInputWidget/widget/index.tsx @@ -33,6 +33,8 @@ import { } from "../component/utilities"; import { mergeWidgetConfig } from "utils/helpers"; import { GRID_DENSITY_MIGRATION_V1 } from "widgets/constants"; +import { generateResponsiveBehaviorConfig } from "utils/ResposniveBehaviorConfig"; +import { ResponsiveBehavior } from "components/constants"; export function defaultValueValidation( value: any, @@ -172,6 +174,7 @@ class CurrencyInputWidget extends BaseInputWidget< }, dependencies: ["decimals"], }, + { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, ], }, ], diff --git a/app/client/src/widgets/DividerWidget/index.ts b/app/client/src/widgets/DividerWidget/index.ts index c50659d7d21c..3962e6692773 100644 --- a/app/client/src/widgets/DividerWidget/index.ts +++ b/app/client/src/widgets/DividerWidget/index.ts @@ -1,3 +1,4 @@ +import { ResponsiveBehavior } from "components/constants"; import { Colors } from "constants/Colors"; import IconSVG from "./icon.svg"; import Widget from "./widget"; @@ -20,6 +21,7 @@ export const CONFIG = { isVisible: true, version: 1, animateLoading: true, + responsiveBehavior: ResponsiveBehavior.Fill, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/DividerWidget/widget/index.tsx b/app/client/src/widgets/DividerWidget/widget/index.tsx index 8c48d112df9f..bebd67b70225 100644 --- a/app/client/src/widgets/DividerWidget/widget/index.tsx +++ b/app/client/src/widgets/DividerWidget/widget/index.tsx @@ -4,6 +4,8 @@ import { WidgetType } from "constants/WidgetConstants"; import DividerComponent from "../component"; import { ValidationTypes } from "constants/WidgetValidation"; +import { generateResponsiveBehaviorConfig } from "utils/ResposniveBehaviorConfig"; +import { ResponsiveBehavior } from "components/constants"; class DividerWidget extends BaseWidget { static getPropertyPaneConfig() { @@ -52,6 +54,7 @@ class DividerWidget extends BaseWidget { isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, + { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, ], }, { diff --git a/app/client/src/widgets/FilePickerWidgetV2/index.ts b/app/client/src/widgets/FilePickerWidgetV2/index.ts index 38573425837b..7db79fd3b264 100644 --- a/app/client/src/widgets/FilePickerWidgetV2/index.ts +++ b/app/client/src/widgets/FilePickerWidgetV2/index.ts @@ -1,3 +1,4 @@ +import { ResponsiveBehavior } from "components/constants"; import FileDataTypes from "./constants"; import IconSVG from "./icon.svg"; import Widget from "./widget"; @@ -24,6 +25,7 @@ export const CONFIG = { isRequired: false, isDisabled: false, animateLoading: true, + responsiveBehavior: ResponsiveBehavior.Fill, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/FilePickerWidgetV2/widget/index.tsx b/app/client/src/widgets/FilePickerWidgetV2/widget/index.tsx index a4d47a34257b..e7720305341a 100644 --- a/app/client/src/widgets/FilePickerWidgetV2/widget/index.tsx +++ b/app/client/src/widgets/FilePickerWidgetV2/widget/index.tsx @@ -21,6 +21,8 @@ import { createGlobalStyle } from "styled-components"; import UpIcon from "assets/icons/ads/up-arrow.svg"; import CloseIcon from "assets/icons/ads/cross.svg"; import { Colors } from "constants/Colors"; +import { generateResponsiveBehaviorConfig } from "utils/ResposniveBehaviorConfig"; +import { ResponsiveBehavior } from "components/constants"; const FilePickerGlobalStyles = createGlobalStyle<{ borderRadius?: string; @@ -362,6 +364,7 @@ class FilePickerWidget extends BaseWidget< isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, + { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, ], }, { diff --git a/app/client/src/widgets/InputWidgetV2/index.ts b/app/client/src/widgets/InputWidgetV2/index.ts index eb2df7e6b434..8203830fecde 100644 --- a/app/client/src/widgets/InputWidgetV2/index.ts +++ b/app/client/src/widgets/InputWidgetV2/index.ts @@ -1,6 +1,7 @@ import Widget from "./widget"; import IconSVG from "./icon.svg"; import { CONFIG as BaseConfig } from "widgets/BaseInputWidget"; +import { ResponsiveBehavior } from "components/constants"; export const CONFIG = { type: Widget.getWidgetType(), @@ -13,7 +14,7 @@ export const CONFIG = { inputType: "TEXT", widgetName: "Input", version: 2, - stretchOnMobile: true, + responsiveBehavior: ResponsiveBehavior.Fill, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/InputWidgetV2/widget/index.tsx b/app/client/src/widgets/InputWidgetV2/widget/index.tsx index b971ad7614a9..56c39dc61c82 100644 --- a/app/client/src/widgets/InputWidgetV2/widget/index.tsx +++ b/app/client/src/widgets/InputWidgetV2/widget/index.tsx @@ -25,6 +25,8 @@ import { BaseInputWidgetProps } from "widgets/BaseInputWidget/widget"; import { mergeWidgetConfig } from "utils/helpers"; import { InputTypes } from "widgets/BaseInputWidget/constants"; import { getParsedText } from "./Utilities"; +import { generateResponsiveBehaviorConfig } from "utils/ResposniveBehaviorConfig"; +import { ResponsiveBehavior } from "components/constants"; export function defaultValueValidation( value: any, @@ -292,6 +294,7 @@ class InputWidget extends BaseInputWidget { }, dependencies: ["inputType"], }, + { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, ], }, { diff --git a/app/client/src/widgets/JSONFormWidget/index.ts b/app/client/src/widgets/JSONFormWidget/index.ts index 399852fe0a47..10df6419cd49 100644 --- a/app/client/src/widgets/JSONFormWidget/index.ts +++ b/app/client/src/widgets/JSONFormWidget/index.ts @@ -2,6 +2,7 @@ import IconSVG from "./icon.svg"; import Widget, { JSONFormWidgetProps } from "./widget"; import { ButtonVariantTypes } from "components/constants"; import { BlueprintOperationTypes } from "widgets/constants"; +import { ResponsiveBehavior } from "components/constants"; const SUBMIT_BUTTON_DEFAULT_STYLES = { buttonVariant: ButtonVariantTypes.PRIMARY, @@ -17,7 +18,7 @@ export const CONFIG = { iconSVG: IconSVG, needsMeta: true, defaults: { - stretchOnMobile: true, + responsiveBehavior: ResponsiveBehavior.Fill, animateLoading: true, backgroundColor: "#fff", columns: 25, diff --git a/app/client/src/widgets/JSONFormWidget/widget/propertyConfig.ts b/app/client/src/widgets/JSONFormWidget/widget/propertyConfig.ts index 7b61d71dc894..8ff5b651a89f 100644 --- a/app/client/src/widgets/JSONFormWidget/widget/propertyConfig.ts +++ b/app/client/src/widgets/JSONFormWidget/widget/propertyConfig.ts @@ -6,11 +6,16 @@ import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; import { JSONFormWidgetProps } from "."; import { ROOT_SCHEMA_KEY } from "../constants"; import { ValidationTypes } from "constants/WidgetValidation"; -import { ButtonVariantTypes, ButtonPlacementTypes } from "components/constants"; +import { + ButtonVariantTypes, + ButtonPlacementTypes, + ResponsiveBehavior, +} from "components/constants"; import { ButtonWidgetProps } from "widgets/ButtonWidget/widget"; import { OnButtonClickProps } from "components/propertyControls/ButtonControl"; import { ComputedSchemaStatus, computeSchema } from "./helper"; import { EVALUATION_PATH } from "utils/DynamicBindingUtils"; +import { generateResponsiveBehaviorConfig } from "utils/ResposniveBehaviorConfig"; const MAX_NESTING_LEVEL = 5; @@ -833,6 +838,7 @@ export default [ isTriggerProperty: false, validation: { type: ValidationTypes.TEXT }, }, + { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, ], }, { diff --git a/app/client/src/widgets/ListWidget/index.ts b/app/client/src/widgets/ListWidget/index.ts index 5cbf57bdb132..f7ae4e678130 100644 --- a/app/client/src/widgets/ListWidget/index.ts +++ b/app/client/src/widgets/ListWidget/index.ts @@ -10,6 +10,7 @@ import { } from "widgets/constants"; import IconSVG from "./icon.svg"; import Widget from "./widget"; +import { ResponsiveBehavior } from "components/constants"; export const CONFIG = { type: Widget.getWidgetType(), @@ -25,7 +26,7 @@ export const CONFIG = { animateLoading: true, gridType: "vertical", template: {}, - stretchOnMobile: true, + responsiveBehavior: ResponsiveBehavior.Fill, enhancements: { child: { autocomplete: (parentProps: any) => { diff --git a/app/client/src/widgets/ListWidget/widget/propertyConfig.ts b/app/client/src/widgets/ListWidget/widget/propertyConfig.ts index 706df2438199..391f42cbe30f 100644 --- a/app/client/src/widgets/ListWidget/widget/propertyConfig.ts +++ b/app/client/src/widgets/ListWidget/widget/propertyConfig.ts @@ -6,6 +6,8 @@ import { ValidationTypes } from "constants/WidgetValidation"; import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; import { EVAL_VALUE_PATH } from "utils/DynamicBindingUtils"; import { AutocompleteDataType } from "utils/autocomplete/TernServer"; +import { generateResponsiveBehaviorConfig } from "utils/ResposniveBehaviorConfig"; +import { ResponsiveBehavior } from "components/constants"; const PropertyPaneConfig = [ { @@ -55,6 +57,7 @@ const PropertyPaneConfig = [ isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, + { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, ], }, { diff --git a/app/client/src/widgets/MapWidget/index.ts b/app/client/src/widgets/MapWidget/index.ts index 64db71f413f2..12f98ec91f22 100644 --- a/app/client/src/widgets/MapWidget/index.ts +++ b/app/client/src/widgets/MapWidget/index.ts @@ -1,5 +1,6 @@ import IconSVG from "./icon.svg"; import Widget from "./widget"; +import { ResponsiveBehavior } from "components/constants"; export const CONFIG = { type: Widget.getWidgetType(), @@ -21,7 +22,7 @@ export const CONFIG = { isClickedMarkerCentered: true, version: 1, animateLoading: true, - stretchOnMobile: true, + responsiveBehavior: ResponsiveBehavior.Fill, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/MapWidget/widget/index.tsx b/app/client/src/widgets/MapWidget/widget/index.tsx index 0efbba7e6e16..2efcbf456cf2 100644 --- a/app/client/src/widgets/MapWidget/widget/index.tsx +++ b/app/client/src/widgets/MapWidget/widget/index.tsx @@ -12,6 +12,8 @@ import { getBorderCSSShorthand } from "constants/DefaultTheme"; import { MarkerProps } from "../constants"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; +import { generateResponsiveBehaviorConfig } from "utils/ResposniveBehaviorConfig"; +import { ResponsiveBehavior } from "components/constants"; const { google } = getAppsmithConfigs(); @@ -201,6 +203,7 @@ class MapWidget extends BaseWidget { isBindProperty: false, isTriggerProperty: false, }, + { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, ], }, { diff --git a/app/client/src/widgets/MultiSelectTreeWidget/index.ts b/app/client/src/widgets/MultiSelectTreeWidget/index.ts index e4ff4c6f4a7f..50a89c655c5a 100644 --- a/app/client/src/widgets/MultiSelectTreeWidget/index.ts +++ b/app/client/src/widgets/MultiSelectTreeWidget/index.ts @@ -2,6 +2,7 @@ import { Alignment } from "@blueprintjs/core"; import { LabelPosition } from "components/constants"; import IconSVG from "./icon.svg"; import Widget from "./widget"; +import { ResponsiveBehavior } from "components/constants"; export const CONFIG = { type: Widget.getWidgetType(), @@ -46,7 +47,7 @@ export const CONFIG = { labelAlignment: Alignment.LEFT, labelWidth: 5, labelTextSize: "0.875rem", - stretchOnMobile: true, + responsiveBehavior: ResponsiveBehavior.Fill, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/MultiSelectTreeWidget/widget/index.tsx b/app/client/src/widgets/MultiSelectTreeWidget/widget/index.tsx index 2dd58d05ad22..370b9fff9729 100644 --- a/app/client/src/widgets/MultiSelectTreeWidget/widget/index.tsx +++ b/app/client/src/widgets/MultiSelectTreeWidget/widget/index.tsx @@ -14,8 +14,9 @@ import { CheckedStrategy } from "rc-tree-select/lib/utils/strategyUtil"; import { GRID_DENSITY_MIGRATION_V1, MinimumPopupRows } from "widgets/constants"; import { AutocompleteDataType } from "utils/autocomplete/TernServer"; import MultiTreeSelectComponent from "../component"; -import { LabelPosition } from "components/constants"; +import { LabelPosition, ResponsiveBehavior } from "components/constants"; import { Alignment } from "@blueprintjs/core"; +import { generateResponsiveBehaviorConfig } from "utils/ResposniveBehaviorConfig"; function defaultOptionValueValidation(value: unknown): ValidationResponse { let values: string[] = []; @@ -234,6 +235,7 @@ class MultiSelectTreeWidget extends BaseWidget< isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, + { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, ], }, { diff --git a/app/client/src/widgets/MultiSelectWidget/index.ts b/app/client/src/widgets/MultiSelectWidget/index.ts index 0896088cf531..f673d675c43f 100644 --- a/app/client/src/widgets/MultiSelectWidget/index.ts +++ b/app/client/src/widgets/MultiSelectWidget/index.ts @@ -2,6 +2,7 @@ import { Alignment } from "@blueprintjs/core"; import { LabelPosition } from "components/constants"; import IconSVG from "./icon.svg"; import Widget from "./widget"; +import { ResponsiveBehavior } from "components/constants"; export const CONFIG = { type: Widget.getWidgetType(), @@ -31,7 +32,7 @@ export const CONFIG = { isRequired: false, isDisabled: false, placeholderText: "Select option(s)", - stretchOnMobile: true, + responsiveBehavior: ResponsiveBehavior.Fill, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/MultiSelectWidget/widget/index.tsx b/app/client/src/widgets/MultiSelectWidget/widget/index.tsx index 6be53bce56e3..3deaea45bf97 100644 --- a/app/client/src/widgets/MultiSelectWidget/widget/index.tsx +++ b/app/client/src/widgets/MultiSelectWidget/widget/index.tsx @@ -14,8 +14,9 @@ import { DefaultValueType } from "rc-select/lib/interface/generator"; import { Layers } from "constants/Layers"; import { AutocompleteDataType } from "utils/autocomplete/TernServer"; import { MinimumPopupRows, GRID_DENSITY_MIGRATION_V1 } from "widgets/constants"; -import { LabelPosition } from "components/constants"; +import { LabelPosition, ResponsiveBehavior } from "components/constants"; import { Alignment } from "@blueprintjs/core"; +import { generateResponsiveBehaviorConfig } from "utils/ResposniveBehaviorConfig"; function defaultOptionValueValidation(value: unknown): ValidationResponse { let values: string[] = []; @@ -185,6 +186,7 @@ class MultiSelectWidget extends BaseWidget< isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, + { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, ], }, { diff --git a/app/client/src/widgets/MultiSelectWidgetV2/index.ts b/app/client/src/widgets/MultiSelectWidgetV2/index.ts index d866b302bad0..7e1d40bb8519 100644 --- a/app/client/src/widgets/MultiSelectWidgetV2/index.ts +++ b/app/client/src/widgets/MultiSelectWidgetV2/index.ts @@ -2,6 +2,7 @@ import Widget from "./widget"; import IconSVG from "./icon.svg"; import { LabelPosition } from "components/constants"; import { Alignment } from "@blueprintjs/core"; +import { ResponsiveBehavior } from "components/constants"; export const CONFIG = { type: Widget.getWidgetType(), @@ -31,7 +32,7 @@ export const CONFIG = { isRequired: false, isDisabled: false, placeholderText: "Select option(s)", - stretchOnMobile: true, + responsiveBehavior: ResponsiveBehavior.Fill, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/MultiSelectWidgetV2/widget/index.tsx b/app/client/src/widgets/MultiSelectWidgetV2/widget/index.tsx index f92f7a6e3eb8..9878cb757a65 100644 --- a/app/client/src/widgets/MultiSelectWidgetV2/widget/index.tsx +++ b/app/client/src/widgets/MultiSelectWidgetV2/widget/index.tsx @@ -23,9 +23,10 @@ import { } from "rc-select/lib/interface/generator"; import { Layers } from "constants/Layers"; import { MinimumPopupRows, GRID_DENSITY_MIGRATION_V1 } from "widgets/constants"; -import { LabelPosition } from "components/constants"; +import { LabelPosition, ResponsiveBehavior } from "components/constants"; import { Alignment } from "@blueprintjs/core"; import { AutocompleteDataType } from "utils/autocomplete/TernServer"; +import { generateResponsiveBehaviorConfig } from "utils/ResposniveBehaviorConfig"; export function defaultOptionValueValidation( value: unknown, @@ -329,6 +330,7 @@ class MultiSelectWidget extends BaseWidget< isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, + { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, ], }, { diff --git a/app/client/src/widgets/PhoneInputWidget/index.ts b/app/client/src/widgets/PhoneInputWidget/index.ts index 3f5870da5eca..7620b951b315 100644 --- a/app/client/src/widgets/PhoneInputWidget/index.ts +++ b/app/client/src/widgets/PhoneInputWidget/index.ts @@ -2,6 +2,7 @@ import Widget from "./widget"; import IconSVG from "./icon.svg"; import { CONFIG as BaseConfig } from "widgets/BaseInputWidget"; import { getDefaultISDCode } from "./component/ISDCodeDropdown"; +import { ResponsiveBehavior } from "components/constants"; export const CONFIG = { type: Widget.getWidgetType(), @@ -16,7 +17,7 @@ export const CONFIG = { defaultDialCode: getDefaultISDCode().dial_code, allowDialCodeChange: false, allowFormatting: true, - stretchOnMobile: true, + responsiveBehavior: ResponsiveBehavior.Fill, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/PhoneInputWidget/widget/index.tsx b/app/client/src/widgets/PhoneInputWidget/widget/index.tsx index 2b4c22a398c5..497484875809 100644 --- a/app/client/src/widgets/PhoneInputWidget/widget/index.tsx +++ b/app/client/src/widgets/PhoneInputWidget/widget/index.tsx @@ -30,6 +30,8 @@ import { import * as Sentry from "@sentry/react"; import log from "loglevel"; import { GRID_DENSITY_MIGRATION_V1 } from "widgets/constants"; +import { generateResponsiveBehaviorConfig } from "utils/ResposniveBehaviorConfig"; +import { ResponsiveBehavior } from "components/constants"; export function defaultValueValidation( value: any, @@ -131,6 +133,7 @@ class PhoneInputWidget extends BaseInputWidget< isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, + { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, ], }, ], diff --git a/app/client/src/widgets/SelectWidget/index.ts b/app/client/src/widgets/SelectWidget/index.ts index 01f80509f831..a2c08b30f551 100644 --- a/app/client/src/widgets/SelectWidget/index.ts +++ b/app/client/src/widgets/SelectWidget/index.ts @@ -2,6 +2,7 @@ import Widget from "./widget"; import IconSVG from "./icon.svg"; import { LabelPosition } from "components/constants"; import { Alignment } from "@blueprintjs/core"; +import { ResponsiveBehavior } from "components/constants"; export const CONFIG = { type: Widget.getWidgetType(), @@ -31,7 +32,7 @@ export const CONFIG = { isDisabled: false, animateLoading: true, labelTextSize: "0.875rem", - stretchOnMobile: true, + responsiveBehavior: ResponsiveBehavior.Fill, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/SelectWidget/widget/index.tsx b/app/client/src/widgets/SelectWidget/widget/index.tsx index b9ccb07c86a5..8ab9bb06c6a1 100644 --- a/app/client/src/widgets/SelectWidget/widget/index.tsx +++ b/app/client/src/widgets/SelectWidget/widget/index.tsx @@ -10,7 +10,7 @@ import { } from "constants/WidgetValidation"; import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; import { MinimumPopupRows, GRID_DENSITY_MIGRATION_V1 } from "widgets/constants"; -import { LabelPosition } from "components/constants"; +import { LabelPosition, ResponsiveBehavior } from "components/constants"; import { Alignment } from "@blueprintjs/core"; import { AutocompleteDataType } from "utils/autocomplete/TernServer"; import { @@ -23,6 +23,7 @@ import { LoDashStatic, } from "lodash"; import derivedProperties from "./parseDerivedProperties"; +import { generateResponsiveBehaviorConfig } from "utils/ResposniveBehaviorConfig"; export function defaultOptionValueValidation( value: unknown, @@ -246,6 +247,7 @@ class SelectWidget extends BaseWidget { isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, + { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, ], }, { @@ -620,7 +622,6 @@ export interface SelectWidgetProps extends WidgetProps { onFilterUpdate: string; isDirty?: boolean; filterText: string; - stretchOnMobile?: boolean; } export default SelectWidget; diff --git a/app/client/src/widgets/SingleSelectTreeWidget/index.ts b/app/client/src/widgets/SingleSelectTreeWidget/index.ts index 8c01937fe287..7465cbf4eaa3 100644 --- a/app/client/src/widgets/SingleSelectTreeWidget/index.ts +++ b/app/client/src/widgets/SingleSelectTreeWidget/index.ts @@ -2,6 +2,7 @@ import { Alignment } from "@blueprintjs/core"; import { LabelPosition } from "components/constants"; import IconSVG from "./icon.svg"; import Widget from "./widget"; +import { ResponsiveBehavior } from "components/constants"; export const CONFIG = { type: Widget.getWidgetType(), @@ -45,7 +46,7 @@ export const CONFIG = { labelAlignment: Alignment.LEFT, labelWidth: 5, labelTextSize: "0.875rem", - stretchOnMobile: true, + responsiveBehavior: ResponsiveBehavior.Fill, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/SingleSelectTreeWidget/widget/index.tsx b/app/client/src/widgets/SingleSelectTreeWidget/widget/index.tsx index bd63d30eb87e..e2f0c57717c7 100644 --- a/app/client/src/widgets/SingleSelectTreeWidget/widget/index.tsx +++ b/app/client/src/widgets/SingleSelectTreeWidget/widget/index.tsx @@ -14,8 +14,9 @@ import { isString } from "../../../utils/helpers"; import { AutocompleteDataType } from "utils/autocomplete/TernServer"; import { GRID_DENSITY_MIGRATION_V1, MinimumPopupRows } from "widgets/constants"; import SingleSelectTreeComponent from "../component"; -import { LabelPosition } from "components/constants"; +import { LabelPosition, ResponsiveBehavior } from "components/constants"; import { Alignment } from "@blueprintjs/core"; +import { generateResponsiveBehaviorConfig } from "utils/ResposniveBehaviorConfig"; function defaultOptionValueValidation(value: unknown): ValidationResponse { if (typeof value === "string") return { isValid: true, parsed: value.trim() }; @@ -198,6 +199,7 @@ class SingleSelectTreeWidget extends BaseWidget< isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, + { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, ], }, { diff --git a/app/client/src/widgets/TableWidgetV2/index.ts b/app/client/src/widgets/TableWidgetV2/index.ts index 409b6da2d246..55264ddd5808 100644 --- a/app/client/src/widgets/TableWidgetV2/index.ts +++ b/app/client/src/widgets/TableWidgetV2/index.ts @@ -10,6 +10,7 @@ import { InlineEditingSaveOptions } from "./constants"; import IconSVG from "./icon.svg"; import Widget from "./widget"; import { escapeString } from "./widget/utilities"; +import { ResponsiveBehavior } from "components/constants"; export const CONFIG = { type: Widget.getWidgetType(), @@ -17,7 +18,7 @@ export const CONFIG = { iconSVG: IconSVG, needsMeta: true, defaults: { - stretchOnMobile: true, + responsiveBehavior: ResponsiveBehavior.Fill, rows: 28, columns: 34, animateLoading: true, diff --git a/app/client/src/widgets/TableWidgetV2/widget/propertyConfig/General.ts b/app/client/src/widgets/TableWidgetV2/widget/propertyConfig/General.ts index 7d7e42fe73fa..c73d2ffd9817 100644 --- a/app/client/src/widgets/TableWidgetV2/widget/propertyConfig/General.ts +++ b/app/client/src/widgets/TableWidgetV2/widget/propertyConfig/General.ts @@ -18,6 +18,8 @@ import { } from "@appsmith/constants/messages"; import panelConfig from "./PanelConfig"; import { composePropertyUpdateHook } from "widgets/WidgetUtils"; +import { generateResponsiveBehaviorConfig } from "utils/ResposniveBehaviorConfig"; +import { ResponsiveBehavior } from "components/constants"; export default { sectionName: "General", @@ -273,5 +275,6 @@ export default { isBindProperty: false, isTriggerProperty: false, }, + { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, ], }; diff --git a/app/client/src/widgets/TabsWidget/index.ts b/app/client/src/widgets/TabsWidget/index.ts index a488135349c4..4a3ecf1f2bb1 100644 --- a/app/client/src/widgets/TabsWidget/index.ts +++ b/app/client/src/widgets/TabsWidget/index.ts @@ -2,6 +2,7 @@ import { WidgetProps } from "widgets/BaseWidget"; import { BlueprintOperationTypes } from "widgets/constants"; import IconSVG from "./icon.svg"; import Widget from "./widget"; +import { ResponsiveBehavior } from "components/constants"; export const CONFIG = { type: Widget.getWidgetType(), @@ -10,7 +11,7 @@ export const CONFIG = { needsMeta: true, isCanvas: true, defaults: { - stretchOnMobile: true, + responsiveBehavior: ResponsiveBehavior.Fill, rows: 40, columns: 24, shouldScrollContents: false, diff --git a/app/client/src/widgets/TabsWidget/widget/index.tsx b/app/client/src/widgets/TabsWidget/widget/index.tsx index 25fe430fe964..8fe73e348d3d 100644 --- a/app/client/src/widgets/TabsWidget/widget/index.tsx +++ b/app/client/src/widgets/TabsWidget/widget/index.tsx @@ -13,7 +13,8 @@ import { AutocompleteDataType } from "utils/autocomplete/TernServer"; import { WidgetProperties } from "selectors/propertyPaneSelectors"; import { WIDGET_PADDING } from "constants/WidgetConstants"; import derivedProperties from "./parseDerivedProperties"; -import { Positioning } from "components/constants"; +import { Positioning, ResponsiveBehavior } from "components/constants"; +import { generateResponsiveBehaviorConfig } from "utils/ResposniveBehaviorConfig"; export function selectedTabValidation( value: unknown, @@ -188,8 +189,24 @@ class TabsWidget extends BaseWidget< isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, + { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, ], }, + { + helpText: "Should the children take up the complete width on mobile", + propertyName: "responsiveBehavior", + label: "Responsive behavior", + controlType: "DROP_DOWN", + defaultValue: ResponsiveBehavior.Fill, + options: [ + { label: "Fill", value: ResponsiveBehavior.Fill }, + { label: "Hug", value: ResponsiveBehavior.Hug }, + ], + isJSConvertible: true, + isBindProperty: false, + isTriggerProperty: true, + validation: { type: ValidationTypes.TEXT }, + }, { sectionName: "Events", children: [ From c901317b8d0f22be2f65d1cc2e0e56852fb095e8 Mon Sep 17 00:00:00 2001 From: Preet Sidhu Date: Fri, 12 Aug 2022 20:05:06 -0400 Subject: [PATCH 043/708] add responsive property to text editor --- app/client/src/widgets/RichTextEditorWidget/index.ts | 3 ++- app/client/src/widgets/RichTextEditorWidget/widget/index.tsx | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/app/client/src/widgets/RichTextEditorWidget/index.ts b/app/client/src/widgets/RichTextEditorWidget/index.ts index 5327ec37396f..75e4c91b4594 100644 --- a/app/client/src/widgets/RichTextEditorWidget/index.ts +++ b/app/client/src/widgets/RichTextEditorWidget/index.ts @@ -1,5 +1,5 @@ import { Alignment } from "@blueprintjs/core"; -import { LabelPosition } from "components/constants"; +import { LabelPosition, ResponsiveBehavior } from "components/constants"; import IconSVG from "./icon.svg"; import Widget from "./widget"; @@ -25,6 +25,7 @@ export const CONFIG = { labelAlignment: Alignment.LEFT, labelWidth: 5, version: 1, + responsiveBehavior: ResponsiveBehavior.Fill, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/RichTextEditorWidget/widget/index.tsx b/app/client/src/widgets/RichTextEditorWidget/widget/index.tsx index 42d8afc44ff1..e1e7379a827a 100644 --- a/app/client/src/widgets/RichTextEditorWidget/widget/index.tsx +++ b/app/client/src/widgets/RichTextEditorWidget/widget/index.tsx @@ -6,10 +6,11 @@ import { ValidationTypes } from "constants/WidgetValidation"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; import Skeleton from "components/utils/Skeleton"; import { retryPromise } from "utils/AppsmithUtils"; -import { LabelPosition } from "components/constants"; +import { LabelPosition, ResponsiveBehavior } from "components/constants"; import { Alignment } from "@blueprintjs/core"; import { GRID_DENSITY_MIGRATION_V1 } from "widgets/constants"; import showdown from "showdown"; +import { generateResponsiveBehaviorConfig } from "utils/ResposniveBehaviorConfig"; export enum RTEFormats { MARKDOWN = "markdown", @@ -112,6 +113,7 @@ class RichTextEditorWidget extends BaseWidget< isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, + { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, ], }, { From 390d75eef8227d06c5ba73d9fe95483e7f441ebb Mon Sep 17 00:00:00 2001 From: Preet Sidhu Date: Fri, 12 Aug 2022 20:30:06 -0400 Subject: [PATCH 044/708] disable reflow in auto layout --- .../pages/common/CanvasArenas/hooks/useCanvasDragging.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index 2a1ba2e2c4c7..76dd6edeaf00 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -39,6 +39,8 @@ export interface XYCord { const CONTAINER_JUMP_ACC_THRESHOLD = 8000; const CONTAINER_JUMP_SPEED_THRESHOLD = 800; +const shouldDisableReflow = true; + let lastTranslatedIndex: number; //Since useCanvasDragging's Instance changes during container jump, metrics is stored outside const containerJumpThresholdMetrics = new ContainerJumpMetrics<{ @@ -639,7 +641,9 @@ export const useCanvasDragging = ( renderNewRows(delta); } else if (!isUpdatingRows) { triggerReflow(e, firstMove); - isCurrentDraggedCanvas && highlightDropPosition(e); + isCurrentDraggedCanvas && + !shouldDisableReflow && + highlightDropPosition(e); renderBlocks(); } scrollObj.lastMouseMoveEvent = { @@ -701,6 +705,7 @@ export const useCanvasDragging = ( const getDropPosition = (val: number): number | undefined => { const pos = getHighlightPosition(null, val); if (!pos) return; + if (shouldDisableReflow) return offsets.length; return offsets.indexOf(pos); }; const renderNewRows = debounce((delta) => { From 54ee6fc4d020a7bf28d683829e3475779f39ffd9 Mon Sep 17 00:00:00 2001 From: Preet Sidhu Date: Mon, 15 Aug 2022 10:53:30 -0400 Subject: [PATCH 045/708] add layout prop utils --- app/client/src/components/constants.ts | 22 +++++ ...rConfig.ts => ResponsiveBehaviorConfig.ts} | 0 app/client/src/utils/layoutPropertiesUtils.ts | 81 +++++++++++++++++++ .../AudioRecorderWidget/widget/index.tsx | 2 +- .../src/widgets/AudioWidget/widget/index.tsx | 2 +- .../ButtonGroupWidget/widget/index.tsx | 2 +- .../src/widgets/ButtonWidget/widget/index.tsx | 2 +- .../ChartWidget/widget/propertyConfig.ts | 2 +- .../CheckboxGroupWidget/widget/index.tsx | 2 +- .../widgets/CheckboxWidget/widget/index.tsx | 2 +- .../CurrencyInputWidget/widget/index.tsx | 2 +- .../widgets/DividerWidget/widget/index.tsx | 2 +- .../FilePickerWidgetV2/widget/index.tsx | 2 +- .../widgets/InputWidgetV2/widget/index.tsx | 2 +- .../JSONFormWidget/widget/propertyConfig.ts | 2 +- .../ListWidget/widget/propertyConfig.ts | 2 +- .../src/widgets/MapWidget/widget/index.tsx | 2 +- .../MultiSelectTreeWidget/widget/index.tsx | 2 +- .../MultiSelectWidget/widget/index.tsx | 2 +- .../MultiSelectWidgetV2/widget/index.tsx | 2 +- .../widgets/PhoneInputWidget/widget/index.tsx | 2 +- .../RichTextEditorWidget/widget/index.tsx | 2 +- .../src/widgets/SelectWidget/widget/index.tsx | 2 +- .../SingleSelectTreeWidget/widget/index.tsx | 2 +- .../widget/propertyConfig/General.ts | 2 +- .../src/widgets/TabsWidget/widget/index.tsx | 2 +- 26 files changed, 126 insertions(+), 23 deletions(-) rename app/client/src/utils/{ResposniveBehaviorConfig.ts => ResponsiveBehaviorConfig.ts} (100%) create mode 100644 app/client/src/utils/layoutPropertiesUtils.ts diff --git a/app/client/src/components/constants.ts b/app/client/src/components/constants.ts index 620657be6feb..2922d0c65016 100644 --- a/app/client/src/components/constants.ts +++ b/app/client/src/components/constants.ts @@ -122,12 +122,14 @@ export enum JustifyContent { SpaceAround = "space-around", SpaceBetween = "space-between", SpaceEvenly = "space-evenly", + FlexEnd = "flex-end", } export enum AlignItems { FlexStart = "flex-start", Center = "center", Stretch = "stretch", + FlexEnd = "flex-end", } export enum Positioning { @@ -140,3 +142,23 @@ export enum ResponsiveBehavior { Fill = "fill", Hug = "hug", } + +export enum FlexDirection { + Row = "row", + RowReverse = "row-reverse", + Column = "column", + ColumnReverse = "column-reverse", +} + +export enum Alignment { + Top = "top", + Bottom = "bottom", + Left = "left", + Right = "right", +} + +export enum Spacing { + none = "none", + Equal = "equal", + SpaceBetween = "space-between", +} diff --git a/app/client/src/utils/ResposniveBehaviorConfig.ts b/app/client/src/utils/ResponsiveBehaviorConfig.ts similarity index 100% rename from app/client/src/utils/ResposniveBehaviorConfig.ts rename to app/client/src/utils/ResponsiveBehaviorConfig.ts diff --git a/app/client/src/utils/layoutPropertiesUtils.ts b/app/client/src/utils/layoutPropertiesUtils.ts new file mode 100644 index 000000000000..0b4b049cebb5 --- /dev/null +++ b/app/client/src/utils/layoutPropertiesUtils.ts @@ -0,0 +1,81 @@ +import { + AlignItems, + Alignment, + FlexDirection, + JustifyContent, + LayoutDirection, + Spacing, +} from "components/constants"; + +interface LayoutProperties { + flexDirection: FlexDirection; + justifyContent: JustifyContent; + alignItems: AlignItems; +} + +export const horizontalAlignment: { [key in Alignment]: LayoutProperties } = { + top: { + flexDirection: FlexDirection.Row, + justifyContent: JustifyContent.FlexStart, + alignItems: AlignItems.FlexStart, + }, + bottom: { + flexDirection: FlexDirection.Row, + justifyContent: JustifyContent.FlexStart, + alignItems: AlignItems.FlexEnd, + }, + left: { + flexDirection: FlexDirection.Row, + justifyContent: JustifyContent.FlexStart, + alignItems: AlignItems.FlexStart, + }, + right: { + flexDirection: FlexDirection.RowReverse, + justifyContent: JustifyContent.FlexStart, + alignItems: AlignItems.FlexStart, + }, +}; + +export const verticalAlignment: { [key in Alignment]: LayoutProperties } = { + top: { + flexDirection: FlexDirection.Column, + justifyContent: JustifyContent.FlexStart, + alignItems: AlignItems.Center, + }, + bottom: { + flexDirection: FlexDirection.ColumnReverse, + justifyContent: JustifyContent.FlexStart, + alignItems: AlignItems.FlexStart, + }, + left: { + flexDirection: FlexDirection.Column, + justifyContent: JustifyContent.FlexStart, + alignItems: AlignItems.FlexStart, + }, + right: { + flexDirection: FlexDirection.Column, + justifyContent: JustifyContent.FlexStart, + alignItems: AlignItems.FlexEnd, + }, +}; + +export function getLayoutProperties( + direction: LayoutDirection, + alignment: Alignment, + spacing: Spacing, +): LayoutProperties { + let properties: LayoutProperties = + direction === LayoutDirection.Horizontal + ? horizontalAlignment[alignment] + : verticalAlignment[alignment]; + if (spacing !== Spacing.none) { + properties = { + ...properties, + justifyContent: + spacing === Spacing.Equal + ? JustifyContent.SpaceEvenly + : JustifyContent.SpaceBetween, + }; + } + return properties; +} diff --git a/app/client/src/widgets/AudioRecorderWidget/widget/index.tsx b/app/client/src/widgets/AudioRecorderWidget/widget/index.tsx index aa7b37b20f3c..dd354ac7afda 100644 --- a/app/client/src/widgets/AudioRecorderWidget/widget/index.tsx +++ b/app/client/src/widgets/AudioRecorderWidget/widget/index.tsx @@ -8,7 +8,7 @@ import AudioRecorderComponent from "../component"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; import { createBlobUrl } from "utils/AppsmithUtils"; import { FileDataTypes } from "widgets/constants"; -import { generateResponsiveBehaviorConfig } from "utils/ResposniveBehaviorConfig"; +import { generateResponsiveBehaviorConfig } from "utils/ResponsiveBehaviorConfig"; import { ResponsiveBehavior } from "components/constants"; export interface AudioRecorderWidgetProps extends WidgetProps { diff --git a/app/client/src/widgets/AudioWidget/widget/index.tsx b/app/client/src/widgets/AudioWidget/widget/index.tsx index fe0428a63416..7373259d1bce 100644 --- a/app/client/src/widgets/AudioWidget/widget/index.tsx +++ b/app/client/src/widgets/AudioWidget/widget/index.tsx @@ -8,7 +8,7 @@ import { retryPromise } from "utils/AppsmithUtils"; import ReactPlayer from "react-player"; import { AutocompleteDataType } from "utils/autocomplete/TernServer"; import { ResponsiveBehavior } from "components/constants"; -import { generateResponsiveBehaviorConfig } from "utils/ResposniveBehaviorConfig"; +import { generateResponsiveBehaviorConfig } from "utils/ResponsiveBehaviorConfig"; const AudioComponent = lazy(() => retryPromise(() => import("../component"))); diff --git a/app/client/src/widgets/ButtonGroupWidget/widget/index.tsx b/app/client/src/widgets/ButtonGroupWidget/widget/index.tsx index 9360191bf00e..300db8650b34 100644 --- a/app/client/src/widgets/ButtonGroupWidget/widget/index.tsx +++ b/app/client/src/widgets/ButtonGroupWidget/widget/index.tsx @@ -15,7 +15,7 @@ import { import ButtonGroupComponent from "../component"; import { MinimumPopupRows } from "widgets/constants"; import { getStylesheetValue } from "./helpers"; -import { generateResponsiveBehaviorConfig } from "utils/ResposniveBehaviorConfig"; +import { generateResponsiveBehaviorConfig } from "utils/ResponsiveBehaviorConfig"; class ButtonGroupWidget extends BaseWidget< ButtonGroupWidgetProps, diff --git a/app/client/src/widgets/ButtonWidget/widget/index.tsx b/app/client/src/widgets/ButtonWidget/widget/index.tsx index 8cd6d5749b12..b7572e576e46 100644 --- a/app/client/src/widgets/ButtonWidget/widget/index.tsx +++ b/app/client/src/widgets/ButtonWidget/widget/index.tsx @@ -20,7 +20,7 @@ import { ResponsiveBehavior, } from "components/constants"; import FormWidget from "widgets/FormWidget/widget"; -import { generateResponsiveBehaviorConfig } from "utils/ResposniveBehaviorConfig"; +import { generateResponsiveBehaviorConfig } from "utils/ResponsiveBehaviorConfig"; class ButtonWidget extends BaseWidget { onButtonClickBound: (event: React.MouseEvent) => void; diff --git a/app/client/src/widgets/ChartWidget/widget/propertyConfig.ts b/app/client/src/widgets/ChartWidget/widget/propertyConfig.ts index ea8995931b82..d90a80786b9d 100644 --- a/app/client/src/widgets/ChartWidget/widget/propertyConfig.ts +++ b/app/client/src/widgets/ChartWidget/widget/propertyConfig.ts @@ -3,7 +3,7 @@ import { ValidationTypes } from "constants/WidgetValidation"; import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; import { CUSTOM_CHART_TYPES, LabelOrientation } from "../constants"; import { isLabelOrientationApplicableFor } from "../component"; -import { generateResponsiveBehaviorConfig } from "utils/ResposniveBehaviorConfig"; +import { generateResponsiveBehaviorConfig } from "utils/ResponsiveBehaviorConfig"; import { ResponsiveBehavior } from "components/constants"; export default [ diff --git a/app/client/src/widgets/CheckboxGroupWidget/widget/index.tsx b/app/client/src/widgets/CheckboxGroupWidget/widget/index.tsx index 5ebd1257dce7..d61692afcfe2 100644 --- a/app/client/src/widgets/CheckboxGroupWidget/widget/index.tsx +++ b/app/client/src/widgets/CheckboxGroupWidget/widget/index.tsx @@ -22,7 +22,7 @@ import { GRID_DENSITY_MIGRATION_V1 } from "widgets/constants"; import CheckboxGroupComponent from "../component"; import { OptionProps, SelectAllState, SelectAllStates } from "../constants"; -import { generateResponsiveBehaviorConfig } from "utils/ResposniveBehaviorConfig"; +import { generateResponsiveBehaviorConfig } from "utils/ResponsiveBehaviorConfig"; export function defaultSelectedValuesValidation( value: unknown, diff --git a/app/client/src/widgets/CheckboxWidget/widget/index.tsx b/app/client/src/widgets/CheckboxWidget/widget/index.tsx index 10f4787a4513..d67c7e5e06d3 100644 --- a/app/client/src/widgets/CheckboxWidget/widget/index.tsx +++ b/app/client/src/widgets/CheckboxWidget/widget/index.tsx @@ -7,7 +7,7 @@ import { ValidationTypes } from "constants/WidgetValidation"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; import { LabelPosition, ResponsiveBehavior } from "components/constants"; import { AlignWidgetTypes } from "widgets/constants"; -import { generateResponsiveBehaviorConfig } from "utils/ResposniveBehaviorConfig"; +import { generateResponsiveBehaviorConfig } from "utils/ResponsiveBehaviorConfig"; class CheckboxWidget extends BaseWidget { static getPropertyPaneConfig() { diff --git a/app/client/src/widgets/CurrencyInputWidget/widget/index.tsx b/app/client/src/widgets/CurrencyInputWidget/widget/index.tsx index 1b369fe614df..c26ff9f30dfe 100644 --- a/app/client/src/widgets/CurrencyInputWidget/widget/index.tsx +++ b/app/client/src/widgets/CurrencyInputWidget/widget/index.tsx @@ -33,7 +33,7 @@ import { } from "../component/utilities"; import { mergeWidgetConfig } from "utils/helpers"; import { GRID_DENSITY_MIGRATION_V1 } from "widgets/constants"; -import { generateResponsiveBehaviorConfig } from "utils/ResposniveBehaviorConfig"; +import { generateResponsiveBehaviorConfig } from "utils/ResponsiveBehaviorConfig"; import { ResponsiveBehavior } from "components/constants"; export function defaultValueValidation( diff --git a/app/client/src/widgets/DividerWidget/widget/index.tsx b/app/client/src/widgets/DividerWidget/widget/index.tsx index bebd67b70225..6b97c7e5dd9d 100644 --- a/app/client/src/widgets/DividerWidget/widget/index.tsx +++ b/app/client/src/widgets/DividerWidget/widget/index.tsx @@ -4,7 +4,7 @@ import { WidgetType } from "constants/WidgetConstants"; import DividerComponent from "../component"; import { ValidationTypes } from "constants/WidgetValidation"; -import { generateResponsiveBehaviorConfig } from "utils/ResposniveBehaviorConfig"; +import { generateResponsiveBehaviorConfig } from "utils/ResponsiveBehaviorConfig"; import { ResponsiveBehavior } from "components/constants"; class DividerWidget extends BaseWidget { diff --git a/app/client/src/widgets/FilePickerWidgetV2/widget/index.tsx b/app/client/src/widgets/FilePickerWidgetV2/widget/index.tsx index e7720305341a..347e28b7ba40 100644 --- a/app/client/src/widgets/FilePickerWidgetV2/widget/index.tsx +++ b/app/client/src/widgets/FilePickerWidgetV2/widget/index.tsx @@ -21,7 +21,7 @@ import { createGlobalStyle } from "styled-components"; import UpIcon from "assets/icons/ads/up-arrow.svg"; import CloseIcon from "assets/icons/ads/cross.svg"; import { Colors } from "constants/Colors"; -import { generateResponsiveBehaviorConfig } from "utils/ResposniveBehaviorConfig"; +import { generateResponsiveBehaviorConfig } from "utils/ResponsiveBehaviorConfig"; import { ResponsiveBehavior } from "components/constants"; const FilePickerGlobalStyles = createGlobalStyle<{ diff --git a/app/client/src/widgets/InputWidgetV2/widget/index.tsx b/app/client/src/widgets/InputWidgetV2/widget/index.tsx index 643c965f80dc..3cc4332c4bba 100644 --- a/app/client/src/widgets/InputWidgetV2/widget/index.tsx +++ b/app/client/src/widgets/InputWidgetV2/widget/index.tsx @@ -25,7 +25,7 @@ import { BaseInputWidgetProps } from "widgets/BaseInputWidget/widget"; import { mergeWidgetConfig } from "utils/helpers"; import { InputTypes } from "widgets/BaseInputWidget/constants"; import { getParsedText } from "./Utilities"; -import { generateResponsiveBehaviorConfig } from "utils/ResposniveBehaviorConfig"; +import { generateResponsiveBehaviorConfig } from "utils/ResponsiveBehaviorConfig"; import { ResponsiveBehavior } from "components/constants"; import { IconNames } from "@blueprintjs/icons"; diff --git a/app/client/src/widgets/JSONFormWidget/widget/propertyConfig.ts b/app/client/src/widgets/JSONFormWidget/widget/propertyConfig.ts index 8ff5b651a89f..90a90b0efaca 100644 --- a/app/client/src/widgets/JSONFormWidget/widget/propertyConfig.ts +++ b/app/client/src/widgets/JSONFormWidget/widget/propertyConfig.ts @@ -15,7 +15,7 @@ import { ButtonWidgetProps } from "widgets/ButtonWidget/widget"; import { OnButtonClickProps } from "components/propertyControls/ButtonControl"; import { ComputedSchemaStatus, computeSchema } from "./helper"; import { EVALUATION_PATH } from "utils/DynamicBindingUtils"; -import { generateResponsiveBehaviorConfig } from "utils/ResposniveBehaviorConfig"; +import { generateResponsiveBehaviorConfig } from "utils/ResponsiveBehaviorConfig"; const MAX_NESTING_LEVEL = 5; diff --git a/app/client/src/widgets/ListWidget/widget/propertyConfig.ts b/app/client/src/widgets/ListWidget/widget/propertyConfig.ts index 391f42cbe30f..1f84efcbca37 100644 --- a/app/client/src/widgets/ListWidget/widget/propertyConfig.ts +++ b/app/client/src/widgets/ListWidget/widget/propertyConfig.ts @@ -6,7 +6,7 @@ import { ValidationTypes } from "constants/WidgetValidation"; import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; import { EVAL_VALUE_PATH } from "utils/DynamicBindingUtils"; import { AutocompleteDataType } from "utils/autocomplete/TernServer"; -import { generateResponsiveBehaviorConfig } from "utils/ResposniveBehaviorConfig"; +import { generateResponsiveBehaviorConfig } from "utils/ResponsiveBehaviorConfig"; import { ResponsiveBehavior } from "components/constants"; const PropertyPaneConfig = [ diff --git a/app/client/src/widgets/MapWidget/widget/index.tsx b/app/client/src/widgets/MapWidget/widget/index.tsx index 2efcbf456cf2..0eeae20b465b 100644 --- a/app/client/src/widgets/MapWidget/widget/index.tsx +++ b/app/client/src/widgets/MapWidget/widget/index.tsx @@ -12,7 +12,7 @@ import { getBorderCSSShorthand } from "constants/DefaultTheme"; import { MarkerProps } from "../constants"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; -import { generateResponsiveBehaviorConfig } from "utils/ResposniveBehaviorConfig"; +import { generateResponsiveBehaviorConfig } from "utils/ResponsiveBehaviorConfig"; import { ResponsiveBehavior } from "components/constants"; const { google } = getAppsmithConfigs(); diff --git a/app/client/src/widgets/MultiSelectTreeWidget/widget/index.tsx b/app/client/src/widgets/MultiSelectTreeWidget/widget/index.tsx index 370b9fff9729..3bdd182da5fe 100644 --- a/app/client/src/widgets/MultiSelectTreeWidget/widget/index.tsx +++ b/app/client/src/widgets/MultiSelectTreeWidget/widget/index.tsx @@ -16,7 +16,7 @@ import { AutocompleteDataType } from "utils/autocomplete/TernServer"; import MultiTreeSelectComponent from "../component"; import { LabelPosition, ResponsiveBehavior } from "components/constants"; import { Alignment } from "@blueprintjs/core"; -import { generateResponsiveBehaviorConfig } from "utils/ResposniveBehaviorConfig"; +import { generateResponsiveBehaviorConfig } from "utils/ResponsiveBehaviorConfig"; function defaultOptionValueValidation(value: unknown): ValidationResponse { let values: string[] = []; diff --git a/app/client/src/widgets/MultiSelectWidget/widget/index.tsx b/app/client/src/widgets/MultiSelectWidget/widget/index.tsx index 3deaea45bf97..ed0f7473ab92 100644 --- a/app/client/src/widgets/MultiSelectWidget/widget/index.tsx +++ b/app/client/src/widgets/MultiSelectWidget/widget/index.tsx @@ -16,7 +16,7 @@ import { AutocompleteDataType } from "utils/autocomplete/TernServer"; import { MinimumPopupRows, GRID_DENSITY_MIGRATION_V1 } from "widgets/constants"; import { LabelPosition, ResponsiveBehavior } from "components/constants"; import { Alignment } from "@blueprintjs/core"; -import { generateResponsiveBehaviorConfig } from "utils/ResposniveBehaviorConfig"; +import { generateResponsiveBehaviorConfig } from "utils/ResponsiveBehaviorConfig"; function defaultOptionValueValidation(value: unknown): ValidationResponse { let values: string[] = []; diff --git a/app/client/src/widgets/MultiSelectWidgetV2/widget/index.tsx b/app/client/src/widgets/MultiSelectWidgetV2/widget/index.tsx index 5fbd46cd6f76..91ec773beb5a 100644 --- a/app/client/src/widgets/MultiSelectWidgetV2/widget/index.tsx +++ b/app/client/src/widgets/MultiSelectWidgetV2/widget/index.tsx @@ -26,7 +26,7 @@ import { MinimumPopupRows, GRID_DENSITY_MIGRATION_V1 } from "widgets/constants"; import { LabelPosition, ResponsiveBehavior } from "components/constants"; import { Alignment } from "@blueprintjs/core"; import { AutocompleteDataType } from "utils/autocomplete/TernServer"; -import { generateResponsiveBehaviorConfig } from "utils/ResposniveBehaviorConfig"; +import { generateResponsiveBehaviorConfig } from "utils/ResponsiveBehaviorConfig"; export function defaultOptionValueValidation( value: unknown, diff --git a/app/client/src/widgets/PhoneInputWidget/widget/index.tsx b/app/client/src/widgets/PhoneInputWidget/widget/index.tsx index 0fad07581a8a..d730e9c307c9 100644 --- a/app/client/src/widgets/PhoneInputWidget/widget/index.tsx +++ b/app/client/src/widgets/PhoneInputWidget/widget/index.tsx @@ -30,7 +30,7 @@ import { import * as Sentry from "@sentry/react"; import log from "loglevel"; import { GRID_DENSITY_MIGRATION_V1 } from "widgets/constants"; -import { generateResponsiveBehaviorConfig } from "utils/ResposniveBehaviorConfig"; +import { generateResponsiveBehaviorConfig } from "utils/ResponsiveBehaviorConfig"; import { ResponsiveBehavior } from "components/constants"; export function defaultValueValidation( diff --git a/app/client/src/widgets/RichTextEditorWidget/widget/index.tsx b/app/client/src/widgets/RichTextEditorWidget/widget/index.tsx index e1e7379a827a..0777951c04ee 100644 --- a/app/client/src/widgets/RichTextEditorWidget/widget/index.tsx +++ b/app/client/src/widgets/RichTextEditorWidget/widget/index.tsx @@ -10,7 +10,7 @@ import { LabelPosition, ResponsiveBehavior } from "components/constants"; import { Alignment } from "@blueprintjs/core"; import { GRID_DENSITY_MIGRATION_V1 } from "widgets/constants"; import showdown from "showdown"; -import { generateResponsiveBehaviorConfig } from "utils/ResposniveBehaviorConfig"; +import { generateResponsiveBehaviorConfig } from "utils/ResponsiveBehaviorConfig"; export enum RTEFormats { MARKDOWN = "markdown", diff --git a/app/client/src/widgets/SelectWidget/widget/index.tsx b/app/client/src/widgets/SelectWidget/widget/index.tsx index c06cb5c3862d..1c427efe7c63 100644 --- a/app/client/src/widgets/SelectWidget/widget/index.tsx +++ b/app/client/src/widgets/SelectWidget/widget/index.tsx @@ -23,7 +23,7 @@ import { LoDashStatic, } from "lodash"; import derivedProperties from "./parseDerivedProperties"; -import { generateResponsiveBehaviorConfig } from "utils/ResposniveBehaviorConfig"; +import { generateResponsiveBehaviorConfig } from "utils/ResponsiveBehaviorConfig"; export function defaultOptionValueValidation( value: unknown, diff --git a/app/client/src/widgets/SingleSelectTreeWidget/widget/index.tsx b/app/client/src/widgets/SingleSelectTreeWidget/widget/index.tsx index e2f0c57717c7..dfa7859ff207 100644 --- a/app/client/src/widgets/SingleSelectTreeWidget/widget/index.tsx +++ b/app/client/src/widgets/SingleSelectTreeWidget/widget/index.tsx @@ -16,7 +16,7 @@ import { GRID_DENSITY_MIGRATION_V1, MinimumPopupRows } from "widgets/constants"; import SingleSelectTreeComponent from "../component"; import { LabelPosition, ResponsiveBehavior } from "components/constants"; import { Alignment } from "@blueprintjs/core"; -import { generateResponsiveBehaviorConfig } from "utils/ResposniveBehaviorConfig"; +import { generateResponsiveBehaviorConfig } from "utils/ResponsiveBehaviorConfig"; function defaultOptionValueValidation(value: unknown): ValidationResponse { if (typeof value === "string") return { isValid: true, parsed: value.trim() }; diff --git a/app/client/src/widgets/TableWidgetV2/widget/propertyConfig/General.ts b/app/client/src/widgets/TableWidgetV2/widget/propertyConfig/General.ts index c73d2ffd9817..dc42c6fdae01 100644 --- a/app/client/src/widgets/TableWidgetV2/widget/propertyConfig/General.ts +++ b/app/client/src/widgets/TableWidgetV2/widget/propertyConfig/General.ts @@ -18,7 +18,7 @@ import { } from "@appsmith/constants/messages"; import panelConfig from "./PanelConfig"; import { composePropertyUpdateHook } from "widgets/WidgetUtils"; -import { generateResponsiveBehaviorConfig } from "utils/ResposniveBehaviorConfig"; +import { generateResponsiveBehaviorConfig } from "utils/ResponsiveBehaviorConfig"; import { ResponsiveBehavior } from "components/constants"; export default { diff --git a/app/client/src/widgets/TabsWidget/widget/index.tsx b/app/client/src/widgets/TabsWidget/widget/index.tsx index 8fe73e348d3d..a16b7062fd38 100644 --- a/app/client/src/widgets/TabsWidget/widget/index.tsx +++ b/app/client/src/widgets/TabsWidget/widget/index.tsx @@ -14,7 +14,7 @@ import { WidgetProperties } from "selectors/propertyPaneSelectors"; import { WIDGET_PADDING } from "constants/WidgetConstants"; import derivedProperties from "./parseDerivedProperties"; import { Positioning, ResponsiveBehavior } from "components/constants"; -import { generateResponsiveBehaviorConfig } from "utils/ResposniveBehaviorConfig"; +import { generateResponsiveBehaviorConfig } from "utils/ResponsiveBehaviorConfig"; export function selectedTabValidation( value: unknown, From 16323a8659db3eceb73ebe6d49d3815f90088c22 Mon Sep 17 00:00:00 2001 From: Preet Sidhu Date: Mon, 15 Aug 2022 11:12:10 -0400 Subject: [PATCH 046/708] rename utils import --- app/client/src/components/constants.ts | 2 +- .../src/utils/ResponsiveBehaviorConfig.ts | 22 ------ app/client/src/utils/layoutPropertiesUtils.ts | 67 ++++++++++++++++++- .../AudioRecorderWidget/widget/index.tsx | 2 +- .../src/widgets/AudioWidget/widget/index.tsx | 2 +- .../ButtonGroupWidget/widget/index.tsx | 2 +- .../src/widgets/ButtonWidget/widget/index.tsx | 2 +- .../ChartWidget/widget/propertyConfig.ts | 2 +- .../CheckboxGroupWidget/widget/index.tsx | 2 +- .../widgets/CheckboxWidget/widget/index.tsx | 2 +- .../ContainerWidget/component/index.tsx | 9 ++- .../CurrencyInputWidget/widget/index.tsx | 2 +- .../widgets/DividerWidget/widget/index.tsx | 2 +- .../FilePickerWidgetV2/widget/index.tsx | 2 +- .../widgets/InputWidgetV2/widget/index.tsx | 2 +- .../JSONFormWidget/widget/propertyConfig.ts | 2 +- .../ListWidget/widget/propertyConfig.ts | 2 +- .../src/widgets/MapWidget/widget/index.tsx | 2 +- .../MultiSelectTreeWidget/widget/index.tsx | 2 +- .../MultiSelectWidget/widget/index.tsx | 2 +- .../MultiSelectWidgetV2/widget/index.tsx | 2 +- .../widgets/PhoneInputWidget/widget/index.tsx | 2 +- .../RichTextEditorWidget/widget/index.tsx | 2 +- .../src/widgets/SelectWidget/widget/index.tsx | 2 +- .../SingleSelectTreeWidget/widget/index.tsx | 2 +- .../widget/propertyConfig/General.ts | 2 +- .../src/widgets/TabsWidget/widget/index.tsx | 2 +- 27 files changed, 94 insertions(+), 52 deletions(-) delete mode 100644 app/client/src/utils/ResponsiveBehaviorConfig.ts diff --git a/app/client/src/components/constants.ts b/app/client/src/components/constants.ts index 2922d0c65016..a81614a60ed3 100644 --- a/app/client/src/components/constants.ts +++ b/app/client/src/components/constants.ts @@ -158,7 +158,7 @@ export enum Alignment { } export enum Spacing { - none = "none", + None = "none", Equal = "equal", SpaceBetween = "space-between", } diff --git a/app/client/src/utils/ResponsiveBehaviorConfig.ts b/app/client/src/utils/ResponsiveBehaviorConfig.ts deleted file mode 100644 index 513895945649..000000000000 --- a/app/client/src/utils/ResponsiveBehaviorConfig.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { ResponsiveBehavior } from "components/constants"; -import { ValidationTypes } from "constants/WidgetValidation"; - -export const generateResponsiveBehaviorConfig = ( - value: ResponsiveBehavior, -): any => { - return { - helpText: "Should the children take up the complete width on mobile", - propertyName: "responsiveBehavior", - label: "Responsive behavior", - controlType: "DROP_DOWN", - defaultValue: value, - options: [ - { label: "Fill", value: ResponsiveBehavior.Fill }, - { label: "Hug", value: ResponsiveBehavior.Hug }, - ], - isJSConvertible: true, - isBindProperty: false, - isTriggerProperty: true, - validation: { type: ValidationTypes.TEXT }, - }; -}; diff --git a/app/client/src/utils/layoutPropertiesUtils.ts b/app/client/src/utils/layoutPropertiesUtils.ts index 0b4b049cebb5..3cbd639c9e36 100644 --- a/app/client/src/utils/layoutPropertiesUtils.ts +++ b/app/client/src/utils/layoutPropertiesUtils.ts @@ -4,8 +4,10 @@ import { FlexDirection, JustifyContent, LayoutDirection, + ResponsiveBehavior, Spacing, } from "components/constants"; +import { ValidationTypes } from "constants/WidgetValidation"; interface LayoutProperties { flexDirection: FlexDirection; @@ -68,7 +70,7 @@ export function getLayoutProperties( direction === LayoutDirection.Horizontal ? horizontalAlignment[alignment] : verticalAlignment[alignment]; - if (spacing !== Spacing.none) { + if (spacing !== Spacing.None) { properties = { ...properties, justifyContent: @@ -79,3 +81,66 @@ export function getLayoutProperties( } return properties; } + +export const generateResponsiveBehaviorConfig = ( + value: ResponsiveBehavior, +): any => { + return { + helpText: "Widget layout behavior on smaller view port", + propertyName: "responsiveBehavior", + label: "Responsive behavior", + controlType: "DROP_DOWN", + defaultValue: value || ResponsiveBehavior.Hug, + options: [ + { label: "Fill", value: ResponsiveBehavior.Fill }, + { label: "Hug", value: ResponsiveBehavior.Hug }, + ], + isJSConvertible: true, + isBindProperty: false, + isTriggerProperty: true, + validation: { type: ValidationTypes.TEXT }, + }; +}; + +export const generateAlignmentConfig = (value: Alignment): any => { + return { + helpText: "Alignment of children with respect to this parent", + propertyName: "alignment", + label: "Alignment", + controlType: "DROP_DOWN", + defaultValue: value || Alignment.Left, + options: [ + { label: "Top", value: Alignment.Top }, + { label: "Bottom", value: Alignment.Bottom }, + { label: "Left", value: Alignment.Left }, + { label: "Right", value: Alignment.Right }, + ], + isJSConvertible: true, + isBindProperty: false, + isTriggerProperty: true, + validation: { type: ValidationTypes.TEXT }, + }; +}; + +export const generateSpacingConfig = (value: Spacing): any => { + return { + helpText: "Spacing between the children", + propertyName: "spacing", + label: "Spacing", + controlType: "DROP_DOWN", + defaultValue: value || Spacing.None, + options: [ + { label: "None", value: Spacing.None }, + { label: "Equal", value: Spacing.Equal }, + { label: "Space between", value: Spacing.SpaceBetween }, + ], + isJSConvertible: true, + isBindProperty: false, + isTriggerProperty: true, + validation: { type: ValidationTypes.TEXT }, + }; +}; + +export function getLayoutConfig(alignment: Alignment, spacing: Spacing): any[] { + return [generateAlignmentConfig(alignment), generateSpacingConfig(spacing)]; +} diff --git a/app/client/src/widgets/AudioRecorderWidget/widget/index.tsx b/app/client/src/widgets/AudioRecorderWidget/widget/index.tsx index dd354ac7afda..1720f447c41e 100644 --- a/app/client/src/widgets/AudioRecorderWidget/widget/index.tsx +++ b/app/client/src/widgets/AudioRecorderWidget/widget/index.tsx @@ -8,7 +8,7 @@ import AudioRecorderComponent from "../component"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; import { createBlobUrl } from "utils/AppsmithUtils"; import { FileDataTypes } from "widgets/constants"; -import { generateResponsiveBehaviorConfig } from "utils/ResponsiveBehaviorConfig"; +import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; import { ResponsiveBehavior } from "components/constants"; export interface AudioRecorderWidgetProps extends WidgetProps { diff --git a/app/client/src/widgets/AudioWidget/widget/index.tsx b/app/client/src/widgets/AudioWidget/widget/index.tsx index 7373259d1bce..5cc245e8527a 100644 --- a/app/client/src/widgets/AudioWidget/widget/index.tsx +++ b/app/client/src/widgets/AudioWidget/widget/index.tsx @@ -8,7 +8,7 @@ import { retryPromise } from "utils/AppsmithUtils"; import ReactPlayer from "react-player"; import { AutocompleteDataType } from "utils/autocomplete/TernServer"; import { ResponsiveBehavior } from "components/constants"; -import { generateResponsiveBehaviorConfig } from "utils/ResponsiveBehaviorConfig"; +import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; const AudioComponent = lazy(() => retryPromise(() => import("../component"))); diff --git a/app/client/src/widgets/ButtonGroupWidget/widget/index.tsx b/app/client/src/widgets/ButtonGroupWidget/widget/index.tsx index 300db8650b34..5f9805d95fbe 100644 --- a/app/client/src/widgets/ButtonGroupWidget/widget/index.tsx +++ b/app/client/src/widgets/ButtonGroupWidget/widget/index.tsx @@ -15,7 +15,7 @@ import { import ButtonGroupComponent from "../component"; import { MinimumPopupRows } from "widgets/constants"; import { getStylesheetValue } from "./helpers"; -import { generateResponsiveBehaviorConfig } from "utils/ResponsiveBehaviorConfig"; +import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; class ButtonGroupWidget extends BaseWidget< ButtonGroupWidgetProps, diff --git a/app/client/src/widgets/ButtonWidget/widget/index.tsx b/app/client/src/widgets/ButtonWidget/widget/index.tsx index b7572e576e46..ab991acd6b44 100644 --- a/app/client/src/widgets/ButtonWidget/widget/index.tsx +++ b/app/client/src/widgets/ButtonWidget/widget/index.tsx @@ -20,7 +20,7 @@ import { ResponsiveBehavior, } from "components/constants"; import FormWidget from "widgets/FormWidget/widget"; -import { generateResponsiveBehaviorConfig } from "utils/ResponsiveBehaviorConfig"; +import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; class ButtonWidget extends BaseWidget { onButtonClickBound: (event: React.MouseEvent) => void; diff --git a/app/client/src/widgets/ChartWidget/widget/propertyConfig.ts b/app/client/src/widgets/ChartWidget/widget/propertyConfig.ts index d90a80786b9d..7d2fb349f6e9 100644 --- a/app/client/src/widgets/ChartWidget/widget/propertyConfig.ts +++ b/app/client/src/widgets/ChartWidget/widget/propertyConfig.ts @@ -3,7 +3,7 @@ import { ValidationTypes } from "constants/WidgetValidation"; import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; import { CUSTOM_CHART_TYPES, LabelOrientation } from "../constants"; import { isLabelOrientationApplicableFor } from "../component"; -import { generateResponsiveBehaviorConfig } from "utils/ResponsiveBehaviorConfig"; +import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; import { ResponsiveBehavior } from "components/constants"; export default [ diff --git a/app/client/src/widgets/CheckboxGroupWidget/widget/index.tsx b/app/client/src/widgets/CheckboxGroupWidget/widget/index.tsx index d61692afcfe2..3bd1c3b90835 100644 --- a/app/client/src/widgets/CheckboxGroupWidget/widget/index.tsx +++ b/app/client/src/widgets/CheckboxGroupWidget/widget/index.tsx @@ -22,7 +22,7 @@ import { GRID_DENSITY_MIGRATION_V1 } from "widgets/constants"; import CheckboxGroupComponent from "../component"; import { OptionProps, SelectAllState, SelectAllStates } from "../constants"; -import { generateResponsiveBehaviorConfig } from "utils/ResponsiveBehaviorConfig"; +import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; export function defaultSelectedValuesValidation( value: unknown, diff --git a/app/client/src/widgets/CheckboxWidget/widget/index.tsx b/app/client/src/widgets/CheckboxWidget/widget/index.tsx index d67c7e5e06d3..3ed669027308 100644 --- a/app/client/src/widgets/CheckboxWidget/widget/index.tsx +++ b/app/client/src/widgets/CheckboxWidget/widget/index.tsx @@ -7,7 +7,7 @@ import { ValidationTypes } from "constants/WidgetValidation"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; import { LabelPosition, ResponsiveBehavior } from "components/constants"; import { AlignWidgetTypes } from "widgets/constants"; -import { generateResponsiveBehaviorConfig } from "utils/ResponsiveBehaviorConfig"; +import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; class CheckboxWidget extends BaseWidget { static getPropertyPaneConfig() { diff --git a/app/client/src/widgets/ContainerWidget/component/index.tsx b/app/client/src/widgets/ContainerWidget/component/index.tsx index d959ab8c0fb7..78ab2ad5deed 100644 --- a/app/client/src/widgets/ContainerWidget/component/index.tsx +++ b/app/client/src/widgets/ContainerWidget/component/index.tsx @@ -11,7 +11,7 @@ import WidgetStyleContainer, { import { pick } from "lodash"; import { ComponentProps } from "widgets/BaseComponent"; import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; -import { JustifyContent, LayoutDirection } from "components/constants"; +import { AlignItems, FlexDirection, JustifyContent, LayoutDirection } from "components/constants"; const scrollContents = css` overflow-y: auto; @@ -61,14 +61,13 @@ const StyledContainerComponent = styled.div< export const FlexContainer = styled.div<{ useAutoLayout?: boolean; - direction?: string; + direction?: FlexDirection; justifyContent?: JustifyContent; - alignItems?: string; + alignItems?: AlignItems; stretchHeight: boolean; }>` display: ${({ useAutoLayout }) => (useAutoLayout ? "flex" : "block")}; - flex-direction: ${({ direction }) => - direction === LayoutDirection.Vertical ? "column" : "row"}; + flex-direction: ${({ direction }) => direction || "row"}; justify-content: ${({ justifyContent }) => justifyContent || "flex-start"}; align-items: ${({ alignItems }) => alignItems || "flex-start"}; flex-wrap: wrap; diff --git a/app/client/src/widgets/CurrencyInputWidget/widget/index.tsx b/app/client/src/widgets/CurrencyInputWidget/widget/index.tsx index c26ff9f30dfe..789477801e1f 100644 --- a/app/client/src/widgets/CurrencyInputWidget/widget/index.tsx +++ b/app/client/src/widgets/CurrencyInputWidget/widget/index.tsx @@ -33,7 +33,7 @@ import { } from "../component/utilities"; import { mergeWidgetConfig } from "utils/helpers"; import { GRID_DENSITY_MIGRATION_V1 } from "widgets/constants"; -import { generateResponsiveBehaviorConfig } from "utils/ResponsiveBehaviorConfig"; +import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; import { ResponsiveBehavior } from "components/constants"; export function defaultValueValidation( diff --git a/app/client/src/widgets/DividerWidget/widget/index.tsx b/app/client/src/widgets/DividerWidget/widget/index.tsx index 6b97c7e5dd9d..618a05f6abba 100644 --- a/app/client/src/widgets/DividerWidget/widget/index.tsx +++ b/app/client/src/widgets/DividerWidget/widget/index.tsx @@ -4,7 +4,7 @@ import { WidgetType } from "constants/WidgetConstants"; import DividerComponent from "../component"; import { ValidationTypes } from "constants/WidgetValidation"; -import { generateResponsiveBehaviorConfig } from "utils/ResponsiveBehaviorConfig"; +import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; import { ResponsiveBehavior } from "components/constants"; class DividerWidget extends BaseWidget { diff --git a/app/client/src/widgets/FilePickerWidgetV2/widget/index.tsx b/app/client/src/widgets/FilePickerWidgetV2/widget/index.tsx index 347e28b7ba40..878a8e07fe61 100644 --- a/app/client/src/widgets/FilePickerWidgetV2/widget/index.tsx +++ b/app/client/src/widgets/FilePickerWidgetV2/widget/index.tsx @@ -21,7 +21,7 @@ import { createGlobalStyle } from "styled-components"; import UpIcon from "assets/icons/ads/up-arrow.svg"; import CloseIcon from "assets/icons/ads/cross.svg"; import { Colors } from "constants/Colors"; -import { generateResponsiveBehaviorConfig } from "utils/ResponsiveBehaviorConfig"; +import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; import { ResponsiveBehavior } from "components/constants"; const FilePickerGlobalStyles = createGlobalStyle<{ diff --git a/app/client/src/widgets/InputWidgetV2/widget/index.tsx b/app/client/src/widgets/InputWidgetV2/widget/index.tsx index 3cc4332c4bba..a3f8834e1087 100644 --- a/app/client/src/widgets/InputWidgetV2/widget/index.tsx +++ b/app/client/src/widgets/InputWidgetV2/widget/index.tsx @@ -25,7 +25,7 @@ import { BaseInputWidgetProps } from "widgets/BaseInputWidget/widget"; import { mergeWidgetConfig } from "utils/helpers"; import { InputTypes } from "widgets/BaseInputWidget/constants"; import { getParsedText } from "./Utilities"; -import { generateResponsiveBehaviorConfig } from "utils/ResponsiveBehaviorConfig"; +import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; import { ResponsiveBehavior } from "components/constants"; import { IconNames } from "@blueprintjs/icons"; diff --git a/app/client/src/widgets/JSONFormWidget/widget/propertyConfig.ts b/app/client/src/widgets/JSONFormWidget/widget/propertyConfig.ts index 90a90b0efaca..72efbf7b4882 100644 --- a/app/client/src/widgets/JSONFormWidget/widget/propertyConfig.ts +++ b/app/client/src/widgets/JSONFormWidget/widget/propertyConfig.ts @@ -15,7 +15,7 @@ import { ButtonWidgetProps } from "widgets/ButtonWidget/widget"; import { OnButtonClickProps } from "components/propertyControls/ButtonControl"; import { ComputedSchemaStatus, computeSchema } from "./helper"; import { EVALUATION_PATH } from "utils/DynamicBindingUtils"; -import { generateResponsiveBehaviorConfig } from "utils/ResponsiveBehaviorConfig"; +import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; const MAX_NESTING_LEVEL = 5; diff --git a/app/client/src/widgets/ListWidget/widget/propertyConfig.ts b/app/client/src/widgets/ListWidget/widget/propertyConfig.ts index 1f84efcbca37..0ba50d7475a2 100644 --- a/app/client/src/widgets/ListWidget/widget/propertyConfig.ts +++ b/app/client/src/widgets/ListWidget/widget/propertyConfig.ts @@ -6,7 +6,7 @@ import { ValidationTypes } from "constants/WidgetValidation"; import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; import { EVAL_VALUE_PATH } from "utils/DynamicBindingUtils"; import { AutocompleteDataType } from "utils/autocomplete/TernServer"; -import { generateResponsiveBehaviorConfig } from "utils/ResponsiveBehaviorConfig"; +import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; import { ResponsiveBehavior } from "components/constants"; const PropertyPaneConfig = [ diff --git a/app/client/src/widgets/MapWidget/widget/index.tsx b/app/client/src/widgets/MapWidget/widget/index.tsx index 0eeae20b465b..2f073db56414 100644 --- a/app/client/src/widgets/MapWidget/widget/index.tsx +++ b/app/client/src/widgets/MapWidget/widget/index.tsx @@ -12,7 +12,7 @@ import { getBorderCSSShorthand } from "constants/DefaultTheme"; import { MarkerProps } from "../constants"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; -import { generateResponsiveBehaviorConfig } from "utils/ResponsiveBehaviorConfig"; +import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; import { ResponsiveBehavior } from "components/constants"; const { google } = getAppsmithConfigs(); diff --git a/app/client/src/widgets/MultiSelectTreeWidget/widget/index.tsx b/app/client/src/widgets/MultiSelectTreeWidget/widget/index.tsx index 3bdd182da5fe..4c863912000e 100644 --- a/app/client/src/widgets/MultiSelectTreeWidget/widget/index.tsx +++ b/app/client/src/widgets/MultiSelectTreeWidget/widget/index.tsx @@ -16,7 +16,7 @@ import { AutocompleteDataType } from "utils/autocomplete/TernServer"; import MultiTreeSelectComponent from "../component"; import { LabelPosition, ResponsiveBehavior } from "components/constants"; import { Alignment } from "@blueprintjs/core"; -import { generateResponsiveBehaviorConfig } from "utils/ResponsiveBehaviorConfig"; +import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; function defaultOptionValueValidation(value: unknown): ValidationResponse { let values: string[] = []; diff --git a/app/client/src/widgets/MultiSelectWidget/widget/index.tsx b/app/client/src/widgets/MultiSelectWidget/widget/index.tsx index ed0f7473ab92..0368d8a97511 100644 --- a/app/client/src/widgets/MultiSelectWidget/widget/index.tsx +++ b/app/client/src/widgets/MultiSelectWidget/widget/index.tsx @@ -16,7 +16,7 @@ import { AutocompleteDataType } from "utils/autocomplete/TernServer"; import { MinimumPopupRows, GRID_DENSITY_MIGRATION_V1 } from "widgets/constants"; import { LabelPosition, ResponsiveBehavior } from "components/constants"; import { Alignment } from "@blueprintjs/core"; -import { generateResponsiveBehaviorConfig } from "utils/ResponsiveBehaviorConfig"; +import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; function defaultOptionValueValidation(value: unknown): ValidationResponse { let values: string[] = []; diff --git a/app/client/src/widgets/MultiSelectWidgetV2/widget/index.tsx b/app/client/src/widgets/MultiSelectWidgetV2/widget/index.tsx index 91ec773beb5a..9f898476c759 100644 --- a/app/client/src/widgets/MultiSelectWidgetV2/widget/index.tsx +++ b/app/client/src/widgets/MultiSelectWidgetV2/widget/index.tsx @@ -26,7 +26,7 @@ import { MinimumPopupRows, GRID_DENSITY_MIGRATION_V1 } from "widgets/constants"; import { LabelPosition, ResponsiveBehavior } from "components/constants"; import { Alignment } from "@blueprintjs/core"; import { AutocompleteDataType } from "utils/autocomplete/TernServer"; -import { generateResponsiveBehaviorConfig } from "utils/ResponsiveBehaviorConfig"; +import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; export function defaultOptionValueValidation( value: unknown, diff --git a/app/client/src/widgets/PhoneInputWidget/widget/index.tsx b/app/client/src/widgets/PhoneInputWidget/widget/index.tsx index d730e9c307c9..c76a0f6ba911 100644 --- a/app/client/src/widgets/PhoneInputWidget/widget/index.tsx +++ b/app/client/src/widgets/PhoneInputWidget/widget/index.tsx @@ -30,7 +30,7 @@ import { import * as Sentry from "@sentry/react"; import log from "loglevel"; import { GRID_DENSITY_MIGRATION_V1 } from "widgets/constants"; -import { generateResponsiveBehaviorConfig } from "utils/ResponsiveBehaviorConfig"; +import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; import { ResponsiveBehavior } from "components/constants"; export function defaultValueValidation( diff --git a/app/client/src/widgets/RichTextEditorWidget/widget/index.tsx b/app/client/src/widgets/RichTextEditorWidget/widget/index.tsx index 0777951c04ee..710a32260a21 100644 --- a/app/client/src/widgets/RichTextEditorWidget/widget/index.tsx +++ b/app/client/src/widgets/RichTextEditorWidget/widget/index.tsx @@ -10,7 +10,7 @@ import { LabelPosition, ResponsiveBehavior } from "components/constants"; import { Alignment } from "@blueprintjs/core"; import { GRID_DENSITY_MIGRATION_V1 } from "widgets/constants"; import showdown from "showdown"; -import { generateResponsiveBehaviorConfig } from "utils/ResponsiveBehaviorConfig"; +import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; export enum RTEFormats { MARKDOWN = "markdown", diff --git a/app/client/src/widgets/SelectWidget/widget/index.tsx b/app/client/src/widgets/SelectWidget/widget/index.tsx index 1c427efe7c63..9d6c6c30339e 100644 --- a/app/client/src/widgets/SelectWidget/widget/index.tsx +++ b/app/client/src/widgets/SelectWidget/widget/index.tsx @@ -23,7 +23,7 @@ import { LoDashStatic, } from "lodash"; import derivedProperties from "./parseDerivedProperties"; -import { generateResponsiveBehaviorConfig } from "utils/ResponsiveBehaviorConfig"; +import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; export function defaultOptionValueValidation( value: unknown, diff --git a/app/client/src/widgets/SingleSelectTreeWidget/widget/index.tsx b/app/client/src/widgets/SingleSelectTreeWidget/widget/index.tsx index dfa7859ff207..bf688ee8871d 100644 --- a/app/client/src/widgets/SingleSelectTreeWidget/widget/index.tsx +++ b/app/client/src/widgets/SingleSelectTreeWidget/widget/index.tsx @@ -16,7 +16,7 @@ import { GRID_DENSITY_MIGRATION_V1, MinimumPopupRows } from "widgets/constants"; import SingleSelectTreeComponent from "../component"; import { LabelPosition, ResponsiveBehavior } from "components/constants"; import { Alignment } from "@blueprintjs/core"; -import { generateResponsiveBehaviorConfig } from "utils/ResponsiveBehaviorConfig"; +import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; function defaultOptionValueValidation(value: unknown): ValidationResponse { if (typeof value === "string") return { isValid: true, parsed: value.trim() }; diff --git a/app/client/src/widgets/TableWidgetV2/widget/propertyConfig/General.ts b/app/client/src/widgets/TableWidgetV2/widget/propertyConfig/General.ts index dc42c6fdae01..dbec5596c127 100644 --- a/app/client/src/widgets/TableWidgetV2/widget/propertyConfig/General.ts +++ b/app/client/src/widgets/TableWidgetV2/widget/propertyConfig/General.ts @@ -18,7 +18,7 @@ import { } from "@appsmith/constants/messages"; import panelConfig from "./PanelConfig"; import { composePropertyUpdateHook } from "widgets/WidgetUtils"; -import { generateResponsiveBehaviorConfig } from "utils/ResponsiveBehaviorConfig"; +import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; import { ResponsiveBehavior } from "components/constants"; export default { diff --git a/app/client/src/widgets/TabsWidget/widget/index.tsx b/app/client/src/widgets/TabsWidget/widget/index.tsx index a16b7062fd38..ae1c2d473275 100644 --- a/app/client/src/widgets/TabsWidget/widget/index.tsx +++ b/app/client/src/widgets/TabsWidget/widget/index.tsx @@ -14,7 +14,7 @@ import { WidgetProperties } from "selectors/propertyPaneSelectors"; import { WIDGET_PADDING } from "constants/WidgetConstants"; import derivedProperties from "./parseDerivedProperties"; import { Positioning, ResponsiveBehavior } from "components/constants"; -import { generateResponsiveBehaviorConfig } from "utils/ResponsiveBehaviorConfig"; +import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; export function selectedTabValidation( value: unknown, From ea324fcf653ae239702f7e1df1627036c41e7e6c Mon Sep 17 00:00:00 2001 From: Preet Sidhu Date: Mon, 15 Aug 2022 13:14:37 -0400 Subject: [PATCH 047/708] refactored props for container --- app/client/src/utils/layoutPropertiesUtils.ts | 8 ++- .../ContainerWidget/component/index.tsx | 6 +- .../widgets/ContainerWidget/widget/index.tsx | 58 ++++++++++++------- 3 files changed, 48 insertions(+), 24 deletions(-) diff --git a/app/client/src/utils/layoutPropertiesUtils.ts b/app/client/src/utils/layoutPropertiesUtils.ts index 3cbd639c9e36..fb32b51f9fb6 100644 --- a/app/client/src/utils/layoutPropertiesUtils.ts +++ b/app/client/src/utils/layoutPropertiesUtils.ts @@ -4,6 +4,7 @@ import { FlexDirection, JustifyContent, LayoutDirection, + Positioning, ResponsiveBehavior, Spacing, } from "components/constants"; @@ -104,7 +105,8 @@ export const generateResponsiveBehaviorConfig = ( export const generateAlignmentConfig = (value: Alignment): any => { return { - helpText: "Alignment of children with respect to this parent", + helpText: + "Alignment of children with respect to this parent (applies to Stack positioning)", propertyName: "alignment", label: "Alignment", controlType: "DROP_DOWN", @@ -119,12 +121,13 @@ export const generateAlignmentConfig = (value: Alignment): any => { isBindProperty: false, isTriggerProperty: true, validation: { type: ValidationTypes.TEXT }, + hidden: (props: any) => props?.positioning === Positioning.Fixed, }; }; export const generateSpacingConfig = (value: Spacing): any => { return { - helpText: "Spacing between the children", + helpText: "Spacing between the children (applies to Stack positioning)", propertyName: "spacing", label: "Spacing", controlType: "DROP_DOWN", @@ -138,6 +141,7 @@ export const generateSpacingConfig = (value: Spacing): any => { isBindProperty: false, isTriggerProperty: true, validation: { type: ValidationTypes.TEXT }, + hidden: (props: any) => props?.positioning === Positioning.Fixed, }; }; diff --git a/app/client/src/widgets/ContainerWidget/component/index.tsx b/app/client/src/widgets/ContainerWidget/component/index.tsx index 78ab2ad5deed..5a027c7d2e76 100644 --- a/app/client/src/widgets/ContainerWidget/component/index.tsx +++ b/app/client/src/widgets/ContainerWidget/component/index.tsx @@ -11,7 +11,11 @@ import WidgetStyleContainer, { import { pick } from "lodash"; import { ComponentProps } from "widgets/BaseComponent"; import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; -import { AlignItems, FlexDirection, JustifyContent, LayoutDirection } from "components/constants"; +import { + AlignItems, + FlexDirection, + JustifyContent, +} from "components/constants"; const scrollContents = css` overflow-y: auto; diff --git a/app/client/src/widgets/ContainerWidget/widget/index.tsx b/app/client/src/widgets/ContainerWidget/widget/index.tsx index 44516611b3e5..01e37a66f1ff 100644 --- a/app/client/src/widgets/ContainerWidget/widget/index.tsx +++ b/app/client/src/widgets/ContainerWidget/widget/index.tsx @@ -21,12 +21,19 @@ import { CanvasDraggingArena } from "pages/common/CanvasArenas/CanvasDraggingAre import { getCanvasSnapRows } from "utils/WidgetPropsUtils"; import { AlignItems, + Alignment, JustifyContent, LayoutDirection, Positioning, ResponsiveBehavior, + Spacing, } from "components/constants"; import { AutoLayoutContext } from "utils/autoLayoutContext"; +import { + generateResponsiveBehaviorConfig, + getLayoutConfig, + getLayoutProperties, +} from "utils/layoutPropertiesUtils"; class ContainerWidget extends BaseWidget< ContainerWidgetProps, @@ -76,6 +83,11 @@ class ContainerWidget extends BaseWidget< isBindProperty: false, isTriggerProperty: false, }, + ], + }, + { + sectionName: "Layout", + children: [ { helpText: "Position styles to be applied to the children", propertyName: "positioning", @@ -92,22 +104,8 @@ class ContainerWidget extends BaseWidget< isTriggerProperty: true, validation: { type: ValidationTypes.TEXT }, }, - { - helpText: - "Should the children take up the complete width on mobile", - propertyName: "responsiveBehavior", - label: "Responsive behavior", - controlType: "DROP_DOWN", - defaultValue: ResponsiveBehavior.Fill, - options: [ - { label: "Fill", value: ResponsiveBehavior.Fill }, - { label: "Hug", value: ResponsiveBehavior.Hug }, - ], - isJSConvertible: true, - isBindProperty: false, - isTriggerProperty: true, - validation: { type: ValidationTypes.TEXT }, - }, + ...getLayoutConfig(Alignment.Left, Spacing.None), + { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, ], }, { @@ -362,9 +360,20 @@ class ContainerWidget extends BaseWidget< // Pass layout controls to children childWidgetData.useAutoLayout = this.state.useAutoLayout; childWidgetData.direction = this.state.direction; - childWidgetData.justifyContent = this.props.justifyContent; - childWidgetData.alignItems = this.props.alignItems; childWidgetData.positioning = this.props.positioning; + childWidgetData.alignment = this.props.alignment; + childWidgetData.spacing = this.props.spacing; + if (this.props.positioning !== Positioning.Fixed) { + const layoutProps = getLayoutProperties( + this.state.direction, + this.props.alignment, + this.props.spacing, + ); + childWidgetData = { + ...childWidgetData, + ...layoutProps, + }; + } return WidgetFactory.createWidget(childWidgetData, this.props.renderMode); } @@ -385,6 +394,11 @@ class ContainerWidget extends BaseWidget< // console.log(`${props.widgetName} : ${props.widgetId} =======`); // console.log(props); const snapRows = getCanvasSnapRows(props.bottomRow, props.canExtend); + const stretchFlexBox = + !this.props.children || !this.props.children?.length + ? true + : this.props.alignment === Alignment.Bottom || + this.props.positioning === Positioning.Vertical; return ( {props.type === "CANVAS_WIDGET" && ( @@ -421,9 +435,9 @@ class ContainerWidget extends BaseWidget< {props.type === "CANVAS_WIDGET" ? ( containerStyle?: ContainerStyle; shouldScrollContents?: boolean; noPad?: boolean; - positioning?: Positioning; + positioning: Positioning; + alignment: Alignment; + spacing: Spacing; } export interface ContainerWidgetState extends WidgetState { From f2bb5a421fada22d93bdfdce9240d5cabd4b19cf Mon Sep 17 00:00:00 2001 From: Preet Sidhu Date: Mon, 15 Aug 2022 16:05:15 -0400 Subject: [PATCH 048/708] update tabs widget --- app/client/src/utils/layoutPropertiesUtils.ts | 2 +- .../ContainerWidget/component/index.tsx | 34 +++++++++++++++--- .../widgets/ContainerWidget/widget/index.tsx | 23 ++++-------- .../src/widgets/TabsWidget/constants.ts | 6 ++-- .../src/widgets/TabsWidget/widget/index.tsx | 35 +++++++++---------- 5 files changed, 57 insertions(+), 43 deletions(-) diff --git a/app/client/src/utils/layoutPropertiesUtils.ts b/app/client/src/utils/layoutPropertiesUtils.ts index fb32b51f9fb6..afd65d3e41f5 100644 --- a/app/client/src/utils/layoutPropertiesUtils.ts +++ b/app/client/src/utils/layoutPropertiesUtils.ts @@ -48,7 +48,7 @@ export const verticalAlignment: { [key in Alignment]: LayoutProperties } = { bottom: { flexDirection: FlexDirection.ColumnReverse, justifyContent: JustifyContent.FlexStart, - alignItems: AlignItems.FlexStart, + alignItems: AlignItems.Center, }, left: { flexDirection: FlexDirection.Column, diff --git a/app/client/src/widgets/ContainerWidget/component/index.tsx b/app/client/src/widgets/ContainerWidget/component/index.tsx index 5a027c7d2e76..d0bf509d7cc2 100644 --- a/app/client/src/widgets/ContainerWidget/component/index.tsx +++ b/app/client/src/widgets/ContainerWidget/component/index.tsx @@ -13,9 +13,13 @@ import { ComponentProps } from "widgets/BaseComponent"; import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; import { AlignItems, + Alignment, FlexDirection, JustifyContent, + LayoutDirection, + Spacing, } from "components/constants"; +import { getLayoutProperties } from "utils/layoutPropertiesUtils"; const scrollContents = css` overflow-y: auto; @@ -65,13 +69,13 @@ const StyledContainerComponent = styled.div< export const FlexContainer = styled.div<{ useAutoLayout?: boolean; - direction?: FlexDirection; + flexDirection?: FlexDirection; justifyContent?: JustifyContent; alignItems?: AlignItems; stretchHeight: boolean; }>` display: ${({ useAutoLayout }) => (useAutoLayout ? "flex" : "block")}; - flex-direction: ${({ direction }) => direction || "row"}; + flex-direction: ${({ flexDirection }) => flexDirection || "row"}; justify-content: ${({ justifyContent }) => justifyContent || "flex-start"}; align-items: ${({ alignItems }) => alignItems || "flex-start"}; flex-wrap: wrap; @@ -113,8 +117,21 @@ function ContainerComponentWrapper(props: ContainerComponentProps) { ); } -export function FlexBox(props: any) { - return {props.children}; +export function FlexBox(props: FlexBoxProps) { + const layoutProps = getLayoutProperties( + props.direction, + props.alignment, + props.spacing, + ); + return ( + + {props.children} + + ); } function ContainerComponent(props: ContainerComponentProps) { @@ -159,4 +176,13 @@ export interface ContainerComponentProps alignItems?: string; } +export interface FlexBoxProps { + alignment: Alignment; + direction: LayoutDirection; + spacing: Spacing; + stretchHeight: boolean; + useAutoLayout: boolean; + children?: ReactNode; +} + export default ContainerComponent; diff --git a/app/client/src/widgets/ContainerWidget/widget/index.tsx b/app/client/src/widgets/ContainerWidget/widget/index.tsx index 01e37a66f1ff..6e9d4baec4ca 100644 --- a/app/client/src/widgets/ContainerWidget/widget/index.tsx +++ b/app/client/src/widgets/ContainerWidget/widget/index.tsx @@ -363,17 +363,6 @@ class ContainerWidget extends BaseWidget< childWidgetData.positioning = this.props.positioning; childWidgetData.alignment = this.props.alignment; childWidgetData.spacing = this.props.spacing; - if (this.props.positioning !== Positioning.Fixed) { - const layoutProps = getLayoutProperties( - this.state.direction, - this.props.alignment, - this.props.spacing, - ); - childWidgetData = { - ...childWidgetData, - ...layoutProps, - }; - } return WidgetFactory.createWidget(childWidgetData, this.props.renderMode); } @@ -434,9 +423,9 @@ class ContainerWidget extends BaseWidget< {/* without the wrapping div onClick events are triggered twice */} {props.type === "CANVAS_WIDGET" ? ( @@ -473,9 +462,9 @@ export interface ContainerWidgetProps containerStyle?: ContainerStyle; shouldScrollContents?: boolean; noPad?: boolean; - positioning: Positioning; - alignment: Alignment; - spacing: Spacing; + positioning?: Positioning; + alignment?: Alignment; + spacing?: Spacing; } export interface ContainerWidgetState extends WidgetState { diff --git a/app/client/src/widgets/TabsWidget/constants.ts b/app/client/src/widgets/TabsWidget/constants.ts index 649928bbb8d1..f9c5cc20f272 100644 --- a/app/client/src/widgets/TabsWidget/constants.ts +++ b/app/client/src/widgets/TabsWidget/constants.ts @@ -1,4 +1,4 @@ -import { Positioning } from "components/constants"; +import { Alignment, Positioning, Spacing } from "components/constants"; import { WidgetProps } from "widgets/BaseWidget"; export interface TabContainerWidgetProps extends WidgetProps { @@ -23,7 +23,9 @@ export interface TabsWidgetProps widgetId: string; isVisible?: boolean; index: number; - positioning?: Positioning; + positioning: Positioning; + alignment: Alignment; + spacing: Spacing; } >; shouldShowTabs: boolean; diff --git a/app/client/src/widgets/TabsWidget/widget/index.tsx b/app/client/src/widgets/TabsWidget/widget/index.tsx index ae1c2d473275..d325bb900320 100644 --- a/app/client/src/widgets/TabsWidget/widget/index.tsx +++ b/app/client/src/widgets/TabsWidget/widget/index.tsx @@ -13,8 +13,16 @@ import { AutocompleteDataType } from "utils/autocomplete/TernServer"; import { WidgetProperties } from "selectors/propertyPaneSelectors"; import { WIDGET_PADDING } from "constants/WidgetConstants"; import derivedProperties from "./parseDerivedProperties"; -import { Positioning, ResponsiveBehavior } from "components/constants"; -import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; +import { + Alignment, + Positioning, + ResponsiveBehavior, + Spacing, +} from "components/constants"; +import { + generateResponsiveBehaviorConfig, + getLayoutConfig, +} from "utils/layoutPropertiesUtils"; export function selectedTabValidation( value: unknown, @@ -125,6 +133,7 @@ class TabsWidget extends BaseWidget< isTriggerProperty: true, validation: { type: ValidationTypes.TEXT }, }, + ...getLayoutConfig(Alignment.Left, Spacing.None), ], }, ], @@ -192,21 +201,6 @@ class TabsWidget extends BaseWidget< { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, ], }, - { - helpText: "Should the children take up the complete width on mobile", - propertyName: "responsiveBehavior", - label: "Responsive behavior", - controlType: "DROP_DOWN", - defaultValue: ResponsiveBehavior.Fill, - options: [ - { label: "Fill", value: ResponsiveBehavior.Fill }, - { label: "Hug", value: ResponsiveBehavior.Hug }, - ], - isJSConvertible: true, - isBindProperty: false, - isTriggerProperty: true, - validation: { type: ValidationTypes.TEXT }, - }, { sectionName: "Events", children: [ @@ -511,9 +505,12 @@ class TabsWidget extends BaseWidget< : componentHeight - 1; childWidgetData.parentId = this.props.widgetId; childWidgetData.minHeight = componentHeight; - childWidgetData.positioning = Object.values(this.props.tabsObj)?.filter( + const selectedTabProps = Object.values(this.props.tabsObj)?.filter( (item) => item.widgetId === selectedTabWidgetId, - )[0]?.positioning; + )[0]; + childWidgetData.positioning = selectedTabProps?.positioning; + childWidgetData.alignment = selectedTabProps?.alignment; + childWidgetData.spacing = selectedTabProps?.spacing; return WidgetFactory.createWidget(childWidgetData, this.props.renderMode); }; From 6d798f41ba75efba16c68a11ea78105c4e8f9fb5 Mon Sep 17 00:00:00 2001 From: Preet Sidhu Date: Mon, 15 Aug 2022 16:12:19 -0400 Subject: [PATCH 049/708] update modal widget --- .../widgets/ContainerWidget/widget/index.tsx | 1 - .../src/widgets/ModalWidget/widget/index.tsx | 45 ++++++++++++------- 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/app/client/src/widgets/ContainerWidget/widget/index.tsx b/app/client/src/widgets/ContainerWidget/widget/index.tsx index 6e9d4baec4ca..5cf865625914 100644 --- a/app/client/src/widgets/ContainerWidget/widget/index.tsx +++ b/app/client/src/widgets/ContainerWidget/widget/index.tsx @@ -32,7 +32,6 @@ import { AutoLayoutContext } from "utils/autoLayoutContext"; import { generateResponsiveBehaviorConfig, getLayoutConfig, - getLayoutProperties, } from "utils/layoutPropertiesUtils"; class ContainerWidget extends BaseWidget< diff --git a/app/client/src/widgets/ModalWidget/widget/index.tsx b/app/client/src/widgets/ModalWidget/widget/index.tsx index d9dc0982bda5..4a23919e6f08 100644 --- a/app/client/src/widgets/ModalWidget/widget/index.tsx +++ b/app/client/src/widgets/ModalWidget/widget/index.tsx @@ -15,7 +15,8 @@ import { AppState } from "reducers"; import { getCanvasWidth, snipingModeSelector } from "selectors/editorSelectors"; import { deselectAllInitAction } from "actions/widgetSelectionActions"; import { ValidationTypes } from "constants/WidgetValidation"; -import { Positioning } from "components/constants"; +import { Alignment, Positioning, Spacing } from "components/constants"; +import { getLayoutConfig } from "utils/layoutPropertiesUtils"; const minSize = 100; @@ -25,22 +26,6 @@ export class ModalWidget extends BaseWidget { { sectionName: "General", children: [ - { - helpText: "Position styles to be applied to the children", - propertyName: "positioning", - label: "Positioning", - controlType: "DROP_DOWN", - defaultValue: Positioning.Fixed, - options: [ - { label: "Fixed", value: Positioning.Fixed }, - { label: "Horizontal stack", value: Positioning.Horizontal }, - { label: "Vertical stack", value: Positioning.Vertical }, - ], - isJSConvertible: false, - isBindProperty: true, - isTriggerProperty: true, - validation: { type: ValidationTypes.TEXT }, - }, { helpText: "Enables scrolling for content inside the widget", propertyName: "shouldScrollContents", @@ -70,6 +55,28 @@ export class ModalWidget extends BaseWidget { }, ], }, + { + sectionName: "Layout", + children: [ + { + helpText: "Position styles to be applied to the children", + propertyName: "positioning", + label: "Positioning", + controlType: "DROP_DOWN", + defaultValue: Positioning.Fixed, + options: [ + { label: "Fixed", value: Positioning.Fixed }, + { label: "Horizontal stack", value: Positioning.Horizontal }, + { label: "Vertical stack", value: Positioning.Vertical }, + ], + isJSConvertible: false, + isBindProperty: true, + isTriggerProperty: true, + validation: { type: ValidationTypes.TEXT }, + }, + ...getLayoutConfig(Alignment.Left, Spacing.None), + ], + }, { sectionName: "Events", children: [ @@ -229,6 +236,8 @@ export class ModalWidget extends BaseWidget { childWidgetData.rightColumn = this.getModalWidth(this.props.width) + WIDGET_PADDING * 2; childWidgetData.positioning = this.props.positioning; + childWidgetData.alignment = this.props.alignment; + childWidgetData.spacing = this.props.spacing; return WidgetFactory.createWidget(childWidgetData, this.props.renderMode); }; @@ -373,6 +382,8 @@ export interface ModalWidgetProps extends WidgetProps { borderRadius: string; mainCanvasWidth: number; positioning?: Positioning; + alignment: Alignment; + spacing: Spacing; } const mapDispatchToProps = (dispatch: any) => ({ From 942b0d77c28eb195130381ee8cfc21dde1937cef Mon Sep 17 00:00:00 2001 From: Preet Sidhu Date: Wed, 17 Aug 2022 10:26:23 -0400 Subject: [PATCH 050/708] re-enable relow for autolayout --- .../pages/common/CanvasArenas/hooks/useCanvasDragging.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index 76dd6edeaf00..2a1ba2e2c4c7 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -39,8 +39,6 @@ export interface XYCord { const CONTAINER_JUMP_ACC_THRESHOLD = 8000; const CONTAINER_JUMP_SPEED_THRESHOLD = 800; -const shouldDisableReflow = true; - let lastTranslatedIndex: number; //Since useCanvasDragging's Instance changes during container jump, metrics is stored outside const containerJumpThresholdMetrics = new ContainerJumpMetrics<{ @@ -641,9 +639,7 @@ export const useCanvasDragging = ( renderNewRows(delta); } else if (!isUpdatingRows) { triggerReflow(e, firstMove); - isCurrentDraggedCanvas && - !shouldDisableReflow && - highlightDropPosition(e); + isCurrentDraggedCanvas && highlightDropPosition(e); renderBlocks(); } scrollObj.lastMouseMoveEvent = { @@ -705,7 +701,6 @@ export const useCanvasDragging = ( const getDropPosition = (val: number): number | undefined => { const pos = getHighlightPosition(null, val); if (!pos) return; - if (shouldDisableReflow) return offsets.length; return offsets.indexOf(pos); }; const renderNewRows = debounce((delta) => { From 380b93b0d2d5380e60366a26847da39d1da1e78c Mon Sep 17 00:00:00 2001 From: Preet Sidhu Date: Wed, 17 Aug 2022 10:49:32 -0400 Subject: [PATCH 051/708] fix highlight at offset 0 --- .../CanvasArenas/hooks/useCanvasDragging.ts | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index 2a1ba2e2c4c7..252e6f105a7b 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -3,7 +3,7 @@ import { CONTAINER_GRID_PADDING, GridDefaults, } from "constants/WidgetConstants"; -import { debounce, isEmpty, isNumber, throttle } from "lodash"; +import { debounce, isEmpty, isNaN, isNumber, throttle } from "lodash"; import { CanvasDraggingArenaProps } from "pages/common/CanvasArenas/CanvasDraggingArena"; import React, { useEffect, useRef } from "react"; import { useSelector } from "react-redux"; @@ -127,7 +127,7 @@ export const useCanvasDragging = ( // console.log(els); // console.log(blocksToDraw); const blocks = blocksToDraw.map((block) => block.widgetId); - // console.log("*********"); + console.log("*********"); els.forEach((el) => { // console.log((el as any).offsetParent); // Extract widget id of current widget @@ -135,9 +135,9 @@ export const useCanvasDragging = ( .split("auto-layout-child-")[1] .split(" ")[0]; // console.log(`parentId: ${widgetId}`); - // console.log(`widgetID: ${mClass}`); - // console.log(`blocks: ${blocks}`); - // console.log(blocks); + console.log(`widgetID: ${mClass}`); + console.log(`blocks: ${blocks}`); + console.log(blocks); /** * If the widget is also being dragged, * then discount its presence from offset calculation. @@ -150,9 +150,10 @@ export const useCanvasDragging = ( const mOffset = isVertical ? (el as any).offsetTop : (el as any).offsetLeft; - // console.log(`offset: ${mOffset}`); + console.log(el); + console.log(`offset: ${mOffset}`); offsets.push(mOffset); - // console.log(offsets); + console.log(offsets); // siblings[mClass] = mOffset; siblingElements.push(el); } @@ -169,7 +170,7 @@ export const useCanvasDragging = ( 8 : 8, ); - // console.log(offsets); + console.log(offsets); } } }; @@ -677,11 +678,14 @@ export const useCanvasDragging = ( const highlightDropPosition = (e: any) => { if (!useAutoLayout) return; const pos: number | undefined = getHighlightPosition(e); - if (!pos) return; + if (isNaN(pos)) return; // console.log(`#### ref: ${dropPositionRef.current}`); if (dropPositionRef && dropPositionRef.current) { + console.log(`highlight position: ${pos - 6}`); dropPositionRef.current.style.opacity = "1"; - if (isVertical) dropPositionRef.current.style.top = pos - 6 + "px"; + if (isVertical) + dropPositionRef.current.style.top = + (pos > 6 ? pos - 6 : 0) + "px"; else dropPositionRef.current.style.left = pos - 6 + "px"; } translateSiblings(pos); @@ -692,10 +696,13 @@ export const useCanvasDragging = ( else base = offsets; const pos = (isVertical ? e?.offsetY : e?.offsetX) || val; // console.log(e); - // console.log(pos); + console.log("START: highlight position calculation"); + console.log(pos); const arr = [...base].sort((a, b) => { return Math.abs(a - pos) - Math.abs(b - pos); }); + console.log(arr); + console.log("END: highlight position calculation"); return arr[0]; }; const getDropPosition = (val: number): number | undefined => { From 308c9c27b6ee7d3531766ed46811c26ca70ad82d Mon Sep 17 00:00:00 2001 From: Preet Sidhu Date: Wed, 17 Aug 2022 12:35:40 -0400 Subject: [PATCH 052/708] fix highlight offset --- .../appsmith/WidgetStyleContainer.tsx | 8 +----- .../CanvasArenas/hooks/useCanvasDragging.ts | 27 ++++++++++++------- .../ContainerWidget/component/index.tsx | 2 ++ .../widgets/ContainerWidget/widget/index.tsx | 1 + 4 files changed, 21 insertions(+), 17 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/WidgetStyleContainer.tsx b/app/client/src/components/designSystems/appsmith/WidgetStyleContainer.tsx index 6c11858ea28f..e70ca3a00c6d 100644 --- a/app/client/src/components/designSystems/appsmith/WidgetStyleContainer.tsx +++ b/app/client/src/components/designSystems/appsmith/WidgetStyleContainer.tsx @@ -39,13 +39,7 @@ const WidgetStyle = styled.div` border-style: solid; background-color: ${(props) => props.backgroundColor || "transparent"}; - display: ${({ useAutoLayout }) => (useAutoLayout ? "flex" : "block")}; - flex-direction: ${({ direction }) => - direction === LayoutDirection.Vertical ? "column" : "row"}; - justify-content: flex-start; - align-items: ${({ direction }) => - direction === LayoutDirection.Vertical ? "stretch" : "flex-start"}; - flex-wrap: wrap; + display: block; & > div { height: 100%; diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index 252e6f105a7b..f08b90b203a1 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -121,11 +121,16 @@ export const useCanvasDragging = ( dragBlocksSize += isVertical ? each.height : each.width; }); // Get all children of current auto layout container + const container = document.querySelector(`.flex-container-qa3j3kll75`); + console.log(container); + console.log((container as any).offsetTop); + const containerOffsetTop = (container as any).offsetTop || 0; + const containerOffsetLeft = (container as any).offsetLeft || 0; const els = document.querySelectorAll(`.auto-layout-parent-${widgetId}`); if (els && els.length && offsets.length !== els.length) { // Get widget ids of all widgets being dragged // console.log(els); - // console.log(blocksToDraw); + console.log(blocksToDraw); const blocks = blocksToDraw.map((block) => block.widgetId); console.log("*********"); els.forEach((el) => { @@ -148,8 +153,8 @@ export const useCanvasDragging = ( return; } else { const mOffset = isVertical - ? (el as any).offsetTop - : (el as any).offsetLeft; + ? (el as any).offsetTop - containerOffsetTop + : (el as any).offsetLeft - containerOffsetLeft; console.log(el); console.log(`offset: ${mOffset}`); offsets.push(mOffset); @@ -161,11 +166,13 @@ export const useCanvasDragging = ( offsets.push( siblingElements.length ? isVertical - ? (siblingElements[siblingElements.length - 1] as any).offsetTop + + ? (siblingElements[siblingElements.length - 1] as any).offsetTop - + containerOffsetTop + siblingElements[siblingElements.length - 1].clientHeight + 8 : (siblingElements[siblingElements.length - 1] as any) - .offsetLeft + + .offsetLeft - + containerOffsetLeft + siblingElements[siblingElements.length - 1].clientWidth + 8 : 8, @@ -681,7 +688,7 @@ export const useCanvasDragging = ( if (isNaN(pos)) return; // console.log(`#### ref: ${dropPositionRef.current}`); if (dropPositionRef && dropPositionRef.current) { - console.log(`highlight position: ${pos - 6}`); + // console.log(`highlight position: ${pos - 6}`); dropPositionRef.current.style.opacity = "1"; if (isVertical) dropPositionRef.current.style.top = @@ -696,13 +703,13 @@ export const useCanvasDragging = ( else base = offsets; const pos = (isVertical ? e?.offsetY : e?.offsetX) || val; // console.log(e); - console.log("START: highlight position calculation"); - console.log(pos); + // console.log("START: highlight position calculation"); + // console.log(pos); const arr = [...base].sort((a, b) => { return Math.abs(a - pos) - Math.abs(b - pos); }); - console.log(arr); - console.log("END: highlight position calculation"); + // console.log(arr); + // console.log("END: highlight position calculation"); return arr[0]; }; const getDropPosition = (val: number): number | undefined => { diff --git a/app/client/src/widgets/ContainerWidget/component/index.tsx b/app/client/src/widgets/ContainerWidget/component/index.tsx index d0bf509d7cc2..ed88ddeece65 100644 --- a/app/client/src/widgets/ContainerWidget/component/index.tsx +++ b/app/client/src/widgets/ContainerWidget/component/index.tsx @@ -125,6 +125,7 @@ export function FlexBox(props: FlexBoxProps) { ); return ( Date: Wed, 17 Aug 2022 14:49:13 -0400 Subject: [PATCH 053/708] 2d highlight position; working vertically --- .../appsmith/WidgetStyleContainer.tsx | 1 - .../CanvasArenas/hooks/useCanvasDragging.ts | 214 ++++++++++++++---- 2 files changed, 164 insertions(+), 51 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/WidgetStyleContainer.tsx b/app/client/src/components/designSystems/appsmith/WidgetStyleContainer.tsx index e70ca3a00c6d..4da107006ecd 100644 --- a/app/client/src/components/designSystems/appsmith/WidgetStyleContainer.tsx +++ b/app/client/src/components/designSystems/appsmith/WidgetStyleContainer.tsx @@ -2,7 +2,6 @@ import React, { ReactNode } from "react"; import styled from "styled-components"; import { ContainerStyle } from "widgets/ContainerWidget/component"; import { Color } from "constants/Colors"; -import { LayoutDirection } from "components/constants"; export enum BoxShadowTypes { NONE = "NONE", diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index f08b90b203a1..85a6f8c8841a 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -3,7 +3,7 @@ import { CONTAINER_GRID_PADDING, GridDefaults, } from "constants/WidgetConstants"; -import { debounce, isEmpty, isNaN, isNumber, throttle } from "lodash"; +import { debounce, isEmpty, isNaN, throttle } from "lodash"; import { CanvasDraggingArenaProps } from "pages/common/CanvasArenas/CanvasDraggingArena"; import React, { useEffect, useRef } from "react"; import { useSelector } from "react-redux"; @@ -36,10 +36,23 @@ export interface XYCord { y: number; } +interface HighlightDimension { + x: number; + y: number; + height: number; + width: number; +} + const CONTAINER_JUMP_ACC_THRESHOLD = 8000; const CONTAINER_JUMP_SPEED_THRESHOLD = 800; let lastTranslatedIndex: number; +let containerDimensions: { + top: number; + left: number; + width: number; + height: number; +}; //Since useCanvasDragging's Instance changes during container jump, metrics is stored outside const containerJumpThresholdMetrics = new ContainerJumpMetrics<{ speed?: number; @@ -110,7 +123,7 @@ export const useCanvasDragging = ( reflow.current = useReflow(draggingSpaces, widgetId || "", gridProps); let dragBlocksSize = 0; - const offsets: number[] = []; + const offsets: HighlightDimension[] = []; // let siblings: { [key: string]: number } = {}; const siblingElements: any[] = []; const isVertical = direction === LayoutDirection.Vertical; @@ -122,17 +135,21 @@ export const useCanvasDragging = ( }); // Get all children of current auto layout container const container = document.querySelector(`.flex-container-qa3j3kll75`); - console.log(container); - console.log((container as any).offsetTop); - const containerOffsetTop = (container as any).offsetTop || 0; - const containerOffsetLeft = (container as any).offsetLeft || 0; + // console.log(container); + // console.log((container as any).offsetTop); + containerDimensions = { + top: (container as any).offsetTop || 0, + left: (container as any).offsetLeft || 0, + width: (container as any).clientWidth, + height: (container as any).clientHeight, + }; const els = document.querySelectorAll(`.auto-layout-parent-${widgetId}`); if (els && els.length && offsets.length !== els.length) { // Get widget ids of all widgets being dragged // console.log(els); - console.log(blocksToDraw); + // console.log(blocksToDraw); const blocks = blocksToDraw.map((block) => block.widgetId); - console.log("*********"); + // console.log("*********"); els.forEach((el) => { // console.log((el as any).offsetParent); // Extract widget id of current widget @@ -140,9 +157,9 @@ export const useCanvasDragging = ( .split("auto-layout-child-")[1] .split(" ")[0]; // console.log(`parentId: ${widgetId}`); - console.log(`widgetID: ${mClass}`); - console.log(`blocks: ${blocks}`); - console.log(blocks); + // console.log(`widgetID: ${mClass}`); + // console.log(`blocks: ${blocks}`); + // console.log(blocks); /** * If the widget is also being dragged, * then discount its presence from offset calculation. @@ -152,32 +169,80 @@ export const useCanvasDragging = ( (el as any).classList.add("auto-temp-no-display"); return; } else { - const mOffset = isVertical - ? (el as any).offsetTop - containerOffsetTop - : (el as any).offsetLeft - containerOffsetLeft; - console.log(el); - console.log(`offset: ${mOffset}`); + let mOffset: HighlightDimension; + if (isVertical) { + mOffset = { + x: 0, + y: (el as any).offsetTop - containerDimensions.top, + width: containerDimensions.width, + height: 4, + }; + } else { + mOffset = { + x: (el as any).offsetLeft - containerDimensions.left, + y: (el as any).offsetTop - containerDimensions.top, + height: (el as any).clientHeight, + width: 4, + }; + } + // console.log(el); + // console.log(`offset: ${mOffset}`); offsets.push(mOffset); - console.log(offsets); + // console.log(offsets); // siblings[mClass] = mOffset; siblingElements.push(el); } }); - offsets.push( - siblingElements.length - ? isVertical - ? (siblingElements[siblingElements.length - 1] as any).offsetTop - - containerOffsetTop + + let finalOffset: HighlightDimension; + if (!siblingElements.length) { + if (isVertical) { + finalOffset = { + x: 0, + y: 8, + width: containerDimensions.width, + height: 4, + }; + } else { + finalOffset = { + x: 8, + y: 0, + width: 4, + height: containerDimensions.height, + }; + } + } else { + if (isVertical) { + finalOffset = { + x: 0, + y: + (siblingElements[siblingElements.length - 1] as any).offsetTop - + containerDimensions.top + siblingElements[siblingElements.length - 1].clientHeight + - 8 - : (siblingElements[siblingElements.length - 1] as any) + 8, + width: containerDimensions.width, + height: 4, + }; + } else { + finalOffset = { + x: + (siblingElements[siblingElements.length - 1] as any) .offsetLeft - - containerOffsetLeft + + containerDimensions.left + siblingElements[siblingElements.length - 1].clientWidth + - 8 - : 8, - ); - console.log(offsets); + 8, + y: + (siblingElements[siblingElements.length - 1] as any).offsetTop - + containerDimensions.top + + siblingElements[siblingElements.length - 1].clientHeight + + 8, + width: 4, + height: (siblingElements[siblingElements.length - 1] as any) + .clientHeight, + }; + } + } + offsets.push(finalOffset); + // console.log(offsets); } } }; @@ -348,7 +413,8 @@ export const useCanvasDragging = ( /** * On mouse up, calculate the top, left, bottom and right positions for each of the reflowed widgets */ - const onMouseUp = () => { + const onMouseUp = (e: any) => { + console.log(e); if (isDragging && canvasIsDragging) { const { movementMap: reflowingWidgets } = currentReflowParams; const reflowedPositionsUpdatesWidgets: OccupiedSpace[] = occSpaces @@ -388,11 +454,10 @@ export const useCanvasDragging = ( }); // console.log(currentRectanglesToDraw); // console.log(reflowedPositionsUpdatesWidgets); - const pos = getDropPosition( - isVertical - ? currentRectanglesToDraw[0].top - : currentRectanglesToDraw[0].left, - ); + const pos = getDropPosition({ + x: currentRectanglesToDraw[0].top, + y: currentRectanglesToDraw[0].left, + }); // console.log(`#### pos: ${pos}`); if (pos !== undefined && useAutoLayout) { // cleanUpTempStyles(); @@ -660,9 +725,12 @@ export const useCanvasDragging = ( onFirstMoveOnCanvas(e); } }; - const translateSiblings = (position: number): void => { + const translateSiblings = (position: HighlightDimension): void => { let dropIndex = 0; - if (isNumber(position)) dropIndex = offsets.indexOf(position); + if (position) + dropIndex = offsets + ?.map((each) => `${each.x},${each.y}`) + .indexOf(`${position.x},${position.y}`); if (dropIndex === lastTranslatedIndex) return; // Get all siblings after the highlighted drop position @@ -684,36 +752,82 @@ export const useCanvasDragging = ( }; const highlightDropPosition = (e: any) => { if (!useAutoLayout) return; - const pos: number | undefined = getHighlightPosition(e); - if (isNaN(pos)) return; + const pos: HighlightDimension | undefined = getHighlightPosition(e); + if (!pos) return; // console.log(`#### ref: ${dropPositionRef.current}`); if (dropPositionRef && dropPositionRef.current) { // console.log(`highlight position: ${pos - 6}`); dropPositionRef.current.style.opacity = "1"; - if (isVertical) - dropPositionRef.current.style.top = - (pos > 6 ? pos - 6 : 0) + "px"; - else dropPositionRef.current.style.left = pos - 6 + "px"; + dropPositionRef.current.style.top = + (pos.y > 6 ? pos.y - 6 : 0) + "px"; + dropPositionRef.current.style.left = + (pos.x > 6 + ? Math.min( + pos.x - 6, + containerDimensions.left + containerDimensions.width - 6, + ) + : 0) + "px"; + dropPositionRef.current.style.width = pos.width + "px"; + dropPositionRef.current.style.height = pos.height + "px"; } translateSiblings(pos); }; - const getHighlightPosition = (e: any, val?: number) => { - let base: number[] = []; - if (!offsets || !offsets.length) base = [8]; - else base = offsets; - const pos = (isVertical ? e?.offsetY : e?.offsetX) || val; + const getHighlightPosition = ( + e: any, + val?: XYCord, + ): HighlightDimension => { + let base: HighlightDimension[] = []; + if (!offsets || !offsets.length) { + if (isVertical) { + base = [ + { + x: 0, + y: 8, + width: containerDimensions.width, + height: 4, + }, + ]; + } else { + base = [ + { + x: 8, + y: 0, + width: 4, + height: containerDimensions.height, + }, + ]; + } + } else base = offsets; + // TODO: add val + const pos: XYCord = { + x: e?.offsetX || val?.x, + y: e?.offsetY || val?.y, + }; + // console.log(e); // console.log("START: highlight position calculation"); // console.log(pos); const arr = [...base].sort((a, b) => { - return Math.abs(a - pos) - Math.abs(b - pos); + return calculateDistance(a, pos) - calculateDistance(b, pos); }); // console.log(arr); // console.log("END: highlight position calculation"); return arr[0]; }; - const getDropPosition = (val: number): number | undefined => { + const calculateDistance = ( + a: HighlightDimension, + b: XYCord, + ): number => { + const x: number = a.x + a.width / 2 - b.x; + const y: number = a.y + a.height / 2 - b.y; + return Math.abs(Math.sqrt(x * x + y * y)); + }; + const getDropPosition = (val: XYCord): number | undefined => { + if (!isNaN(lastTranslatedIndex)) return lastTranslatedIndex; const pos = getHighlightPosition(null, val); + // console.log(`START drop position`); + // console.log(val); + // console.log(lastTranslatedIndex); if (!pos) return; return offsets.indexOf(pos); }; From b8115e8dc6e730ea74d3f62dbfba0766980ddc6f Mon Sep 17 00:00:00 2001 From: Preet Sidhu Date: Wed, 17 Aug 2022 14:53:46 -0400 Subject: [PATCH 054/708] fix final horizontal highlight --- .../src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index 85a6f8c8841a..0be6a57f70c1 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -233,7 +233,6 @@ export const useCanvasDragging = ( y: (siblingElements[siblingElements.length - 1] as any).offsetTop - containerDimensions.top + - siblingElements[siblingElements.length - 1].clientHeight + 8, width: 4, height: (siblingElements[siblingElements.length - 1] as any) From 4f5e5242d31cd744805070e3d7fb2c62a40fca6a Mon Sep 17 00:00:00 2001 From: Preet Sidhu Date: Thu, 18 Aug 2022 09:13:15 -0400 Subject: [PATCH 055/708] re-enable reflow for autolayout containers --- .../src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts | 3 +-- app/client/src/resizable/resizenreflow/index.tsx | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index 0be6a57f70c1..9d4bcbbf9bfb 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -412,8 +412,7 @@ export const useCanvasDragging = ( /** * On mouse up, calculate the top, left, bottom and right positions for each of the reflowed widgets */ - const onMouseUp = (e: any) => { - console.log(e); + const onMouseUp = () => { if (isDragging && canvasIsDragging) { const { movementMap: reflowingWidgets } = currentReflowParams; const reflowedPositionsUpdatesWidgets: OccupiedSpace[] = occSpaces diff --git a/app/client/src/resizable/resizenreflow/index.tsx b/app/client/src/resizable/resizenreflow/index.tsx index 5aabe5425c27..0504da748e49 100644 --- a/app/client/src/resizable/resizenreflow/index.tsx +++ b/app/client/src/resizable/resizenreflow/index.tsx @@ -249,7 +249,7 @@ export function ReflowResizable(props: ResizableProps) { bottomMostRow = 0, movementLimitMap: MovementLimitMap | undefined = {}; - if (resizedPositions) { + if (!props?.useAutoLayout && resizedPositions) { const isColliding = checkForCollision(resizedPositions); if (isColliding) { return prevState; From 9cce86d4b33d144492a589af9f8590ed93ed67d4 Mon Sep 17 00:00:00 2001 From: Preet Sidhu Date: Fri, 19 Aug 2022 07:07:45 -0400 Subject: [PATCH 056/708] remove hardcoded widget id --- .../src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index 9d4bcbbf9bfb..79bf3ad7cb26 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -134,7 +134,7 @@ export const useCanvasDragging = ( dragBlocksSize += isVertical ? each.height : each.width; }); // Get all children of current auto layout container - const container = document.querySelector(`.flex-container-qa3j3kll75`); + const container = document.querySelector(`.flex-container-${widgetId}`); // console.log(container); // console.log((container as any).offsetTop); containerDimensions = { From f10eaf89c7d3b6e5b6b018bd6848c23373f3e05c Mon Sep 17 00:00:00 2001 From: Preet Sidhu Date: Fri, 19 Aug 2022 07:50:05 -0400 Subject: [PATCH 057/708] fix merge issues --- .../editorComponents/ResizableComponent.tsx | 2 -- app/client/src/widgets/CanvasWidget.tsx | 26 +++++++++---------- .../widgets/ContainerWidget/widget/index.tsx | 12 ++++----- .../src/widgets/ModalWidget/widget/index.tsx | 6 ++--- app/client/src/widgets/constants.ts | 25 +++++++++++++++--- 5 files changed, 44 insertions(+), 27 deletions(-) diff --git a/app/client/src/components/editorComponents/ResizableComponent.tsx b/app/client/src/components/editorComponents/ResizableComponent.tsx index 127e2e039c84..bbed3fc17757 100644 --- a/app/client/src/components/editorComponents/ResizableComponent.tsx +++ b/app/client/src/components/editorComponents/ResizableComponent.tsx @@ -65,8 +65,6 @@ export const ResizableComponent = memo(function ResizableComponent( } = useContext(AutoLayoutContext || null); const isHorizontallyStretched = direction === LayoutDirection.Vertical && alignItems === AlignItems.Stretch; - const canvasWidgets = useSelector(getCanvasWidgets); - const isSnipingMode = useSelector(snipingModeSelector); const isPreviewMode = useSelector(previewModeSelector); diff --git a/app/client/src/widgets/CanvasWidget.tsx b/app/client/src/widgets/CanvasWidget.tsx index 9a9b65ce0138..d2c781865d61 100644 --- a/app/client/src/widgets/CanvasWidget.tsx +++ b/app/client/src/widgets/CanvasWidget.tsx @@ -54,24 +54,24 @@ class CanvasWidget extends ContainerWidget { const childWidget = { ...childWidgetData }; const snapSpaces = this.getSnapSpaces(); - childWidgetData.parentColumnSpace = snapSpaces.snapColumnSpace; - childWidgetData.parentRowSpace = snapSpaces.snapRowSpace; - if (this.props.noPad) childWidgetData.noContainerOffset = true; - childWidgetData.parentId = this.props.widgetId; + childWidget.parentColumnSpace = snapSpaces.snapColumnSpace; + childWidget.parentRowSpace = snapSpaces.snapRowSpace; + if (this.props.noPad) childWidget.noContainerOffset = true; + childWidget.parentId = this.props.widgetId; // Pass layout controls to children - childWidgetData.positioning = - childWidgetData?.positioning || this.props.positioning; - childWidgetData.useAutoLayout = this.state.useAutoLayout; - childWidgetData.direction = this.state.direction; - childWidgetData.justifyContent = this.props.justifyContent; - childWidgetData.alignItems = this.props.alignItems; + childWidget.positioning = + childWidget?.positioning || this.props.positioning; + childWidget.useAutoLayout = this.state.useAutoLayout; + childWidget.direction = this.state.direction; + childWidget.justifyContent = this.props.justifyContent; + childWidget.alignItems = this.props.alignItems; if ( - childWidgetData?.responsiveBehavior === ResponsiveBehavior.Fill && + childWidget?.responsiveBehavior === ResponsiveBehavior.Fill && this.state.isMobile ) { - childWidgetData.leftColumn = 0; - childWidgetData.rightColumn = 64; + childWidget.leftColumn = 0; + childWidget.rightColumn = 64; } return WidgetFactory.createWidget(childWidget, this.props.renderMode); diff --git a/app/client/src/widgets/ContainerWidget/widget/index.tsx b/app/client/src/widgets/ContainerWidget/widget/index.tsx index 29f568e5b3bd..20e94b682ce1 100644 --- a/app/client/src/widgets/ContainerWidget/widget/index.tsx +++ b/app/client/src/widgets/ContainerWidget/widget/index.tsx @@ -351,13 +351,13 @@ class ContainerWidget extends BaseWidget< childWidget.shouldScrollContents = false; childWidget.canExtend = this.props.shouldScrollContents; - childWidgetData.parentId = this.props.widgetId; + childWidget.parentId = this.props.widgetId; // Pass layout controls to children - childWidgetData.useAutoLayout = this.state.useAutoLayout; - childWidgetData.direction = this.state.direction; - childWidgetData.positioning = this.props.positioning; - childWidgetData.alignment = this.props.alignment; - childWidgetData.spacing = this.props.spacing; + childWidget.useAutoLayout = this.state.useAutoLayout; + childWidget.direction = this.state.direction; + childWidget.positioning = this.props.positioning; + childWidget.alignment = this.props.alignment; + childWidget.spacing = this.props.spacing; return WidgetFactory.createWidget(childWidget, this.props.renderMode); } diff --git a/app/client/src/widgets/ModalWidget/widget/index.tsx b/app/client/src/widgets/ModalWidget/widget/index.tsx index 7482495adbd3..86e3f0fa7ffe 100644 --- a/app/client/src/widgets/ModalWidget/widget/index.tsx +++ b/app/client/src/widgets/ModalWidget/widget/index.tsx @@ -236,9 +236,9 @@ export class ModalWidget extends BaseWidget { childData.rightColumn = this.getModalWidth(this.props.width) + WIDGET_PADDING * 2; - childWidgetData.positioning = this.props.positioning; - childWidgetData.alignment = this.props.alignment; - childWidgetData.spacing = this.props.spacing; + childData.positioning = this.props.positioning; + childData.alignment = this.props.alignment; + childData.spacing = this.props.spacing; return WidgetFactory.createWidget(childWidgetData, this.props.renderMode); }; diff --git a/app/client/src/widgets/constants.ts b/app/client/src/widgets/constants.ts index ad657dc0855d..6f1bfd0c1f60 100644 --- a/app/client/src/widgets/constants.ts +++ b/app/client/src/widgets/constants.ts @@ -1,4 +1,11 @@ import { IconNames } from "@blueprintjs/icons"; +import { + AlignItems, + JustifyContent, + LayoutDirection, + Positioning, + ResponsiveBehavior, +} from "components/constants"; import { PropertyPaneConfig } from "constants/PropertyControlConstants"; import { WIDGET_STATIC_PROPS } from "constants/WidgetConstants"; import { omit } from "lodash"; @@ -45,13 +52,25 @@ export interface DSLWidget extends WidgetProps { children?: DSLWidget[]; } +interface LayoutProps { + positioning?: Positioning; + useAutoLayout?: boolean; + direction?: LayoutDirection; + justifyContent?: JustifyContent; + alignItems?: AlignItems; + responsiveBehavior?: ResponsiveBehavior; + parentColumnSpace?: number; + parentRowSpace?: number; +} + const staticProps = omit(WIDGET_STATIC_PROPS, "children"); export type CanvasWidgetStructure = Pick< WidgetProps, keyof typeof staticProps -> & { - children?: CanvasWidgetStructure[]; -}; +> & + LayoutProps & { + children?: CanvasWidgetStructure[]; + }; export enum FileDataTypes { Base64 = "Base64", From aa9e26df0efe77a89d5c12c5d3f549813f3c2e29 Mon Sep 17 00:00:00 2001 From: Preet Sidhu Date: Fri, 19 Aug 2022 10:23:16 -0400 Subject: [PATCH 058/708] enable reflow but keep the collision check --- .../src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts | 4 +--- app/client/src/widgets/constants.ts | 2 -- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index 79bf3ad7cb26..37e1061b7420 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -566,9 +566,7 @@ export const useCanvasDragging = ( const canReflowBasedOnMouseSpeed = canReflowForCurrentMouseMove(); const isReflowing = !isEmpty(currentReflowParams.movementMap); const canReflow = - !currentRectanglesToDraw[0].detachFromLayout && - !dropDisabled && - false; + !currentRectanglesToDraw[0].detachFromLayout && !dropDisabled; const currentBlock = currentRectanglesToDraw[0]; const [leftColumn, topRow] = getDropZoneOffsets( snapColumnSpace, diff --git a/app/client/src/widgets/constants.ts b/app/client/src/widgets/constants.ts index 6f1bfd0c1f60..e3408206e86b 100644 --- a/app/client/src/widgets/constants.ts +++ b/app/client/src/widgets/constants.ts @@ -59,8 +59,6 @@ interface LayoutProps { justifyContent?: JustifyContent; alignItems?: AlignItems; responsiveBehavior?: ResponsiveBehavior; - parentColumnSpace?: number; - parentRowSpace?: number; } const staticProps = omit(WIDGET_STATIC_PROPS, "children"); From b76f7d607a7a7d3c0919bddb8275bb2d6c31d1eb Mon Sep 17 00:00:00 2001 From: Preet Sidhu Date: Fri, 19 Aug 2022 10:39:51 -0400 Subject: [PATCH 059/708] removed flex wrap and disabled sibling translation --- .../src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts | 2 +- app/client/src/widgets/ContainerWidget/component/index.tsx | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index 37e1061b7420..5e2d391f44aa 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -766,7 +766,7 @@ export const useCanvasDragging = ( dropPositionRef.current.style.width = pos.width + "px"; dropPositionRef.current.style.height = pos.height + "px"; } - translateSiblings(pos); + // translateSiblings(pos); }; const getHighlightPosition = ( e: any, diff --git a/app/client/src/widgets/ContainerWidget/component/index.tsx b/app/client/src/widgets/ContainerWidget/component/index.tsx index ed88ddeece65..0fa2cfda0a98 100644 --- a/app/client/src/widgets/ContainerWidget/component/index.tsx +++ b/app/client/src/widgets/ContainerWidget/component/index.tsx @@ -78,10 +78,12 @@ export const FlexContainer = styled.div<{ flex-direction: ${({ flexDirection }) => flexDirection || "row"}; justify-content: ${({ justifyContent }) => justifyContent || "flex-start"}; align-items: ${({ alignItems }) => alignItems || "flex-start"}; - flex-wrap: wrap; + flex-wrap: nowrap; width: 100%; height: ${({ stretchHeight }) => (stretchHeight ? "100%" : "auto")}; + + overflow: hidden; `; function ContainerComponentWrapper(props: ContainerComponentProps) { From d0572f93d6d43c414bbc890bd6774d92d9a5c106 Mon Sep 17 00:00:00 2001 From: Preet Sidhu Date: Fri, 19 Aug 2022 13:03:42 -0400 Subject: [PATCH 060/708] Add overflow property; Auto width for flex --- app/client/src/components/AutoLayoutWrapper.tsx | 13 +++++++++++-- app/client/src/components/constants.ts | 7 +++++++ .../designSystems/appsmith/PositionedContainer.tsx | 5 +++-- app/client/src/resizable/resizenreflow/index.tsx | 2 +- app/client/src/utils/autoLayoutContext.ts | 3 ++- app/client/src/widgets/BaseWidget.tsx | 1 + .../src/widgets/ContainerWidget/component/index.tsx | 11 +++++++++-- .../src/widgets/ContainerWidget/widget/index.tsx | 3 +++ 8 files changed, 37 insertions(+), 8 deletions(-) diff --git a/app/client/src/components/AutoLayoutWrapper.tsx b/app/client/src/components/AutoLayoutWrapper.tsx index e1c5f4c238c0..3b2b70c5a14f 100644 --- a/app/client/src/components/AutoLayoutWrapper.tsx +++ b/app/client/src/components/AutoLayoutWrapper.tsx @@ -2,7 +2,7 @@ import { WidgetType } from "constants/WidgetConstants"; import styled from "styled-components"; import React, { ReactNode, useCallback } from "react"; import { useClickToSelectWidget } from "utils/hooks/useClickToSelectWidget"; -import { AlignItems, LayoutDirection } from "./constants"; +import { AlignItems, LayoutDirection, ResponsiveBehavior } from "./constants"; import { useSelector } from "react-redux"; import { AppState } from "reducers"; import { checkIsDropTarget } from "./designSystems/appsmith/PositionedContainer"; @@ -17,15 +17,23 @@ export type AutoLayoutProps = { alignItems?: AlignItems; direction?: LayoutDirection; parentId?: string; + responsiveBehavior?: ResponsiveBehavior; }; const AutoLayout = styled("div")<{ alignItems?: AlignItems; direction?: LayoutDirection; useAutoLayout?: boolean; + responsiveBehavior?: ResponsiveBehavior; }>` position: unset; - width: fit-content; + width: auto; + flex: ${({ responsiveBehavior }) => + responsiveBehavior === ResponsiveBehavior.Fill + ? "1 1 auto" + : "0 1 fit-content"}; + align-self: ${({ responsiveBehavior }) => + responsiveBehavior === ResponsiveBehavior.Fill ? "stretch" : "auto"}; `; const ZIndexContainer = styled.div<{ @@ -75,6 +83,7 @@ export function AutoLayoutWrapper(props: AutoLayoutProps) { alignItems={props.alignItems} direction={props.direction} onClickCapture={onClickFn} + responsiveBehavior={props.responsiveBehavior} useAutoLayout={props.useAutoLayout} > ); } From 82f3b8921d0364910e4375a93190db353482d59d Mon Sep 17 00:00:00 2001 From: Preet Sidhu Date: Wed, 31 Aug 2022 12:45:31 -0400 Subject: [PATCH 085/708] refactor canvas dragging hook --- .../hooks/useAutoLayoutHighlights.ts | 393 ++++++++++++++++++ .../CanvasArenas/hooks/useCanvasDragging.ts | 373 +---------------- 2 files changed, 412 insertions(+), 354 deletions(-) create mode 100644 app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts new file mode 100644 index 000000000000..e8c822b2e5b5 --- /dev/null +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -0,0 +1,393 @@ +import { getWidgets } from "sagas/selectors"; +import { useSelector } from "store"; + +import { LayoutDirection, ResponsiveBehavior } from "components/constants"; +import { isArray } from "lodash"; +import { WidgetDraggingBlock } from "./useBlocksToBeDraggedOnCanvas"; + +interface XYCord { + x: number; + y: number; +} + +export interface Highlight { + x: number; + y: number; + height: number; + width: number; +} + +export interface AutoLayoutHighlightProps { + blocksToDraw: WidgetDraggingBlock[]; + direction?: LayoutDirection; + dropPositionRef: React.RefObject; + isCurrentDraggedCanvas: boolean; + isDragging: boolean; + useAutoLayout?: boolean; + widgetId: string; + widgetName?: string; +} + +const BASE_OFFSET_SIZE = 100; +const OFFSET_WIDTH = 4; + +export const useAutoLayoutHighlights = ({ + blocksToDraw, + direction, + dropPositionRef, + isCurrentDraggedCanvas, + isDragging, + useAutoLayout, + widgetId, + widgetName, +}: AutoLayoutHighlightProps) => { + const allWidgets = useSelector(getWidgets); + const isVertical = direction === LayoutDirection.Vertical; + + let offsets: Highlight[] = []; + let dragBlocksSize = 0; + const siblings: DOMRect[] = []; + const siblingElements: any[] = []; + let lastTranslatedIndex: number; + let containerDimensions: { + top: number; + left: number; + width: number; + height: number; + }; + + /** + * START AUTO LAYOUT OFFSET CALCULATION + */ + + // Determines whether nested wrappers can be introduced in the current canvas + const enableNestedWrappers = (): boolean => { + const canvas = allWidgets[widgetId]; + // If the canvas is not a wrapper, then return false. + if (!canvas?.isWrapper) return false; + const children = canvas.children || []; + // TODO: what to do when there are no children? + if (!children.length) return true; + // If the canvas has a fill child, then return false. + return !( + children.filter( + (child) => + allWidgets[child]?.responsiveBehavior === ResponsiveBehavior.Fill, + ).length > 0 + ); + }; + + // Create and add an initial offset for an empty canvas + const initializeOffsets = (): void => { + offsets = []; + let mOffset: Highlight; + if (isVertical) { + mOffset = { + x: 0, + y: 8, + width: containerDimensions?.width || BASE_OFFSET_SIZE, + height: OFFSET_WIDTH, + }; + } else { + mOffset = { + x: 8, + y: 0, + width: OFFSET_WIDTH, + height: containerDimensions?.height || BASE_OFFSET_SIZE, + }; + } + offsets.push(mOffset); + }; + // Get DOM element for a given widgetId + const getDomElement = (widgetId: string): any => + document.querySelector(`.auto-layout-child-${widgetId}`); + + const cleanUpTempStyles = () => { + // reset display of all dragged blocks + const els = document.querySelectorAll(`.auto-layout-parent-${widgetId}`); + if (els && els.length) { + els.forEach((el) => { + (el as any).classList.remove("auto-temp-no-display"); + (el as any).style.transform = null; + }); + } + + // reset state + dragBlocksSize = 0; + lastTranslatedIndex = -10; + + // Hide the highlight + if (dropPositionRef && dropPositionRef.current) { + dropPositionRef.current.style.opacity = "0"; + dropPositionRef.current.style.display = "none"; + } + }; + + // Fetcha and update the dimensions of the containing canvas. + const updateContainerDimensions = (): boolean => { + const container = document.querySelector(`.appsmith_widget_${widgetId}`); + const containerRect: + | DOMRect + | undefined = container?.getBoundingClientRect(); + if (!container || !containerRect) return false; + // console.log(`#### container rect: ${JSON.stringify(containerRect)}`); + containerDimensions = { + top: containerRect.top || 0, + left: containerRect.left || 0, + width: containerRect.width, + height: containerRect.height, + }; + return true; + }; + + // Get a list of widgetIds that are being dragged. + const getDraggedBlocks = (): string[] => { + const blocks = blocksToDraw.map((block) => block.widgetId); + // console.log(`#### blocksToDraw: ${JSON.stringify(blocksToDraw)}`); + // console.log(`#### blocks: ${JSON.stringify(blocks)}`); + return blocks; + }; + + // Get the total 1D size of the drag block. + const calculateDragBlockSize = (): void => { + blocksToDraw?.forEach((each) => { + dragBlocksSize += isVertical ? each.height : each.width; + }); + }; + + const getOffset = ( + rect: DOMRect, + flexOffsetTop: number, + isFinal?: boolean, + ): Highlight => { + let mOffset: Highlight; + // Remove the offsets of the canvas and the flex container. + const valueToSubtract = { + x: containerDimensions?.left, + y: containerDimensions?.top + flexOffsetTop, + }; + // For the final offset, include the size of the last sibling and a margin. + const valueToAdd = isFinal + ? { + x: rect.width + 8, + y: isVertical ? rect.height + 8 : 8, + } + : { x: 0, y: 0 }; + + if (isVertical) { + mOffset = { + x: 0, + y: rect.top - valueToSubtract.y + valueToAdd.y, + width: containerDimensions.width, + height: OFFSET_WIDTH, + }; + } else { + mOffset = { + x: rect.left - valueToSubtract.x + valueToAdd.x, + y: rect.top - valueToSubtract.y + valueToAdd.y, + height: rect.height, + width: OFFSET_WIDTH, + }; + } + // console.log(`#### offset: ${JSON.stringify(mOffset)}`); + return mOffset; + }; + + // Get the nearest ancestor that is a wrapper, including itself. + const getNearestWrapperAncestor = (widgetId: string): string => { + const widget = allWidgets[widgetId]; + if (widget?.isWrapper) return widgetId; + const parentId = widget?.parentId; + if (!parentId) return ""; + return getNearestWrapperAncestor(parentId); + }; + + const isWrapperEmpty = ( + widgetId: string, + draggedBlocks: string[], + ): boolean => { + const widget = allWidgets[widgetId]; + if (!widget) return true; + const rest = + widget?.children?.filter((child) => draggedBlocks?.indexOf(child) == -1) + .length || 0; + return rest === 0; + }; + + const hideDraggedItems = (arr: string[]) => { + arr?.forEach((each) => { + let el; + const widgetId = getNearestWrapperAncestor(each); + if (isWrapperEmpty(widgetId, arr)) { + el = getDomElement(widgetId); + } else el = getDomElement(each); + el?.classList?.add("auto-temp-no-display"); + }); + }; + + const calculateHighlightOffsets = (): Highlight[] => { + cleanUpTempStyles(); + // console.log( + // `#### ${widgetName} - ${isDragging} - ${isCurrentDraggedCanvas}`, + // ); + if (useAutoLayout && isDragging && isCurrentDraggedCanvas) { + // console.log("#### START calculate highlight offsets"); + // console.log(`#### canvas id: ${widgetId} : ${widgetName}`); + // calculate total drag size to translate siblings by + calculateDragBlockSize(); + const blocks = getDraggedBlocks(); + + if (!blocks || !blocks.length) return []; + + /** + * update dimensions of the current canvas + * and break out of the function if returned value is false. + * That implies the container is null. + */ + if (!updateContainerDimensions()) return []; + + // Temporarily hide dragged children to discount them from offset calculation + hideDraggedItems(blocks); + // Get all children of current dragging canvas + const canvas = allWidgets[widgetId]; + const canvasChildren = canvas.children || []; + const offsetChildren = canvasChildren.filter((each) => { + if (canvas.isWrapper) return blocks.indexOf(each) === -1; + const children = allWidgets[each].children?.filter( + (item) => blocks.indexOf(item) === -1, + ); + return isArray(children) && children.length > 0; + }); + + // console.log(`#### canvas children: ${JSON.stringify(canvasChildren)}`); + // console.log(`#### offset children: ${JSON.stringify(offsetChildren)}`); + const flex = document.querySelector(`.flex-container-${widgetId}`); + const flexOffsetTop = (flex as any)?.offsetTop || 0; + // console.log( + // `#### flex container offset top: ${(flex as any)?.offsetTop}`, + // ); + if (offsetChildren && offsetChildren.length) { + // Get widget ids of all widgets being dragged + offsetChildren.forEach((each) => { + const el = getDomElement(each); + if (!el) return; + + // console.log(`#### child: ${el.className}`); + // console.log(`#### offset parent: ${el.offsetParent.className}`); + const rect: DOMRect = el.getBoundingClientRect(); + // console.log(`#### bounding rect: ${JSON.stringify(rect)}`); + // Add a new offset using the current element's dimensions and position + offsets.push(getOffset(rect, flexOffsetTop, false)); + // siblings[each] = mOffset; + siblings.push(rect); + siblingElements.push(el); + }); + /** + * If the dragged element has siblings, + * then add another offset at the end of the last sibling + * to demarcate the final drop position. + */ + if (siblings.length) { + offsets.push( + getOffset(siblings[siblings.length - 1], flexOffsetTop, true), + ); + } + offsets = [...new Set(offsets)]; + } + if (!offsets || !offsets.length) initializeOffsets(); + // console.log(`#### offsets: ${JSON.stringify(offsets)}`); + // console.log(`#### END calculate highlight offsets`); + } + return offsets; + }; + + /** + * END AUTO LAYOUT OFFSET CALCULATION + */ + + const translateSiblings = (position: Highlight): void => { + let dropIndex = 0; + if (position) + dropIndex = offsets + ?.map((each) => `${each.x},${each.y}`) + .indexOf(`${position.x},${position.y}`); + if (dropIndex === lastTranslatedIndex) return; + + lastTranslatedIndex = dropIndex; + // console.log(`#### lastTranslatedIndex: ${lastTranslatedIndex}`); + return; + // Get all siblings after the highlighted drop position + const arr = [...siblingElements]; + + // translate each element in the appropriate direction + const x = !isVertical ? dragBlocksSize : 0; + const y = isVertical ? dragBlocksSize : 0; + arr.forEach((each, index) => { + if (index < dropIndex) { + each.style.transform = null; + } else { + each.style.transform = `translate(${x}px, ${y}px)`; + each.style.transitionDuration = "0.2s"; + } + }); + }; + + const highlightDropPosition = (e: any) => { + if (!useAutoLayout) return; + const pos: Highlight | undefined = getHighlightPosition(e); + // console.log(`#### Highlight position: ${JSON.stringify(pos)}`); + if (!pos) return; + if (dropPositionRef && dropPositionRef.current) { + dropPositionRef.current.style.opacity = "1"; + dropPositionRef.current.style.top = (pos.y > 6 ? pos.y - 6 : 0) + "px"; + dropPositionRef.current.style.left = + (pos.x > 6 + ? Math.min( + pos.x - 6, + containerDimensions.left + containerDimensions.width - 6, + ) + : 0) + "px"; + dropPositionRef.current.style.width = pos.width + "px"; + dropPositionRef.current.style.height = pos.height + "px"; + dropPositionRef.current.style.display = "block"; + } + translateSiblings(pos); + }; + + const getHighlightPosition = (e: any, val?: XYCord): Highlight => { + let base: Highlight[] = []; + if (!offsets || !offsets.length) initializeOffsets(); + base = offsets; + + const pos: XYCord = { + x: e?.offsetX || val?.x, + y: e?.offsetY || val?.y, + }; + + const arr = [...base].sort((a, b) => { + return calculateDistance(a, pos) - calculateDistance(b, pos); + }); + return arr[0]; + }; + + const calculateDistance = (a: Highlight, b: XYCord): number => { + const x: number = a.x + a.width / 2 - b.x; + const y: number = a.y + a.height / 2 - b.y; + return Math.abs(Math.sqrt(x * x + y * y)); + }; + + const getDropPosition = (val: XYCord): number | undefined => { + if (!isNaN(lastTranslatedIndex) && lastTranslatedIndex >= 0) + return lastTranslatedIndex; + const pos = getHighlightPosition(null, val); + if (!pos) return; + return offsets.indexOf(pos); + }; + + return { + calculateHighlightOffsets, + cleanUpTempStyles, + getDropPosition, + highlightDropPosition, + }; +}; diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index e3e993f8d2b0..d881f4749f4e 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -3,7 +3,7 @@ import { CONTAINER_GRID_PADDING, GridDefaults, } from "constants/WidgetConstants"; -import { debounce, isArray, isEmpty, isNaN, throttle } from "lodash"; +import { debounce, isEmpty, throttle } from "lodash"; import { CanvasDraggingArenaProps } from "pages/common/CanvasArenas/CanvasDraggingArena"; import React, { useEffect, useRef } from "react"; import { useSelector } from "store"; @@ -29,32 +29,16 @@ import { } from "./useBlocksToBeDraggedOnCanvas"; import { useCanvasDragToScroll } from "./useCanvasDragToScroll"; import ContainerJumpMetrics from "./ContainerJumpMetric"; -import { LayoutDirection, ResponsiveBehavior } from "components/constants"; -import { getWidgets } from "sagas/selectors"; +import { useAutoLayoutHighlights } from "./useAutoLayoutHighlights"; export interface XYCord { x: number; y: number; } -interface HighlightDimension { - x: number; - y: number; - height: number; - width: number; -} - const CONTAINER_JUMP_ACC_THRESHOLD = 8000; const CONTAINER_JUMP_SPEED_THRESHOLD = 800; -const BASE_OFFSET_SIZE = 100; -let lastTranslatedIndex: number; -let containerDimensions: { - top: number; - left: number; - width: number; - height: number; -}; //Since useCanvasDragging's Instance changes during container jump, metrics is stored outside const containerJumpThresholdMetrics = new ContainerJumpMetrics<{ speed?: number; @@ -125,267 +109,30 @@ export const useCanvasDragging = ( const reflow = useRef(); reflow.current = useReflow(draggingSpaces, widgetId || "", gridProps); - const allWidgets = useSelector(getWidgets); - - /** - * START AUTO LAYOUT OFFSET CALCULATION - */ - - let dragBlocksSize = 0; - let offsets: HighlightDimension[] = []; - const siblings: DOMRect[] = []; - const siblingElements: any[] = []; - const isVertical = direction === LayoutDirection.Vertical; - - // Determines whether nested wrappers can be introduced in the current canvas - const enableNestedWrappers = (): boolean => { - const canvas = allWidgets[widgetId]; - // If the canvas is not a wrapper, then return false. - if (!canvas?.isWrapper) return false; - const children = canvas.children || []; - // TODO: what to do when there are no children? - if (!children.length) return true; - // If the canvas has a fill child, then return false. - return !( - children.filter( - (child) => - allWidgets[child]?.responsiveBehavior === ResponsiveBehavior.Fill, - ).length > 0 - ); - }; - - const initializeOffsets = (): void => { - offsets = []; - let mOffset: HighlightDimension; - if (isVertical) { - mOffset = { - x: 0, - y: 8, - width: containerDimensions?.width || BASE_OFFSET_SIZE, - height: 4, - }; - } else { - mOffset = { - x: 8, - y: 0, - width: 4, - height: containerDimensions?.height || BASE_OFFSET_SIZE, - }; - } - offsets.push(mOffset); - }; - const getChildNode = (widgetId: string): any => - document.querySelector(`.auto-layout-child-${widgetId}`); - const hideDraggedChildren = (arr: string[]): void => { - arr.forEach((each) => { - const el = getChildNode(each); - if (!el) return; - el?.classList?.add("auto-temp-no-display"); - }); - }; - - const cleanUpTempStyles = () => { - // reset display of all dragged blocks - const els = document.querySelectorAll(`.auto-layout-parent-${widgetId}`); - if (els && els.length) { - els.forEach((el) => { - (el as any).classList.remove("auto-temp-no-display"); - (el as any).style.transform = null; - }); - } - - // reset state - dragBlocksSize = 0; - lastTranslatedIndex = -10; - - if (dropPositionRef && dropPositionRef.current) { - dropPositionRef.current.style.opacity = "0"; - dropPositionRef.current.style.display = "none"; - } - }; - - const updateContainerDimensions = (): boolean => { - const container = document.querySelector(`.appsmith_widget_${widgetId}`); - const containerRect: - | DOMRect - | undefined = container?.getBoundingClientRect(); - if (!container || !containerRect) return false; - // console.log(`#### container rect: ${JSON.stringify(containerRect)}`); - containerDimensions = { - top: containerRect.top || 0, - left: containerRect.left || 0, - width: containerRect.width, - height: containerRect.height, - }; - return true; - }; + // eslint-disable-next-line prefer-const - const getDraggedBlocks = (): string[] => { - const blocks = blocksToDraw.map((block) => block.widgetId); - // console.log(`#### blocksToDraw: ${JSON.stringify(blocksToDraw)}`); - console.log(`#### blocks: ${JSON.stringify(blocks)}`); - return blocks; - }; - - const calculateDragBlockSize = (): void => { - blocksToDraw?.forEach((each) => { - dragBlocksSize += isVertical ? each.height : each.width; - }); - }; - - const getOffset = ( - rect: DOMRect, - flexOffsetTop: number, - isFinal?: boolean, - ): HighlightDimension => { - let mOffset: HighlightDimension; - // Remove the offsets of the canvas and the flex container. - const valueToSubtract = { - x: containerDimensions?.left, - y: containerDimensions?.top + flexOffsetTop, - }; - // For the final offset, include the size of the last sibling and a margin. - const valueToAdd = isFinal - ? { - x: rect.width + 8, - y: isVertical ? rect.height + 8 : 8, - } - : { x: 0, y: 0 }; - - if (isVertical) { - mOffset = { - x: 0, - y: rect.top - valueToSubtract.y + valueToAdd.y, - width: containerDimensions.width, - height: 4, - }; - } else { - mOffset = { - x: rect.left - valueToSubtract.x + valueToAdd.x, - y: rect.top - valueToSubtract.y + valueToAdd.y, - height: rect.height, - width: 4, - }; - } - // console.log(`#### offset: ${JSON.stringify(mOffset)}`); - return mOffset; - }; - - const getNearestWrapperAncestor = (widgetId: string): string => { - const widget = allWidgets[widgetId]; - if (widget?.isWrapper) return widgetId; - const parentId = widget?.parentId; - if (!parentId) return ""; - return getNearestWrapperAncestor(parentId); - }; - - const isWrapperEmpty = ( - widgetId: string, - draggedBlocks: string[], - ): boolean => { - const widget = allWidgets[widgetId]; - if (!widget) return true; - const rest = - widget?.children?.filter((child) => draggedBlocks?.indexOf(child) == -1) - .length || 0; - return rest === 0; - }; - - const hideDraggedItems = (arr: string[]) => { - arr?.forEach((each) => { - let el; - const widgetId = getNearestWrapperAncestor(each); - if (isWrapperEmpty(widgetId, arr)) { - el = getChildNode(widgetId); - } else el = getChildNode(each); - el?.classList?.add("auto-temp-no-display"); - }); - }; - - const calculateHighlightOffsets = () => { - cleanUpTempStyles(); - // console.log( - // `#### ${widgetName} - ${isDragging} - ${isCurrentDraggedCanvas}`, - // ); - if (useAutoLayout && isDragging && isCurrentDraggedCanvas) { - console.log("#### START calculate highlight offsets"); - console.log(`#### canvas id: ${widgetId} : ${widgetName}`); - // calculate total drag size to translate siblings by - calculateDragBlockSize(); - const blocks = getDraggedBlocks(); - - if (!blocks || !blocks.length) return; - - /** - * update dimensions of the current canvas - * and break out of the function if returned value is false. - * That implies the container is null. - */ - if (!updateContainerDimensions()) return; - - // Temporarily hide dragged children to discount them from offset calculation - hideDraggedItems(blocks); - // Get all children of current dragging canvas - const canvas = allWidgets[widgetId]; - const canvasChildren = canvas.children || []; - const offsetChildren = canvasChildren.filter((each) => { - if (canvas.isWrapper) return blocks.indexOf(each) === -1; - const children = allWidgets[each].children?.filter( - (item) => blocks.indexOf(item) === -1, - ); - return isArray(children) && children.length > 0; - }); + const { + calculateHighlightOffsets, + cleanUpTempStyles, + getDropPosition, + highlightDropPosition, + } = useAutoLayoutHighlights({ + blocksToDraw, + direction, + dropPositionRef, + isCurrentDraggedCanvas, + isDragging, + useAutoLayout, + widgetId, + widgetName, + }); - console.log(`#### canvas children: ${JSON.stringify(canvasChildren)}`); - console.log(`#### offset children: ${JSON.stringify(offsetChildren)}`); - const flex = document.querySelector(`.flex-container-${widgetId}`); - const flexOffsetTop = (flex as any)?.offsetTop || 0; - // console.log( - // `#### flex container offset top: ${(flex as any)?.offsetTop}`, - // ); - if (offsetChildren && offsetChildren.length) { - // Get widget ids of all widgets being dragged - offsetChildren.forEach((each) => { - const el = getChildNode(each); - if (!el) return; - - // console.log(`#### child: ${el.className}`); - // console.log(`#### offset parent: ${el.offsetParent.className}`); - const rect: DOMRect = el.getBoundingClientRect(); - // console.log(`#### bounding rect: ${JSON.stringify(rect)}`); - // Add a new offset using the current element's dimensions and position - offsets.push(getOffset(rect, flexOffsetTop, false)); - // siblings[each] = mOffset; - siblings.push(rect); - siblingElements.push(el); - }); - /** - * If the dragged element has siblings, - * then add another offset at the end of the last sibling - * to demarcate the final drop position. - */ - if (siblings.length) { - offsets.push( - getOffset(siblings[siblings.length - 1], flexOffsetTop, true), - ); - } - offsets = [...new Set(offsets)]; - } - if (!offsets || !offsets.length) initializeOffsets(); - // console.log(`#### offsets: ${JSON.stringify(offsets)}`); - // console.log(`#### END calculate highlight offsets`); - } - }; - calculateHighlightOffsets(); + const offsets = calculateHighlightOffsets(); if (!isDragging || !isCurrentDraggedCanvas) { cleanUpTempStyles(); } - /** - * END AUTO LAYOUT OFFSET CALCULATION - */ - const { setDraggingCanvas, setDraggingNewWidget, @@ -846,88 +593,6 @@ export const useCanvasDragging = ( onFirstMoveOnCanvas(e); } }; - const translateSiblings = (position: HighlightDimension): void => { - let dropIndex = 0; - if (position) - dropIndex = offsets - ?.map((each) => `${each.x},${each.y}`) - .indexOf(`${position.x},${position.y}`); - - if (dropIndex === lastTranslatedIndex) return; - - lastTranslatedIndex = dropIndex; - // console.log(`#### lastTranslatedIndex: ${lastTranslatedIndex}`); - return; - // Get all siblings after the highlighted drop position - const arr = [...siblingElements]; - - // translate each element in the appropriate direction - const x = !isVertical ? dragBlocksSize : 0; - const y = isVertical ? dragBlocksSize : 0; - arr.forEach((each, index) => { - if (index < dropIndex) { - each.style.transform = null; - } else { - each.style.transform = `translate(${x}px, ${y}px)`; - each.style.transitionDuration = "0.2s"; - } - }); - }; - const highlightDropPosition = (e: any) => { - if (!useAutoLayout) return; - const pos: HighlightDimension | undefined = getHighlightPosition(e); - // console.log(`#### Highlight position: ${JSON.stringify(pos)}`); - if (!pos) return; - if (dropPositionRef && dropPositionRef.current) { - dropPositionRef.current.style.opacity = "1"; - dropPositionRef.current.style.top = - (pos.y > 6 ? pos.y - 6 : 0) + "px"; - dropPositionRef.current.style.left = - (pos.x > 6 - ? Math.min( - pos.x - 6, - containerDimensions.left + containerDimensions.width - 6, - ) - : 0) + "px"; - dropPositionRef.current.style.width = pos.width + "px"; - dropPositionRef.current.style.height = pos.height + "px"; - dropPositionRef.current.style.display = "block"; - } - translateSiblings(pos); - }; - const getHighlightPosition = ( - e: any, - val?: XYCord, - ): HighlightDimension => { - let base: HighlightDimension[] = []; - if (!offsets || !offsets.length) initializeOffsets(); - base = offsets; - - const pos: XYCord = { - x: e?.offsetX || val?.x, - y: e?.offsetY || val?.y, - }; - - const arr = [...base].sort((a, b) => { - return calculateDistance(a, pos) - calculateDistance(b, pos); - }); - return arr[0]; - }; - const calculateDistance = ( - a: HighlightDimension, - b: XYCord, - ): number => { - const x: number = a.x + a.width / 2 - b.x; - const y: number = a.y + a.height / 2 - b.y; - return Math.abs(Math.sqrt(x * x + y * y)); - }; - const getDropPosition = (val: XYCord): number | undefined => { - if (!isNaN(lastTranslatedIndex) && lastTranslatedIndex >= 0) - return lastTranslatedIndex; - const pos = getHighlightPosition(null, val); - if (!pos) return; - return offsets.indexOf(pos); - }; const renderNewRows = debounce((delta) => { isUpdatingRows = true; if (slidingArenaRef.current && stickyCanvasRef.current) { From 4eac1c26d03bb81d3802ee635542b2751d37481c Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 31 Aug 2022 13:11:16 -0400 Subject: [PATCH 086/708] update hide block logic Don't hide if dragging canvas is the same as the wrapper. --- .../hooks/useAutoLayoutHighlights.ts | 32 +++++++++++-------- .../CanvasArenas/hooks/useCanvasDragging.ts | 2 +- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index e8c822b2e5b5..55c9919f6f1b 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -19,12 +19,12 @@ export interface Highlight { export interface AutoLayoutHighlightProps { blocksToDraw: WidgetDraggingBlock[]; + canvasId: string; direction?: LayoutDirection; dropPositionRef: React.RefObject; isCurrentDraggedCanvas: boolean; isDragging: boolean; useAutoLayout?: boolean; - widgetId: string; widgetName?: string; } @@ -33,12 +33,12 @@ const OFFSET_WIDTH = 4; export const useAutoLayoutHighlights = ({ blocksToDraw, + canvasId, direction, dropPositionRef, isCurrentDraggedCanvas, isDragging, useAutoLayout, - widgetId, widgetName, }: AutoLayoutHighlightProps) => { const allWidgets = useSelector(getWidgets); @@ -62,7 +62,7 @@ export const useAutoLayoutHighlights = ({ // Determines whether nested wrappers can be introduced in the current canvas const enableNestedWrappers = (): boolean => { - const canvas = allWidgets[widgetId]; + const canvas = allWidgets[canvasId]; // If the canvas is not a wrapper, then return false. if (!canvas?.isWrapper) return false; const children = canvas.children || []; @@ -104,7 +104,7 @@ export const useAutoLayoutHighlights = ({ const cleanUpTempStyles = () => { // reset display of all dragged blocks - const els = document.querySelectorAll(`.auto-layout-parent-${widgetId}`); + const els = document.querySelectorAll(`.auto-layout-parent-${canvasId}`); if (els && els.length) { els.forEach((el) => { (el as any).classList.remove("auto-temp-no-display"); @@ -125,7 +125,7 @@ export const useAutoLayoutHighlights = ({ // Fetcha and update the dimensions of the containing canvas. const updateContainerDimensions = (): boolean => { - const container = document.querySelector(`.appsmith_widget_${widgetId}`); + const container = document.querySelector(`.appsmith_widget_${canvasId}`); const containerRect: | DOMRect | undefined = container?.getBoundingClientRect(); @@ -214,13 +214,19 @@ export const useAutoLayoutHighlights = ({ return rest === 0; }; - const hideDraggedItems = (arr: string[]) => { + const hideDraggedItems = (arr: string[]): void => { arr?.forEach((each) => { - let el; - const widgetId = getNearestWrapperAncestor(each); - if (isWrapperEmpty(widgetId, arr)) { - el = getDomElement(widgetId); - } else el = getDomElement(each); + // Get the parent wrapper + const wrapperId = getNearestWrapperAncestor(each); + if (wrapperId === canvasId) return; + /** + * If the wrapper is not the dragging canvas and is empty, + * then hide it, + * else hide the child element. + */ + const el = isWrapperEmpty(wrapperId, arr) + ? getDomElement(wrapperId) + : getDomElement(each); el?.classList?.add("auto-temp-no-display"); }); }; @@ -249,7 +255,7 @@ export const useAutoLayoutHighlights = ({ // Temporarily hide dragged children to discount them from offset calculation hideDraggedItems(blocks); // Get all children of current dragging canvas - const canvas = allWidgets[widgetId]; + const canvas = allWidgets[canvasId]; const canvasChildren = canvas.children || []; const offsetChildren = canvasChildren.filter((each) => { if (canvas.isWrapper) return blocks.indexOf(each) === -1; @@ -261,7 +267,7 @@ export const useAutoLayoutHighlights = ({ // console.log(`#### canvas children: ${JSON.stringify(canvasChildren)}`); // console.log(`#### offset children: ${JSON.stringify(offsetChildren)}`); - const flex = document.querySelector(`.flex-container-${widgetId}`); + const flex = document.querySelector(`.flex-container-${canvasId}`); const flexOffsetTop = (flex as any)?.offsetTop || 0; // console.log( // `#### flex container offset top: ${(flex as any)?.offsetTop}`, diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index d881f4749f4e..8225e5c7faf8 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -118,12 +118,12 @@ export const useCanvasDragging = ( highlightDropPosition, } = useAutoLayoutHighlights({ blocksToDraw, + canvasId: widgetId, direction, dropPositionRef, isCurrentDraggedCanvas, isDragging, useAutoLayout, - widgetId, widgetName, }); From a4b0833bcd2136288aa2b998b767ac6e61954ebd Mon Sep 17 00:00:00 2001 From: Preet Sidhu Date: Wed, 31 Aug 2022 15:02:29 -0400 Subject: [PATCH 087/708] refactor calculate offset highlights --- .../hooks/useAutoLayoutHighlights.ts | 70 ++++++++++--------- 1 file changed, 38 insertions(+), 32 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index 55c9919f6f1b..931c4acb7365 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -264,42 +264,14 @@ export const useAutoLayoutHighlights = ({ ); return isArray(children) && children.length > 0; }); - // console.log(`#### canvas children: ${JSON.stringify(canvasChildren)}`); // console.log(`#### offset children: ${JSON.stringify(offsetChildren)}`); const flex = document.querySelector(`.flex-container-${canvasId}`); const flexOffsetTop = (flex as any)?.offsetTop || 0; - // console.log( - // `#### flex container offset top: ${(flex as any)?.offsetTop}`, - // ); - if (offsetChildren && offsetChildren.length) { - // Get widget ids of all widgets being dragged - offsetChildren.forEach((each) => { - const el = getDomElement(each); - if (!el) return; - - // console.log(`#### child: ${el.className}`); - // console.log(`#### offset parent: ${el.offsetParent.className}`); - const rect: DOMRect = el.getBoundingClientRect(); - // console.log(`#### bounding rect: ${JSON.stringify(rect)}`); - // Add a new offset using the current element's dimensions and position - offsets.push(getOffset(rect, flexOffsetTop, false)); - // siblings[each] = mOffset; - siblings.push(rect); - siblingElements.push(el); - }); - /** - * If the dragged element has siblings, - * then add another offset at the end of the last sibling - * to demarcate the final drop position. - */ - if (siblings.length) { - offsets.push( - getOffset(siblings[siblings.length - 1], flexOffsetTop, true), - ); - } - offsets = [...new Set(offsets)]; - } + + const temp = evaluateOffsets(offsetChildren, flexOffsetTop); + offsets = [...offsets, ...temp]; + if (!offsets || !offsets.length) initializeOffsets(); // console.log(`#### offsets: ${JSON.stringify(offsets)}`); // console.log(`#### END calculate highlight offsets`); @@ -307,6 +279,40 @@ export const useAutoLayoutHighlights = ({ return offsets; }; + const evaluateOffsets = ( + arr: string[], + flexOffsetTop: number, + ): Highlight[] => { + let res: Highlight[] = []; + if (arr && arr.length) { + // Get widget ids of all widgets being dragged + arr.forEach((each) => { + const el = getDomElement(each); + if (!el) return; + + // console.log(`#### child: ${el.className}`); + // console.log(`#### offset parent: ${el.offsetParent.className}`); + const rect: DOMRect = el.getBoundingClientRect(); + // console.log(`#### bounding rect: ${JSON.stringify(rect)}`); + // Add a new offset using the current element's dimensions and position + res.push(getOffset(rect, flexOffsetTop, false)); + // siblings[each] = mOffset; + siblings.push(rect); + siblingElements.push(el); + }); + /** + * If the dragged element has siblings, + * then add another offset at the end of the last sibling + * to demarcate the final drop position. + */ + if (siblings.length) { + res.push(getOffset(siblings[siblings.length - 1], flexOffsetTop, true)); + } + res = [...new Set(res)]; + } + return res; + }; + /** * END AUTO LAYOUT OFFSET CALCULATION */ From cb3b9c7a3463be4a4b53c94c5dea286196bc55bf Mon Sep 17 00:00:00 2001 From: Preet Sidhu Date: Thu, 1 Sep 2022 09:16:56 -0400 Subject: [PATCH 088/708] working end wrapper --- .../hooks/useAutoLayoutHighlights.ts | 114 ++++++++++++++---- .../hooks/useBlocksToBeDraggedOnCanvas.ts | 21 +++- .../CanvasArenas/hooks/useCanvasDragging.ts | 13 +- .../ContainerWidget/component/index.tsx | 10 +- 4 files changed, 122 insertions(+), 36 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index 931c4acb7365..809755b08eef 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -1,7 +1,11 @@ import { getWidgets } from "sagas/selectors"; import { useSelector } from "store"; -import { LayoutDirection, ResponsiveBehavior } from "components/constants"; +import { + LayoutDirection, + LayoutWrapperType, + ResponsiveBehavior, +} from "components/constants"; import { isArray } from "lodash"; import { WidgetDraggingBlock } from "./useBlocksToBeDraggedOnCanvas"; @@ -15,6 +19,7 @@ export interface Highlight { y: number; height: number; width: number; + wrapperType: LayoutWrapperType; } export interface AutoLayoutHighlightProps { @@ -28,6 +33,11 @@ export interface AutoLayoutHighlightProps { widgetName?: string; } +export interface DropPositionPayload { + index: number; + wrapperType: LayoutWrapperType; +} + const BASE_OFFSET_SIZE = 100; const OFFSET_WIDTH = 4; @@ -42,6 +52,8 @@ export const useAutoLayoutHighlights = ({ widgetName, }: AutoLayoutHighlightProps) => { const allWidgets = useSelector(getWidgets); + const canvas = allWidgets[canvasId]; + const isCanvasWrapper = canvas.isWrapper || false; const isVertical = direction === LayoutDirection.Vertical; let offsets: Highlight[] = []; @@ -51,7 +63,9 @@ export const useAutoLayoutHighlights = ({ let lastTranslatedIndex: number; let containerDimensions: { top: number; + bottom: number; left: number; + right: number; width: number; height: number; }; @@ -62,7 +76,6 @@ export const useAutoLayoutHighlights = ({ // Determines whether nested wrappers can be introduced in the current canvas const enableNestedWrappers = (): boolean => { - const canvas = allWidgets[canvasId]; // If the canvas is not a wrapper, then return false. if (!canvas?.isWrapper) return false; const children = canvas.children || []; @@ -78,25 +91,30 @@ export const useAutoLayoutHighlights = ({ }; // Create and add an initial offset for an empty canvas - const initializeOffsets = (): void => { - offsets = []; + const getInitialOffset = ( + isWrapper: boolean, + wrapperType: LayoutWrapperType = LayoutWrapperType.Start, + ): Highlight => { let mOffset: Highlight; + const reverse = isWrapper && wrapperType === LayoutWrapperType.End; if (isVertical) { mOffset = { x: 0, - y: 8, + y: reverse ? containerDimensions.bottom - 8 : 8, width: containerDimensions?.width || BASE_OFFSET_SIZE, height: OFFSET_WIDTH, + wrapperType, }; } else { mOffset = { - x: 8, + x: reverse ? containerDimensions.right - 8 : 8, y: 0, width: OFFSET_WIDTH, height: containerDimensions?.height || BASE_OFFSET_SIZE, + wrapperType, }; } - offsets.push(mOffset); + return mOffset; }; // Get DOM element for a given widgetId const getDomElement = (widgetId: string): any => @@ -123,19 +141,20 @@ export const useAutoLayoutHighlights = ({ } }; - // Fetcha and update the dimensions of the containing canvas. + // Fetch and update the dimensions of the containing canvas. const updateContainerDimensions = (): boolean => { const container = document.querySelector(`.appsmith_widget_${canvasId}`); const containerRect: | DOMRect | undefined = container?.getBoundingClientRect(); if (!container || !containerRect) return false; - // console.log(`#### container rect: ${JSON.stringify(containerRect)}`); containerDimensions = { - top: containerRect.top || 0, - left: containerRect.left || 0, - width: containerRect.width, - height: containerRect.height, + top: containerRect?.top || 0, + bottom: containerRect?.bottom - containerDimensions?.top || 0, + left: containerRect?.left || 0, + right: containerRect?.right - containerRect?.left || 0, + width: containerRect?.width, + height: containerRect?.height, }; return true; }; @@ -158,6 +177,7 @@ export const useAutoLayoutHighlights = ({ const getOffset = ( rect: DOMRect, flexOffsetTop: number, + wrapperType: LayoutWrapperType, isFinal?: boolean, ): Highlight => { let mOffset: Highlight; @@ -180,6 +200,7 @@ export const useAutoLayoutHighlights = ({ y: rect.top - valueToSubtract.y + valueToAdd.y, width: containerDimensions.width, height: OFFSET_WIDTH, + wrapperType, }; } else { mOffset = { @@ -187,6 +208,7 @@ export const useAutoLayoutHighlights = ({ y: rect.top - valueToSubtract.y + valueToAdd.y, height: rect.height, width: OFFSET_WIDTH, + wrapperType, }; } // console.log(`#### offset: ${JSON.stringify(mOffset)}`); @@ -255,7 +277,6 @@ export const useAutoLayoutHighlights = ({ // Temporarily hide dragged children to discount them from offset calculation hideDraggedItems(blocks); // Get all children of current dragging canvas - const canvas = allWidgets[canvasId]; const canvasChildren = canvas.children || []; const offsetChildren = canvasChildren.filter((each) => { if (canvas.isWrapper) return blocks.indexOf(each) === -1; @@ -268,11 +289,39 @@ export const useAutoLayoutHighlights = ({ // console.log(`#### offset children: ${JSON.stringify(offsetChildren)}`); const flex = document.querySelector(`.flex-container-${canvasId}`); const flexOffsetTop = (flex as any)?.offsetTop || 0; - - const temp = evaluateOffsets(offsetChildren, flexOffsetTop); + let temp: Highlight[] = []; + if (canvas.isWrapper) { + const start: string[] = [], + end: string[] = []; + offsetChildren.forEach((each) => { + if (allWidgets[each]?.wrapperType === LayoutWrapperType.Start) + start.push(each); + else end.push(each); + }); + const arr1 = evaluateOffsets( + start, + flexOffsetTop, + true, + LayoutWrapperType.Start, + ); + const arr2 = evaluateOffsets( + end, + flexOffsetTop, + true, + LayoutWrapperType.End, + ); + temp = [...arr1, ...arr2]; + } else + temp = evaluateOffsets( + offsetChildren, + flexOffsetTop, + false, + LayoutWrapperType.Start, + ); offsets = [...offsets, ...temp]; - if (!offsets || !offsets.length) initializeOffsets(); + if (!offsets || !offsets.length) + offsets = [getInitialOffset(isCanvasWrapper)]; // console.log(`#### offsets: ${JSON.stringify(offsets)}`); // console.log(`#### END calculate highlight offsets`); } @@ -282,6 +331,8 @@ export const useAutoLayoutHighlights = ({ const evaluateOffsets = ( arr: string[], flexOffsetTop: number, + isWrapper: boolean, + wrapperType: LayoutWrapperType, ): Highlight[] => { let res: Highlight[] = []; if (arr && arr.length) { @@ -295,8 +346,7 @@ export const useAutoLayoutHighlights = ({ const rect: DOMRect = el.getBoundingClientRect(); // console.log(`#### bounding rect: ${JSON.stringify(rect)}`); // Add a new offset using the current element's dimensions and position - res.push(getOffset(rect, flexOffsetTop, false)); - // siblings[each] = mOffset; + res.push(getOffset(rect, flexOffsetTop, wrapperType, false)); siblings.push(rect); siblingElements.push(el); }); @@ -306,10 +356,17 @@ export const useAutoLayoutHighlights = ({ * to demarcate the final drop position. */ if (siblings.length) { - res.push(getOffset(siblings[siblings.length - 1], flexOffsetTop, true)); + res.push( + getOffset( + siblings[siblings.length - 1], + flexOffsetTop, + wrapperType, + true, + ), + ); } res = [...new Set(res)]; - } + } else res = [getInitialOffset(isWrapper, wrapperType)]; return res; }; @@ -368,7 +425,8 @@ export const useAutoLayoutHighlights = ({ const getHighlightPosition = (e: any, val?: XYCord): Highlight => { let base: Highlight[] = []; - if (!offsets || !offsets.length) initializeOffsets(); + if (!offsets || !offsets.length) + offsets = [getInitialOffset(isCanvasWrapper)]; base = offsets; const pos: XYCord = { @@ -388,12 +446,18 @@ export const useAutoLayoutHighlights = ({ return Math.abs(Math.sqrt(x * x + y * y)); }; - const getDropPosition = (val: XYCord): number | undefined => { + const getDropPosition = (val: XYCord): DropPositionPayload | undefined => { if (!isNaN(lastTranslatedIndex) && lastTranslatedIndex >= 0) - return lastTranslatedIndex; + return { + index: lastTranslatedIndex, + wrapperType: offsets[lastTranslatedIndex]?.wrapperType, + }; const pos = getHighlightPosition(null, val); if (!pos) return; - return offsets.indexOf(pos); + return { + index: offsets.indexOf(pos), + wrapperType: offsets[lastTranslatedIndex]?.wrapperType, + }; }; return { diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts b/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts index 26c8a6433750..2a8e215c987c 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts @@ -30,7 +30,11 @@ import { DragDetails } from "reducers/uiReducers/dragResizeReducer"; import { getIsReflowing } from "selectors/widgetReflowSelectors"; import { XYCord } from "./useCanvasDragging"; import ContainerJumpMetrics from "./ContainerJumpMetric"; -import { AlignItems, LayoutDirection } from "components/constants"; +import { + AlignItems, + LayoutDirection, + LayoutWrapperType, +} from "components/constants"; export interface WidgetDraggingUpdateParams extends WidgetDraggingBlock { updateWidgetParams: WidgetOperationParams; @@ -297,13 +301,15 @@ export const useBlocksToBeDraggedOnCanvas = ({ const updateChildrenPositions = ( index: number, drawingBlocks: WidgetDraggingBlock[], + wrapperType: LayoutWrapperType, ): void => { // console.log("**********"); // console.log(index); // console.log(allWidgets); // console.log(selectedWidgets); // console.log(widgetId); - if (isNewWidget) addNewWidgetToAutoLayout(index, drawingBlocks); + if (isNewWidget) + addNewWidgetToAutoLayout(index, drawingBlocks, wrapperType); dispatch({ type: ReduxActionTypes.AUTOLAYOUT_REORDER_WIDGETS, payload: { @@ -316,6 +322,7 @@ export const useBlocksToBeDraggedOnCanvas = ({ const addNewWidgetToAutoLayout = ( index: number, drawingBlocks: WidgetDraggingBlock[], + wrapperType: LayoutWrapperType, ) => { logContainerJumpOnDrop(); const blocksToUpdate = drawingBlocks.map((each) => { @@ -338,11 +345,19 @@ export const useBlocksToBeDraggedOnCanvas = ({ updateWidgetParams, }; }); + // Add wrapperType to props of the new widget + const widgetPayload = { + ...blocksToUpdate[0]?.updateWidgetParams?.payload, + props: { + ...blocksToUpdate[0]?.updateWidgetParams?.payload?.props, + wrapperType, + }, + }; dispatch({ type: ReduxActionTypes.AUTOLAYOUT_ADD_NEW_WIDGETS, payload: { index, - newWidget: blocksToUpdate[0].updateWidgetParams.payload, + newWidget: widgetPayload, parentId: widgetId, }, }); diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index 8225e5c7faf8..9e5c9e6ab330 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -29,7 +29,10 @@ import { } from "./useBlocksToBeDraggedOnCanvas"; import { useCanvasDragToScroll } from "./useCanvasDragToScroll"; import ContainerJumpMetrics from "./ContainerJumpMetric"; -import { useAutoLayoutHighlights } from "./useAutoLayoutHighlights"; +import { + DropPositionPayload, + useAutoLayoutHighlights, +} from "./useAutoLayoutHighlights"; export interface XYCord { x: number; @@ -319,13 +322,17 @@ export const useCanvasDragging = ( } return each; }); - const pos = getDropPosition({ + const pos: DropPositionPayload | undefined = getDropPosition({ x: currentRectanglesToDraw[0].top, y: currentRectanglesToDraw[0].left, }); if (pos !== undefined && useAutoLayout) { // cleanUpTempStyles(); - updateChildrenPositions(pos, currentRectanglesToDraw); + updateChildrenPositions( + pos.index, + currentRectanglesToDraw, + pos.wrapperType, + ); } else onDrop(currentRectanglesToDraw, reflowedPositionsUpdatesWidgets); } diff --git a/app/client/src/widgets/ContainerWidget/component/index.tsx b/app/client/src/widgets/ContainerWidget/component/index.tsx index 7e2b2dcb52e4..6fc1682eb70c 100644 --- a/app/client/src/widgets/ContainerWidget/component/index.tsx +++ b/app/client/src/widgets/ContainerWidget/component/index.tsx @@ -32,6 +32,8 @@ import { getLayoutProperties, LayoutProperties, } from "utils/layoutPropertiesUtils"; +import { useSelector } from "store"; +import { getWidgets } from "sagas/selectors"; const scrollContents = css` overflow-y: auto; @@ -176,6 +178,7 @@ export function FlexBox(props: FlexBoxProps) { } export function LayoutWrapper(props: FlexBoxProps): JSX.Element { + const allWidgets = useSelector(getWidgets); const [startChildren, setStartChildren] = useState([]); const [endChildren, setEndChildren] = useState([]); @@ -184,11 +187,8 @@ export function LayoutWrapper(props: FlexBoxProps): JSX.Element { end: any = []; if (isArray(props.children) && props.children?.length > 0) { for (const child of props.children) { - if ( - child && - (child as any).props && - (child as any).props.wrapperType === LayoutWrapperType.End - ) + const widget = allWidgets[(child as any).props?.widgetId]; + if (widget && widget.wrapperType === LayoutWrapperType.End) end.push(child); else start.push(child); } From dbe87b56c86767043a8724d2cdb7a32f29e99ee0 Mon Sep 17 00:00:00 2001 From: Preet Sidhu Date: Thu, 1 Sep 2022 09:23:45 -0400 Subject: [PATCH 089/708] fix redundant reorder call --- .../hooks/useBlocksToBeDraggedOnCanvas.ts | 22 ++++++++----------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts b/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts index 2a8e215c987c..65b807b33e04 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts @@ -303,21 +303,17 @@ export const useBlocksToBeDraggedOnCanvas = ({ drawingBlocks: WidgetDraggingBlock[], wrapperType: LayoutWrapperType, ): void => { - // console.log("**********"); - // console.log(index); - // console.log(allWidgets); - // console.log(selectedWidgets); - // console.log(widgetId); if (isNewWidget) addNewWidgetToAutoLayout(index, drawingBlocks, wrapperType); - dispatch({ - type: ReduxActionTypes.AUTOLAYOUT_REORDER_WIDGETS, - payload: { - index, - movedWidgets: selectedWidgets, - parentId: widgetId, - }, - }); + else + dispatch({ + type: ReduxActionTypes.AUTOLAYOUT_REORDER_WIDGETS, + payload: { + index, + movedWidgets: selectedWidgets, + parentId: widgetId, + }, + }); }; const addNewWidgetToAutoLayout = ( index: number, From 22e4bc58796ca65f4e9742efaef5eb6e84f28438 Mon Sep 17 00:00:00 2001 From: Preet Sidhu Date: Thu, 1 Sep 2022 11:42:52 -0400 Subject: [PATCH 090/708] add wrapperType on reorder --- .../hooks/useBlocksToBeDraggedOnCanvas.ts | 2 + .../sagas/CanvasSagas/DraggingCanvasSagas.ts | 39 +++++++++++++++---- 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts b/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts index 65b807b33e04..62412d9c4d4a 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts @@ -312,6 +312,7 @@ export const useBlocksToBeDraggedOnCanvas = ({ index, movedWidgets: selectedWidgets, parentId: widgetId, + wrapperType, }, }); }; @@ -355,6 +356,7 @@ export const useBlocksToBeDraggedOnCanvas = ({ index, newWidget: widgetPayload, parentId: widgetId, + wrapperType, }, }); }; diff --git a/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts b/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts index 6252c8af22be..b7b72ca215ad 100644 --- a/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts +++ b/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts @@ -35,7 +35,12 @@ import { import { MainCanvasReduxState } from "reducers/uiReducers/mainCanvasReducer"; import { CANVAS_DEFAULT_MIN_HEIGHT_PX } from "constants/AppConstants"; import { generateReactKey } from "utils/generators"; -import { Alignment, ButtonBoxShadowTypes, Spacing } from "components/constants"; +import { + Alignment, + ButtonBoxShadowTypes, + LayoutWrapperType, + Spacing, +} from "components/constants"; import { getLayoutWrapperName, purgeEmptyWrappers, @@ -350,11 +355,12 @@ function* autolayoutReorderSaga( movedWidgets: string[]; index: number; parentId: string; + wrapperType: LayoutWrapperType; }>, ) { const start = performance.now(); - const { index, movedWidgets, parentId } = actionPayload.payload; + const { index, movedWidgets, parentId, wrapperType } = actionPayload.payload; // console.log(`#### moved widgets: ${JSON.stringify(movedWidgets)}`); // console.log(`#### parentId: ${parentId}`); try { @@ -367,8 +373,10 @@ function* autolayoutReorderSaga( index, parentId, allWidgets, + wrapperType, }, ); + yield put(updateAndSaveLayout(updatedWidgets)); log.debug("reorder computations took", performance.now() - start, "ms"); } catch (e) { @@ -381,8 +389,9 @@ function* reorderAutolayoutChildren(params: { index: number; parentId: string; allWidgets: CanvasWidgetsReduxState; + wrapperType: LayoutWrapperType; }) { - const { allWidgets, index, movedWidgets, parentId } = params; + const { allWidgets, index, movedWidgets, parentId, wrapperType } = params; const widgets = Object.assign({}, allWidgets); if (!movedWidgets) return widgets; const selectedWidgets = [...movedWidgets]; @@ -419,11 +428,11 @@ function* reorderAutolayoutChildren(params: { // Update moved widgets. Add wrappers to those missing one. const { newMovedWidgets, updatedWidgets } = yield call( updateMovedWidgets, - movedWidgets, + selectedWidgets, trimmedWidgets, parentId, + wrapperType, ); - const items = [...(updatedWidgets[parentId].children || [])]; // remove moved widegts from children const newItems = items.filter((item) => newMovedWidgets.indexOf(item) === -1); @@ -444,10 +453,22 @@ function* updateMovedWidgets( movedWidgets: string[], allWidgets: CanvasWidgetsReduxState, parentId: string, + wrapperType: LayoutWrapperType, ) { const stateParent = allWidgets[parentId]; - if (!movedWidgets || !allWidgets || stateParent.isWrapper) + if (!movedWidgets || !allWidgets) return { newMovedWidgets: movedWidgets, updatedWidgets: allWidgets }; + if (stateParent.isWrapper) { + // If widgets are being dropped in a wrapper, + // then updated the wrapper type and return + for (const each of movedWidgets) { + allWidgets[each] = { + ...allWidgets[each], + wrapperType, + }; + } + return { newMovedWidgets: movedWidgets, updatedWidgets: allWidgets }; + } const newMovedWidgets: string[] = []; for (const each of movedWidgets) { const widget = allWidgets[each]; @@ -497,7 +518,7 @@ function* updateMovedWidgets( children: [...(wrapper.children || []), each], }; // Update parent of the widget - allWidgets[each] = { ...widget, parentId: wrapper.widgetId }; + allWidgets[each] = { ...widget, parentId: wrapper.widgetId, wrapperType }; allWidgets[wrapper.widgetId] = wrapper; newMovedWidgets.push(wrapper.widgetId); } @@ -509,10 +530,11 @@ function* addWidgetAndReorderSaga( newWidget: WidgetAddChild; index: number; parentId: string; + wrapperType: LayoutWrapperType; }>, ) { const start = performance.now(); - const { index, newWidget, parentId } = actionPayload.payload; + const { index, newWidget, parentId, wrapperType } = actionPayload.payload; try { const updatedWidgetsOnAddition: { widgets: CanvasWidgetsReduxState; @@ -528,6 +550,7 @@ function* addWidgetAndReorderSaga( index, parentId, allWidgets: updatedWidgetsOnAddition.widgets, + wrapperType, }, ); yield put(updateAndSaveLayout(updatedWidgetsOnMove)); From 2767d3046f22ecd91cc48d1a8e60af6ba28be3dc Mon Sep 17 00:00:00 2001 From: Preet Sidhu Date: Thu, 1 Sep 2022 16:37:56 -0400 Subject: [PATCH 091/708] fix end wrapper item order --- .../hooks/useAutoLayoutHighlights.ts | 34 +++++++++++----- .../CanvasArenas/hooks/useCanvasDragging.ts | 4 +- .../ContainerWidget/component/index.tsx | 40 ++++++------------- 3 files changed, 37 insertions(+), 41 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index 809755b08eef..c300608ff0cc 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -6,7 +6,7 @@ import { LayoutWrapperType, ResponsiveBehavior, } from "components/constants"; -import { isArray } from "lodash"; +import { isArray, isNaN } from "lodash"; import { WidgetDraggingBlock } from "./useBlocksToBeDraggedOnCanvas"; interface XYCord { @@ -58,7 +58,7 @@ export const useAutoLayoutHighlights = ({ let offsets: Highlight[] = []; let dragBlocksSize = 0; - const siblings: DOMRect[] = []; + let startOffsetsCount = 0; const siblingElements: any[] = []; let lastTranslatedIndex: number; let containerDimensions: { @@ -133,7 +133,7 @@ export const useAutoLayoutHighlights = ({ // reset state dragBlocksSize = 0; lastTranslatedIndex = -10; - + startOffsetsCount = 0; // Hide the highlight if (dropPositionRef && dropPositionRef.current) { dropPositionRef.current.style.opacity = "0"; @@ -190,10 +190,9 @@ export const useAutoLayoutHighlights = ({ const valueToAdd = isFinal ? { x: rect.width + 8, - y: isVertical ? rect.height + 8 : 8, + y: isVertical ? rect.height + 8 : 0, } : { x: 0, y: 0 }; - if (isVertical) { mOffset = { x: 0, @@ -311,6 +310,7 @@ export const useAutoLayoutHighlights = ({ LayoutWrapperType.End, ); temp = [...arr1, ...arr2]; + startOffsetsCount = arr1.length; } else temp = evaluateOffsets( offsetChildren, @@ -335,6 +335,7 @@ export const useAutoLayoutHighlights = ({ wrapperType: LayoutWrapperType, ): Highlight[] => { let res: Highlight[] = []; + const siblings: DOMRect[] = []; if (arr && arr.length) { // Get widget ids of all widgets being dragged arr.forEach((each) => { @@ -358,7 +359,9 @@ export const useAutoLayoutHighlights = ({ if (siblings.length) { res.push( getOffset( - siblings[siblings.length - 1], + wrapperType === LayoutWrapperType.End + ? siblings[siblings.length - 1] + : siblings[siblings.length - 1], flexOffsetTop, wrapperType, true, @@ -446,24 +449,33 @@ export const useAutoLayoutHighlights = ({ return Math.abs(Math.sqrt(x * x + y * y)); }; - const getDropPosition = (val: XYCord): DropPositionPayload | undefined => { + const getDropPosition = (index: number): number => { + if (isNaN(index)) return 0; + const wrapperType: LayoutWrapperType = + offsets[index]?.wrapperType || LayoutWrapperType.Start; + if (wrapperType === LayoutWrapperType.End) return index - startOffsetsCount; + return index; + }; + + const getDropInfo = (val: XYCord): DropPositionPayload | undefined => { if (!isNaN(lastTranslatedIndex) && lastTranslatedIndex >= 0) return { - index: lastTranslatedIndex, + index: getDropPosition(lastTranslatedIndex), wrapperType: offsets[lastTranslatedIndex]?.wrapperType, }; const pos = getHighlightPosition(null, val); if (!pos) return; + const dropPos: number = offsets.indexOf(pos); return { - index: offsets.indexOf(pos), - wrapperType: offsets[lastTranslatedIndex]?.wrapperType, + index: getDropPosition(dropPos), + wrapperType: offsets[dropPos]?.wrapperType, }; }; return { calculateHighlightOffsets, cleanUpTempStyles, - getDropPosition, + getDropInfo, highlightDropPosition, }; }; diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index 9e5c9e6ab330..3ecad204fa32 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -117,7 +117,7 @@ export const useCanvasDragging = ( const { calculateHighlightOffsets, cleanUpTempStyles, - getDropPosition, + getDropInfo, highlightDropPosition, } = useAutoLayoutHighlights({ blocksToDraw, @@ -322,7 +322,7 @@ export const useCanvasDragging = ( } return each; }); - const pos: DropPositionPayload | undefined = getDropPosition({ + const pos: DropPositionPayload | undefined = getDropInfo({ x: currentRectanglesToDraw[0].top, y: currentRectanglesToDraw[0].left, }); diff --git a/app/client/src/widgets/ContainerWidget/component/index.tsx b/app/client/src/widgets/ContainerWidget/component/index.tsx index 6fc1682eb70c..15278b290261 100644 --- a/app/client/src/widgets/ContainerWidget/component/index.tsx +++ b/app/client/src/widgets/ContainerWidget/component/index.tsx @@ -1,11 +1,4 @@ -import React, { - ReactNode, - useRef, - useEffect, - RefObject, - useMemo, - useState, -} from "react"; +import React, { ReactNode, useRef, useEffect, RefObject, useMemo } from "react"; import styled, { css } from "styled-components"; import { isArray, pick } from "lodash"; import tinycolor from "tinycolor2"; @@ -179,23 +172,14 @@ export function FlexBox(props: FlexBoxProps) { export function LayoutWrapper(props: FlexBoxProps): JSX.Element { const allWidgets = useSelector(getWidgets); - const [startChildren, setStartChildren] = useState([]); - const [endChildren, setEndChildren] = useState([]); - - useEffect(() => { - const start: any = [], - end: any = []; - if (isArray(props.children) && props.children?.length > 0) { - for (const child of props.children) { - const widget = allWidgets[(child as any).props?.widgetId]; - if (widget && widget.wrapperType === LayoutWrapperType.End) - end.push(child); - else start.push(child); - } - setStartChildren(start); - setEndChildren(end); - } - }, [props.children]); + const start: JSX.Element[] = [], + end: JSX.Element[] = []; + isArray(props.children) && + (props.children as JSX.Element[]).map((child: JSX.Element) => { + const widget = allWidgets[child.props?.widgetId]; + if (widget?.wrapperType === LayoutWrapperType.End) end.push(child); + else start.push(child); + }); const layoutProps: LayoutProperties = useMemo( () => getLayoutProperties(props.direction, props.alignment, props.spacing), @@ -213,13 +197,13 @@ export function LayoutWrapper(props: FlexBoxProps): JSX.Element { className={`wrapper start-wrapper-${props.widgetId}`} flexDirection={layoutProps.flexDirection} > - {startChildren} + {start} - {endChildren} + {end} ); From 81e801067987fc35a10121192cdd58128785c7cc Mon Sep 17 00:00:00 2001 From: Preet Date: Fri, 2 Sep 2022 08:35:07 -0400 Subject: [PATCH 092/708] fix fill child and end order issues --- .../hooks/useAutoLayoutHighlights.ts | 42 ++++++++----------- .../sagas/CanvasSagas/DraggingCanvasSagas.ts | 16 ++++++- .../ContainerWidget/component/index.tsx | 34 +++++++++++---- 3 files changed, 59 insertions(+), 33 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index c300608ff0cc..6fb2e03f8404 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -49,7 +49,6 @@ export const useAutoLayoutHighlights = ({ isCurrentDraggedCanvas, isDragging, useAutoLayout, - widgetName, }: AutoLayoutHighlightProps) => { const allWidgets = useSelector(getWidgets); const canvas = allWidgets[canvasId]; @@ -58,7 +57,6 @@ export const useAutoLayoutHighlights = ({ let offsets: Highlight[] = []; let dragBlocksSize = 0; - let startOffsetsCount = 0; const siblingElements: any[] = []; let lastTranslatedIndex: number; let containerDimensions: { @@ -74,22 +72,6 @@ export const useAutoLayoutHighlights = ({ * START AUTO LAYOUT OFFSET CALCULATION */ - // Determines whether nested wrappers can be introduced in the current canvas - const enableNestedWrappers = (): boolean => { - // If the canvas is not a wrapper, then return false. - if (!canvas?.isWrapper) return false; - const children = canvas.children || []; - // TODO: what to do when there are no children? - if (!children.length) return true; - // If the canvas has a fill child, then return false. - return !( - children.filter( - (child) => - allWidgets[child]?.responsiveBehavior === ResponsiveBehavior.Fill, - ).length > 0 - ); - }; - // Create and add an initial offset for an empty canvas const getInitialOffset = ( isWrapper: boolean, @@ -133,7 +115,6 @@ export const useAutoLayoutHighlights = ({ // reset state dragBlocksSize = 0; lastTranslatedIndex = -10; - startOffsetsCount = 0; // Hide the highlight if (dropPositionRef && dropPositionRef.current) { dropPositionRef.current.style.opacity = "0"; @@ -252,6 +233,19 @@ export const useAutoLayoutHighlights = ({ }); }; + const hasFillChild = (arr: string[]): boolean => { + if (!arr || !arr.length) return false; + let flag = false; + for (const widgetId of arr) { + const widget = allWidgets[widgetId]; + if (widget?.responsiveBehavior === ResponsiveBehavior.Fill) { + flag = true; + break; + } + } + return flag; + }; + const calculateHighlightOffsets = (): Highlight[] => { cleanUpTempStyles(); // console.log( @@ -289,7 +283,8 @@ export const useAutoLayoutHighlights = ({ const flex = document.querySelector(`.flex-container-${canvasId}`); const flexOffsetTop = (flex as any)?.offsetTop || 0; let temp: Highlight[] = []; - if (canvas.isWrapper) { + const discardEndWrapper: boolean = hasFillChild(offsetChildren); + if (canvas.isWrapper && !discardEndWrapper) { const start: string[] = [], end: string[] = []; offsetChildren.forEach((each) => { @@ -297,20 +292,19 @@ export const useAutoLayoutHighlights = ({ start.push(each); else end.push(each); }); - const arr1 = evaluateOffsets( + const arr1: Highlight[] = evaluateOffsets( start, flexOffsetTop, true, LayoutWrapperType.Start, ); - const arr2 = evaluateOffsets( + const arr2: Highlight[] = evaluateOffsets( end, flexOffsetTop, true, LayoutWrapperType.End, ); temp = [...arr1, ...arr2]; - startOffsetsCount = arr1.length; } else temp = evaluateOffsets( offsetChildren, @@ -453,7 +447,7 @@ export const useAutoLayoutHighlights = ({ if (isNaN(index)) return 0; const wrapperType: LayoutWrapperType = offsets[index]?.wrapperType || LayoutWrapperType.Start; - if (wrapperType === LayoutWrapperType.End) return index - startOffsetsCount; + if (wrapperType === LayoutWrapperType.End) return index - 1; return index; }; diff --git a/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts b/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts index b7b72ca215ad..8354a13284e0 100644 --- a/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts +++ b/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts @@ -39,6 +39,7 @@ import { Alignment, ButtonBoxShadowTypes, LayoutWrapperType, + ResponsiveBehavior, Spacing, } from "components/constants"; import { @@ -460,13 +461,26 @@ function* updateMovedWidgets( return { newMovedWidgets: movedWidgets, updatedWidgets: allWidgets }; if (stateParent.isWrapper) { // If widgets are being dropped in a wrapper, - // then updated the wrapper type and return + // then updated the wrapper type and return' + let hasFillChild = false; for (const each of movedWidgets) { + if (allWidgets[each].responsiveBehavior === ResponsiveBehavior.Fill) { + hasFillChild = true; + break; + } allWidgets[each] = { ...allWidgets[each], wrapperType, }; } + if (hasFillChild) { + for (const each of movedWidgets) { + allWidgets[each] = { + ...allWidgets[each], + wrapperType: LayoutWrapperType.Start, + }; + } + } return { newMovedWidgets: movedWidgets, updatedWidgets: allWidgets }; } const newMovedWidgets: string[] = []; diff --git a/app/client/src/widgets/ContainerWidget/component/index.tsx b/app/client/src/widgets/ContainerWidget/component/index.tsx index 15278b290261..3d626d217665 100644 --- a/app/client/src/widgets/ContainerWidget/component/index.tsx +++ b/app/client/src/widgets/ContainerWidget/component/index.tsx @@ -19,6 +19,7 @@ import { LayoutDirection, LayoutWrapperType, Overflow, + ResponsiveBehavior, Spacing, } from "components/constants"; import { @@ -72,6 +73,10 @@ const StyledContainerComponent = styled.div< position: absolute; left: -9999px; } + + .no-display { + display: none; + } `; export const FlexContainer = styled.div<{ @@ -172,14 +177,25 @@ export function FlexBox(props: FlexBoxProps) { export function LayoutWrapper(props: FlexBoxProps): JSX.Element { const allWidgets = useSelector(getWidgets); - const start: JSX.Element[] = [], + let start: JSX.Element[] = [], end: JSX.Element[] = []; - isArray(props.children) && - (props.children as JSX.Element[]).map((child: JSX.Element) => { - const widget = allWidgets[child.props?.widgetId]; - if (widget?.wrapperType === LayoutWrapperType.End) end.push(child); - else start.push(child); - }); + let hasFillChild = false; + if (isArray(props.children)) { + for (const child of props.children) { + const widget = allWidgets[(child as JSX.Element).props?.widgetId]; + if (widget.responsiveBehavior === ResponsiveBehavior.Fill) { + hasFillChild = true; + break; + } + if (widget?.wrapperType === LayoutWrapperType.End) + end.push(child as JSX.Element); + else start.push(child as JSX.Element); + } + } + if (hasFillChild) { + start = props.children as JSX.Element[]; + end = []; + } const layoutProps: LayoutProperties = useMemo( () => getLayoutProperties(props.direction, props.alignment, props.spacing), @@ -200,7 +216,9 @@ export function LayoutWrapper(props: FlexBoxProps): JSX.Element { {start} {end} From 4554d2ff0da889b37feab7952f2e2d4599042aad Mon Sep 17 00:00:00 2001 From: Preet Date: Fri, 2 Sep 2022 08:55:11 -0400 Subject: [PATCH 093/708] fix merge issues --- app/client/src/widgets/SingleSelectTreeWidget/widget/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/app/client/src/widgets/SingleSelectTreeWidget/widget/index.tsx b/app/client/src/widgets/SingleSelectTreeWidget/widget/index.tsx index 8fd7ea0ee518..d420e77b3704 100644 --- a/app/client/src/widgets/SingleSelectTreeWidget/widget/index.tsx +++ b/app/client/src/widgets/SingleSelectTreeWidget/widget/index.tsx @@ -16,6 +16,7 @@ import SingleSelectTreeComponent from "../component"; import { LabelPosition, ResponsiveBehavior } from "components/constants"; import { Alignment } from "@blueprintjs/core"; import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; +import derivedProperties from "./parseDerivedProperties"; function defaultOptionValueValidation(value: unknown): ValidationResponse { if (typeof value === "string") return { isValid: true, parsed: value.trim() }; From 8f701fa69a8fd9e7e4d198c5fe51aa600c58a0eb Mon Sep 17 00:00:00 2001 From: Preet Sidhu Date: Tue, 6 Sep 2022 11:04:44 -0400 Subject: [PATCH 094/708] add center wrapper --- .../ContainerWidget/component/index.tsx | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/app/client/src/widgets/ContainerWidget/component/index.tsx b/app/client/src/widgets/ContainerWidget/component/index.tsx index 3d626d217665..95470dbd2ba2 100644 --- a/app/client/src/widgets/ContainerWidget/component/index.tsx +++ b/app/client/src/widgets/ContainerWidget/component/index.tsx @@ -124,6 +124,15 @@ const EndWrapper = styled.div<{ align-items: center; `; +const CenterWrapper = styled.div<{ + flexDirection: FlexDirection; +}>` + display: flex; + flex-direction: ${({ flexDirection }) => flexDirection || "row"}; + justify-content: center; + align-items: center; +`; + function ContainerComponentWrapper(props: ContainerComponentProps) { const containerStyle = props.containerStyle || "card"; const containerRef: RefObject = useRef(null); @@ -178,6 +187,7 @@ export function FlexBox(props: FlexBoxProps) { export function LayoutWrapper(props: FlexBoxProps): JSX.Element { const allWidgets = useSelector(getWidgets); let start: JSX.Element[] = [], + center: JSX.Element[] = [], end: JSX.Element[] = []; let hasFillChild = false; if (isArray(props.children)) { @@ -189,11 +199,14 @@ export function LayoutWrapper(props: FlexBoxProps): JSX.Element { } if (widget?.wrapperType === LayoutWrapperType.End) end.push(child as JSX.Element); + else if (widget?.wrapperType === LayoutWrapperType.Center) + center.push(child as JSX.Element); else start.push(child as JSX.Element); } } if (hasFillChild) { start = props.children as JSX.Element[]; + center = []; end = []; } @@ -215,6 +228,14 @@ export function LayoutWrapper(props: FlexBoxProps): JSX.Element { > {start} + + {center} + Date: Tue, 6 Sep 2022 11:17:19 -0400 Subject: [PATCH 095/708] update calculateHighlights and drop position index --- .../CanvasArenas/hooks/useAutoLayoutHighlights.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index 6fb2e03f8404..1c23358ed051 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -286,6 +286,7 @@ export const useAutoLayoutHighlights = ({ const discardEndWrapper: boolean = hasFillChild(offsetChildren); if (canvas.isWrapper && !discardEndWrapper) { const start: string[] = [], + center: string[] = [], end: string[] = []; offsetChildren.forEach((each) => { if (allWidgets[each]?.wrapperType === LayoutWrapperType.Start) @@ -299,12 +300,18 @@ export const useAutoLayoutHighlights = ({ LayoutWrapperType.Start, ); const arr2: Highlight[] = evaluateOffsets( + center, + flexOffsetTop, + true, + LayoutWrapperType.Center, + ); + const arr3: Highlight[] = evaluateOffsets( end, flexOffsetTop, true, LayoutWrapperType.End, ); - temp = [...arr1, ...arr2]; + temp = [...arr1, ...arr2, ...arr3]; } else temp = evaluateOffsets( offsetChildren, @@ -447,7 +454,8 @@ export const useAutoLayoutHighlights = ({ if (isNaN(index)) return 0; const wrapperType: LayoutWrapperType = offsets[index]?.wrapperType || LayoutWrapperType.Start; - if (wrapperType === LayoutWrapperType.End) return index - 1; + if (wrapperType === LayoutWrapperType.Center) return index - 1; + if (wrapperType === LayoutWrapperType.End) return index - 2; return index; }; From a577b22c33714a304f970f2badac0adb0ab10fad Mon Sep 17 00:00:00 2001 From: Preet Sidhu Date: Tue, 6 Sep 2022 12:45:44 -0400 Subject: [PATCH 096/708] working center offsets and positioning --- .../hooks/useAutoLayoutHighlights.ts | 125 ++++++++++++------ .../ContainerWidget/component/index.tsx | 4 +- 2 files changed, 86 insertions(+), 43 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index 1c23358ed051..f5fd61465953 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -72,31 +72,94 @@ export const useAutoLayoutHighlights = ({ * START AUTO LAYOUT OFFSET CALCULATION */ + // Fetch and update the dimensions of the containing canvas. + const updateContainerDimensions = (): boolean => { + const container = document.querySelector(`.appsmith_widget_${canvasId}`); + const containerRect: + | DOMRect + | undefined = container?.getBoundingClientRect(); + if (!container || !containerRect) return false; + containerDimensions = { + top: containerRect?.top || 0, + bottom: containerRect?.bottom - containerDimensions?.top || 0, + left: containerRect?.left || 0, + right: containerRect?.right - containerRect?.left || 0, + width: containerRect?.width, + height: containerRect?.height, + }; + // console.log( + // `#### container dimensions: ${JSON.stringify(containerDimensions)}`, + // ); + return true; + }; + + const getContainerDimensions = () => { + if (!containerDimensions) updateContainerDimensions(); + return containerDimensions; + }; + + const initialOffsets: Record< + LayoutWrapperType, + Record + > = { + [LayoutWrapperType.Start]: { + [LayoutDirection.Vertical]: { + x: 0, + y: 8, + width: getContainerDimensions()?.width || BASE_OFFSET_SIZE, + height: OFFSET_WIDTH, + wrapperType: LayoutWrapperType.Start, + }, + [LayoutDirection.Horizontal]: { + x: 8, + y: 0, + width: OFFSET_WIDTH, + height: getContainerDimensions()?.height || BASE_OFFSET_SIZE, + wrapperType: LayoutWrapperType.Start, + }, + }, + [LayoutWrapperType.Center]: { + [LayoutDirection.Vertical]: { + x: 0, + y: getContainerDimensions()?.height / 2, + width: getContainerDimensions()?.width || BASE_OFFSET_SIZE, + height: OFFSET_WIDTH, + wrapperType: LayoutWrapperType.Center, + }, + [LayoutDirection.Horizontal]: { + x: getContainerDimensions()?.width / 2, + y: 0, + width: OFFSET_WIDTH, + height: getContainerDimensions()?.height || BASE_OFFSET_SIZE, + wrapperType: LayoutWrapperType.Center, + }, + }, + [LayoutWrapperType.End]: { + [LayoutDirection.Vertical]: { + x: 0, + y: getContainerDimensions()?.bottom, + width: getContainerDimensions()?.width || BASE_OFFSET_SIZE, + height: OFFSET_WIDTH, + wrapperType: LayoutWrapperType.End, + }, + [LayoutDirection.Horizontal]: { + x: getContainerDimensions()?.right, + y: 0, + width: OFFSET_WIDTH, + height: getContainerDimensions()?.height || BASE_OFFSET_SIZE, + wrapperType: LayoutWrapperType.End, + }, + }, + }; + // Create and add an initial offset for an empty canvas const getInitialOffset = ( isWrapper: boolean, wrapperType: LayoutWrapperType = LayoutWrapperType.Start, ): Highlight => { - let mOffset: Highlight; - const reverse = isWrapper && wrapperType === LayoutWrapperType.End; - if (isVertical) { - mOffset = { - x: 0, - y: reverse ? containerDimensions.bottom - 8 : 8, - width: containerDimensions?.width || BASE_OFFSET_SIZE, - height: OFFSET_WIDTH, - wrapperType, - }; - } else { - mOffset = { - x: reverse ? containerDimensions.right - 8 : 8, - y: 0, - width: OFFSET_WIDTH, - height: containerDimensions?.height || BASE_OFFSET_SIZE, - wrapperType, - }; - } - return mOffset; + const dir: LayoutDirection = direction || LayoutDirection.Horizontal; + if (isWrapper) return initialOffsets[wrapperType][dir]; + return initialOffsets[LayoutWrapperType.Start][dir]; }; // Get DOM element for a given widgetId const getDomElement = (widgetId: string): any => @@ -122,24 +185,6 @@ export const useAutoLayoutHighlights = ({ } }; - // Fetch and update the dimensions of the containing canvas. - const updateContainerDimensions = (): boolean => { - const container = document.querySelector(`.appsmith_widget_${canvasId}`); - const containerRect: - | DOMRect - | undefined = container?.getBoundingClientRect(); - if (!container || !containerRect) return false; - containerDimensions = { - top: containerRect?.top || 0, - bottom: containerRect?.bottom - containerDimensions?.top || 0, - left: containerRect?.left || 0, - right: containerRect?.right - containerRect?.left || 0, - width: containerRect?.width, - height: containerRect?.height, - }; - return true; - }; - // Get a list of widgetIds that are being dragged. const getDraggedBlocks = (): string[] => { const blocks = blocksToDraw.map((block) => block.widgetId); @@ -360,9 +405,7 @@ export const useAutoLayoutHighlights = ({ if (siblings.length) { res.push( getOffset( - wrapperType === LayoutWrapperType.End - ? siblings[siblings.length - 1] - : siblings[siblings.length - 1], + siblings[siblings.length - 1], flexOffsetTop, wrapperType, true, diff --git a/app/client/src/widgets/ContainerWidget/component/index.tsx b/app/client/src/widgets/ContainerWidget/component/index.tsx index 95470dbd2ba2..ce9745c33cdb 100644 --- a/app/client/src/widgets/ContainerWidget/component/index.tsx +++ b/app/client/src/widgets/ContainerWidget/component/index.tsx @@ -232,7 +232,7 @@ export function LayoutWrapper(props: FlexBoxProps): JSX.Element { className={`wrapper center-wrapper-${props.widgetId} ${ hasFillChild ? "no-display" : "" }`} - flexDirection={FlexDirection.Row} + flexDirection={layoutProps.flexDirection} > {center} @@ -240,7 +240,7 @@ export function LayoutWrapper(props: FlexBoxProps): JSX.Element { className={`wrapper end-wrapper-${props.widgetId} ${ hasFillChild ? "no-display" : "" }`} - flexDirection={FlexDirection.Row} + flexDirection={layoutProps.flexDirection} > {end} From 99010712a8eb8c8bdb9fb567163c84d213e454f3 Mon Sep 17 00:00:00 2001 From: Preet Sidhu Date: Wed, 7 Sep 2022 07:53:59 -0400 Subject: [PATCH 097/708] fix center offset calculation --- .../common/CanvasArenas/hooks/useAutoLayoutHighlights.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index f5fd61465953..82bbc94b806f 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -336,6 +336,8 @@ export const useAutoLayoutHighlights = ({ offsetChildren.forEach((each) => { if (allWidgets[each]?.wrapperType === LayoutWrapperType.Start) start.push(each); + else if (allWidgets[each]?.wrapperType === LayoutWrapperType.Center) + center.push(each); else end.push(each); }); const arr1: Highlight[] = evaluateOffsets( @@ -368,8 +370,8 @@ export const useAutoLayoutHighlights = ({ if (!offsets || !offsets.length) offsets = [getInitialOffset(isCanvasWrapper)]; - // console.log(`#### offsets: ${JSON.stringify(offsets)}`); - // console.log(`#### END calculate highlight offsets`); + console.log(`#### offsets: ${JSON.stringify(offsets)}`); + console.log(`#### END calculate highlight offsets`); } return offsets; }; From e74bab0b54a85d20822749de7fcb136d7f98c59b Mon Sep 17 00:00:00 2001 From: Preet Sidhu Date: Wed, 7 Sep 2022 07:54:46 -0400 Subject: [PATCH 098/708] remove logs --- .../common/CanvasArenas/hooks/useAutoLayoutHighlights.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index 82bbc94b806f..b1a51ebaede2 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -370,8 +370,8 @@ export const useAutoLayoutHighlights = ({ if (!offsets || !offsets.length) offsets = [getInitialOffset(isCanvasWrapper)]; - console.log(`#### offsets: ${JSON.stringify(offsets)}`); - console.log(`#### END calculate highlight offsets`); + // console.log(`#### offsets: ${JSON.stringify(offsets)}`); + // console.log(`#### END calculate highlight offsets`); } return offsets; }; From 5d401a1106b60ce6fd814e98acd0a23a89c8f52b Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 7 Sep 2022 12:19:49 -0400 Subject: [PATCH 099/708] fix column space in wrapper and refactor payload generation --- app/client/src/actions/autoLayoutActions.ts | 4 +- .../hooks/useBlocksToBeDraggedOnCanvas.ts | 2 + .../sagas/CanvasSagas/DraggingCanvasSagas.ts | 97 +++++++++++-------- app/client/src/sagas/LayoutUpdateSagas.tsx | 7 +- app/client/src/sagas/WidgetAdditionSagas.ts | 31 ++---- app/client/src/sagas/WidgetOperationUtils.ts | 78 +++++++++------ .../ContainerWidget/component/index.tsx | 2 +- .../widgets/ContainerWidget/widget/index.tsx | 9 +- 8 files changed, 131 insertions(+), 99 deletions(-) diff --git a/app/client/src/actions/autoLayoutActions.ts b/app/client/src/actions/autoLayoutActions.ts index 6eb0833fcae1..2e1310596c35 100644 --- a/app/client/src/actions/autoLayoutActions.ts +++ b/app/client/src/actions/autoLayoutActions.ts @@ -1,13 +1,15 @@ import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants"; +import { LayoutDirection } from "components/constants"; export const removeWrappers = (parentId: string) => ({ type: ReduxActionTypes.REMOVE_CHILD_WRAPPERS, payload: { parentId }, }); -export const addWrappers = (parentId: string) => ({ +export const addWrappers = (parentId: string, direction: LayoutDirection) => ({ type: ReduxActionTypes.ADD_CHILD_WRAPPERS, payload: { parentId, + direction, }, }); diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts b/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts index 1ca009f413d2..a8fc5889cb44 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts @@ -313,6 +313,7 @@ export const useBlocksToBeDraggedOnCanvas = ({ movedWidgets: selectedWidgets, parentId: widgetId, wrapperType, + direction, }, }); }; @@ -357,6 +358,7 @@ export const useBlocksToBeDraggedOnCanvas = ({ newWidget: widgetPayload, parentId: widgetId, wrapperType, + direction, }, }); }; diff --git a/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts b/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts index 8354a13284e0..338eb28bf3e8 100644 --- a/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts +++ b/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts @@ -34,16 +34,13 @@ import { } from "sagas/WidgetAdditionSagas"; import { MainCanvasReduxState } from "reducers/uiReducers/mainCanvasReducer"; import { CANVAS_DEFAULT_MIN_HEIGHT_PX } from "constants/AppConstants"; -import { generateReactKey } from "utils/generators"; import { - Alignment, - ButtonBoxShadowTypes, + LayoutDirection, LayoutWrapperType, ResponsiveBehavior, - Spacing, } from "components/constants"; import { - getLayoutWrapperName, + getLayoutWrapperPayload, purgeEmptyWrappers, } from "../WidgetOperationUtils"; @@ -357,11 +354,18 @@ function* autolayoutReorderSaga( index: number; parentId: string; wrapperType: LayoutWrapperType; + direction: LayoutDirection; }>, ) { const start = performance.now(); - const { index, movedWidgets, parentId, wrapperType } = actionPayload.payload; + const { + direction, + index, + movedWidgets, + parentId, + wrapperType, + } = actionPayload.payload; // console.log(`#### moved widgets: ${JSON.stringify(movedWidgets)}`); // console.log(`#### parentId: ${parentId}`); try { @@ -375,6 +379,7 @@ function* autolayoutReorderSaga( parentId, allWidgets, wrapperType, + direction, }, ); @@ -391,8 +396,16 @@ function* reorderAutolayoutChildren(params: { parentId: string; allWidgets: CanvasWidgetsReduxState; wrapperType: LayoutWrapperType; + direction: LayoutDirection; }) { - const { allWidgets, index, movedWidgets, parentId, wrapperType } = params; + const { + allWidgets, + direction, + index, + movedWidgets, + parentId, + wrapperType, + } = params; const widgets = Object.assign({}, allWidgets); if (!movedWidgets) return widgets; const selectedWidgets = [...movedWidgets]; @@ -433,6 +446,7 @@ function* reorderAutolayoutChildren(params: { trimmedWidgets, parentId, wrapperType, + direction, ); const items = [...(updatedWidgets[parentId].children || [])]; // remove moved widegts from children @@ -455,6 +469,7 @@ function* updateMovedWidgets( allWidgets: CanvasWidgetsReduxState, parentId: string, wrapperType: LayoutWrapperType, + direction: LayoutDirection, ) { const stateParent = allWidgets[parentId]; if (!movedWidgets || !allWidgets) @@ -493,33 +508,19 @@ function* updateMovedWidgets( } // wrap the widget in a wrapper widget - const wrapperPayload = { - newWidgetId: generateReactKey(), - type: "LAYOUT_WRAPPER_WIDGET", - widgetName: getLayoutWrapperName(allWidgets), - props: { - containerStyle: "none", - canExtend: false, - detachFromLayout: true, - children: [], - alignment: Alignment.Left, - spacing: Spacing.None, - isWrapper: true, - backgroundColor: "transparent", - boxShadow: ButtonBoxShadowTypes.NONE, - borderStyle: "none", + const wrapperPayload = getLayoutWrapperPayload( + allWidgets, + { + rows: widget.rows, + columns: widget.columns, + topRow: widget.topRow, + leftColumn: widget.leftColumn, + parentRowSpace: widget.parentRowSpace, + parentColumnSpace: stateParent.parentColumnSpace, + widgetId: parentId, }, - rows: widget.rows ? widget.rows + 1 : widget.bottomRow - widget.top + 1, - columns: widget.columns - ? widget.columns + 1 - : widget.rightColumn - widget.leftColumn + 1, - widgetId: parentId, - leftColumn: widget.leftColumn, - topRow: widget.topRow, - parentRowSpace: stateParent.parentRowSpace, - parentColumnSpace: stateParent.parentColumnSpace, - tabId: "", - }; + direction, + ); const containerPayload: GeneratedWidgetPayload = yield generateChildWidgets( stateParent, wrapperPayload, @@ -545,15 +546,22 @@ function* addWidgetAndReorderSaga( index: number; parentId: string; wrapperType: LayoutWrapperType; + direction: LayoutDirection; }>, ) { const start = performance.now(); - const { index, newWidget, parentId, wrapperType } = actionPayload.payload; + const { + direction, + index, + newWidget, + parentId, + wrapperType, + } = actionPayload.payload; try { const updatedWidgetsOnAddition: { widgets: CanvasWidgetsReduxState; containerId?: string; - } = yield call(addAutoLayoutChild, newWidget, parentId); + } = yield call(addAutoLayoutChild, newWidget, parentId, direction); const updatedWidgetsOnMove: CanvasWidgetsReduxState = yield call( reorderAutolayoutChildren, @@ -565,6 +573,7 @@ function* addWidgetAndReorderSaga( parentId, allWidgets: updatedWidgetsOnAddition.widgets, wrapperType, + direction, }, ); yield put(updateAndSaveLayout(updatedWidgetsOnMove)); @@ -574,7 +583,11 @@ function* addWidgetAndReorderSaga( } } -function* addAutoLayoutChild(newWidget: WidgetAddChild, parentId: string) { +function* addAutoLayoutChild( + newWidget: WidgetAddChild, + parentId: string, + direction: LayoutDirection, +) { const allWidgets: CanvasWidgetsReduxState = yield select(getWidgets); const stateParent: FlattenedWidgetProps = allWidgets[parentId]; @@ -591,10 +604,14 @@ function* addAutoLayoutChild(newWidget: WidgetAddChild, parentId: string) { const updatedWidgetsOnAddition: { widgets: CanvasWidgetsReduxState; containerId?: string; - } = yield call(getUpdateDslAfterCreatingAutoLayoutChild, { - ...newWidget, - widgetId: parentId, - }); + } = yield call( + getUpdateDslAfterCreatingAutoLayoutChild, + { + ...newWidget, + widgetId: parentId, + }, + direction, + ); return updatedWidgetsOnAddition; } diff --git a/app/client/src/sagas/LayoutUpdateSagas.tsx b/app/client/src/sagas/LayoutUpdateSagas.tsx index b14b0aa80800..819ea9b4c6c0 100644 --- a/app/client/src/sagas/LayoutUpdateSagas.tsx +++ b/app/client/src/sagas/LayoutUpdateSagas.tsx @@ -4,6 +4,7 @@ import { ReduxActionErrorTypes, ReduxActionTypes, } from "ce/constants/ReduxActionConstants"; +import { LayoutDirection } from "components/constants"; import log from "loglevel"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; import { all, call, put, select, takeLatest } from "redux-saga/effects"; @@ -12,6 +13,7 @@ import { purgeChildWrappers, wrapChildren } from "./WidgetOperationUtils"; type LayoutUpdatePayload = { parentId: string; + direction: LayoutDirection; }; function* removeChildWrappers(actionPayload: ReduxAction) { @@ -39,12 +41,13 @@ function* removeChildWrappers(actionPayload: ReduxAction) { function* addChildWrappers(actionPayload: ReduxAction) { try { const start = performance.now(); - const { parentId } = actionPayload.payload; + const { direction, parentId } = actionPayload.payload; const allWidgets: CanvasWidgetsReduxState = yield select(getWidgets); const updatedWidgets: CanvasWidgetsReduxState = yield call( wrapChildren, allWidgets, parentId, + direction, ); yield put(updateAndSaveLayout(updatedWidgets)); log.debug("empty wrapper removal took", performance.now() - start, "ms"); @@ -52,7 +55,7 @@ function* addChildWrappers(actionPayload: ReduxAction) { yield put({ type: ReduxActionErrorTypes.WIDGET_OPERATION_ERROR, payload: { - action: ReduxActionTypes.REMOVE_CHILD_WRAPPERS, + action: ReduxActionTypes.ADD_CHILD_WRAPPERS, error, }, }); diff --git a/app/client/src/sagas/WidgetAdditionSagas.ts b/app/client/src/sagas/WidgetAdditionSagas.ts index aa77a1d83c40..ae63aa9e5ed5 100644 --- a/app/client/src/sagas/WidgetAdditionSagas.ts +++ b/app/client/src/sagas/WidgetAdditionSagas.ts @@ -24,7 +24,7 @@ import { traverseTreeAndExecuteBlueprintChildOperations, } from "./WidgetBlueprintSagas"; import { - getLayoutWrapperName, + getLayoutWrapperPayload, getParentBottomRowAfterAddingWidget, } from "./WidgetOperationUtils"; import log from "loglevel"; @@ -39,7 +39,7 @@ import { getSelectedAppThemeStylesheet } from "selectors/appThemingSelectors"; import { getPropertiesToUpdate } from "./WidgetOperationSagas"; import { klona as clone } from "klona/full"; import { DataTree } from "entities/DataTree/dataTreeFactory"; -import { Alignment, ButtonBoxShadowTypes, Spacing } from "components/constants"; +import { LayoutDirection } from "components/constants"; const WidgetTypes = WidgetFactory.widgetTypes; @@ -327,6 +327,7 @@ export function* getUpdateDslAfterCreatingChild( export function* getUpdateDslAfterCreatingAutoLayoutChild( addChildPayload: WidgetAddChild, + direction: LayoutDirection, ) { // console.log(addChildPayload); // NOTE: widgetId here is the parentId of the dropped widget ( we should rename it to avoid confusion ) @@ -346,27 +347,11 @@ export function* getUpdateDslAfterCreatingAutoLayoutChild( */ // Generate a payload for canvas widget - const newContainerWidgetPayload = { - ...addChildPayload, - newWidgetId: generateReactKey(), - type: "LAYOUT_WRAPPER_WIDGET", - widgetName: getLayoutWrapperName(widgets), - props: { - containerStyle: "none", - canExtend: false, - detachFromLayout: true, - children: [], - alignment: Alignment.Left, - spacing: Spacing.None, - isWrapper: true, - backgroundColor: "transparent", - boxShadow: ButtonBoxShadowTypes.NONE, - borderStyle: "none", - }, - rows: addChildPayload.rows + 1, - columns: addChildPayload.columns + 2, - }; - // console.log(newContainerWidgetPayload); + const newContainerWidgetPayload = getLayoutWrapperPayload( + widgets, + addChildPayload, + direction, + ); const containerPayload: GeneratedWidgetPayload = yield generateChildWidgets( stateParent, newContainerWidgetPayload, diff --git a/app/client/src/sagas/WidgetOperationUtils.ts b/app/client/src/sagas/WidgetOperationUtils.ts index 256450e446b3..36b371ab0bb7 100644 --- a/app/client/src/sagas/WidgetOperationUtils.ts +++ b/app/client/src/sagas/WidgetOperationUtils.ts @@ -53,11 +53,17 @@ import { reflow } from "reflow"; import { getBottomRowAfterReflow } from "utils/reflowHookUtils"; import { DataTreeWidget } from "entities/DataTree/dataTreeFactory"; import { isWidget } from "../workers/evaluationUtils"; -import { Alignment, ButtonBoxShadowTypes, Spacing } from "components/constants"; +import { + Alignment, + ButtonBoxShadowTypes, + LayoutDirection, + Spacing, +} from "components/constants"; import { generateChildWidgets, GeneratedWidgetPayload, } from "./WidgetAdditionSagas"; +import { WidgetAddChild } from "actions/pageActions"; export interface CopiedWidgetGroup { widgetId: string; @@ -1747,6 +1753,7 @@ export function purgeChildWrappers( export function* wrapChildren( allWidgets: CanvasWidgetsReduxState, containerId: string, + direction: LayoutDirection, ) { const widgets = { ...allWidgets }; const container = widgets[containerId]; @@ -1762,7 +1769,6 @@ export function* wrapChildren( ...parent, children: [], }; - /** * Generate new wrappers * and wrap each child in its own wrapper @@ -1771,33 +1777,19 @@ export function* wrapChildren( const child = widgets[each]; if (!child || child.isWrapper) continue; // Create new wrapper - const wrapperPayload = { - newWidgetId: generateReactKey(), - type: "LAYOUT_WRAPPER_WIDGET", - widgetName: getLayoutWrapperName(widgets), - props: { - containerStyle: "none", - canExtend: false, - detachFromLayout: true, - children: [], - alignment: Alignment.Left, - spacing: Spacing.None, - isWrapper: true, - backgroundColor: "transparent", - boxShadow: ButtonBoxShadowTypes.NONE, - borderStyle: "none", + const wrapperPayload = getLayoutWrapperPayload( + widgets, + { + rows: child.rows, + columns: child.columns, + topRow: child.topRow, + leftColumn: child.leftColumn, + parentColumnSpace: parent.parentColumnSpace, + parentRowSpace: child.parentRowSpace, + widgetId: canvasId, }, - rows: child.rows ? child.rows + 1 : child.bottomRow - child.top + 1, - columns: child.columns - ? child.columns + 1 - : child.rightColumn - child.leftColumn + 1, - leftColumn: child.leftColumn, - topRow: child.topRow, - parentRowSpace: parent.parentRowSpace, - parentColumnSpace: parent.parentColumnSpace, - tabId: "", - widgetId: canvasId, - }; + direction, + ); const containerPayload: GeneratedWidgetPayload = yield generateChildWidgets( parent, wrapperPayload, @@ -1832,3 +1824,33 @@ export function getLayoutWrapperName(widgets: CanvasWidgetsReduxState): string { const suffix = arr.length > 0 ? parseInt(arr[0]) + 1 : 1; return `LayoutWrapper${suffix}`; } + +export function getLayoutWrapperPayload( + widgets: CanvasWidgetsReduxState, + payload: Omit< + WidgetAddChild, + "newWidgetId" | "tabId" | "widgetName" | "type" + >, + direction: LayoutDirection = LayoutDirection.Vertical, +): WidgetAddChild { + return { + ...payload, + newWidgetId: generateReactKey(), + type: "LAYOUT_WRAPPER_WIDGET", + widgetName: getLayoutWrapperName(widgets), + props: { + containerStyle: "none", + canExtend: false, + detachFromLayout: true, + children: [], + alignment: Alignment.Left, + spacing: Spacing.None, + isWrapper: true, + backgroundColor: "transparent", + boxShadow: ButtonBoxShadowTypes.NONE, + borderStyle: "none", + }, + columns: direction === LayoutDirection.Vertical ? 64 : payload.columns + 1, + tabId: "0", + }; +} diff --git a/app/client/src/widgets/ContainerWidget/component/index.tsx b/app/client/src/widgets/ContainerWidget/component/index.tsx index ce9745c33cdb..8ebac1c9ab44 100644 --- a/app/client/src/widgets/ContainerWidget/component/index.tsx +++ b/app/client/src/widgets/ContainerWidget/component/index.tsx @@ -102,7 +102,7 @@ export const FlexContainer = styled.div<{ padding: 4px; .wrapper { - flex: 1 1 auto; + flex: 1 1 33.3%; } `; diff --git a/app/client/src/widgets/ContainerWidget/widget/index.tsx b/app/client/src/widgets/ContainerWidget/widget/index.tsx index 68a4d017dad8..db9176c4ebc3 100644 --- a/app/client/src/widgets/ContainerWidget/widget/index.tsx +++ b/app/client/src/widgets/ContainerWidget/widget/index.tsx @@ -317,7 +317,8 @@ export class ContainerWidget extends BaseWidget< this.props.removeWrappers && this.props.removeWrappers(this.props.widgetId); } else if (prevProps.positioning === Positioning.Fixed) - this.props.addWrappers && this.props.addWrappers(this.props.widgetId); + this.props.addWrappers && + this.props.addWrappers(this.props.widgetId, this.state.direction); }; getSnapSpaces = () => { @@ -326,8 +327,7 @@ export class ContainerWidget extends BaseWidget< let padding = (CONTAINER_GRID_PADDING + WIDGET_PADDING) * 2; if ( this.props.widgetId === MAIN_CONTAINER_WIDGET_ID || - this.props.type === "CONTAINER_WIDGET" || - this.props.type === "LAYOUT_WRAPPER_WIDGET" + this.props.type === "CONTAINER_WIDGET" ) { //For MainContainer and any Container Widget padding doesn't exist coz there is already container padding. padding = CONTAINER_GRID_PADDING * 2; @@ -410,7 +410,8 @@ export class ContainerWidget extends BaseWidget< const mapDispatchToProps = (dispatch: any) => ({ removeWrappers: (id: string) => dispatch(removeWrappers(id)), - addWrappers: (id: string) => dispatch(addWrappers(id)), + addWrappers: (id: string, direction: LayoutDirection) => + dispatch(addWrappers(id, direction)), }); export interface ContainerWidgetProps From e61df394bb0ff72a922bca28818eeb0d01297884 Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 7 Sep 2022 12:45:18 -0400 Subject: [PATCH 100/708] fix resize behavior --- .../appsmith/PositionedContainer.tsx | 8 +++++- .../editorComponents/ResizableComponent.tsx | 26 +++++++++++++++++-- .../src/resizable/resizenreflow/index.tsx | 11 +++++++- app/client/src/widgets/BaseWidget.tsx | 9 +++---- .../widgets/ContainerWidget/widget/index.tsx | 2 +- 5 files changed, 45 insertions(+), 11 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/PositionedContainer.tsx b/app/client/src/components/designSystems/appsmith/PositionedContainer.tsx index a60cdf31ea5f..c38cba5dde79 100644 --- a/app/client/src/components/designSystems/appsmith/PositionedContainer.tsx +++ b/app/client/src/components/designSystems/appsmith/PositionedContainer.tsx @@ -12,6 +12,7 @@ import { memoize } from "lodash"; import { getReflowSelector } from "selectors/widgetReflowSelectors"; import { AppState } from "@appsmith/reducers"; import { POSITIONED_WIDGET } from "constants/componentClassNameConstants"; +import { LayoutDirection, ResponsiveBehavior } from "components/constants"; const PositionedWidget = styled.div<{ zIndexOnHover: number }>` &:hover { @@ -29,6 +30,8 @@ export type PositionedContainerProps = { resizeDisabled?: boolean; useAutoLayout?: boolean; isWrapper?: boolean; + responsiveBehavior?: ResponsiveBehavior; + direction?: LayoutDirection; }; export const checkIsDropTarget = memoize(function isDropTarget( @@ -111,7 +114,10 @@ export function PositionedContainer(props: PositionedContainerProps) { ? "auto" : props.style.componentHeight + (props.style.heightUnit || "px"), width: - reflowWidth || props.useAutoLayout + reflowWidth || + (props.useAutoLayout && + props.direction === LayoutDirection.Horizontal && + props.responsiveBehavior === ResponsiveBehavior.Fill) ? "auto" : props.style.componentWidth + (props.style.widthUnit || "px"), padding: padding + "px", diff --git a/app/client/src/components/editorComponents/ResizableComponent.tsx b/app/client/src/components/editorComponents/ResizableComponent.tsx index a608ffb064e7..0afda513f47b 100644 --- a/app/client/src/components/editorComponents/ResizableComponent.tsx +++ b/app/client/src/components/editorComponents/ResizableComponent.tsx @@ -41,7 +41,11 @@ import { focusWidget } from "actions/widgetActions"; import { GridDefaults } from "constants/WidgetConstants"; import { DropTargetContext } from "./DropTargetComponent"; import { XYCord } from "pages/common/CanvasArenas/hooks/useCanvasDragging"; -import { AlignItems, LayoutDirection } from "components/constants"; +import { + AlignItems, + LayoutDirection, + ResponsiveBehavior, +} from "components/constants"; import { AutoLayoutContext } from "utils/autoLayoutContext"; import { getParentToOpenSelector } from "selectors/widgetSelectors"; @@ -302,6 +306,21 @@ export const ResizableComponent = memo(function ResizableComponent( widgetType: props.type, }); }; + let disabledHorizontalHandles: string[] = []; + if ( + props.useAutoLayout && + props.direction === LayoutDirection.Horizontal && + props.responsiveBehavior === ResponsiveBehavior.Fill + ) { + disabledHorizontalHandles = [ + "left", + "right", + "bottomLeft", + "bottomRight", + "topLeft", + "topRight", + ]; + } const handles = useMemo(() => { const allHandles = { left: LeftHandleStyles, @@ -316,8 +335,9 @@ export const ResizableComponent = memo(function ResizableComponent( let handlesToOmit = get(props, "disabledResizeHandles", []); if (disabledResizeHandles && disabledResizeHandles.length) handlesToOmit = [...handlesToOmit, ...disabledResizeHandles]; + handlesToOmit = [...handlesToOmit, ...disabledHorizontalHandles]; return omit(allHandles, handlesToOmit); - }, [props, disabledResizeHandles]); + }, [props, disabledResizeHandles, disabledHorizontalHandles]); const isEnabled = !isDragging && @@ -355,6 +375,7 @@ export const ResizableComponent = memo(function ResizableComponent( allowResize={!isMultiSelectedWidget} componentHeight={componentHeight} componentWidth={componentWidth} + direction={props.direction} enable={isEnabled} getResizedPositions={getResizedPositions} gridProps={gridProps} @@ -364,6 +385,7 @@ export const ResizableComponent = memo(function ResizableComponent( onStop={updateSize} originalPositions={originalPositions} parentId={props.parentId} + responsiveBehavior={props.responsiveBehavior} snapGrid={{ x: props.parentColumnSpace, y: props.parentRowSpace }} updateBottomRow={updateBottomRow} useAutoLayout={useAutoLayout} diff --git a/app/client/src/resizable/resizenreflow/index.tsx b/app/client/src/resizable/resizenreflow/index.tsx index b42dd44c005b..2222c7f89347 100644 --- a/app/client/src/resizable/resizenreflow/index.tsx +++ b/app/client/src/resizable/resizenreflow/index.tsx @@ -19,6 +19,7 @@ import { import { getNearestParentCanvas } from "utils/generators"; import { getContainerOccupiedSpacesSelectorWhileResizing } from "selectors/editorSelectors"; import { isDropZoneOccupied } from "utils/WidgetPropsUtils"; +import { LayoutDirection, ResponsiveBehavior } from "components/constants"; const ResizeWrapper = styled(animated.div)<{ prevents: boolean }>` display: block; @@ -157,6 +158,8 @@ type ResizableProps = { zWidgetId?: string; useAutoLayout?: boolean; isWrapper?: boolean; + direction?: LayoutDirection; + responsiveBehavior?: ResponsiveBehavior; }; export function ReflowResizable(props: ResizableProps) { @@ -502,7 +505,13 @@ export function ReflowResizable(props: ResizableProps) { }} immediate={newDimensions.reset ? true : false} to={{ - width: props.isWrapper || props.useAutoLayout ? "auto" : widgetWidth, + width: + props.isWrapper || + (props.useAutoLayout && + props.direction === LayoutDirection.Horizontal && + props.responsiveBehavior === ResponsiveBehavior.Fill) + ? "auto" + : widgetWidth, height: props.isWrapper ? "auto" : widgetHeight, transform: `translate3d(${newDimensions.x}px,${newDimensions.y}px,0)`, }} diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index 8b0d26f0eb57..21eb30396ca3 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -283,10 +283,12 @@ abstract class BaseWidget< const style = this.getPositionStyle(); return ( alignment?: Alignment; spacing?: Spacing; removeWrappers?: (id: string) => void; - addWrappers?: (id: string) => void; + addWrappers?: (id: string, direction: LayoutDirection) => void; } export interface ContainerWidgetState extends WidgetState { From c845e95b37ced4391e4d625b1e9f6d2da4108e1a Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 7 Sep 2022 12:56:33 -0400 Subject: [PATCH 101/708] refactor sub wrapper --- .../ContainerWidget/component/index.tsx | 33 +++++++------------ 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/app/client/src/widgets/ContainerWidget/component/index.tsx b/app/client/src/widgets/ContainerWidget/component/index.tsx index 8ebac1c9ab44..01bff0f6bf05 100644 --- a/app/client/src/widgets/ContainerWidget/component/index.tsx +++ b/app/client/src/widgets/ContainerWidget/component/index.tsx @@ -100,37 +100,28 @@ export const FlexContainer = styled.div<{ overflow: ${({ overflow }) => overflow?.indexOf("wrap") === -1 ? overflow : "hidden"}; padding: 4px; - - .wrapper { - flex: 1 1 33.3%; - } `; -const StartWrapper = styled.div<{ +const SubWrapper = styled.div<{ flexDirection: FlexDirection; }>` + flex: 1 1 33.3%; display: flex; flex-direction: ${({ flexDirection }) => flexDirection || "row"}; + align-items: ${({ flexDirection }) => + flexDirection === FlexDirection.Column ? "flex-start" : "center"}; +`; + +const StartWrapper = styled(SubWrapper)` justify-content: flex-start; - align-items: center; `; -const EndWrapper = styled.div<{ - flexDirection: FlexDirection; -}>` - display: flex; - flex-direction: ${({ flexDirection }) => flexDirection || "row"}; +const EndWrapper = styled(SubWrapper)` justify-content: flex-end; - align-items: center; `; -const CenterWrapper = styled.div<{ - flexDirection: FlexDirection; -}>` - display: flex; - flex-direction: ${({ flexDirection }) => flexDirection || "row"}; +const CenterWrapper = styled(SubWrapper)` justify-content: center; - align-items: center; `; function ContainerComponentWrapper(props: ContainerComponentProps) { @@ -223,13 +214,13 @@ export function LayoutWrapper(props: FlexBoxProps): JSX.Element { useAutoLayout={props.useAutoLayout} > {start} Date: Sat, 10 Sep 2022 13:01:19 -0400 Subject: [PATCH 102/708] fixes --- app/client/src/sagas/WidgetOperationUtils.ts | 4 +-- app/client/src/utils/layoutPropertiesUtils.ts | 31 ++++++++++++++++--- .../src/widgets/ButtonWidget/widget/index.tsx | 1 + .../widgets/ContainerWidget/widget/index.tsx | 7 +++-- 4 files changed, 35 insertions(+), 8 deletions(-) diff --git a/app/client/src/sagas/WidgetOperationUtils.ts b/app/client/src/sagas/WidgetOperationUtils.ts index 36b371ab0bb7..17e0205ccdc8 100644 --- a/app/client/src/sagas/WidgetOperationUtils.ts +++ b/app/client/src/sagas/WidgetOperationUtils.ts @@ -1780,8 +1780,8 @@ export function* wrapChildren( const wrapperPayload = getLayoutWrapperPayload( widgets, { - rows: child.rows, - columns: child.columns, + rows: child.rows || child.rightColumn - child.leftColumn, + columns: child.columns || child.bottomRow - child.topRow, topRow: child.topRow, leftColumn: child.leftColumn, parentColumnSpace: parent.parentColumnSpace, diff --git a/app/client/src/utils/layoutPropertiesUtils.ts b/app/client/src/utils/layoutPropertiesUtils.ts index d3448832c48b..aa649ddecf86 100644 --- a/app/client/src/utils/layoutPropertiesUtils.ts +++ b/app/client/src/utils/layoutPropertiesUtils.ts @@ -103,14 +103,16 @@ export const generateResponsiveBehaviorConfig = ( }; }; -export const generateAlignmentConfig = (value: Alignment): any => { +export const generateAlignmentConfig = ( + value: Alignment = Alignment.Left, +): any => { return { helpText: "Alignment of children with respect to this parent (applies to Stack positioning)", propertyName: "alignment", label: "Alignment", controlType: "DROP_DOWN", - defaultValue: value || Alignment.Left, + defaultValue: value, options: [ { label: "Top", value: Alignment.Top }, { label: "Bottom", value: Alignment.Bottom }, @@ -125,13 +127,13 @@ export const generateAlignmentConfig = (value: Alignment): any => { }; }; -export const generateSpacingConfig = (value: Spacing): any => { +export const generateSpacingConfig = (value: Spacing = Spacing.None): any => { return { helpText: "Spacing between the children (applies to Stack positioning)", propertyName: "spacing", label: "Spacing", controlType: "DROP_DOWN", - defaultValue: value || Spacing.None, + defaultValue: value, options: [ { label: "None", value: Spacing.None }, { label: "Equal", value: Spacing.Equal }, @@ -145,6 +147,27 @@ export const generateSpacingConfig = (value: Spacing): any => { }; }; +export const generatePositioningConfig = ( + value: Positioning = Positioning.Fixed, +): any => { + return { + helpText: "Position styles to be applied to the children", + propertyName: "positioning", + label: "Positioning", + controlType: "DROP_DOWN", + defaultValue: value, + options: [ + { label: "Fixed", value: Positioning.Fixed }, + { label: "Horizontal stack", value: Positioning.Horizontal }, + { label: "Vertical stack", value: Positioning.Vertical }, + ], + isJSConvertible: false, + isBindProperty: true, + isTriggerProperty: true, + validation: { type: ValidationTypes.TEXT }, + }; +}; + export function getLayoutConfig(alignment: Alignment, spacing: Spacing): any[] { return [generateAlignmentConfig(alignment), generateSpacingConfig(spacing)]; } diff --git a/app/client/src/widgets/ButtonWidget/widget/index.tsx b/app/client/src/widgets/ButtonWidget/widget/index.tsx index e9fa893523ee..0b7cc21cf211 100644 --- a/app/client/src/widgets/ButtonWidget/widget/index.tsx +++ b/app/client/src/widgets/ButtonWidget/widget/index.tsx @@ -103,6 +103,7 @@ class ButtonWidget extends BaseWidget { isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, + { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug) }, ], }, { diff --git a/app/client/src/widgets/ContainerWidget/widget/index.tsx b/app/client/src/widgets/ContainerWidget/widget/index.tsx index 494af7e650e3..4c534de7ec7d 100644 --- a/app/client/src/widgets/ContainerWidget/widget/index.tsx +++ b/app/client/src/widgets/ContainerWidget/widget/index.tsx @@ -24,6 +24,7 @@ import { Spacing, } from "components/constants"; import { + generatePositioningConfig, generateResponsiveBehaviorConfig, getLayoutConfig, } from "utils/layoutPropertiesUtils"; @@ -99,8 +100,8 @@ export class ContainerWidget extends BaseWidget< isTriggerProperty: true, validation: { type: ValidationTypes.TEXT }, }, - ...getLayoutConfig(Alignment.Left, Spacing.None), - { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, + // ...getLayoutConfig(Alignment.Left, Spacing.None), + // { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, ], }, { @@ -198,6 +199,8 @@ export class ContainerWidget extends BaseWidget< isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, + generatePositioningConfig(), + { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, ], }, ]; From c166d28ec874bfe5573597a425eee0caabe26dcc Mon Sep 17 00:00:00 2001 From: Preet Sidhu Date: Sun, 11 Sep 2022 10:40:40 -0400 Subject: [PATCH 103/708] fix wrapper size --- .../src/sagas/CanvasSagas/DraggingCanvasSagas.ts | 1 + app/client/src/sagas/WidgetOperationUtils.ts | 11 ++++++++--- .../src/widgets/ContainerWidget/widget/index.tsx | 12 +++++++++--- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts b/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts index 338eb28bf3e8..5149d2ec2819 100644 --- a/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts +++ b/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts @@ -520,6 +520,7 @@ function* updateMovedWidgets( widgetId: parentId, }, direction, + true, ); const containerPayload: GeneratedWidgetPayload = yield generateChildWidgets( stateParent, diff --git a/app/client/src/sagas/WidgetOperationUtils.ts b/app/client/src/sagas/WidgetOperationUtils.ts index 17e0205ccdc8..aed73484839a 100644 --- a/app/client/src/sagas/WidgetOperationUtils.ts +++ b/app/client/src/sagas/WidgetOperationUtils.ts @@ -1780,8 +1780,8 @@ export function* wrapChildren( const wrapperPayload = getLayoutWrapperPayload( widgets, { - rows: child.rows || child.rightColumn - child.leftColumn, - columns: child.columns || child.bottomRow - child.topRow, + rows: child.rows || child.bottomRow - child.topRow, + columns: child.columns || child.rightColumn - child.leftColumn, topRow: child.topRow, leftColumn: child.leftColumn, parentColumnSpace: parent.parentColumnSpace, @@ -1832,7 +1832,12 @@ export function getLayoutWrapperPayload( "newWidgetId" | "tabId" | "widgetName" | "type" >, direction: LayoutDirection = LayoutDirection.Vertical, + isWrapper = false, ): WidgetAddChild { + // A horizontal wrapper should stretch to occupy the parent's entire width. + const shouldStretch = isWrapper + ? direction === LayoutDirection.Horizontal + : direction === LayoutDirection.Vertical; return { ...payload, newWidgetId: generateReactKey(), @@ -1850,7 +1855,7 @@ export function getLayoutWrapperPayload( boxShadow: ButtonBoxShadowTypes.NONE, borderStyle: "none", }, - columns: direction === LayoutDirection.Vertical ? 64 : payload.columns + 1, + columns: shouldStretch ? 64 : payload.columns + 1, tabId: "0", }; } diff --git a/app/client/src/widgets/ContainerWidget/widget/index.tsx b/app/client/src/widgets/ContainerWidget/widget/index.tsx index 4c534de7ec7d..5ef5e5d11bbf 100644 --- a/app/client/src/widgets/ContainerWidget/widget/index.tsx +++ b/app/client/src/widgets/ContainerWidget/widget/index.tsx @@ -26,7 +26,6 @@ import { import { generatePositioningConfig, generateResponsiveBehaviorConfig, - getLayoutConfig, } from "utils/layoutPropertiesUtils"; import { connect } from "react-redux"; import { addWrappers, removeWrappers } from "actions/autoLayoutActions"; @@ -319,9 +318,15 @@ export class ContainerWidget extends BaseWidget< if (this.props.positioning === Positioning.Fixed) { this.props.removeWrappers && this.props.removeWrappers(this.props.widgetId); - } else if (prevProps.positioning === Positioning.Fixed) + } else if (prevProps.positioning === Positioning.Fixed) { this.props.addWrappers && - this.props.addWrappers(this.props.widgetId, this.state.direction); + this.props.addWrappers( + this.props.widgetId, + this.props.positioning === Positioning.Horizontal + ? LayoutDirection.Horizontal + : LayoutDirection.Vertical, + ); + } }; getSnapSpaces = () => { @@ -386,6 +391,7 @@ export class ContainerWidget extends BaseWidget< }; renderAsContainerComponent(props: ContainerWidgetProps) { + console.log(this.state); // console.log(`${props.widgetName} : ${props.widgetId} =======`); // console.log(props); return ( From ac2808977fe502c5519cb1d1bad29b4f9115943a Mon Sep 17 00:00:00 2001 From: Preet Date: Sun, 11 Sep 2022 12:12:56 -0400 Subject: [PATCH 104/708] fix end offset issue --- .../CanvasArenas/hooks/useAutoLayoutHighlights.ts | 12 ++++++------ .../src/sagas/CanvasSagas/DraggingCanvasSagas.ts | 2 +- app/client/src/sagas/WidgetOperationUtils.ts | 7 ++++++- .../src/widgets/ContainerWidget/widget/index.tsx | 1 - 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index b1a51ebaede2..4a23c261d166 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -334,11 +334,11 @@ export const useAutoLayoutHighlights = ({ center: string[] = [], end: string[] = []; offsetChildren.forEach((each) => { - if (allWidgets[each]?.wrapperType === LayoutWrapperType.Start) - start.push(each); - else if (allWidgets[each]?.wrapperType === LayoutWrapperType.Center) + if (allWidgets[each]?.wrapperType === LayoutWrapperType.Center) center.push(each); - else end.push(each); + else if (allWidgets[each]?.wrapperType === LayoutWrapperType.End) + end.push(each); + else start.push(each); }); const arr1: Highlight[] = evaluateOffsets( start, @@ -370,8 +370,8 @@ export const useAutoLayoutHighlights = ({ if (!offsets || !offsets.length) offsets = [getInitialOffset(isCanvasWrapper)]; - // console.log(`#### offsets: ${JSON.stringify(offsets)}`); - // console.log(`#### END calculate highlight offsets`); + console.log(`#### offsets: ${JSON.stringify(offsets)}`); + console.log(`#### END calculate highlight offsets`); } return offsets; }; diff --git a/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts b/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts index 5149d2ec2819..c1aa4901ffd0 100644 --- a/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts +++ b/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts @@ -520,7 +520,7 @@ function* updateMovedWidgets( widgetId: parentId, }, direction, - true, + stateParent?.isWrapper || false, ); const containerPayload: GeneratedWidgetPayload = yield generateChildWidgets( stateParent, diff --git a/app/client/src/sagas/WidgetOperationUtils.ts b/app/client/src/sagas/WidgetOperationUtils.ts index aed73484839a..f59c99e37a0d 100644 --- a/app/client/src/sagas/WidgetOperationUtils.ts +++ b/app/client/src/sagas/WidgetOperationUtils.ts @@ -57,6 +57,7 @@ import { Alignment, ButtonBoxShadowTypes, LayoutDirection, + LayoutWrapperType, Spacing, } from "components/constants"; import { @@ -1807,7 +1808,11 @@ export function* wrapChildren( children: [...(parent.children || []), wrapper.widgetId], }; widgets[wrapper.widgetId] = wrapper; - widgets[each] = { ...widgets[each], parentId: wrapper.widgetId }; + widgets[each] = { + ...widgets[each], + parentId: wrapper.widgetId, + wrapperType: LayoutWrapperType.Start, + }; } // Update parent diff --git a/app/client/src/widgets/ContainerWidget/widget/index.tsx b/app/client/src/widgets/ContainerWidget/widget/index.tsx index 5ef5e5d11bbf..bd848c6d7b48 100644 --- a/app/client/src/widgets/ContainerWidget/widget/index.tsx +++ b/app/client/src/widgets/ContainerWidget/widget/index.tsx @@ -391,7 +391,6 @@ export class ContainerWidget extends BaseWidget< }; renderAsContainerComponent(props: ContainerWidgetProps) { - console.log(this.state); // console.log(`${props.widgetName} : ${props.widgetId} =======`); // console.log(props); return ( From bb20d0bda6d0542b6487295ecbbc567bfa62aba4 Mon Sep 17 00:00:00 2001 From: Preet Date: Sun, 11 Sep 2022 18:22:24 -0400 Subject: [PATCH 105/708] refactor widget wrap --- .../appsmith/PositionedContainer.tsx | 5 +- .../sagas/CanvasSagas/DraggingCanvasSagas.ts | 67 +++----- app/client/src/sagas/WidgetAdditionSagas.ts | 98 +++-------- app/client/src/sagas/WidgetOperationUtils.ts | 154 ++++++++++++++---- 4 files changed, 166 insertions(+), 158 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/PositionedContainer.tsx b/app/client/src/components/designSystems/appsmith/PositionedContainer.tsx index c38cba5dde79..cf88f7f4326c 100644 --- a/app/client/src/components/designSystems/appsmith/PositionedContainer.tsx +++ b/app/client/src/components/designSystems/appsmith/PositionedContainer.tsx @@ -103,10 +103,7 @@ export function PositionedContainer(props: PositionedContainerProps) { : {}; const styles: CSSProperties = { // TODO: remove the widget type check. Add check for parent type. - position: - props?.useAutoLayout && !props.widgetType?.includes("AUTOLAYOUT") - ? "unset" - : "absolute", + position: props?.useAutoLayout ? "unset" : "absolute", left: x, top: y, height: diff --git a/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts b/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts index c1aa4901ffd0..2b05e74782f5 100644 --- a/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts +++ b/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts @@ -27,8 +27,6 @@ import { collisionCheckPostReflow } from "utils/reflowHookUtils"; import { WidgetDraggingUpdateParams } from "pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas"; import { getWidget, getWidgets } from "sagas/selectors"; import { - generateChildWidgets, - GeneratedWidgetPayload, getUpdateDslAfterCreatingAutoLayoutChild, getUpdateDslAfterCreatingChild, } from "sagas/WidgetAdditionSagas"; @@ -40,8 +38,9 @@ import { ResponsiveBehavior, } from "components/constants"; import { - getLayoutWrapperPayload, + addAndWrapWidget, purgeEmptyWrappers, + WrappedWidgetPayload, } from "../WidgetOperationUtils"; export type WidgetMoveParams = { @@ -454,7 +453,7 @@ function* reorderAutolayoutChildren(params: { // calculate valid position for drop const pos = index > newItems.length ? newItems.length : index; updatedWidgets[parentId] = { - ...trimmedWidgets[parentId], + ...updatedWidgets[parentId], children: [ ...newItems.slice(0, pos), ...newMovedWidgets, @@ -471,74 +470,58 @@ function* updateMovedWidgets( wrapperType: LayoutWrapperType, direction: LayoutDirection, ) { - const stateParent = allWidgets[parentId]; - if (!movedWidgets || !allWidgets) - return { newMovedWidgets: movedWidgets, updatedWidgets: allWidgets }; + let widgets = { ...allWidgets }; + if (!movedWidgets || !widgets) + return { newMovedWidgets: movedWidgets, updatedWidgets: widgets }; + const stateParent = widgets[parentId]; if (stateParent.isWrapper) { // If widgets are being dropped in a wrapper, // then updated the wrapper type and return' let hasFillChild = false; for (const each of movedWidgets) { - if (allWidgets[each].responsiveBehavior === ResponsiveBehavior.Fill) { + if (widgets[each].responsiveBehavior === ResponsiveBehavior.Fill) { hasFillChild = true; break; } - allWidgets[each] = { - ...allWidgets[each], + widgets[each] = { + ...widgets[each], wrapperType, }; } if (hasFillChild) { for (const each of movedWidgets) { - allWidgets[each] = { - ...allWidgets[each], + widgets[each] = { + ...widgets[each], wrapperType: LayoutWrapperType.Start, }; } } - return { newMovedWidgets: movedWidgets, updatedWidgets: allWidgets }; + return { newMovedWidgets: movedWidgets, updatedWidgets: widgets }; } const newMovedWidgets: string[] = []; for (const each of movedWidgets) { - const widget = allWidgets[each]; + const widget = widgets[each]; if (!widget) continue; if (widget.isWrapper) { newMovedWidgets.push(each); continue; } - // wrap the widget in a wrapper widget - const wrapperPayload = getLayoutWrapperPayload( - allWidgets, - { - rows: widget.rows, - columns: widget.columns, - topRow: widget.topRow, - leftColumn: widget.leftColumn, - parentRowSpace: widget.parentRowSpace, - parentColumnSpace: stateParent.parentColumnSpace, - widgetId: parentId, - }, + const res: WrappedWidgetPayload = yield call( + addAndWrapWidget, + widgets, + parentId, + each, + undefined, direction, stateParent?.isWrapper || false, + wrapperType, ); - const containerPayload: GeneratedWidgetPayload = yield generateChildWidgets( - stateParent, - wrapperPayload, - allWidgets, - ); - // Add widget to the wrapper - let wrapper = containerPayload.widgets[containerPayload.widgetId]; - wrapper = { - ...wrapper, - children: [...(wrapper.children || []), each], - }; - // Update parent of the widget - allWidgets[each] = { ...widget, parentId: wrapper.widgetId, wrapperType }; - allWidgets[wrapper.widgetId] = wrapper; - newMovedWidgets.push(wrapper.widgetId); + + widgets = res.widgets; + newMovedWidgets.push(res.wrapperId); } - return { newMovedWidgets, updatedWidgets: allWidgets }; + return { newMovedWidgets, updatedWidgets: widgets }; } function* addWidgetAndReorderSaga( diff --git a/app/client/src/sagas/WidgetAdditionSagas.ts b/app/client/src/sagas/WidgetAdditionSagas.ts index ae63aa9e5ed5..7d6d2f5cb881 100644 --- a/app/client/src/sagas/WidgetAdditionSagas.ts +++ b/app/client/src/sagas/WidgetAdditionSagas.ts @@ -24,8 +24,9 @@ import { traverseTreeAndExecuteBlueprintChildOperations, } from "./WidgetBlueprintSagas"; import { - getLayoutWrapperPayload, + addAndWrapWidget, getParentBottomRowAfterAddingWidget, + WrappedWidgetPayload, } from "./WidgetOperationUtils"; import log from "loglevel"; import { getDataTree } from "selectors/dataTreeSelectors"; @@ -331,113 +332,58 @@ export function* getUpdateDslAfterCreatingAutoLayoutChild( ) { // console.log(addChildPayload); // NOTE: widgetId here is the parentId of the dropped widget ( we should rename it to avoid confusion ) - const { widgetId } = addChildPayload; + const { widgetId: parentId } = addChildPayload; // Get the current parent widget whose child will be the new widget. - const stateParent: FlattenedWidgetProps = yield select(getWidget, widgetId); // console.log("State parent"); // console.log(stateParent); // const parent = Object.assign({}, stateParent); // Get all the widgets from the canvasWidgetsReducer const stateWidgets: CanvasWidgetsReduxState = yield select(getWidgets); - const widgets = Object.assign({}, stateWidgets); + let widgets = Object.assign({}, stateWidgets); // console.log(widgets); - /** - * START create container widget wrapper - */ - - // Generate a payload for canvas widget - const newContainerWidgetPayload = getLayoutWrapperPayload( + const res: WrappedWidgetPayload = yield call( + addAndWrapWidget, widgets, + parentId, + undefined, addChildPayload, direction, ); - const containerPayload: GeneratedWidgetPayload = yield generateChildWidgets( - stateParent, - newContainerWidgetPayload, - widgets, - ); - // console.log(containerPayload); - - /** - * END create container widget wrapper - */ - - /** - * START create new widget - */ - // const containerChildren = - // containerPayload.widgets[containerPayload.widgetId].children || []; - // const canvasWidgetId = containerChildren ? containerChildren[0] : null; - // if (!canvasWidgetId) return { widgets }; - // Generate the full WidgetProps of the widget to be added. - const childWidgetPayload: GeneratedWidgetPayload = yield generateChildWidgets( - containerPayload.widgets[containerPayload.widgetId], - addChildPayload, - containerPayload.widgets, - // sending blueprint for onboarding usecase - addChildPayload.props?.blueprint, - ); - - /** - * END create new widget - */ - - // Update canvas widget - childWidgetPayload.widgets[containerPayload.widgetId] = { - ...childWidgetPayload.widgets[containerPayload.widgetId], - children: [ - ...(childWidgetPayload.widgets[containerPayload.widgetId].children || []), - childWidgetPayload.widgetId, - ], - }; + widgets = res.widgets; // Update container - canvas relationship - const containerWidget = childWidgetPayload.widgets[containerPayload.widgetId]; yield put({ type: WidgetReduxActionTypes.WIDGET_CHILD_ADDED, payload: { - widgetId: containerWidget.widgetId, - type: containerWidget.type, + widgetId: res.wrapperId, + type: "LAYOUT_WRAPPER_WIDGET", }, }); - // console.log(canvasWidget); - // console.log(childWidgetPayload); - // yield put({ - // type: WidgetReduxActionTypes.WIDGET_CHILD_ADDED, - // payload: { - // widgetId: canvasWidget.widgetId, - // type: canvasWidget.type, - // }, - // }); - const parentBottomRow = getParentBottomRowAfterAddingWidget( - stateParent, - containerWidget, - ); + const parent = widgets[parentId]; + const wrapper = widgets[res.wrapperId]; + const child = widgets[addChildPayload.newWidgetId]; - // Update widgets to put back in the canvasWidgetsReducer - // TODO(abhinav): This won't work if dont already have an empty children: [] - const parent = { - ...stateParent, + const parentBottomRow = getParentBottomRowAfterAddingWidget(parent, wrapper); + widgets[parent.widgetId] = { + ...parent, bottomRow: parentBottomRow, - children: [...(stateParent.children || []), containerWidget.widgetId], }; - childWidgetPayload.widgets[parent.widgetId] = parent; // console.log(widgets); AppsmithConsole.info({ text: "Widget was created", source: { type: ENTITY_TYPE.WIDGET, - id: childWidgetPayload.widgetId, - name: childWidgetPayload.widgets[childWidgetPayload.widgetId].widgetName, + id: child.widgetId, + name: child.widgetName, }, }); yield put({ type: WidgetReduxActionTypes.WIDGET_CHILD_ADDED, payload: { - widgetId: childWidgetPayload.widgetId, + widgetId: child.widgetId, type: addChildPayload.type, }, }); @@ -448,10 +394,10 @@ export function* getUpdateDslAfterCreatingAutoLayoutChild( traverseTreeAndExecuteBlueprintChildOperations, parent, addChildPayload.newWidgetId, - childWidgetPayload.widgets, + widgets, ); // console.log(updatedWidgets); - return { widgets: updatedWidgets, containerId: containerWidget.widgetId }; + return { widgets: updatedWidgets, containerId: wrapper.widgetId }; } /** diff --git a/app/client/src/sagas/WidgetOperationUtils.ts b/app/client/src/sagas/WidgetOperationUtils.ts index f59c99e37a0d..8aaeb878ca26 100644 --- a/app/client/src/sagas/WidgetOperationUtils.ts +++ b/app/client/src/sagas/WidgetOperationUtils.ts @@ -1756,7 +1756,7 @@ export function* wrapChildren( containerId: string, direction: LayoutDirection, ) { - const widgets = { ...allWidgets }; + let widgets = { ...allWidgets }; const container = widgets[containerId]; if (!container) return widgets; const canvasId = container.children ? container.children[0] : ""; @@ -1778,45 +1778,17 @@ export function* wrapChildren( const child = widgets[each]; if (!child || child.isWrapper) continue; // Create new wrapper - const wrapperPayload = getLayoutWrapperPayload( + const res: WrappedWidgetPayload = yield call( + addAndWrapWidget, widgets, - { - rows: child.rows || child.bottomRow - child.topRow, - columns: child.columns || child.rightColumn - child.leftColumn, - topRow: child.topRow, - leftColumn: child.leftColumn, - parentColumnSpace: parent.parentColumnSpace, - parentRowSpace: child.parentRowSpace, - widgetId: canvasId, - }, + canvasId, + each, + undefined, direction, + false, ); - const containerPayload: GeneratedWidgetPayload = yield generateChildWidgets( - parent, - wrapperPayload, - widgets, - ); - let wrapper = containerPayload.widgets[containerPayload.widgetId]; - wrapper = { - ...wrapper, - children: [...(wrapper.children || []), each], - }; - - // Add the wrapper to the parent - parent = { - ...parent, - children: [...(parent.children || []), wrapper.widgetId], - }; - widgets[wrapper.widgetId] = wrapper; - widgets[each] = { - ...widgets[each], - parentId: wrapper.widgetId, - wrapperType: LayoutWrapperType.Start, - }; + widgets = res.widgets; } - - // Update parent - widgets[canvasId] = parent; return widgets; } @@ -1864,3 +1836,113 @@ export function getLayoutWrapperPayload( tabId: "0", }; } + +export interface WrappedWidgetPayload { + widgets: CanvasWidgetsReduxState; + wrapperId: string; +} + +export function* addAndWrapWidget( + allWidgets: CanvasWidgetsReduxState, + parentId: string, + childId?: string, + addChildPayload?: WidgetAddChild, + direction = LayoutDirection.Horizontal, + isWrapper = false, + wrapperType = LayoutWrapperType.Start, +) { + let widgets = { ...allWidgets }; + const widgetId: string = childId || addChildPayload?.newWidgetId || ""; + if (!widgetId) return { widgets, wrapperId: parentId }; + let child = widgets[widgetId]; + let parent = widgets[parentId]; + + // remove widget from parent's children + parent = { + ...parent, + children: [...(parent.children || []).filter((each) => each !== widgetId)], + }; + + const payload = childId + ? { + rows: child.rows || child.bottomRow - child.topRow, + columns: child.columns || child.rightColumn - child.leftColumn, + topRow: child.topRow, + leftColumn: child.leftColumn, + parentColumnSpace: parent.parentColumnSpace, + parentRowSpace: child.parentRowSpace, + widgetId: parentId, + } + : addChildPayload + ? { + rows: addChildPayload.rows, + columns: addChildPayload.columns, + topRow: addChildPayload.topRow, + leftColumn: addChildPayload.leftColumn, + parentColumnSpace: parent.parentColumnSpace, + parentRowSpace: addChildPayload.parentRowSpace, + widgetId: parentId, + } + : null; + if (!payload) return { widgets, wrapperId: parentId }; + + // Create new wrapper + const wrapperPayload = getLayoutWrapperPayload( + widgets, + payload, + direction, + isWrapper, + ); + const widgetsAfterCreatingWrapper: GeneratedWidgetPayload = yield generateChildWidgets( + parent, + wrapperPayload, + widgets, + ); + let wrapper = + widgetsAfterCreatingWrapper.widgets[widgetsAfterCreatingWrapper.widgetId]; + + widgets = widgetsAfterCreatingWrapper.widgets; + + // Add child to wrapper's children + wrapper = { + ...wrapper, + children: [...(wrapper.children || []), widgetId], + }; + + // If new widget, then create it. + if (addChildPayload) { + const childPayload: WidgetAddChild = { + ...addChildPayload, + widgetId: wrapper.widgetId, + }; + const widgetsAfterCreatingChild: GeneratedWidgetPayload = yield generateChildWidgets( + wrapper, + childPayload, + widgets, + childPayload?.props?.blueprint, + ); + widgets = widgetsAfterCreatingChild.widgets; + } + + child = widgets[widgetId]; + child = { + ...child, + parentId: wrapper.widgetId, + wrapperType, + }; + + // if (!isWrapper && direction === LayoutDirection.Horizontal) + // child = { + // ...child, + // leftColumn: 0, + // rightColumn: 63, + // }; + + widgets[wrapper.widgetId] = wrapper; + widgets[widgetId] = child; + widgets[parentId] = { + ...parent, + children: [...(parent.children || []), wrapper.widgetId], + }; + return { widgets, wrapperId: wrapper.widgetId }; +} From 9915999e9fdc80b9b9e2f983c77e98865af8e73f Mon Sep 17 00:00:00 2001 From: Preet Date: Sun, 11 Sep 2022 22:50:10 -0400 Subject: [PATCH 106/708] add update wrappers action --- app/client/src/actions/autoLayoutActions.ts | 11 +++ .../src/ce/constants/ReduxActionConstants.tsx | 1 + .../common/CanvasArenas/StickyCanvasArena.tsx | 1 + .../hooks/useAutoLayoutHighlights.ts | 21 +++--- app/client/src/sagas/LayoutUpdateSagas.tsx | 35 ++++++++- app/client/src/sagas/WidgetOperationUtils.ts | 72 ++++++++++++++++--- .../widgets/ContainerWidget/widget/index.tsx | 19 ++++- 7 files changed, 141 insertions(+), 19 deletions(-) diff --git a/app/client/src/actions/autoLayoutActions.ts b/app/client/src/actions/autoLayoutActions.ts index 2e1310596c35..fcb35a2359d0 100644 --- a/app/client/src/actions/autoLayoutActions.ts +++ b/app/client/src/actions/autoLayoutActions.ts @@ -13,3 +13,14 @@ export const addWrappers = (parentId: string, direction: LayoutDirection) => ({ direction, }, }); + +export const updateWrappers = ( + parentId: string, + direction: LayoutDirection, +) => ({ + type: ReduxActionTypes.UPDATE_WRAPPER_DIMENSIONS, + payload: { + parentId, + direction, + }, +}); diff --git a/app/client/src/ce/constants/ReduxActionConstants.tsx b/app/client/src/ce/constants/ReduxActionConstants.tsx index 9c845f6defd9..8fc4ed7bbbce 100644 --- a/app/client/src/ce/constants/ReduxActionConstants.tsx +++ b/app/client/src/ce/constants/ReduxActionConstants.tsx @@ -664,6 +664,7 @@ export const ReduxActionTypes = { AUTOLAYOUT_ADD_NEW_WIDGETS: "AUTOLAYOUT_ADD_NEW_WIDGETS", REMOVE_CHILD_WRAPPERS: "REMOVE_CHILD_WRAPPERS", ADD_CHILD_WRAPPERS: "ADD_CHILD_WRAPPERS", + UPDATE_WRAPPER_DIMENSIONS: "UPDATE_WRAPPER_DIMENSIONS", }; export type ReduxActionType = typeof ReduxActionTypes[keyof typeof ReduxActionTypes]; diff --git a/app/client/src/pages/common/CanvasArenas/StickyCanvasArena.tsx b/app/client/src/pages/common/CanvasArenas/StickyCanvasArena.tsx index 5457bf4c89d0..1198c089c7ef 100644 --- a/app/client/src/pages/common/CanvasArenas/StickyCanvasArena.tsx +++ b/app/client/src/pages/common/CanvasArenas/StickyCanvasArena.tsx @@ -47,6 +47,7 @@ const Highlight = styled.div<{ background-color: rgba(217, 89, 183, 0.8); position: absolute; opacity: 0; + z-index: 4; `; const StickyCanvas = styled.canvas` diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index 4a23c261d166..b3a0c24e2e1d 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -265,15 +265,19 @@ export const useAutoLayoutHighlights = ({ arr?.forEach((each) => { // Get the parent wrapper const wrapperId = getNearestWrapperAncestor(each); - if (wrapperId === canvasId) return; + const isEmpty = isWrapperEmpty(wrapperId, arr); + let el; + if (wrapperId === canvasId) { + if (isEmpty) return; + el = getDomElement(each); + } else { + el = isEmpty ? getDomElement(wrapperId) : getDomElement(each); + } /** * If the wrapper is not the dragging canvas and is empty, * then hide it, * else hide the child element. */ - const el = isWrapperEmpty(wrapperId, arr) - ? getDomElement(wrapperId) - : getDomElement(each); el?.classList?.add("auto-temp-no-display"); }); }; @@ -328,8 +332,9 @@ export const useAutoLayoutHighlights = ({ const flex = document.querySelector(`.flex-container-${canvasId}`); const flexOffsetTop = (flex as any)?.offsetTop || 0; let temp: Highlight[] = []; - const discardEndWrapper: boolean = hasFillChild(offsetChildren); - if (canvas.isWrapper && !discardEndWrapper) { + const discardExtraWrappers: boolean = + hasFillChild(offsetChildren) || direction === LayoutDirection.Vertical; + if (canvas.isWrapper && !discardExtraWrappers) { const start: string[] = [], center: string[] = [], end: string[] = []; @@ -370,8 +375,8 @@ export const useAutoLayoutHighlights = ({ if (!offsets || !offsets.length) offsets = [getInitialOffset(isCanvasWrapper)]; - console.log(`#### offsets: ${JSON.stringify(offsets)}`); - console.log(`#### END calculate highlight offsets`); + // console.log(`#### offsets: ${JSON.stringify(offsets)}`); + // console.log(`#### END calculate highlight offsets`); } return offsets; }; diff --git a/app/client/src/sagas/LayoutUpdateSagas.tsx b/app/client/src/sagas/LayoutUpdateSagas.tsx index 819ea9b4c6c0..c3c222db846a 100644 --- a/app/client/src/sagas/LayoutUpdateSagas.tsx +++ b/app/client/src/sagas/LayoutUpdateSagas.tsx @@ -9,7 +9,11 @@ import log from "loglevel"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; import { all, call, put, select, takeLatest } from "redux-saga/effects"; import { getWidgets } from "./selectors"; -import { purgeChildWrappers, wrapChildren } from "./WidgetOperationUtils"; +import { + purgeChildWrappers, + updateWrapperDimensions, + wrapChildren, +} from "./WidgetOperationUtils"; type LayoutUpdatePayload = { parentId: string; @@ -62,9 +66,38 @@ function* addChildWrappers(actionPayload: ReduxAction) { } } +function* updateChildWrappers(actionPayload: ReduxAction) { + try { + const start = performance.now(); + const { direction, parentId } = actionPayload.payload; + const allWidgets: CanvasWidgetsReduxState = yield select(getWidgets); + const updatedWidgets: CanvasWidgetsReduxState = yield call( + updateWrapperDimensions, + allWidgets, + parentId, + direction, + ); + yield put(updateAndSaveLayout(updatedWidgets)); + log.debug( + "update wrapper dimensions took", + performance.now() - start, + "ms", + ); + } catch (error) { + yield put({ + type: ReduxActionErrorTypes.WIDGET_OPERATION_ERROR, + payload: { + action: ReduxActionTypes.UPDATE_WRAPPER_DIMENSIONS, + error, + }, + }); + } +} + export default function* layoutUpdateSagas() { yield all([ takeLatest(ReduxActionTypes.ADD_CHILD_WRAPPERS, addChildWrappers), takeLatest(ReduxActionTypes.REMOVE_CHILD_WRAPPERS, removeChildWrappers), + takeLatest(ReduxActionTypes.UPDATE_WRAPPER_DIMENSIONS, updateChildWrappers), ]); } diff --git a/app/client/src/sagas/WidgetOperationUtils.ts b/app/client/src/sagas/WidgetOperationUtils.ts index 8aaeb878ca26..5ce68d1fd0a9 100644 --- a/app/client/src/sagas/WidgetOperationUtils.ts +++ b/app/client/src/sagas/WidgetOperationUtils.ts @@ -1869,7 +1869,7 @@ export function* addAndWrapWidget( columns: child.columns || child.rightColumn - child.leftColumn, topRow: child.topRow, leftColumn: child.leftColumn, - parentColumnSpace: parent.parentColumnSpace, + parentColumnSpace: child.parentColumnSpace, parentRowSpace: child.parentRowSpace, widgetId: parentId, } @@ -1879,7 +1879,7 @@ export function* addAndWrapWidget( columns: addChildPayload.columns, topRow: addChildPayload.topRow, leftColumn: addChildPayload.leftColumn, - parentColumnSpace: parent.parentColumnSpace, + parentColumnSpace: addChildPayload.parentColumnSpace, parentRowSpace: addChildPayload.parentRowSpace, widgetId: parentId, } @@ -1931,12 +1931,17 @@ export function* addAndWrapWidget( wrapperType, }; - // if (!isWrapper && direction === LayoutDirection.Horizontal) - // child = { - // ...child, - // leftColumn: 0, - // rightColumn: 63, - // }; + // Update child to fill the width of the parent if it is wrapped in a vertical wrapper. + if ( + (!isWrapper && direction === LayoutDirection.Horizontal) || + (isWrapper && direction === LayoutDirection.Vertical) + ) { + child = { + ...child, + leftColumn: 0, + rightColumn: 63, + }; + } widgets[wrapper.widgetId] = wrapper; widgets[widgetId] = child; @@ -1946,3 +1951,54 @@ export function* addAndWrapWidget( }; return { widgets, wrapperId: wrapper.widgetId }; } + +export function updateWrapperDimensions( + allWidgets: CanvasWidgetsReduxState, + parentId: string, + direction: LayoutDirection, +): CanvasWidgetsReduxState { + const widgets = { ...allWidgets }; + const container = widgets[parentId]; + if (!container?.children) return widgets; + const canvas = widgets[container.children[0]]; + const wrappers = canvas?.children; + if (!wrappers) return widgets; + if (direction === LayoutDirection.Vertical) { + for (const each of wrappers) { + const wrapper = widgets[each]; + if (!wrapper || !wrapper.children) continue; + const prevRightColumn = wrapper.rightColumn; + widgets[each] = { + ...wrapper, + leftColumn: 0, + rightColumn: 63, + }; + const children = wrapper.children; + for (const child of children) { + const childWidget = widgets[child]; + widgets[child] = { + ...childWidget, + leftColumn: 0, + rightColumn: prevRightColumn ? prevRightColumn - 1 : 0, + }; + } + } + } else { + for (const each of wrappers) { + const wrapper = widgets[each]; + if (!wrapper || !wrapper.children) continue; + const children = wrapper.children; + let max = 0; + for (const child of children) { + const childWidget = widgets[child]; + max = Math.max(max, childWidget.rightColumn - childWidget.leftColumn); + } + widgets[each] = { + ...wrapper, + leftColumn: 0, + rightColumn: max + 1, + }; + } + } + return widgets; +} diff --git a/app/client/src/widgets/ContainerWidget/widget/index.tsx b/app/client/src/widgets/ContainerWidget/widget/index.tsx index bd848c6d7b48..2de0c23d8a43 100644 --- a/app/client/src/widgets/ContainerWidget/widget/index.tsx +++ b/app/client/src/widgets/ContainerWidget/widget/index.tsx @@ -28,7 +28,11 @@ import { generateResponsiveBehaviorConfig, } from "utils/layoutPropertiesUtils"; import { connect } from "react-redux"; -import { addWrappers, removeWrappers } from "actions/autoLayoutActions"; +import { + addWrappers, + removeWrappers, + updateWrappers, +} from "actions/autoLayoutActions"; export class ContainerWidget extends BaseWidget< ContainerWidgetProps, @@ -326,11 +330,19 @@ export class ContainerWidget extends BaseWidget< ? LayoutDirection.Horizontal : LayoutDirection.Vertical, ); - } + } else + this.props.updateWrappers && + this.props.updateWrappers( + this.props.widgetId, + this.props.positioning === Positioning.Horizontal + ? LayoutDirection.Horizontal + : LayoutDirection.Vertical, + ); }; getSnapSpaces = () => { const { componentWidth } = this.getComponentDimensions(); + console.log(`${this.props.widgetName}: ${componentWidth}`); // For all widgets inside a container, we remove both container padding as well as widget padding from component width let padding = (CONTAINER_GRID_PADDING + WIDGET_PADDING) * 2; if ( @@ -420,6 +432,8 @@ const mapDispatchToProps = (dispatch: any) => ({ removeWrappers: (id: string) => dispatch(removeWrappers(id)), addWrappers: (id: string, direction: LayoutDirection) => dispatch(addWrappers(id, direction)), + updateWrappers: (id: string, direction: LayoutDirection) => + dispatch(updateWrappers(id, direction)), }); export interface ContainerWidgetProps @@ -433,6 +447,7 @@ export interface ContainerWidgetProps spacing?: Spacing; removeWrappers?: (id: string) => void; addWrappers?: (id: string, direction: LayoutDirection) => void; + updateWrappers?: (id: string, direction: LayoutDirection) => void; } export interface ContainerWidgetState extends WidgetState { From 2956f6363304e41519be641dd718c9d21b1b1b05 Mon Sep 17 00:00:00 2001 From: Preet Date: Sun, 11 Sep 2022 23:00:39 -0400 Subject: [PATCH 107/708] remove console log --- app/client/src/widgets/ContainerWidget/widget/index.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/app/client/src/widgets/ContainerWidget/widget/index.tsx b/app/client/src/widgets/ContainerWidget/widget/index.tsx index 2de0c23d8a43..a5b7fb8b0dcb 100644 --- a/app/client/src/widgets/ContainerWidget/widget/index.tsx +++ b/app/client/src/widgets/ContainerWidget/widget/index.tsx @@ -342,7 +342,6 @@ export class ContainerWidget extends BaseWidget< getSnapSpaces = () => { const { componentWidth } = this.getComponentDimensions(); - console.log(`${this.props.widgetName}: ${componentWidth}`); // For all widgets inside a container, we remove both container padding as well as widget padding from component width let padding = (CONTAINER_GRID_PADDING + WIDGET_PADDING) * 2; if ( From 7cf5b5338012d7c32c4546e66e2d85e2e103e07c Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 14 Sep 2022 10:39:58 -0400 Subject: [PATCH 108/708] update content config --- app/client/src/widgets/AudioRecorderWidget/index.ts | 2 ++ .../src/widgets/AudioRecorderWidget/widget/index.tsx | 1 + app/client/src/widgets/AudioWidget/index.tsx | 2 ++ app/client/src/widgets/AudioWidget/widget/index.tsx | 1 + app/client/src/widgets/BaseInputWidget/index.ts | 3 ++- app/client/src/widgets/BaseInputWidget/widget/index.tsx | 4 +++- app/client/src/widgets/ButtonGroupWidget/widget/index.tsx | 5 +++++ app/client/src/widgets/CheckboxWidget/widget/index.tsx | 1 + app/client/src/widgets/DatePickerWidget2/index.ts | 3 ++- app/client/src/widgets/DatePickerWidget2/widget/index.tsx | 4 +++- app/client/src/widgets/DividerWidget/widget/index.tsx | 1 + .../src/widgets/FilePickerWidgetV2/widget/index.tsx | 1 + app/client/src/widgets/IconButtonWidget/widget/index.tsx | 8 +++++++- .../src/widgets/ListWidget/widget/propertyConfig.ts | 1 + app/client/src/widgets/ModalWidget/widget/index.tsx | 6 +++++- .../src/widgets/MultiSelectTreeWidget/widget/index.tsx | 1 + .../src/widgets/MultiSelectWidgetV2/widget/index.tsx | 1 + .../src/widgets/RichTextEditorWidget/widget/index.tsx | 1 + app/client/src/widgets/SelectWidget/widget/index.tsx | 1 + .../src/widgets/SingleSelectTreeWidget/widget/index.tsx | 1 + .../TableWidgetV2/widget/propertyConfig/contentConfig.ts | 3 +++ app/client/src/widgets/TabsWidget/widget/index.tsx | 3 +++ app/client/src/widgets/TextWidget/index.ts | 2 ++ app/client/src/widgets/TextWidget/widget/index.tsx | 3 +++ 24 files changed, 53 insertions(+), 6 deletions(-) diff --git a/app/client/src/widgets/AudioRecorderWidget/index.ts b/app/client/src/widgets/AudioRecorderWidget/index.ts index b5b0c3bc5dc1..c7c67220dcbb 100644 --- a/app/client/src/widgets/AudioRecorderWidget/index.ts +++ b/app/client/src/widgets/AudioRecorderWidget/index.ts @@ -1,5 +1,6 @@ import Widget from "./widget"; import IconSVG from "./icon.svg"; +import { ResponsiveBehavior } from "components/constants"; export const CONFIG = { type: Widget.getWidgetType(), @@ -16,6 +17,7 @@ export const CONFIG = { widgetName: "AudioRecorder", version: 1, animateLoading: true, + responsiveBehavior: ResponsiveBehavior.Fill, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/AudioRecorderWidget/widget/index.tsx b/app/client/src/widgets/AudioRecorderWidget/widget/index.tsx index 1720f447c41e..c9afa617f849 100644 --- a/app/client/src/widgets/AudioRecorderWidget/widget/index.tsx +++ b/app/client/src/widgets/AudioRecorderWidget/widget/index.tsx @@ -182,6 +182,7 @@ class AudioRecorderWidget extends BaseWidget< isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, + { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, ], }, { diff --git a/app/client/src/widgets/AudioWidget/index.tsx b/app/client/src/widgets/AudioWidget/index.tsx index 513300c1a743..50836a27eb5d 100644 --- a/app/client/src/widgets/AudioWidget/index.tsx +++ b/app/client/src/widgets/AudioWidget/index.tsx @@ -1,5 +1,6 @@ import Widget from "./widget"; import IconSVG from "./icon.svg"; +import { ResponsiveBehavior } from "components/constants"; export const CONFIG = { type: Widget.getWidgetType(), @@ -15,6 +16,7 @@ export const CONFIG = { autoPlay: false, version: 1, animateLoading: true, + responsiveBehavior: ResponsiveBehavior.Fill, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/AudioWidget/widget/index.tsx b/app/client/src/widgets/AudioWidget/widget/index.tsx index 5cc245e8527a..eb10f5a967ce 100644 --- a/app/client/src/widgets/AudioWidget/widget/index.tsx +++ b/app/client/src/widgets/AudioWidget/widget/index.tsx @@ -177,6 +177,7 @@ class AudioWidget extends BaseWidget { isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, + { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, ], }, { diff --git a/app/client/src/widgets/BaseInputWidget/index.ts b/app/client/src/widgets/BaseInputWidget/index.ts index 51ee367b404d..ab7b50508778 100644 --- a/app/client/src/widgets/BaseInputWidget/index.ts +++ b/app/client/src/widgets/BaseInputWidget/index.ts @@ -1,6 +1,6 @@ import Widget from "./widget"; import IconSVG from "./icon.svg"; -import { LabelPosition } from "components/constants"; +import { LabelPosition, ResponsiveBehavior } from "components/constants"; import { Alignment } from "@blueprintjs/core"; export const CONFIG = { @@ -27,6 +27,7 @@ export const CONFIG = { isRequired: false, isDisabled: false, animateLoading: true, + responsiveBehavior: ResponsiveBehavior.Fill, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/BaseInputWidget/widget/index.tsx b/app/client/src/widgets/BaseInputWidget/widget/index.tsx index 858df84479f2..d29bfee955b7 100644 --- a/app/client/src/widgets/BaseInputWidget/widget/index.tsx +++ b/app/client/src/widgets/BaseInputWidget/widget/index.tsx @@ -11,7 +11,8 @@ import { ValidationTypes } from "constants/WidgetValidation"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; import BaseInputComponent from "../component"; import { InputTypes } from "../constants"; -import { LabelPosition } from "components/constants"; +import { LabelPosition, ResponsiveBehavior } from "components/constants"; +import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; class BaseInputWidget< T extends BaseInputWidgetProps, @@ -582,6 +583,7 @@ class BaseInputWidget< return props.type !== "PHONE_INPUT_WIDGET"; }, }, + { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, ], }, { diff --git a/app/client/src/widgets/ButtonGroupWidget/widget/index.tsx b/app/client/src/widgets/ButtonGroupWidget/widget/index.tsx index 51249717003e..9f4301c14d64 100644 --- a/app/client/src/widgets/ButtonGroupWidget/widget/index.tsx +++ b/app/client/src/widgets/ButtonGroupWidget/widget/index.tsx @@ -774,6 +774,11 @@ class ButtonGroupWidget extends BaseWidget< isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, + { + ...generateResponsiveBehaviorConfig( + ResponsiveBehavior.Fill, + ), + }, ], }, { diff --git a/app/client/src/widgets/CheckboxWidget/widget/index.tsx b/app/client/src/widgets/CheckboxWidget/widget/index.tsx index ddd5535bc49e..6536cf3aae49 100644 --- a/app/client/src/widgets/CheckboxWidget/widget/index.tsx +++ b/app/client/src/widgets/CheckboxWidget/widget/index.tsx @@ -347,6 +347,7 @@ class CheckboxWidget extends BaseWidget { isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, + { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, ], }, { diff --git a/app/client/src/widgets/DatePickerWidget2/index.ts b/app/client/src/widgets/DatePickerWidget2/index.ts index fcaa30e74825..16de366a21b5 100644 --- a/app/client/src/widgets/DatePickerWidget2/index.ts +++ b/app/client/src/widgets/DatePickerWidget2/index.ts @@ -1,5 +1,5 @@ import { Alignment } from "@blueprintjs/core"; -import { LabelPosition } from "components/constants"; +import { LabelPosition, ResponsiveBehavior } from "components/constants"; import moment from "moment"; import { TimePrecision } from "./constants"; import IconSVG from "./icon.svg"; @@ -33,6 +33,7 @@ export const CONFIG = { firstDayOfWeek: 0, timePrecision: TimePrecision.MINUTE, animateLoading: true, + responsiveBehavior: ResponsiveBehavior.Fill, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/DatePickerWidget2/widget/index.tsx b/app/client/src/widgets/DatePickerWidget2/widget/index.tsx index 70f68267cf98..25147ebc2d37 100644 --- a/app/client/src/widgets/DatePickerWidget2/widget/index.tsx +++ b/app/client/src/widgets/DatePickerWidget2/widget/index.tsx @@ -10,10 +10,11 @@ import { AutocompleteDataType } from "utils/autocomplete/TernServer"; import derivedProperties from "./parseDerivedProperties"; import { DatePickerType, TimePrecision } from "../constants"; -import { LabelPosition } from "components/constants"; +import { LabelPosition, ResponsiveBehavior } from "components/constants"; import { Alignment } from "@blueprintjs/core"; import { GRID_DENSITY_MIGRATION_V1 } from "widgets/constants"; import { DateFormatOptions } from "./constants"; +import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; function allowedRange(value: any) { const allowedValues = [0, 1, 2, 3, 4, 5, 6]; @@ -654,6 +655,7 @@ class DatePickerWidget extends BaseWidget { isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, + { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, ], }, { diff --git a/app/client/src/widgets/DividerWidget/widget/index.tsx b/app/client/src/widgets/DividerWidget/widget/index.tsx index 618a05f6abba..fd23d0ccf593 100644 --- a/app/client/src/widgets/DividerWidget/widget/index.tsx +++ b/app/client/src/widgets/DividerWidget/widget/index.tsx @@ -212,6 +212,7 @@ class DividerWidget extends BaseWidget { isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, + { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, ], }, ]; diff --git a/app/client/src/widgets/FilePickerWidgetV2/widget/index.tsx b/app/client/src/widgets/FilePickerWidgetV2/widget/index.tsx index 878a8e07fe61..d57976ce234f 100644 --- a/app/client/src/widgets/FilePickerWidgetV2/widget/index.tsx +++ b/app/client/src/widgets/FilePickerWidgetV2/widget/index.tsx @@ -600,6 +600,7 @@ class FilePickerWidget extends BaseWidget< isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, + { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, ], }, { diff --git a/app/client/src/widgets/IconButtonWidget/widget/index.tsx b/app/client/src/widgets/IconButtonWidget/widget/index.tsx index e5423aa932ea..cbf8021f71c3 100644 --- a/app/client/src/widgets/IconButtonWidget/widget/index.tsx +++ b/app/client/src/widgets/IconButtonWidget/widget/index.tsx @@ -8,7 +8,12 @@ import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; import IconButtonComponent from "../component"; import { IconNames } from "@blueprintjs/icons"; -import { ButtonVariant, ButtonVariantTypes } from "components/constants"; +import { + ButtonVariant, + ButtonVariantTypes, + ResponsiveBehavior, +} from "components/constants"; +import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; const ICON_NAMES = Object.keys(IconNames).map( (name: string) => IconNames[name as keyof typeof IconNames], @@ -261,6 +266,7 @@ class IconButtonWidget extends BaseWidget { isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, + { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug) }, ], }, ]; diff --git a/app/client/src/widgets/ListWidget/widget/propertyConfig.ts b/app/client/src/widgets/ListWidget/widget/propertyConfig.ts index 0ba50d7475a2..37714c2ec074 100644 --- a/app/client/src/widgets/ListWidget/widget/propertyConfig.ts +++ b/app/client/src/widgets/ListWidget/widget/propertyConfig.ts @@ -275,6 +275,7 @@ export const PropertyPaneContentConfig = [ isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, + { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, ], }, { diff --git a/app/client/src/widgets/ModalWidget/widget/index.tsx b/app/client/src/widgets/ModalWidget/widget/index.tsx index 7393e9646bb3..ecca34e1433b 100644 --- a/app/client/src/widgets/ModalWidget/widget/index.tsx +++ b/app/client/src/widgets/ModalWidget/widget/index.tsx @@ -16,7 +16,10 @@ import { getCanvasWidth, snipingModeSelector } from "selectors/editorSelectors"; import { deselectAllInitAction } from "actions/widgetSelectionActions"; import { ValidationTypes } from "constants/WidgetValidation"; import { Alignment, Positioning, Spacing } from "components/constants"; -import { getLayoutConfig } from "utils/layoutPropertiesUtils"; +import { + generatePositioningConfig, + getLayoutConfig, +} from "utils/layoutPropertiesUtils"; const minSize = 100; @@ -154,6 +157,7 @@ export class ModalWidget extends BaseWidget { isBindProperty: false, isTriggerProperty: false, }, + { ...generatePositioningConfig(Positioning.Fixed) }, ], }, { diff --git a/app/client/src/widgets/MultiSelectTreeWidget/widget/index.tsx b/app/client/src/widgets/MultiSelectTreeWidget/widget/index.tsx index b96e53068956..96e7e216473c 100644 --- a/app/client/src/widgets/MultiSelectTreeWidget/widget/index.tsx +++ b/app/client/src/widgets/MultiSelectTreeWidget/widget/index.tsx @@ -705,6 +705,7 @@ class MultiSelectTreeWidget extends BaseWidget< isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, + { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, ], }, { diff --git a/app/client/src/widgets/MultiSelectWidgetV2/widget/index.tsx b/app/client/src/widgets/MultiSelectWidgetV2/widget/index.tsx index 2ada69459c5b..61d979d61217 100644 --- a/app/client/src/widgets/MultiSelectWidgetV2/widget/index.tsx +++ b/app/client/src/widgets/MultiSelectWidgetV2/widget/index.tsx @@ -794,6 +794,7 @@ class MultiSelectWidget extends BaseWidget< isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, + { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, ], }, { diff --git a/app/client/src/widgets/RichTextEditorWidget/widget/index.tsx b/app/client/src/widgets/RichTextEditorWidget/widget/index.tsx index 710a32260a21..0efa16eaf215 100644 --- a/app/client/src/widgets/RichTextEditorWidget/widget/index.tsx +++ b/app/client/src/widgets/RichTextEditorWidget/widget/index.tsx @@ -472,6 +472,7 @@ class RichTextEditorWidget extends BaseWidget< isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, + { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, ], }, { diff --git a/app/client/src/widgets/SelectWidget/widget/index.tsx b/app/client/src/widgets/SelectWidget/widget/index.tsx index 689733b05fa7..bece6e998798 100644 --- a/app/client/src/widgets/SelectWidget/widget/index.tsx +++ b/app/client/src/widgets/SelectWidget/widget/index.tsx @@ -704,6 +704,7 @@ class SelectWidget extends BaseWidget { isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, + { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, ], }, { diff --git a/app/client/src/widgets/SingleSelectTreeWidget/widget/index.tsx b/app/client/src/widgets/SingleSelectTreeWidget/widget/index.tsx index d420e77b3704..856cabf19515 100644 --- a/app/client/src/widgets/SingleSelectTreeWidget/widget/index.tsx +++ b/app/client/src/widgets/SingleSelectTreeWidget/widget/index.tsx @@ -646,6 +646,7 @@ class SingleSelectTreeWidget extends BaseWidget< isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, + { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, ], }, { diff --git a/app/client/src/widgets/TableWidgetV2/widget/propertyConfig/contentConfig.ts b/app/client/src/widgets/TableWidgetV2/widget/propertyConfig/contentConfig.ts index 628ade832424..e165ee4ef4f2 100644 --- a/app/client/src/widgets/TableWidgetV2/widget/propertyConfig/contentConfig.ts +++ b/app/client/src/widgets/TableWidgetV2/widget/propertyConfig/contentConfig.ts @@ -19,6 +19,8 @@ import { import panelConfig from "./PanelConfig"; import { composePropertyUpdateHook } from "widgets/WidgetUtils"; import { PropertyPaneConfig } from "constants/PropertyControlConstants"; +import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; +import { ResponsiveBehavior } from "components/constants"; export default [ { @@ -399,6 +401,7 @@ export default [ hidden: (props: TableWidgetProps) => !props.isVisibleDownload, dependencies: ["isVisibleDownload"], }, + { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, ], }, ] as PropertyPaneConfig[]; diff --git a/app/client/src/widgets/TabsWidget/widget/index.tsx b/app/client/src/widgets/TabsWidget/widget/index.tsx index ededba815658..21bd272a1663 100644 --- a/app/client/src/widgets/TabsWidget/widget/index.tsx +++ b/app/client/src/widgets/TabsWidget/widget/index.tsx @@ -20,6 +20,7 @@ import { Spacing, } from "components/constants"; import { + generatePositioningConfig, generateResponsiveBehaviorConfig, getLayoutConfig, } from "utils/layoutPropertiesUtils"; @@ -315,6 +316,7 @@ class TabsWidget extends BaseWidget< isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, + { ...generatePositioningConfig(Positioning.Fixed) }, ], }, ], @@ -384,6 +386,7 @@ class TabsWidget extends BaseWidget< isBindProperty: false, isTriggerProperty: false, }, + { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, ], }, { diff --git a/app/client/src/widgets/TextWidget/index.ts b/app/client/src/widgets/TextWidget/index.ts index abed67cbd506..8f225ca79428 100644 --- a/app/client/src/widgets/TextWidget/index.ts +++ b/app/client/src/widgets/TextWidget/index.ts @@ -1,3 +1,4 @@ +import { ResponsiveBehavior } from "components/constants"; import { DEFAULT_FONT_SIZE } from "constants/WidgetConstants"; import { OverflowTypes } from "./constants"; import IconSVG from "./icon.svg"; @@ -22,6 +23,7 @@ export const CONFIG = { overflow: OverflowTypes.NONE, version: 1, animateLoading: true, + responsiveBehavior: ResponsiveBehavior.Fill, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/TextWidget/widget/index.tsx b/app/client/src/widgets/TextWidget/widget/index.tsx index f3784052ad24..0bcbe58854b5 100644 --- a/app/client/src/widgets/TextWidget/widget/index.tsx +++ b/app/client/src/widgets/TextWidget/widget/index.tsx @@ -14,6 +14,8 @@ import { AutocompleteDataType } from "utils/autocomplete/TernServer"; import { OverflowTypes } from "../constants"; import WidgetStyleContainer from "components/designSystems/appsmith/WidgetStyleContainer"; import { pick } from "lodash"; +import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; +import { ResponsiveBehavior } from "components/constants"; const MAX_HTML_PARSING_LENGTH = 1000; class TextWidget extends BaseWidget { @@ -384,6 +386,7 @@ class TextWidget extends BaseWidget { isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, + { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, ], }, ]; From 05c8ba8abb72105689db8603d456c70e14a9179d Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 15 Sep 2022 12:55:55 -0400 Subject: [PATCH 109/708] auto width of horizontal children --- .../src/components/AutoLayoutWrapper.tsx | 11 +++---- .../appsmith/PositionedContainer.tsx | 6 ++-- .../editorComponents/DropTargetComponent.tsx | 8 +++-- .../hooks/useBlocksToBeDraggedOnCanvas.ts | 29 +++---------------- .../src/resizable/resizenreflow/index.tsx | 6 ++-- app/client/src/sagas/WidgetOperationUtils.ts | 20 ++++++------- 6 files changed, 32 insertions(+), 48 deletions(-) diff --git a/app/client/src/components/AutoLayoutWrapper.tsx b/app/client/src/components/AutoLayoutWrapper.tsx index 84e32fe2c287..f640d27cc743 100644 --- a/app/client/src/components/AutoLayoutWrapper.tsx +++ b/app/client/src/components/AutoLayoutWrapper.tsx @@ -62,13 +62,10 @@ const ZIndexContainer = styled.div<{ `; export function AutoLayoutWrapper(props: AutoLayoutProps) { - const clickToSelectWidget = useClickToSelectWidget(); - const onClickFn = useCallback( - (e) => { - clickToSelectWidget(e, props.widgetId); - }, - [props.widgetId, clickToSelectWidget], - ); + const clickToSelectWidget = useClickToSelectWidget(props.widgetId); + const onClickFn = useCallback(() => { + clickToSelectWidget(props.widgetId); + }, [props.widgetId, clickToSelectWidget]); const isDropTarget = checkIsDropTarget(props.widgetType); const isDragging = useSelector( diff --git a/app/client/src/components/designSystems/appsmith/PositionedContainer.tsx b/app/client/src/components/designSystems/appsmith/PositionedContainer.tsx index 081a876a251b..6054a8265d54 100644 --- a/app/client/src/components/designSystems/appsmith/PositionedContainer.tsx +++ b/app/client/src/components/designSystems/appsmith/PositionedContainer.tsx @@ -155,8 +155,10 @@ export function PositionedContainer(props: PositionedContainerProps) { width: reflowWidth || (props.useAutoLayout && - props.direction === LayoutDirection.Horizontal && - props.responsiveBehavior === ResponsiveBehavior.Fill) + !( + props.direction === LayoutDirection.Horizontal && + props.responsiveBehavior === ResponsiveBehavior.Hug + )) ? "auto" : style.componentWidth + (style.widthUnit || "px"), padding: padding + "px", diff --git a/app/client/src/components/editorComponents/DropTargetComponent.tsx b/app/client/src/components/editorComponents/DropTargetComponent.tsx index eb448d2b72c1..684f78851db0 100644 --- a/app/client/src/components/editorComponents/DropTargetComponent.tsx +++ b/app/client/src/components/editorComponents/DropTargetComponent.tsx @@ -40,6 +40,7 @@ type DropTargetComponentProps = WidgetProps & { const StyledDropTarget = styled.div<{ direction?: LayoutDirection; + isDragging: boolean; isWrapper: boolean; }>` transition: height 100ms ease-in; @@ -48,8 +49,10 @@ const StyledDropTarget = styled.div<{ background: none; user-select: none; z-index: ${({ isWrapper }) => (isWrapper ? 2 : 1)}; - margin-bottom: ${({ direction, isWrapper }) => - isWrapper && direction === LayoutDirection.Horizontal ? "6px" : 0}; + margin-top: ${({ direction, isDragging, isWrapper }) => + isWrapper && isDragging && direction === LayoutDirection.Horizontal + ? "8px" + : 0}; margin-right: ${({ direction, isWrapper }) => isWrapper && direction === LayoutDirection.Vertical ? "6px" : 0}; `; @@ -208,6 +211,7 @@ export function DropTargetComponent(props: DropTargetComponentProps) { { const dispatch = useDispatch(); @@ -229,19 +228,9 @@ export const useBlocksToBeDraggedOnCanvas = ({ { top: 0, left: 0, - width: - useAutoLayout && - direction === LayoutDirection.Vertical && - alignItems === AlignItems.Stretch - ? 64 * snapColumnSpace - : newWidget.columns * snapColumnSpace, + width: newWidget.columns * snapColumnSpace, height: newWidget.rows * snapRowSpace, - columnWidth: - useAutoLayout && - direction === LayoutDirection.Vertical && - alignItems === AlignItems.Stretch - ? 64 - : newWidget.columns, + columnWidth: newWidget.columns, rowHeight: newWidget.rows, widgetId: newWidget.widgetId, detachFromLayout: newWidget.detachFromLayout, @@ -267,19 +256,9 @@ export const useBlocksToBeDraggedOnCanvas = ({ blocksToDraw: draggingSpaces.map((each) => ({ top: each.top * snapRowSpace + containerPadding, left: each.left * snapColumnSpace + containerPadding, - width: - useAutoLayout && - direction === LayoutDirection.Vertical && - alignItems === AlignItems.Stretch - ? 64 * snapColumnSpace - : (each.right - each.left) * snapColumnSpace, + width: (each.right - each.left) * snapColumnSpace, height: (each.bottom - each.top) * snapRowSpace, - columnWidth: - useAutoLayout && - direction === LayoutDirection.Vertical && - alignItems === AlignItems.Stretch - ? 64 - : each.right - each.left, + columnWidth: each.right - each.left, rowHeight: each.bottom - each.top, widgetId: each.id, isNotColliding: true, diff --git a/app/client/src/resizable/resizenreflow/index.tsx b/app/client/src/resizable/resizenreflow/index.tsx index 2222c7f89347..d0929a5ddef2 100644 --- a/app/client/src/resizable/resizenreflow/index.tsx +++ b/app/client/src/resizable/resizenreflow/index.tsx @@ -508,8 +508,10 @@ export function ReflowResizable(props: ResizableProps) { width: props.isWrapper || (props.useAutoLayout && - props.direction === LayoutDirection.Horizontal && - props.responsiveBehavior === ResponsiveBehavior.Fill) + !( + props.direction === LayoutDirection.Horizontal && + props.responsiveBehavior === ResponsiveBehavior.Hug + )) ? "auto" : widgetWidth, height: props.isWrapper ? "auto" : widgetHeight, diff --git a/app/client/src/sagas/WidgetOperationUtils.ts b/app/client/src/sagas/WidgetOperationUtils.ts index 5ce68d1fd0a9..3889d04ae424 100644 --- a/app/client/src/sagas/WidgetOperationUtils.ts +++ b/app/client/src/sagas/WidgetOperationUtils.ts @@ -1932,16 +1932,16 @@ export function* addAndWrapWidget( }; // Update child to fill the width of the parent if it is wrapped in a vertical wrapper. - if ( - (!isWrapper && direction === LayoutDirection.Horizontal) || - (isWrapper && direction === LayoutDirection.Vertical) - ) { - child = { - ...child, - leftColumn: 0, - rightColumn: 63, - }; - } + // if ( + // (!isWrapper && direction === LayoutDirection.Horizontal) || + // (isWrapper && direction === LayoutDirection.Vertical) + // ) { + // child = { + // ...child, + // leftColumn: 0, + // rightColumn: 63, + // }; + // } widgets[wrapper.widgetId] = wrapper; widgets[widgetId] = child; From 48d3390389f0975193b8486862a3aea891b52ff2 Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 20 Sep 2022 08:45:56 -0400 Subject: [PATCH 110/708] fix wrapper height --- app/client/src/widgets/LayoutWrapperWidget.tsx | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/app/client/src/widgets/LayoutWrapperWidget.tsx b/app/client/src/widgets/LayoutWrapperWidget.tsx index 0eb7d8faeed5..aadbf203b2c5 100644 --- a/app/client/src/widgets/LayoutWrapperWidget.tsx +++ b/app/client/src/widgets/LayoutWrapperWidget.tsx @@ -189,16 +189,9 @@ class LayoutWrapperWidget extends ContainerWidget { } getPageView() { - let height = 0; - const snapRows = getCanvasSnapRows( - this.props.bottomRow, - this.props.canExtend, - ); - height = snapRows * GridDefaults.DEFAULT_GRID_ROW_HEIGHT; - const style: CSSProperties = { width: "100%", - height: `${height}px`, + height: "100%", background: "none", position: "relative", }; From 79273c538305d616bf41fd989a874d7f7cdbadea Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 28 Sep 2022 11:31:53 -0400 Subject: [PATCH 111/708] add FlexComponent, reset PositionedContainer --- .../src/components/AutoLayoutWrapper.tsx | 102 ------------------ .../designSystems/appsmith/FlexComponent.tsx | 101 +++++++++++++++++ .../appsmith/PositionedContainer.tsx | 26 ++--- .../hooks/usePositionedContainerZIndex.ts | 19 ++-- app/client/src/widgets/BaseWidget.tsx | 26 ++--- 5 files changed, 128 insertions(+), 146 deletions(-) delete mode 100644 app/client/src/components/AutoLayoutWrapper.tsx create mode 100644 app/client/src/components/designSystems/appsmith/FlexComponent.tsx diff --git a/app/client/src/components/AutoLayoutWrapper.tsx b/app/client/src/components/AutoLayoutWrapper.tsx deleted file mode 100644 index f640d27cc743..000000000000 --- a/app/client/src/components/AutoLayoutWrapper.tsx +++ /dev/null @@ -1,102 +0,0 @@ -import { WidgetType } from "constants/WidgetConstants"; -import styled from "styled-components"; -import React, { ReactNode, useCallback } from "react"; -import { useClickToSelectWidget } from "utils/hooks/useClickToSelectWidget"; -import { AlignItems, LayoutDirection, ResponsiveBehavior } from "./constants"; -import { useSelector } from "react-redux"; - -import { checkIsDropTarget } from "./designSystems/appsmith/PositionedContainer"; -import { getSelectedWidgets } from "selectors/ui"; -import { Layers } from "constants/Layers"; -import { AppState } from "ce/reducers"; - -export type AutoLayoutProps = { - children: ReactNode; - widgetId: string; - widgetType: WidgetType; - useAutoLayout?: boolean; - alignItems?: AlignItems; - direction?: LayoutDirection; - parentId?: string; - responsiveBehavior?: ResponsiveBehavior; - isWrapper?: boolean; -}; - -const AutoLayout = styled("div")<{ - alignItems?: AlignItems; - direction?: LayoutDirection; - useAutoLayout?: boolean; - responsiveBehavior?: ResponsiveBehavior; - isWrapper?: boolean; -}>` - position: unset; - width: auto; - flex: ${({ responsiveBehavior }) => - responsiveBehavior === ResponsiveBehavior.Fill - ? "1 1 auto" - : "0 1 fit-content"}; - align-self: ${({ isWrapper, responsiveBehavior }) => - responsiveBehavior === ResponsiveBehavior.Fill || isWrapper - ? "stretch" - : "auto"}; -`; - -const ZIndexContainer = styled.div<{ - alignItems?: AlignItems; - direction?: LayoutDirection; - zIndex: number; -}>` - position: relative; - z-index: ${({ zIndex }) => zIndex || Layers.positionedWidget}; - - width: ${({ alignItems, direction }) => - alignItems === AlignItems.Stretch && direction === LayoutDirection.Vertical - ? "calc(100% - 16px)" - : "auto"}; - height: ${({ alignItems, direction }) => - alignItems === AlignItems.Stretch && - direction === LayoutDirection.Horizontal - ? "calc(100% - 16px)" - : "auto"}; - min-height: 30px; -`; - -export function AutoLayoutWrapper(props: AutoLayoutProps) { - const clickToSelectWidget = useClickToSelectWidget(props.widgetId); - const onClickFn = useCallback(() => { - clickToSelectWidget(props.widgetId); - }, [props.widgetId, clickToSelectWidget]); - - const isDropTarget = checkIsDropTarget(props.widgetType); - const isDragging = useSelector( - (state: AppState) => state.ui.widgetDragResize.isDragging, - ); - const selectedWidgets = useSelector(getSelectedWidgets); - const isThisWidgetDragging = - isDragging && selectedWidgets.includes(props.widgetId); - - const zIndex = - isDragging && !(!isThisWidgetDragging && isDropTarget) - ? -1 - : Layers.positionedWidget + 1; - - return ( - - - {props.children} - - - ); -} diff --git a/app/client/src/components/designSystems/appsmith/FlexComponent.tsx b/app/client/src/components/designSystems/appsmith/FlexComponent.tsx new file mode 100644 index 000000000000..465d855adcb8 --- /dev/null +++ b/app/client/src/components/designSystems/appsmith/FlexComponent.tsx @@ -0,0 +1,101 @@ +import React, { ReactNode, useCallback } from "react"; +import styled from "styled-components"; + +import { LayoutDirection, ResponsiveBehavior } from "components/constants"; +import { WidgetType, WIDGET_PADDING } from "constants/WidgetConstants"; +import { useClickToSelectWidget } from "utils/hooks/useClickToSelectWidget"; +import { usePositionedContainerZIndex } from "utils/hooks/usePositionedContainerZIndex"; +import { checkIsDropTarget } from "./PositionedContainer"; +import { useSelector } from "store"; +import { snipingModeSelector } from "selectors/editorSelectors"; + +export type AutoLayoutProps = { + children: ReactNode; + componentHeight: number; + componentWidth: number; + direction?: LayoutDirection; + focused?: boolean; + minWidth?: number; + parentId?: string; + responsiveBehavior?: ResponsiveBehavior; + selected?: boolean; + widgetId: string; + widgetType: WidgetType; +}; + +const FlexWidget = styled.div<{ + componentHeight: number; + componentWidth: number; + isFillWidget: boolean; + minWidth?: number; + padding: number; + zIndex: number; + zIndexOnHover: number; +}>` + position: relative; + z-index: ${({ zIndex }) => zIndex}; + + width: ${({ componentWidth, isFillWidget }) => + isFillWidget ? "auto" : `${Math.floor(componentWidth)}px`}; + height: ${({ componentHeight }) => Math.floor(componentHeight) + "px"}; + min-width: ${({ minWidth }) => minWidth + "px"}; + min-height: 30px; + padding: ${({ padding }) => padding + "px"}; + + flex-grow: ${({ isFillWidget }) => (isFillWidget ? "1" : "0")}; + + &:hover { + z-index: ${({ zIndexOnHover }) => zIndexOnHover} !important; + } +`; + +// TODO: update min width logic. + +export function FlexComponent(props: AutoLayoutProps) { + const isSnipingMode = useSelector(snipingModeSelector); + const clickToSelectWidget = useClickToSelectWidget(props.widgetId); + const onClickFn = useCallback(() => { + clickToSelectWidget(props.widgetId); + }, [props.widgetId, clickToSelectWidget]); + + const isDropTarget = checkIsDropTarget(props.widgetType); + const { onHoverZIndex, zIndex } = usePositionedContainerZIndex( + isDropTarget, + props.widgetId, + props.focused, + props.selected, + ); + + const stopEventPropagation = (e: any) => { + !isSnipingMode && e.stopPropagation(); + }; + + const isFillWidget: boolean = + props.direction === LayoutDirection.Vertical && + props.responsiveBehavior === ResponsiveBehavior.Fill; + const className = `auto-layout-parent-${props.parentId} auto-layout-child-${ + props.widgetId + } t--widget-${props.widgetType + .split("_") + .join("") + .toLowerCase()}`; + + return ( + + {props.children} + + ); +} + +export default FlexComponent; diff --git a/app/client/src/components/designSystems/appsmith/PositionedContainer.tsx b/app/client/src/components/designSystems/appsmith/PositionedContainer.tsx index 6054a8265d54..f4c0d4f097de 100644 --- a/app/client/src/components/designSystems/appsmith/PositionedContainer.tsx +++ b/app/client/src/components/designSystems/appsmith/PositionedContainer.tsx @@ -20,7 +20,6 @@ import { getReflowSelector, } from "selectors/widgetReflowSelectors"; import { POSITIONED_WIDGET } from "constants/componentClassNameConstants"; -import { LayoutDirection, ResponsiveBehavior } from "components/constants"; import equal from "fast-deep-equal"; const PositionedWidget = styled.div<{ zIndexOnHover: number }>` @@ -38,10 +37,6 @@ export type PositionedContainerProps = { selected?: boolean; focused?: boolean; resizeDisabled?: boolean; - useAutoLayout?: boolean; - isWrapper?: boolean; - responsiveBehavior?: ResponsiveBehavior; - direction?: LayoutDirection; topRow: number; parentRowSpace: number; noContainerOffset?: boolean; @@ -102,8 +97,10 @@ export function PositionedContainer(props: PositionedContainerProps) { const isDropTarget = checkIsDropTarget(props.widgetType); const { onHoverZIndex, zIndex } = usePositionedContainerZIndex( - props, isDropTarget, + props.widgetId, + props.focused, + props.selected, ); const reflowedPosition = useSelector( @@ -144,23 +141,12 @@ export function PositionedContainer(props: PositionedContainerProps) { : {}; const styles: CSSProperties = { - // TODO: remove the widget type check. Add check for parent type. - position: props?.useAutoLayout ? "unset" : "absolute", + position: "absolute", left: x, top: y, height: - reflowHeight || props.isWrapper - ? "auto" - : style.componentHeight + (style.heightUnit || "px"), - width: - reflowWidth || - (props.useAutoLayout && - !( - props.direction === LayoutDirection.Horizontal && - props.responsiveBehavior === ResponsiveBehavior.Hug - )) - ? "auto" - : style.componentWidth + (style.widthUnit || "px"), + reflowHeight || style.componentHeight + (style.heightUnit || "px"), + width: reflowWidth || style.componentWidth + (style.widthUnit || "px"), padding: padding + "px", zIndex, backgroundColor: "inherit", diff --git a/app/client/src/utils/hooks/usePositionedContainerZIndex.ts b/app/client/src/utils/hooks/usePositionedContainerZIndex.ts index c43bd4ebcddc..538784eef4ad 100644 --- a/app/client/src/utils/hooks/usePositionedContainerZIndex.ts +++ b/app/client/src/utils/hooks/usePositionedContainerZIndex.ts @@ -1,4 +1,3 @@ -import { PositionedContainerProps } from "components/designSystems/appsmith/PositionedContainer"; import { Layers } from "constants/Layers"; import { useMemo } from "react"; @@ -7,13 +6,15 @@ import { isWidgetSelected } from "selectors/widgetSelectors"; import { useSelector } from "store"; export const usePositionedContainerZIndex = ( - props: PositionedContainerProps, droppableWidget: boolean, + widgetId: string, + focused?: boolean, + selected?: boolean, ) => { const isDragging = useSelector( (state: AppState) => state.ui.widgetDragResize.isDragging, ); - const isSelected = useSelector(isWidgetSelected(props.widgetId)); + const isSelected = useSelector(isWidgetSelected(widgetId)); const isThisWidgetDragging = isDragging && isSelected; const zIndex = useMemo(() => { @@ -29,19 +30,13 @@ export const usePositionedContainerZIndex = ( } else { // common use case when nothing is dragged - return props.focused + return focused ? Layers.focusedWidget - : props.selected + : selected ? Layers.selectedWidget : Layers.positionedWidget; } - }, [ - isDragging, - isThisWidgetDragging, - droppableWidget, - props.selected, - props.focused, - ]); + }, [isDragging, isThisWidgetDragging, droppableWidget, selected, focused]); const zIndicesObj = useMemo(() => { const onHoverZIndex = isDragging ? zIndex : Layers.positionedWidget + 1; diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index 99cc6459ea49..d77069e6c15f 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -39,10 +39,10 @@ import { LayoutDirection, ResponsiveBehavior, } from "components/constants"; -import { AutoLayoutWrapper } from "components/AutoLayoutWrapper"; import { CanvasWidgetStructure } from "./constants"; import { DataTreeWidget } from "entities/DataTree/dataTreeFactory"; import Skeleton from "./Skeleton"; +import FlexComponent from "components/designSystems/appsmith/FlexComponent"; /*** * BaseWidget @@ -330,20 +330,23 @@ abstract class BaseWidget< ); } - addAutoLayoutWrapper(content: ReactNode) { + makeFlex(content: ReactNode) { + const { componentHeight, componentWidth } = this.getComponentDimensions(); + return ( - {content} - + ); } @@ -382,9 +385,8 @@ abstract class BaseWidget< content = this.makeDraggable(content); content = this.makeSnipeable(content); // NOTE: In sniping mode we are not blocking onClick events from PositionWrapper. - content = this.makePositioned(content); - if (this.props.useAutoLayout) - content = this.addAutoLayoutWrapper(content); + if (this.props.useAutoLayout) content = this.makeFlex(content); + else content = this.makePositioned(content); } return content; @@ -393,10 +395,10 @@ abstract class BaseWidget< content = this.getWidgetComponent(); if (this.props.isVisible) { content = this.addErrorBoundary(content); - if (!this.props.detachFromLayout) { + if (this.props.useAutoLayout) content = this.makeFlex(content); + else if (!this.props.detachFromLayout) { content = this.makePositioned(content); } - content = this.addAutoLayoutWrapper(content); return content; } return null; From 7650e6b12579f9bb4225ed416406dfd467902839 Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 28 Sep 2022 11:40:02 -0400 Subject: [PATCH 112/708] code clean up --- .../designSystems/appsmith/FlexComponent.tsx | 16 +++++++++++----- .../appsmith/PositionedContainer.tsx | 6 ++---- app/client/src/constants/WidgetConstants.tsx | 5 +++++ 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/FlexComponent.tsx b/app/client/src/components/designSystems/appsmith/FlexComponent.tsx index 465d855adcb8..548eccb3dc53 100644 --- a/app/client/src/components/designSystems/appsmith/FlexComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/FlexComponent.tsx @@ -2,7 +2,11 @@ import React, { ReactNode, useCallback } from "react"; import styled from "styled-components"; import { LayoutDirection, ResponsiveBehavior } from "components/constants"; -import { WidgetType, WIDGET_PADDING } from "constants/WidgetConstants"; +import { + WidgetType, + widgetTypeClassname, + WIDGET_PADDING, +} from "constants/WidgetConstants"; import { useClickToSelectWidget } from "utils/hooks/useClickToSelectWidget"; import { usePositionedContainerZIndex } from "utils/hooks/usePositionedContainerZIndex"; import { checkIsDropTarget } from "./PositionedContainer"; @@ -70,15 +74,17 @@ export function FlexComponent(props: AutoLayoutProps) { !isSnipingMode && e.stopPropagation(); }; + /** + * In a vertical stack, + * Fill widgets grow / shrink to take up all the available space. + * => width: auto && flex-grow: 1; + */ const isFillWidget: boolean = props.direction === LayoutDirection.Vertical && props.responsiveBehavior === ResponsiveBehavior.Fill; const className = `auto-layout-parent-${props.parentId} auto-layout-child-${ props.widgetId - } t--widget-${props.widgetType - .split("_") - .join("") - .toLowerCase()}`; + } ${widgetTypeClassname(props.widgetType)}`; return ( { return ( generateClassName(props.widgetId) + - ` ${POSITIONED_WIDGET} t--widget-${props.widgetType - .split("_") - .join("") - .toLowerCase()}` + ` ${POSITIONED_WIDGET} ${widgetTypeClassname(props.widgetType)}` ); }, [props.widgetType, props.widgetId]); const isDropTarget = checkIsDropTarget(props.widgetType); diff --git a/app/client/src/constants/WidgetConstants.tsx b/app/client/src/constants/WidgetConstants.tsx index 62548a32f012..0f60d42588a4 100644 --- a/app/client/src/constants/WidgetConstants.tsx +++ b/app/client/src/constants/WidgetConstants.tsx @@ -94,6 +94,11 @@ export const MAIN_CONTAINER_WIDGET_ID = "0"; export const MAIN_CONTAINER_WIDGET_NAME = "MainContainer"; export const MODAL_PORTAL_CLASSNAME = "bp3-modal-widget"; export const CANVAS_SELECTOR = "canvas"; +export const widgetTypeClassname = (widgetType: string): string => + `t--widget-${widgetType + .split("_") + .join("") + .toLowerCase()}`; export const DEFAULT_CENTER = { lat: -34.397, lng: 150.644 }; From 232587f3a32ef54076d139d2f64f17c8e55a9cfa Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 28 Sep 2022 11:41:07 -0400 Subject: [PATCH 113/708] remove unnecessary props --- app/client/src/widgets/BaseWidget.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index d77069e6c15f..90fbce7cf956 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -297,19 +297,15 @@ abstract class BaseWidget< From 9918b3ce176b15e9fd4d69cd2e3d5d155503673e Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 29 Sep 2022 11:43:41 -0400 Subject: [PATCH 114/708] remove state variables in container widget --- app/client/src/widgets/CanvasWidget.tsx | 25 ++++------ .../widgets/ContainerWidget/widget/index.tsx | 43 ++++------------ .../src/widgets/LayoutWrapperWidget.tsx | 50 ++++++------------- 3 files changed, 35 insertions(+), 83 deletions(-) diff --git a/app/client/src/widgets/CanvasWidget.tsx b/app/client/src/widgets/CanvasWidget.tsx index e28fc00d1263..b844965a067f 100644 --- a/app/client/src/widgets/CanvasWidget.tsx +++ b/app/client/src/widgets/CanvasWidget.tsx @@ -15,6 +15,7 @@ import { AlignItems, Alignment, JustifyContent, + LayoutDirection, Overflow, Positioning, ResponsiveBehavior, @@ -75,19 +76,11 @@ class CanvasWidget extends ContainerWidget { // Pass layout controls to children childWidget.positioning = childWidget?.positioning || this.props.positioning; - childWidget.useAutoLayout = this.state.useAutoLayout; - childWidget.direction = this.state.direction; + childWidget.useAutoLayout = this.props.useAutoLayout; + childWidget.direction = this.props.direction; childWidget.justifyContent = this.props.justifyContent; childWidget.alignItems = this.props.alignItems; - if ( - childWidget?.responsiveBehavior === ResponsiveBehavior.Fill && - this.state.isMobile - ) { - childWidget.leftColumn = 0; - childWidget.rightColumn = 64; - } - return WidgetFactory.createWidget(childWidget, this.props.renderMode); } @@ -108,12 +101,12 @@ class CanvasWidget extends ContainerWidget { {...this.getSnapSpaces()} alignItems={props.alignItems} canExtend={props.canExtend} - direction={this.state.direction} + direction={this.props.direction} dropDisabled={!!props.dropDisabled} noPad={this.props.noPad} parentId={props.parentId} snapRows={snapRows} - useAutoLayout={this.state.useAutoLayout} + useAutoLayout={this.props.useAutoLayout} widgetId={props.widgetId} widgetName={props.widgetName} /> @@ -136,17 +129,17 @@ class CanvasWidget extends ContainerWidget { {/* without the wrapping div onClick events are triggered twice */} , - ContainerWidgetState + WidgetState > { constructor(props: ContainerWidgetProps) { super(props); - this.state = { - useAutoLayout: false, - direction: LayoutDirection.Horizontal, - isMobile: false, - }; this.renderChildWidget = this.renderChildWidget.bind(this); } @@ -278,7 +273,10 @@ export class ContainerWidget extends BaseWidget< } static getDerivedPropertiesMap(): DerivedPropertiesMap { - return {}; + return { + useAutoLayout: `{{ this.positioning && this.positioning !== "fixed" }}`, + direction: `{{ this.positioning && this.positioning === "vertical" ? "Vertical" : "Horizontal" }}`, + }; } static getDefaultPropertiesMap(): Record { return {}; @@ -289,14 +287,12 @@ export class ContainerWidget extends BaseWidget< componentDidMount(): void { super.componentDidMount(); - this.updatePositioningInformation(); this.checkIsMobile(); } componentDidUpdate(prevProps: ContainerWidgetProps): void { super.componentDidUpdate(prevProps); if (this.props.positioning !== prevProps.positioning) { - this.updatePositioningInformation(); this.updateWrappers(prevProps); } } @@ -305,19 +301,6 @@ export class ContainerWidget extends BaseWidget< if (window.innerWidth < 767) this.setState({ isMobile: true }); }; - updatePositioningInformation = (): void => { - if (!this.props.positioning || this.props.positioning === Positioning.Fixed) - this.setState({ useAutoLayout: false }); - else - this.setState({ - useAutoLayout: true, - direction: - this.props.positioning === Positioning.Horizontal - ? LayoutDirection.Horizontal - : LayoutDirection.Vertical, - }); - }; - updateWrappers = (prevProps: ContainerWidgetProps): void => { if (this.props.positioning === Positioning.Fixed) { this.props.removeWrappers && @@ -380,8 +363,8 @@ export class ContainerWidget extends BaseWidget< childWidget.parentId = this.props.widgetId; // Pass layout controls to children - childWidget.useAutoLayout = this.state.useAutoLayout; - childWidget.direction = this.state.direction; + childWidget.useAutoLayout = this.props.useAutoLayout; + childWidget.direction = this.props.direction; childWidget.positioning = this.props.positioning; childWidget.alignment = this.props.alignment; childWidget.spacing = this.props.spacing; @@ -394,7 +377,7 @@ export class ContainerWidget extends BaseWidget< // sort by row so stacking context is correct // TODO(abhinav): This is hacky. The stacking context should increase for widgets rendered top to bottom, always. // Figure out a way in which the stacking context is consistent. - this.state.useAutoLayout + this.props.useAutoLayout ? this.props.children : sortBy(compact(this.props.children), (child) => child.topRow), this.renderChildWidget, @@ -402,8 +385,8 @@ export class ContainerWidget extends BaseWidget< }; renderAsContainerComponent(props: ContainerWidgetProps) { - // console.log(`${props.widgetName} : ${props.widgetId} =======`); - // console.log(props); + console.log(`${props.widgetName} : ${props.widgetId} =======`); + console.log(props); return ( updateWrappers?: (id: string, direction: LayoutDirection) => void; } -export interface ContainerWidgetState extends WidgetState { - useAutoLayout: boolean; - direction: LayoutDirection; - isMobile: boolean; -} - export default connect(null, mapDispatchToProps)(ContainerWidget); // export default ContainerWidget; diff --git a/app/client/src/widgets/LayoutWrapperWidget.tsx b/app/client/src/widgets/LayoutWrapperWidget.tsx index aadbf203b2c5..95e431f397ae 100644 --- a/app/client/src/widgets/LayoutWrapperWidget.tsx +++ b/app/client/src/widgets/LayoutWrapperWidget.tsx @@ -9,7 +9,7 @@ import { import { DropTargetComponent } from "components/editorComponents/DropTargetComponent"; import { CANVAS_DEFAULT_MIN_HEIGHT_PX } from "constants/AppConstants"; import { getCanvasClassName } from "utils/generators"; -import { GridDefaults, RenderModes } from "constants/WidgetConstants"; +import { RenderModes } from "constants/WidgetConstants"; import { getCanvasSnapRows } from "utils/WidgetPropsUtils"; import { AlignItems, @@ -46,22 +46,6 @@ class LayoutWrapperWidget extends ContainerWidget { static getWidgetType() { return "LAYOUT_WRAPPER_WIDGET"; } - componentDidMount(): void { - super.componentDidMount(); - } - componentDidUpdate(prevProps: ContainerWidgetProps): void { - if (this.props.positioning !== prevProps.positioning) - this.updatePositioningInformation(); - } - updatePositioningInformation = (): void => { - this.setState({ - useAutoLayout: true, - direction: - this.props.positioning === Positioning.Horizontal - ? LayoutDirection.Vertical - : LayoutDirection.Horizontal, - }); - }; getCanvasProps(): ContainerWidgetProps { return { @@ -80,7 +64,7 @@ class LayoutWrapperWidget extends ContainerWidget { {this.renderAsContainerComponent(canvasProps)} @@ -101,22 +85,20 @@ class LayoutWrapperWidget extends ContainerWidget { // Pass layout controls to children childWidget.positioning = childWidget?.positioning || this.props.positioning; - childWidget.useAutoLayout = this.state.useAutoLayout; - childWidget.direction = this.state.direction; + childWidget.useAutoLayout = this.props.useAutoLayout; + childWidget.direction = this.getInverseDirection(this.props.direction); childWidget.justifyContent = this.props.justifyContent; childWidget.alignItems = this.props.alignItems; - if ( - childWidget?.responsiveBehavior === ResponsiveBehavior.Fill && - this.state.isMobile - ) { - childWidget.leftColumn = 0; - childWidget.rightColumn = 64; - } - return WidgetFactory.createWidget(childWidget, this.props.renderMode); } + getInverseDirection(direction = LayoutDirection.Vertical): LayoutDirection { + return direction === LayoutDirection.Vertical + ? LayoutDirection.Horizontal + : LayoutDirection.Vertical; + } + renderAsContainerComponent( props: ContainerWidgetProps, ): JSX.Element { @@ -134,12 +116,12 @@ class LayoutWrapperWidget extends ContainerWidget { {...this.getSnapSpaces()} alignItems={props.alignItems} canExtend={props.canExtend} - direction={this.state.direction} + direction={this.getInverseDirection(this.props.direction)} dropDisabled={!!props.dropDisabled} noPad={this.props.noPad} parentId={props.parentId} snapRows={snapRows} - useAutoLayout={this.state.useAutoLayout} + useAutoLayout widgetId={props.widgetId} widgetName={props.widgetName} /> @@ -162,8 +144,8 @@ class LayoutWrapperWidget extends ContainerWidget { {/* without the wrapping div onClick events are triggered twice */} {this.renderChildren()} From 50e335cdda93cd6391ab8e56c4c7113814fbe0a9 Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 29 Sep 2022 11:43:41 -0400 Subject: [PATCH 115/708] Revert "remove state variables in container widget" This reverts commit 9918b3ce176b15e9fd4d69cd2e3d5d155503673e. --- app/client/src/widgets/CanvasWidget.tsx | 25 ++++++---- .../widgets/ContainerWidget/widget/index.tsx | 43 ++++++++++++---- .../src/widgets/LayoutWrapperWidget.tsx | 50 +++++++++++++------ 3 files changed, 83 insertions(+), 35 deletions(-) diff --git a/app/client/src/widgets/CanvasWidget.tsx b/app/client/src/widgets/CanvasWidget.tsx index b844965a067f..e28fc00d1263 100644 --- a/app/client/src/widgets/CanvasWidget.tsx +++ b/app/client/src/widgets/CanvasWidget.tsx @@ -15,7 +15,6 @@ import { AlignItems, Alignment, JustifyContent, - LayoutDirection, Overflow, Positioning, ResponsiveBehavior, @@ -76,11 +75,19 @@ class CanvasWidget extends ContainerWidget { // Pass layout controls to children childWidget.positioning = childWidget?.positioning || this.props.positioning; - childWidget.useAutoLayout = this.props.useAutoLayout; - childWidget.direction = this.props.direction; + childWidget.useAutoLayout = this.state.useAutoLayout; + childWidget.direction = this.state.direction; childWidget.justifyContent = this.props.justifyContent; childWidget.alignItems = this.props.alignItems; + if ( + childWidget?.responsiveBehavior === ResponsiveBehavior.Fill && + this.state.isMobile + ) { + childWidget.leftColumn = 0; + childWidget.rightColumn = 64; + } + return WidgetFactory.createWidget(childWidget, this.props.renderMode); } @@ -101,12 +108,12 @@ class CanvasWidget extends ContainerWidget { {...this.getSnapSpaces()} alignItems={props.alignItems} canExtend={props.canExtend} - direction={this.props.direction} + direction={this.state.direction} dropDisabled={!!props.dropDisabled} noPad={this.props.noPad} parentId={props.parentId} snapRows={snapRows} - useAutoLayout={this.props.useAutoLayout} + useAutoLayout={this.state.useAutoLayout} widgetId={props.widgetId} widgetName={props.widgetName} /> @@ -129,17 +136,17 @@ class CanvasWidget extends ContainerWidget { {/* without the wrapping div onClick events are triggered twice */} , - WidgetState + ContainerWidgetState > { constructor(props: ContainerWidgetProps) { super(props); + this.state = { + useAutoLayout: false, + direction: LayoutDirection.Horizontal, + isMobile: false, + }; this.renderChildWidget = this.renderChildWidget.bind(this); } @@ -273,10 +278,7 @@ export class ContainerWidget extends BaseWidget< } static getDerivedPropertiesMap(): DerivedPropertiesMap { - return { - useAutoLayout: `{{ this.positioning && this.positioning !== "fixed" }}`, - direction: `{{ this.positioning && this.positioning === "vertical" ? "Vertical" : "Horizontal" }}`, - }; + return {}; } static getDefaultPropertiesMap(): Record { return {}; @@ -287,12 +289,14 @@ export class ContainerWidget extends BaseWidget< componentDidMount(): void { super.componentDidMount(); + this.updatePositioningInformation(); this.checkIsMobile(); } componentDidUpdate(prevProps: ContainerWidgetProps): void { super.componentDidUpdate(prevProps); if (this.props.positioning !== prevProps.positioning) { + this.updatePositioningInformation(); this.updateWrappers(prevProps); } } @@ -301,6 +305,19 @@ export class ContainerWidget extends BaseWidget< if (window.innerWidth < 767) this.setState({ isMobile: true }); }; + updatePositioningInformation = (): void => { + if (!this.props.positioning || this.props.positioning === Positioning.Fixed) + this.setState({ useAutoLayout: false }); + else + this.setState({ + useAutoLayout: true, + direction: + this.props.positioning === Positioning.Horizontal + ? LayoutDirection.Horizontal + : LayoutDirection.Vertical, + }); + }; + updateWrappers = (prevProps: ContainerWidgetProps): void => { if (this.props.positioning === Positioning.Fixed) { this.props.removeWrappers && @@ -363,8 +380,8 @@ export class ContainerWidget extends BaseWidget< childWidget.parentId = this.props.widgetId; // Pass layout controls to children - childWidget.useAutoLayout = this.props.useAutoLayout; - childWidget.direction = this.props.direction; + childWidget.useAutoLayout = this.state.useAutoLayout; + childWidget.direction = this.state.direction; childWidget.positioning = this.props.positioning; childWidget.alignment = this.props.alignment; childWidget.spacing = this.props.spacing; @@ -377,7 +394,7 @@ export class ContainerWidget extends BaseWidget< // sort by row so stacking context is correct // TODO(abhinav): This is hacky. The stacking context should increase for widgets rendered top to bottom, always. // Figure out a way in which the stacking context is consistent. - this.props.useAutoLayout + this.state.useAutoLayout ? this.props.children : sortBy(compact(this.props.children), (child) => child.topRow), this.renderChildWidget, @@ -385,8 +402,8 @@ export class ContainerWidget extends BaseWidget< }; renderAsContainerComponent(props: ContainerWidgetProps) { - console.log(`${props.widgetName} : ${props.widgetId} =======`); - console.log(props); + // console.log(`${props.widgetName} : ${props.widgetId} =======`); + // console.log(props); return ( updateWrappers?: (id: string, direction: LayoutDirection) => void; } +export interface ContainerWidgetState extends WidgetState { + useAutoLayout: boolean; + direction: LayoutDirection; + isMobile: boolean; +} + export default connect(null, mapDispatchToProps)(ContainerWidget); // export default ContainerWidget; diff --git a/app/client/src/widgets/LayoutWrapperWidget.tsx b/app/client/src/widgets/LayoutWrapperWidget.tsx index 95e431f397ae..aadbf203b2c5 100644 --- a/app/client/src/widgets/LayoutWrapperWidget.tsx +++ b/app/client/src/widgets/LayoutWrapperWidget.tsx @@ -9,7 +9,7 @@ import { import { DropTargetComponent } from "components/editorComponents/DropTargetComponent"; import { CANVAS_DEFAULT_MIN_HEIGHT_PX } from "constants/AppConstants"; import { getCanvasClassName } from "utils/generators"; -import { RenderModes } from "constants/WidgetConstants"; +import { GridDefaults, RenderModes } from "constants/WidgetConstants"; import { getCanvasSnapRows } from "utils/WidgetPropsUtils"; import { AlignItems, @@ -46,6 +46,22 @@ class LayoutWrapperWidget extends ContainerWidget { static getWidgetType() { return "LAYOUT_WRAPPER_WIDGET"; } + componentDidMount(): void { + super.componentDidMount(); + } + componentDidUpdate(prevProps: ContainerWidgetProps): void { + if (this.props.positioning !== prevProps.positioning) + this.updatePositioningInformation(); + } + updatePositioningInformation = (): void => { + this.setState({ + useAutoLayout: true, + direction: + this.props.positioning === Positioning.Horizontal + ? LayoutDirection.Vertical + : LayoutDirection.Horizontal, + }); + }; getCanvasProps(): ContainerWidgetProps { return { @@ -64,7 +80,7 @@ class LayoutWrapperWidget extends ContainerWidget { {this.renderAsContainerComponent(canvasProps)} @@ -85,18 +101,20 @@ class LayoutWrapperWidget extends ContainerWidget { // Pass layout controls to children childWidget.positioning = childWidget?.positioning || this.props.positioning; - childWidget.useAutoLayout = this.props.useAutoLayout; - childWidget.direction = this.getInverseDirection(this.props.direction); + childWidget.useAutoLayout = this.state.useAutoLayout; + childWidget.direction = this.state.direction; childWidget.justifyContent = this.props.justifyContent; childWidget.alignItems = this.props.alignItems; - return WidgetFactory.createWidget(childWidget, this.props.renderMode); - } + if ( + childWidget?.responsiveBehavior === ResponsiveBehavior.Fill && + this.state.isMobile + ) { + childWidget.leftColumn = 0; + childWidget.rightColumn = 64; + } - getInverseDirection(direction = LayoutDirection.Vertical): LayoutDirection { - return direction === LayoutDirection.Vertical - ? LayoutDirection.Horizontal - : LayoutDirection.Vertical; + return WidgetFactory.createWidget(childWidget, this.props.renderMode); } renderAsContainerComponent( @@ -116,12 +134,12 @@ class LayoutWrapperWidget extends ContainerWidget { {...this.getSnapSpaces()} alignItems={props.alignItems} canExtend={props.canExtend} - direction={this.getInverseDirection(this.props.direction)} + direction={this.state.direction} dropDisabled={!!props.dropDisabled} noPad={this.props.noPad} parentId={props.parentId} snapRows={snapRows} - useAutoLayout + useAutoLayout={this.state.useAutoLayout} widgetId={props.widgetId} widgetName={props.widgetName} /> @@ -144,8 +162,8 @@ class LayoutWrapperWidget extends ContainerWidget { {/* without the wrapping div onClick events are triggered twice */} {this.renderChildren()} From 449830fc88f079ac4b58c93ccdc88cf0ab3130f9 Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 29 Sep 2022 15:54:15 -0400 Subject: [PATCH 116/708] add AutoLayoutLayer component --- .../appsmith/AutoLayoutLayer.tsx | 87 +++++++++++++++++++ .../editorComponents/ResizableComponent.tsx | 27 +++--- .../src/resizable/resizenreflow/index.tsx | 6 +- app/client/src/widgets/CanvasWidget.tsx | 20 +---- .../ContainerWidget/component/index.tsx | 70 ++++++++++++++- .../src/widgets/LayoutWrapperWidget.tsx | 2 +- 6 files changed, 172 insertions(+), 40 deletions(-) create mode 100644 app/client/src/components/designSystems/appsmith/AutoLayoutLayer.tsx diff --git a/app/client/src/components/designSystems/appsmith/AutoLayoutLayer.tsx b/app/client/src/components/designSystems/appsmith/AutoLayoutLayer.tsx new file mode 100644 index 000000000000..f952b7f6e311 --- /dev/null +++ b/app/client/src/components/designSystems/appsmith/AutoLayoutLayer.tsx @@ -0,0 +1,87 @@ +import React, { ReactNode } from "react"; +import styled from "styled-components"; + +import { FlexDirection, LayoutDirection } from "components/constants"; + +/** + * 1. Given a direction if should employ flex in perpendicular direction. + * 2. It should be able to render children within three nested wrappers for start, center and end alignment. + * 3. Only render start wrapper if a fill widget is present. + */ + +export interface AutoLayoutLayerProps { + start?: ReactNode; + center?: ReactNode; + end?: ReactNode; + direction: LayoutDirection; + hasFillChild?: boolean; +} + +const LayoutLayerContainer = styled.div<{ + flexDirection: FlexDirection; +}>` + display: flex; + flex-direction: ${({ flexDirection }) => flexDirection || FlexDirection.Row}; + justify-content: flex-start; + align-items: flex-start; + + width: 100%; + height: auto; +`; + +const SubWrapper = styled.div<{ + flexDirection: FlexDirection; +}>` + flex: 1 1 33.3%; + display: flex; + flex-direction: ${({ flexDirection }) => flexDirection || "row"}; + align-items: ${({ flexDirection }) => + flexDirection === FlexDirection.Column ? "flex-start" : "center"}; +`; + +const StartWrapper = styled(SubWrapper)` + justify-content: flex-start; +`; + +const EndWrapper = styled(SubWrapper)` + justify-content: flex-end; +`; + +const CenterWrapper = styled(SubWrapper)` + justify-content: center; +`; + +function getFlexDirection(direction: LayoutDirection): FlexDirection { + return direction === LayoutDirection.Horizontal + ? FlexDirection.Row + : FlexDirection.Column; +} + +function getInverseDirection(direction: LayoutDirection): LayoutDirection { + return direction === LayoutDirection.Horizontal + ? LayoutDirection.Vertical + : LayoutDirection.Horizontal; +} + +function AutoLayoutLayer(props: AutoLayoutLayerProps) { + const flexDirection = getFlexDirection(getInverseDirection(props.direction)); + return ( + + {props.start} + + {props.center} + + + {props.end} + + + ); +} + +export default AutoLayoutLayer; diff --git a/app/client/src/components/editorComponents/ResizableComponent.tsx b/app/client/src/components/editorComponents/ResizableComponent.tsx index 9d5cf6270068..c380f0b6c121 100644 --- a/app/client/src/components/editorComponents/ResizableComponent.tsx +++ b/app/client/src/components/editorComponents/ResizableComponent.tsx @@ -67,14 +67,9 @@ export const ResizableComponent = memo(function ResizableComponent( // Fetch information from the context const { updateWidget } = useContext(EditorContext); - const { - alignItems, - direction, - disabledResizeHandles, - useAutoLayout, - } = useContext(AutoLayoutContext || null); const isHorizontallyStretched = - direction === LayoutDirection.Vertical && alignItems === AlignItems.Stretch; + props.direction === LayoutDirection.Vertical && + props.alignItems === AlignItems.Stretch; const isSnipingMode = useSelector(snipingModeSelector); const isPreviewMode = useSelector(previewModeSelector); @@ -112,7 +107,7 @@ export const ResizableComponent = memo(function ResizableComponent( // if (props.widgetName.toLowerCase().includes("button")) // console.log(`#### ${props.widgetName} : Initial dimensions`); setComponentWidth( - useAutoLayout && isHorizontallyStretched + props.useAutoLayout && isHorizontallyStretched ? 64 * props.parentColumnSpace - 2 * props.paddingOffset : (props.rightColumn - props.leftColumn) * props.parentColumnSpace - 2 * props.paddingOffset, @@ -121,13 +116,13 @@ export const ResizableComponent = memo(function ResizableComponent( (props.bottomRow - props.topRow) * props.parentRowSpace - 2 * props.paddingOffset, ); - }, [useAutoLayout, direction, alignItems]); + }, [props.useAutoLayout, props.direction, props.alignItems]); useEffect(() => { // if (props.widgetName.toLowerCase().includes("button")) // console.log(`#### ${props.widgetName} : Manual resize`); setComponentWidth( - useAutoLayout && isHorizontallyStretched + props.useAutoLayout && isHorizontallyStretched ? 64 * props.parentColumnSpace - 2 * props.paddingOffset : (props.rightColumn - props.leftColumn) * props.parentColumnSpace - 2 * props.paddingOffset, @@ -141,7 +136,7 @@ export const ResizableComponent = memo(function ResizableComponent( useEffect(() => { // if (props.widgetName.toLowerCase().includes("button")) // console.log(`#### ${props.widgetName} : Parent resize`); - if (!useAutoLayout) { + if (!props.useAutoLayout) { setComponentWidth( (props.rightColumn - props.leftColumn) * props.parentColumnSpace - 2 * props.paddingOffset, @@ -157,7 +152,7 @@ export const ResizableComponent = memo(function ResizableComponent( ); } } - }, [props.parentColumnSpace, props.parentRowSpace, useAutoLayout]); + }, [props.parentColumnSpace, props.parentRowSpace, props.useAutoLayout]); // Calculate the dimensions of the widget, // The ResizableContainer's size prop is controlled @@ -331,11 +326,11 @@ export const ResizableComponent = memo(function ResizableComponent( bottomLeft: BottomLeftHandleStyles, }; let handlesToOmit = get(props, "disabledResizeHandles", []); - if (disabledResizeHandles && disabledResizeHandles.length) - handlesToOmit = [...handlesToOmit, ...disabledResizeHandles]; + // if (disabledResizeHandles && disabledResizeHandles.length) + // handlesToOmit = [...handlesToOmit, ...disabledResizeHandles]; handlesToOmit = [...handlesToOmit, ...disabledHorizontalHandles]; return omit(allHandles, handlesToOmit); - }, [props, disabledResizeHandles, disabledHorizontalHandles]); + }, [props, disabledHorizontalHandles]); const isEnabled: boolean = !isDragging && @@ -383,7 +378,7 @@ export const ResizableComponent = memo(function ResizableComponent( responsiveBehavior={props.responsiveBehavior} snapGrid={{ x: props.parentColumnSpace, y: props.parentRowSpace }} updateBottomRow={updateBottomRow} - useAutoLayout={useAutoLayout} + useAutoLayout={props.useAutoLayout} widgetId={props.widgetId} // Used only for performance tracking, can be removed after optimization. zWidgetId={props.widgetId} diff --git a/app/client/src/resizable/resizenreflow/index.tsx b/app/client/src/resizable/resizenreflow/index.tsx index d0929a5ddef2..58d911a0ea67 100644 --- a/app/client/src/resizable/resizenreflow/index.tsx +++ b/app/client/src/resizable/resizenreflow/index.tsx @@ -508,10 +508,8 @@ export function ReflowResizable(props: ResizableProps) { width: props.isWrapper || (props.useAutoLayout && - !( - props.direction === LayoutDirection.Horizontal && - props.responsiveBehavior === ResponsiveBehavior.Hug - )) + props.direction === LayoutDirection.Vertical && + props.responsiveBehavior === ResponsiveBehavior.Fill) ? "auto" : widgetWidth, height: props.isWrapper ? "auto" : widgetHeight, diff --git a/app/client/src/widgets/CanvasWidget.tsx b/app/client/src/widgets/CanvasWidget.tsx index e28fc00d1263..65a01130e032 100644 --- a/app/client/src/widgets/CanvasWidget.tsx +++ b/app/client/src/widgets/CanvasWidget.tsx @@ -12,9 +12,7 @@ import WidgetFactory, { DerivedPropertiesMap } from "utils/WidgetFactory"; import { CanvasWidgetStructure } from "./constants"; import { CANVAS_DEFAULT_MIN_HEIGHT_PX } from "constants/AppConstants"; import { - AlignItems, Alignment, - JustifyContent, Overflow, Positioning, ResponsiveBehavior, @@ -24,7 +22,6 @@ import ContainerComponent, { FlexBox } from "./ContainerWidget/component"; import { CanvasDraggingArena } from "pages/common/CanvasArenas/CanvasDraggingArena"; import { CanvasSelectionArena } from "pages/common/CanvasArenas/CanvasSelectionArena"; import WidgetsMultiSelectBox from "pages/Editor/WidgetsMultiSelectBox"; -import { AutoLayoutContext } from "utils/autoLayoutContext"; class CanvasWidget extends ContainerWidget { static getPropertyPaneConfig() { @@ -66,7 +63,7 @@ class CanvasWidget extends ContainerWidget { if (!childWidgetData) return null; const childWidget = { ...childWidgetData }; - + console.log("#### child widget", childWidget); const snapSpaces = this.getSnapSpaces(); childWidget.parentColumnSpace = snapSpaces.snapColumnSpace; childWidget.parentRowSpace = snapSpaces.snapRowSpace; @@ -143,20 +140,7 @@ class CanvasWidget extends ContainerWidget { useAutoLayout={this.state.useAutoLayout} widgetId={this.props.widgetId} > - - {this.renderChildren()} - + {this.renderChildren()} ); diff --git a/app/client/src/widgets/ContainerWidget/component/index.tsx b/app/client/src/widgets/ContainerWidget/component/index.tsx index 01bff0f6bf05..81757d40b81d 100644 --- a/app/client/src/widgets/ContainerWidget/component/index.tsx +++ b/app/client/src/widgets/ContainerWidget/component/index.tsx @@ -28,6 +28,7 @@ import { } from "utils/layoutPropertiesUtils"; import { useSelector } from "store"; import { getWidgets } from "sagas/selectors"; +import AutoLayoutLayer from "components/designSystems/appsmith/AutoLayoutLayer"; const scrollContents = css` overflow-y: auto; @@ -158,10 +159,77 @@ function ContainerComponentWrapper(props: ContainerComponentProps) { } export function FlexBox(props: FlexBoxProps) { + // const allWidgets = useSelector(getWidgets); const layoutProps = useMemo( () => getLayoutProperties(props.direction, props.alignment, props.spacing), [props.direction, props.alignment, props.spacing], ); + const renderChildren = () => { + if (!props.children) return null; + if (!props.useAutoLayout) return props.children; + const flexLayers: any[] = [ + // { + // children: [ + // { + // id: "2f5yim088g", + // align: "start", + // }, + // { + // id: "92hs8bgv4h", + // align: "start", + // }, + // ], + // }, + // { + // children: [ + // { + // id: "crfmj4atrz", + // align: "end", + // }, + // ], + // }, + { + children: [ + { + id: "tbp7meiwtq", + align: "end", + }, + ], + hasFillChild: true, + }, + ]; + const map: any = {}; + if (isArray(props.children)) { + for (const child of props.children) { + map[(child as JSX.Element).props?.widgetId] = child; + } + } + return flexLayers.map((layer: any, index: number) => { + if (!layer.children || !layer.children.length) return null; + const start = [], + center = [], + end = []; + for (const child of layer.children) { + if (layer.hasFillChild) { + start.push(map[child.id]); + continue; + } + if (child.align === "end") end.push(map[child.id]); + else if (child.align === "center") center.push(map[child.id]); + else start.push(map[child.id]); + } + return ( + + ); + }); + }; return ( - {props.children} + <>{renderChildren()} ); } diff --git a/app/client/src/widgets/LayoutWrapperWidget.tsx b/app/client/src/widgets/LayoutWrapperWidget.tsx index aadbf203b2c5..80cca2418e2c 100644 --- a/app/client/src/widgets/LayoutWrapperWidget.tsx +++ b/app/client/src/widgets/LayoutWrapperWidget.tsx @@ -9,7 +9,7 @@ import { import { DropTargetComponent } from "components/editorComponents/DropTargetComponent"; import { CANVAS_DEFAULT_MIN_HEIGHT_PX } from "constants/AppConstants"; import { getCanvasClassName } from "utils/generators"; -import { GridDefaults, RenderModes } from "constants/WidgetConstants"; +import { RenderModes } from "constants/WidgetConstants"; import { getCanvasSnapRows } from "utils/WidgetPropsUtils"; import { AlignItems, From d9344d1e9ba5086153b4646913315dccb8b1843d Mon Sep 17 00:00:00 2001 From: Preet Date: Fri, 30 Sep 2022 11:29:44 -0400 Subject: [PATCH 117/708] add FlexBoxComponent --- app/client/src/components/constants.ts | 2 +- .../{ => autoLayout}/AutoLayoutLayer.tsx | 0 .../appsmith/autoLayout/FlexBoxComponent.tsx | 125 ++++++++++++++++ .../{ => autoLayout}/FlexComponent.tsx | 2 +- .../hooks/useAutoLayoutHighlights.ts | 54 +++---- .../hooks/useBlocksToBeDraggedOnCanvas.ts | 6 +- .../sagas/CanvasSagas/DraggingCanvasSagas.ts | 16 +-- app/client/src/sagas/WidgetOperationUtils.ts | 4 +- app/client/src/utils/layoutPropertiesUtils.ts | 2 +- app/client/src/widgets/BaseWidget.tsx | 2 +- app/client/src/widgets/CanvasWidget.tsx | 48 ++++++- .../ContainerWidget/component/index.tsx | 136 +----------------- .../widgets/ContainerWidget/widget/index.tsx | 15 +- .../src/widgets/LayoutWrapperWidget.tsx | 11 +- 14 files changed, 228 insertions(+), 195 deletions(-) rename app/client/src/components/designSystems/appsmith/{ => autoLayout}/AutoLayoutLayer.tsx (100%) create mode 100644 app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx rename app/client/src/components/designSystems/appsmith/{ => autoLayout}/FlexComponent.tsx (98%) diff --git a/app/client/src/components/constants.ts b/app/client/src/components/constants.ts index 543d85136875..341080a4ec77 100644 --- a/app/client/src/components/constants.ts +++ b/app/client/src/components/constants.ts @@ -171,7 +171,7 @@ export enum Overflow { Auto = "auto", } -export enum LayoutWrapperType { +export enum FlexLayerAlignment { Start = "start", Center = "center", End = "end", diff --git a/app/client/src/components/designSystems/appsmith/AutoLayoutLayer.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx similarity index 100% rename from app/client/src/components/designSystems/appsmith/AutoLayoutLayer.tsx rename to app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx new file mode 100644 index 000000000000..8982909bffd9 --- /dev/null +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx @@ -0,0 +1,125 @@ +import React, { ReactNode, useMemo } from "react"; +import styled from "styled-components"; +import { isArray } from "lodash"; + +import { + AlignItems, + Alignment, + FlexDirection, + FlexLayerAlignment, + JustifyContent, + LayoutDirection, + Overflow, + Spacing, +} from "components/constants"; +import { getLayoutProperties } from "utils/layoutPropertiesUtils"; +import AutoLayoutLayer from "./AutoLayoutLayer"; + +export interface FlexBoxProps { + alignment: Alignment; + direction?: LayoutDirection; + spacing: Spacing; + stretchHeight: boolean; + useAutoLayout: boolean; + children?: ReactNode; + widgetId: string; + overflow: Overflow; + flexLayers: FlexLayer[]; +} + +export interface FlexLayer { + children: { id: string; align: FlexLayerAlignment }[]; + hasFillChild?: boolean; +} + +export const FlexContainer = styled.div<{ + useAutoLayout?: boolean; + flexDirection?: FlexDirection; + justifyContent?: JustifyContent; + alignItems?: AlignItems; + stretchHeight: boolean; + overflow: Overflow; +}>` + display: ${({ useAutoLayout }) => (useAutoLayout ? "flex" : "block")}; + flex-direction: ${({ flexDirection }) => flexDirection || "row"}; + justify-content: ${({ justifyContent }) => justifyContent || "flex-start"}; + align-items: ${({ alignItems }) => alignItems || "flex-start"}; + flex-wrap: ${({ overflow }) => + overflow?.indexOf("wrap") > -1 ? overflow : "nowrap"}; + + width: 100%; + height: ${({ stretchHeight }) => (stretchHeight ? "100%" : "auto")}; + + overflow: ${({ overflow }) => + overflow?.indexOf("wrap") === -1 ? overflow : "hidden"}; + padding: 4px; +`; + +function FlexBoxComponent(props: FlexBoxProps) { + const direction: LayoutDirection = + props.direction || LayoutDirection.Horizontal; + + const layoutProps = useMemo( + () => getLayoutProperties(props.direction, props.alignment, props.spacing), + [props.direction, props.alignment, props.spacing], + ); + + const renderChildren = () => { + if (!props.children) return null; + if ( + !props.useAutoLayout || + direction === LayoutDirection.Horizontal || + !(props.flexLayers && props.flexLayers.length) + ) + return props.children; + /** + * Wrap children of a Vertical Stack in a flex layer. + */ + const map: { [key: string]: any } = {}; + if (isArray(props.children)) { + for (const child of props.children) { + map[(child as JSX.Element).props?.widgetId] = child; + } + } + return props.flexLayers.map((layer: FlexLayer, index: number) => { + const { children, hasFillChild } = layer; + if (!children || !children.length) return null; + const start = [], + center = [], + end = []; + for (const child of children) { + const widget = map[child.id]; + if (hasFillChild) { + start.push(widget); + continue; + } + if (child.align === "end") end.push(widget); + else if (child.align === "center") center.push(widget); + else start.push(widget); + } + return ( + + ); + }); + }; + return ( + + <>{renderChildren()} + + ); +} + +export default FlexBoxComponent; diff --git a/app/client/src/components/designSystems/appsmith/FlexComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx similarity index 98% rename from app/client/src/components/designSystems/appsmith/FlexComponent.tsx rename to app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx index 548eccb3dc53..6f36e88bdb8f 100644 --- a/app/client/src/components/designSystems/appsmith/FlexComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx @@ -9,7 +9,7 @@ import { } from "constants/WidgetConstants"; import { useClickToSelectWidget } from "utils/hooks/useClickToSelectWidget"; import { usePositionedContainerZIndex } from "utils/hooks/usePositionedContainerZIndex"; -import { checkIsDropTarget } from "./PositionedContainer"; +import { checkIsDropTarget } from "../PositionedContainer"; import { useSelector } from "store"; import { snipingModeSelector } from "selectors/editorSelectors"; diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index b3a0c24e2e1d..9669d221767d 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -3,7 +3,7 @@ import { useSelector } from "store"; import { LayoutDirection, - LayoutWrapperType, + FlexLayerAlignment, ResponsiveBehavior, } from "components/constants"; import { isArray, isNaN } from "lodash"; @@ -19,7 +19,7 @@ export interface Highlight { y: number; height: number; width: number; - wrapperType: LayoutWrapperType; + wrapperType: FlexLayerAlignment; } export interface AutoLayoutHighlightProps { @@ -35,7 +35,7 @@ export interface AutoLayoutHighlightProps { export interface DropPositionPayload { index: number; - wrapperType: LayoutWrapperType; + wrapperType: FlexLayerAlignment; } const BASE_OFFSET_SIZE = 100; @@ -99,55 +99,55 @@ export const useAutoLayoutHighlights = ({ }; const initialOffsets: Record< - LayoutWrapperType, + FlexLayerAlignment, Record > = { - [LayoutWrapperType.Start]: { + [FlexLayerAlignment.Start]: { [LayoutDirection.Vertical]: { x: 0, y: 8, width: getContainerDimensions()?.width || BASE_OFFSET_SIZE, height: OFFSET_WIDTH, - wrapperType: LayoutWrapperType.Start, + wrapperType: FlexLayerAlignment.Start, }, [LayoutDirection.Horizontal]: { x: 8, y: 0, width: OFFSET_WIDTH, height: getContainerDimensions()?.height || BASE_OFFSET_SIZE, - wrapperType: LayoutWrapperType.Start, + wrapperType: FlexLayerAlignment.Start, }, }, - [LayoutWrapperType.Center]: { + [FlexLayerAlignment.Center]: { [LayoutDirection.Vertical]: { x: 0, y: getContainerDimensions()?.height / 2, width: getContainerDimensions()?.width || BASE_OFFSET_SIZE, height: OFFSET_WIDTH, - wrapperType: LayoutWrapperType.Center, + wrapperType: FlexLayerAlignment.Center, }, [LayoutDirection.Horizontal]: { x: getContainerDimensions()?.width / 2, y: 0, width: OFFSET_WIDTH, height: getContainerDimensions()?.height || BASE_OFFSET_SIZE, - wrapperType: LayoutWrapperType.Center, + wrapperType: FlexLayerAlignment.Center, }, }, - [LayoutWrapperType.End]: { + [FlexLayerAlignment.End]: { [LayoutDirection.Vertical]: { x: 0, y: getContainerDimensions()?.bottom, width: getContainerDimensions()?.width || BASE_OFFSET_SIZE, height: OFFSET_WIDTH, - wrapperType: LayoutWrapperType.End, + wrapperType: FlexLayerAlignment.End, }, [LayoutDirection.Horizontal]: { x: getContainerDimensions()?.right, y: 0, width: OFFSET_WIDTH, height: getContainerDimensions()?.height || BASE_OFFSET_SIZE, - wrapperType: LayoutWrapperType.End, + wrapperType: FlexLayerAlignment.End, }, }, }; @@ -155,11 +155,11 @@ export const useAutoLayoutHighlights = ({ // Create and add an initial offset for an empty canvas const getInitialOffset = ( isWrapper: boolean, - wrapperType: LayoutWrapperType = LayoutWrapperType.Start, + wrapperType: FlexLayerAlignment = FlexLayerAlignment.Start, ): Highlight => { const dir: LayoutDirection = direction || LayoutDirection.Horizontal; if (isWrapper) return initialOffsets[wrapperType][dir]; - return initialOffsets[LayoutWrapperType.Start][dir]; + return initialOffsets[FlexLayerAlignment.Start][dir]; }; // Get DOM element for a given widgetId const getDomElement = (widgetId: string): any => @@ -203,7 +203,7 @@ export const useAutoLayoutHighlights = ({ const getOffset = ( rect: DOMRect, flexOffsetTop: number, - wrapperType: LayoutWrapperType, + wrapperType: FlexLayerAlignment, isFinal?: boolean, ): Highlight => { let mOffset: Highlight; @@ -339,9 +339,9 @@ export const useAutoLayoutHighlights = ({ center: string[] = [], end: string[] = []; offsetChildren.forEach((each) => { - if (allWidgets[each]?.wrapperType === LayoutWrapperType.Center) + if (allWidgets[each]?.wrapperType === FlexLayerAlignment.Center) center.push(each); - else if (allWidgets[each]?.wrapperType === LayoutWrapperType.End) + else if (allWidgets[each]?.wrapperType === FlexLayerAlignment.End) end.push(each); else start.push(each); }); @@ -349,19 +349,19 @@ export const useAutoLayoutHighlights = ({ start, flexOffsetTop, true, - LayoutWrapperType.Start, + FlexLayerAlignment.Start, ); const arr2: Highlight[] = evaluateOffsets( center, flexOffsetTop, true, - LayoutWrapperType.Center, + FlexLayerAlignment.Center, ); const arr3: Highlight[] = evaluateOffsets( end, flexOffsetTop, true, - LayoutWrapperType.End, + FlexLayerAlignment.End, ); temp = [...arr1, ...arr2, ...arr3]; } else @@ -369,7 +369,7 @@ export const useAutoLayoutHighlights = ({ offsetChildren, flexOffsetTop, false, - LayoutWrapperType.Start, + FlexLayerAlignment.Start, ); offsets = [...offsets, ...temp]; @@ -385,7 +385,7 @@ export const useAutoLayoutHighlights = ({ arr: string[], flexOffsetTop: number, isWrapper: boolean, - wrapperType: LayoutWrapperType, + wrapperType: FlexLayerAlignment, ): Highlight[] => { let res: Highlight[] = []; const siblings: DOMRect[] = []; @@ -502,10 +502,10 @@ export const useAutoLayoutHighlights = ({ const getDropPosition = (index: number): number => { if (isNaN(index)) return 0; - const wrapperType: LayoutWrapperType = - offsets[index]?.wrapperType || LayoutWrapperType.Start; - if (wrapperType === LayoutWrapperType.Center) return index - 1; - if (wrapperType === LayoutWrapperType.End) return index - 2; + const wrapperType: FlexLayerAlignment = + offsets[index]?.wrapperType || FlexLayerAlignment.Start; + if (wrapperType === FlexLayerAlignment.Center) return index - 1; + if (wrapperType === FlexLayerAlignment.End) return index - 2; return index; }; diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts b/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts index 44cfbaf17db4..c4d1ccf26cd0 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts @@ -34,7 +34,7 @@ import ContainerJumpMetrics from "./ContainerJumpMetric"; import { AlignItems, LayoutDirection, - LayoutWrapperType, + FlexLayerAlignment, } from "components/constants"; export interface WidgetDraggingUpdateParams extends WidgetDraggingBlock { @@ -280,7 +280,7 @@ export const useBlocksToBeDraggedOnCanvas = ({ const updateChildrenPositions = ( index: number, drawingBlocks: WidgetDraggingBlock[], - wrapperType: LayoutWrapperType, + wrapperType: FlexLayerAlignment, ): void => { if (isNewWidget) addNewWidgetToAutoLayout(index, drawingBlocks, wrapperType); @@ -299,7 +299,7 @@ export const useBlocksToBeDraggedOnCanvas = ({ const addNewWidgetToAutoLayout = ( index: number, drawingBlocks: WidgetDraggingBlock[], - wrapperType: LayoutWrapperType, + wrapperType: FlexLayerAlignment, ) => { logContainerJumpOnDrop(); const blocksToUpdate = drawingBlocks.map((each) => { diff --git a/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts b/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts index 6af781f794cd..78a280aef087 100644 --- a/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts +++ b/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts @@ -35,7 +35,7 @@ import { CANVAS_DEFAULT_MIN_HEIGHT_PX } from "constants/AppConstants"; import AnalyticsUtil from "utils/AnalyticsUtil"; import { LayoutDirection, - LayoutWrapperType, + FlexLayerAlignment, ResponsiveBehavior, } from "components/constants"; import { @@ -410,7 +410,7 @@ function* autolayoutReorderSaga( movedWidgets: string[]; index: number; parentId: string; - wrapperType: LayoutWrapperType; + wrapperType: FlexLayerAlignment; direction: LayoutDirection; }>, ) { @@ -452,7 +452,7 @@ function* reorderAutolayoutChildren(params: { index: number; parentId: string; allWidgets: CanvasWidgetsReduxState; - wrapperType: LayoutWrapperType; + wrapperType: FlexLayerAlignment; direction: LayoutDirection; }) { const { @@ -525,14 +525,14 @@ function* updateMovedWidgets( movedWidgets: string[], allWidgets: CanvasWidgetsReduxState, parentId: string, - wrapperType: LayoutWrapperType, + wrapperType: FlexLayerAlignment, direction: LayoutDirection, ) { let widgets = { ...allWidgets }; if (!movedWidgets || !widgets) return { newMovedWidgets: movedWidgets, updatedWidgets: widgets }; const stateParent = widgets[parentId]; - if (stateParent.isWrapper) { + if (stateParent.isWrapper || true) { // If widgets are being dropped in a wrapper, // then updated the wrapper type and return' let hasFillChild = false; @@ -550,7 +550,7 @@ function* updateMovedWidgets( for (const each of movedWidgets) { widgets[each] = { ...widgets[each], - wrapperType: LayoutWrapperType.Start, + wrapperType: FlexLayerAlignment.Start, }; } } @@ -587,7 +587,7 @@ function* addWidgetAndReorderSaga( newWidget: WidgetAddChild; index: number; parentId: string; - wrapperType: LayoutWrapperType; + wrapperType: FlexLayerAlignment; direction: LayoutDirection; }>, ) { @@ -633,7 +633,7 @@ function* addAutoLayoutChild( const allWidgets: CanvasWidgetsReduxState = yield select(getWidgets); const stateParent: FlattenedWidgetProps = allWidgets[parentId]; - if (stateParent?.isWrapper) { + if (stateParent?.isWrapper || true) { const updatedWidgetsOnAddition: CanvasWidgetsReduxState = yield call( getUpdateDslAfterCreatingChild, { diff --git a/app/client/src/sagas/WidgetOperationUtils.ts b/app/client/src/sagas/WidgetOperationUtils.ts index 3889d04ae424..53d42139557f 100644 --- a/app/client/src/sagas/WidgetOperationUtils.ts +++ b/app/client/src/sagas/WidgetOperationUtils.ts @@ -57,7 +57,7 @@ import { Alignment, ButtonBoxShadowTypes, LayoutDirection, - LayoutWrapperType, + FlexLayerAlignment, Spacing, } from "components/constants"; import { @@ -1849,7 +1849,7 @@ export function* addAndWrapWidget( addChildPayload?: WidgetAddChild, direction = LayoutDirection.Horizontal, isWrapper = false, - wrapperType = LayoutWrapperType.Start, + wrapperType = FlexLayerAlignment.Start, ) { let widgets = { ...allWidgets }; const widgetId: string = childId || addChildPayload?.newWidgetId || ""; diff --git a/app/client/src/utils/layoutPropertiesUtils.ts b/app/client/src/utils/layoutPropertiesUtils.ts index aa649ddecf86..97b93e4b6061 100644 --- a/app/client/src/utils/layoutPropertiesUtils.ts +++ b/app/client/src/utils/layoutPropertiesUtils.ts @@ -63,7 +63,7 @@ export const verticalAlignment: { [key in Alignment]: LayoutProperties } = { }; export function getLayoutProperties( - direction: LayoutDirection, + direction: LayoutDirection = LayoutDirection.Horizontal, alignment: Alignment, spacing: Spacing, ): LayoutProperties { diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index 90fbce7cf956..356e493d9312 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -42,7 +42,7 @@ import { import { CanvasWidgetStructure } from "./constants"; import { DataTreeWidget } from "entities/DataTree/dataTreeFactory"; import Skeleton from "./Skeleton"; -import FlexComponent from "components/designSystems/appsmith/FlexComponent"; +import FlexComponent from "components/designSystems/appsmith/autoLayout/FlexComponent"; /*** * BaseWidget diff --git a/app/client/src/widgets/CanvasWidget.tsx b/app/client/src/widgets/CanvasWidget.tsx index 65a01130e032..72a0babdd2e5 100644 --- a/app/client/src/widgets/CanvasWidget.tsx +++ b/app/client/src/widgets/CanvasWidget.tsx @@ -18,10 +18,43 @@ import { ResponsiveBehavior, Spacing, } from "components/constants"; -import ContainerComponent, { FlexBox } from "./ContainerWidget/component"; +import ContainerComponent from "./ContainerWidget/component"; import { CanvasDraggingArena } from "pages/common/CanvasArenas/CanvasDraggingArena"; import { CanvasSelectionArena } from "pages/common/CanvasArenas/CanvasSelectionArena"; import WidgetsMultiSelectBox from "pages/Editor/WidgetsMultiSelectBox"; +import FlexBoxComponent from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; + +const flexLayers: any[] = [ + { + children: [ + { + id: "2f5yim088g", + align: "start", + }, + { + id: "92hs8bgv4h", + align: "start", + }, + ], + }, + { + children: [ + { + id: "crfmj4atrz", + align: "end", + }, + ], + }, + { + children: [ + { + id: "tbp7meiwtq", + align: "end", + }, + ], + hasFillChild: true, + }, +]; class CanvasWidget extends ContainerWidget { static getPropertyPaneConfig() { @@ -63,7 +96,7 @@ class CanvasWidget extends ContainerWidget { if (!childWidgetData) return null; const childWidget = { ...childWidgetData }; - console.log("#### child widget", childWidget); + const snapSpaces = this.getSnapSpaces(); childWidget.parentColumnSpace = snapSpaces.snapColumnSpace; childWidget.parentRowSpace = snapSpaces.snapRowSpace; @@ -73,7 +106,7 @@ class CanvasWidget extends ContainerWidget { childWidget.positioning = childWidget?.positioning || this.props.positioning; childWidget.useAutoLayout = this.state.useAutoLayout; - childWidget.direction = this.state.direction; + childWidget.direction = this.props.direction; childWidget.justifyContent = this.props.justifyContent; childWidget.alignItems = this.props.alignItems; @@ -105,7 +138,7 @@ class CanvasWidget extends ContainerWidget { {...this.getSnapSpaces()} alignItems={props.alignItems} canExtend={props.canExtend} - direction={this.state.direction} + direction={this.props.direction} dropDisabled={!!props.dropDisabled} noPad={this.props.noPad} parentId={props.parentId} @@ -131,9 +164,10 @@ class CanvasWidget extends ContainerWidget { widgetType={this.props.type} /> {/* without the wrapping div onClick events are triggered twice */} - {this.renderChildren()} - + ); } diff --git a/app/client/src/widgets/ContainerWidget/component/index.tsx b/app/client/src/widgets/ContainerWidget/component/index.tsx index 81757d40b81d..b9a753f63b65 100644 --- a/app/client/src/widgets/ContainerWidget/component/index.tsx +++ b/app/client/src/widgets/ContainerWidget/component/index.tsx @@ -12,15 +12,9 @@ import WidgetStyleContainer, { import { ComponentProps } from "widgets/BaseComponent"; import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; import { - AlignItems, - Alignment, FlexDirection, - JustifyContent, - LayoutDirection, - LayoutWrapperType, - Overflow, + FlexLayerAlignment, ResponsiveBehavior, - Spacing, } from "components/constants"; import { getLayoutProperties, @@ -28,7 +22,10 @@ import { } from "utils/layoutPropertiesUtils"; import { useSelector } from "store"; import { getWidgets } from "sagas/selectors"; -import AutoLayoutLayer from "components/designSystems/appsmith/AutoLayoutLayer"; +import { + FlexBoxProps, + FlexContainer, +} from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; const scrollContents = css` overflow-y: auto; @@ -80,29 +77,6 @@ const StyledContainerComponent = styled.div< } `; -export const FlexContainer = styled.div<{ - useAutoLayout?: boolean; - flexDirection?: FlexDirection; - justifyContent?: JustifyContent; - alignItems?: AlignItems; - stretchHeight: boolean; - overflow: Overflow; -}>` - display: ${({ useAutoLayout }) => (useAutoLayout ? "flex" : "block")}; - flex-direction: ${({ flexDirection }) => flexDirection || "row"}; - justify-content: ${({ justifyContent }) => justifyContent || "flex-start"}; - align-items: ${({ alignItems }) => alignItems || "flex-start"}; - flex-wrap: ${({ overflow }) => - overflow?.indexOf("wrap") > -1 ? overflow : "nowrap"}; - - width: 100%; - height: ${({ stretchHeight }) => (stretchHeight ? "100%" : "auto")}; - - overflow: ${({ overflow }) => - overflow?.indexOf("wrap") === -1 ? overflow : "hidden"}; - padding: 4px; -`; - const SubWrapper = styled.div<{ flexDirection: FlexDirection; }>` @@ -158,91 +132,6 @@ function ContainerComponentWrapper(props: ContainerComponentProps) { ); } -export function FlexBox(props: FlexBoxProps) { - // const allWidgets = useSelector(getWidgets); - const layoutProps = useMemo( - () => getLayoutProperties(props.direction, props.alignment, props.spacing), - [props.direction, props.alignment, props.spacing], - ); - const renderChildren = () => { - if (!props.children) return null; - if (!props.useAutoLayout) return props.children; - const flexLayers: any[] = [ - // { - // children: [ - // { - // id: "2f5yim088g", - // align: "start", - // }, - // { - // id: "92hs8bgv4h", - // align: "start", - // }, - // ], - // }, - // { - // children: [ - // { - // id: "crfmj4atrz", - // align: "end", - // }, - // ], - // }, - { - children: [ - { - id: "tbp7meiwtq", - align: "end", - }, - ], - hasFillChild: true, - }, - ]; - const map: any = {}; - if (isArray(props.children)) { - for (const child of props.children) { - map[(child as JSX.Element).props?.widgetId] = child; - } - } - return flexLayers.map((layer: any, index: number) => { - if (!layer.children || !layer.children.length) return null; - const start = [], - center = [], - end = []; - for (const child of layer.children) { - if (layer.hasFillChild) { - start.push(map[child.id]); - continue; - } - if (child.align === "end") end.push(map[child.id]); - else if (child.align === "center") center.push(map[child.id]); - else start.push(map[child.id]); - } - return ( - - ); - }); - }; - return ( - - <>{renderChildren()} - - ); -} - export function LayoutWrapper(props: FlexBoxProps): JSX.Element { const allWidgets = useSelector(getWidgets); let start: JSX.Element[] = [], @@ -256,9 +145,9 @@ export function LayoutWrapper(props: FlexBoxProps): JSX.Element { hasFillChild = true; break; } - if (widget?.wrapperType === LayoutWrapperType.End) + if (widget?.wrapperType === FlexLayerAlignment.End) end.push(child as JSX.Element); - else if (widget?.wrapperType === LayoutWrapperType.Center) + else if (widget?.wrapperType === FlexLayerAlignment.Center) center.push(child as JSX.Element); else start.push(child as JSX.Element); } @@ -349,15 +238,4 @@ export interface ContainerComponentProps alignItems?: string; } -export interface FlexBoxProps { - alignment: Alignment; - direction: LayoutDirection; - spacing: Spacing; - stretchHeight: boolean; - useAutoLayout: boolean; - children?: ReactNode; - widgetId: string; - overflow: Overflow; -} - export default ContainerComponent; diff --git a/app/client/src/widgets/ContainerWidget/widget/index.tsx b/app/client/src/widgets/ContainerWidget/widget/index.tsx index a5b7fb8b0dcb..2192f2ef588a 100644 --- a/app/client/src/widgets/ContainerWidget/widget/index.tsx +++ b/app/client/src/widgets/ContainerWidget/widget/index.tsx @@ -42,8 +42,6 @@ export class ContainerWidget extends BaseWidget< super(props); this.state = { useAutoLayout: false, - direction: LayoutDirection.Horizontal, - isMobile: false, }; this.renderChildWidget = this.renderChildWidget.bind(this); } @@ -278,7 +276,9 @@ export class ContainerWidget extends BaseWidget< } static getDerivedPropertiesMap(): DerivedPropertiesMap { - return {}; + return { + direction: `{{ this.positioning === "vertical" ? "Vertical" : "Horizontal" }}`, + }; } static getDefaultPropertiesMap(): Record { return {}; @@ -311,10 +311,6 @@ export class ContainerWidget extends BaseWidget< else this.setState({ useAutoLayout: true, - direction: - this.props.positioning === Positioning.Horizontal - ? LayoutDirection.Horizontal - : LayoutDirection.Vertical, }); }; @@ -381,7 +377,7 @@ export class ContainerWidget extends BaseWidget< childWidget.parentId = this.props.widgetId; // Pass layout controls to children childWidget.useAutoLayout = this.state.useAutoLayout; - childWidget.direction = this.state.direction; + childWidget.direction = this.props.direction; childWidget.positioning = this.props.positioning; childWidget.alignment = this.props.alignment; childWidget.spacing = this.props.spacing; @@ -444,6 +440,7 @@ export interface ContainerWidgetProps positioning?: Positioning; alignment?: Alignment; spacing?: Spacing; + direction?: LayoutDirection; removeWrappers?: (id: string) => void; addWrappers?: (id: string, direction: LayoutDirection) => void; updateWrappers?: (id: string, direction: LayoutDirection) => void; @@ -451,8 +448,6 @@ export interface ContainerWidgetProps export interface ContainerWidgetState extends WidgetState { useAutoLayout: boolean; - direction: LayoutDirection; - isMobile: boolean; } export default connect(null, mapDispatchToProps)(ContainerWidget); diff --git a/app/client/src/widgets/LayoutWrapperWidget.tsx b/app/client/src/widgets/LayoutWrapperWidget.tsx index 80cca2418e2c..df23fbbad3e6 100644 --- a/app/client/src/widgets/LayoutWrapperWidget.tsx +++ b/app/client/src/widgets/LayoutWrapperWidget.tsx @@ -80,7 +80,7 @@ class LayoutWrapperWidget extends ContainerWidget { {this.renderAsContainerComponent(canvasProps)} @@ -102,7 +102,7 @@ class LayoutWrapperWidget extends ContainerWidget { childWidget.positioning = childWidget?.positioning || this.props.positioning; childWidget.useAutoLayout = this.state.useAutoLayout; - childWidget.direction = this.state.direction; + childWidget.direction = this.props.direction; childWidget.justifyContent = this.props.justifyContent; childWidget.alignItems = this.props.alignItems; @@ -134,7 +134,7 @@ class LayoutWrapperWidget extends ContainerWidget { {...this.getSnapSpaces()} alignItems={props.alignItems} canExtend={props.canExtend} - direction={this.state.direction} + direction={this.props.direction} dropDisabled={!!props.dropDisabled} noPad={this.props.noPad} parentId={props.parentId} @@ -163,7 +163,7 @@ class LayoutWrapperWidget extends ContainerWidget { Date: Tue, 4 Oct 2022 08:46:31 -0400 Subject: [PATCH 118/708] update add / remove wrappers --- app/client/src/sagas/LayoutUpdateSagas.tsx | 3 +- app/client/src/sagas/WidgetOperationUtils.ts | 64 +++++--------------- app/client/src/widgets/CanvasWidget.tsx | 2 +- 3 files changed, 16 insertions(+), 53 deletions(-) diff --git a/app/client/src/sagas/LayoutUpdateSagas.tsx b/app/client/src/sagas/LayoutUpdateSagas.tsx index c3c222db846a..dd51932121b4 100644 --- a/app/client/src/sagas/LayoutUpdateSagas.tsx +++ b/app/client/src/sagas/LayoutUpdateSagas.tsx @@ -45,13 +45,12 @@ function* removeChildWrappers(actionPayload: ReduxAction) { function* addChildWrappers(actionPayload: ReduxAction) { try { const start = performance.now(); - const { direction, parentId } = actionPayload.payload; + const { parentId } = actionPayload.payload; const allWidgets: CanvasWidgetsReduxState = yield select(getWidgets); const updatedWidgets: CanvasWidgetsReduxState = yield call( wrapChildren, allWidgets, parentId, - direction, ); yield put(updateAndSaveLayout(updatedWidgets)); log.debug("empty wrapper removal took", performance.now() - start, "ms"); diff --git a/app/client/src/sagas/WidgetOperationUtils.ts b/app/client/src/sagas/WidgetOperationUtils.ts index 53d42139557f..971e737024be 100644 --- a/app/client/src/sagas/WidgetOperationUtils.ts +++ b/app/client/src/sagas/WidgetOperationUtils.ts @@ -59,12 +59,14 @@ import { LayoutDirection, FlexLayerAlignment, Spacing, + ResponsiveBehavior, } from "components/constants"; import { generateChildWidgets, GeneratedWidgetPayload, } from "./WidgetAdditionSagas"; import { WidgetAddChild } from "actions/pageActions"; +import { FlexLayer } from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; export interface CopiedWidgetGroup { widgetId: string; @@ -1720,33 +1722,7 @@ export function purgeChildWrappers( const canvasId = container.children ? container.children[0] : ""; let parent = widgets[canvasId]; if (!parent) return widgets; - const wrapperChildren = parent.children?.filter( - (each) => widgets[each] && widgets[each].isWrapper, - ); - - if (!wrapperChildren || !wrapperChildren.length) return widgets; - parent = { - ...parent, - children: [ - ...(parent.children || []).filter( - (each) => wrapperChildren.indexOf(each) === -1, - ), - ], - }; - wrapperChildren.forEach((each) => { - const wrapper = widgets[each]; - const children = wrapper.children || []; - if (children.length) { - children.forEach((child) => { - parent = { - ...parent, - children: [...(parent.children || []), child], - }; - widgets[child] = { ...widgets[child], parentId: canvasId }; - }); - } - delete widgets[each]; - }); + parent = { ...parent, flexLayers: [] }; widgets[canvasId] = parent; return widgets; } @@ -1754,9 +1730,8 @@ export function purgeChildWrappers( export function* wrapChildren( allWidgets: CanvasWidgetsReduxState, containerId: string, - direction: LayoutDirection, ) { - let widgets = { ...allWidgets }; + const widgets = { ...allWidgets }; const container = widgets[containerId]; if (!container) return widgets; const canvasId = container.children ? container.children[0] : ""; @@ -1765,30 +1740,19 @@ export function* wrapChildren( const children = parent.children || []; if (!children.length) return widgets; - // Remove current children from the parent - parent = { - ...parent, - children: [], - }; - /** - * Generate new wrappers - * and wrap each child in its own wrapper - */ + const flexLayers: FlexLayer[] = []; + for (const each of children) { const child = widgets[each]; - if (!child || child.isWrapper) continue; - // Create new wrapper - const res: WrappedWidgetPayload = yield call( - addAndWrapWidget, - widgets, - canvasId, - each, - undefined, - direction, - false, - ); - widgets = res.widgets; + if (!child) continue; + flexLayers.push({ + children: [{ id: child.widgetId, align: FlexLayerAlignment.Start }], + hasFillChild: + child.responsiveBehavior === ResponsiveBehavior.Fill || false, + }); } + parent = { ...parent, flexLayers }; + widgets[canvasId] = parent; return widgets; } diff --git a/app/client/src/widgets/CanvasWidget.tsx b/app/client/src/widgets/CanvasWidget.tsx index 72a0babdd2e5..1b91030f05c3 100644 --- a/app/client/src/widgets/CanvasWidget.tsx +++ b/app/client/src/widgets/CanvasWidget.tsx @@ -167,7 +167,7 @@ class CanvasWidget extends ContainerWidget { Date: Tue, 4 Oct 2022 12:40:16 -0400 Subject: [PATCH 119/708] working add_new_child --- .../hooks/useAutoLayoutHighlights.ts | 13 +-- .../hooks/useBlocksToBeDraggedOnCanvas.ts | 1 + .../sagas/CanvasSagas/DraggingCanvasSagas.ts | 102 ++++++++++++++---- app/client/src/widgets/CanvasWidget.tsx | 34 +----- .../widgets/ContainerWidget/widget/index.tsx | 23 ++-- 5 files changed, 96 insertions(+), 77 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index 9669d221767d..db61e37fb1e4 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -308,7 +308,7 @@ export const useAutoLayoutHighlights = ({ const blocks = getDraggedBlocks(); if (!blocks || !blocks.length) return []; - + // console.log("#### blocks: ", blocks); /** * update dimensions of the current canvas * and break out of the function if returned value is false. @@ -321,11 +321,12 @@ export const useAutoLayoutHighlights = ({ // Get all children of current dragging canvas const canvasChildren = canvas.children || []; const offsetChildren = canvasChildren.filter((each) => { - if (canvas.isWrapper) return blocks.indexOf(each) === -1; - const children = allWidgets[each].children?.filter( - (item) => blocks.indexOf(item) === -1, - ); - return isArray(children) && children.length > 0; + return blocks.indexOf(each) === -1; + // if (canvas.isWrapper) return blocks.indexOf(each) === -1; + // const children = allWidgets[each].children?.filter( + // (item) => blocks.indexOf(item) === -1, + // ); + // return isArray(children) && children.length > 0; }); // console.log(`#### canvas children: ${JSON.stringify(canvasChildren)}`); // console.log(`#### offset children: ${JSON.stringify(offsetChildren)}`); diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts b/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts index c4d1ccf26cd0..3349a3895003 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts @@ -338,6 +338,7 @@ export const useBlocksToBeDraggedOnCanvas = ({ parentId: widgetId, wrapperType, direction, + isNewLayer: true, }, }); }; diff --git a/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts b/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts index 78a280aef087..966f6dc0350b 100644 --- a/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts +++ b/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts @@ -43,6 +43,7 @@ import { purgeEmptyWrappers, WrappedWidgetPayload, } from "../WidgetOperationUtils"; +import { FlexLayer } from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; export type WidgetMoveParams = { widgetId: string; @@ -409,6 +410,7 @@ function* autolayoutReorderSaga( actionPayload: ReduxAction<{ movedWidgets: string[]; index: number; + isNewLayer: boolean; parentId: string; wrapperType: FlexLayerAlignment; direction: LayoutDirection; @@ -419,6 +421,7 @@ function* autolayoutReorderSaga( const { direction, index, + isNewLayer, movedWidgets, parentId, wrapperType, @@ -433,6 +436,7 @@ function* autolayoutReorderSaga( { movedWidgets, index, + isNewLayer, parentId, allWidgets, wrapperType, @@ -450,6 +454,7 @@ function* autolayoutReorderSaga( function* reorderAutolayoutChildren(params: { movedWidgets: string[]; index: number; + isNewLayer: boolean; parentId: string; allWidgets: CanvasWidgetsReduxState; wrapperType: FlexLayerAlignment; @@ -459,6 +464,7 @@ function* reorderAutolayoutChildren(params: { allWidgets, direction, index, + isNewLayer, movedWidgets, parentId, wrapperType, @@ -493,34 +499,80 @@ function* reorderAutolayoutChildren(params: { }; }); } - // Remove all empty wrappers - const trimmedWidgets = purgeEmptyWrappers(widgets); - // console.log(`#### trimmed widgets: ${JSON.stringify(trimmedWidgets)}`); - // Update moved widgets. Add wrappers to those missing one. - const { newMovedWidgets, updatedWidgets } = yield call( - updateMovedWidgets, - selectedWidgets, - trimmedWidgets, - parentId, - wrapperType, - direction, - ); - const items = [...(updatedWidgets[parentId].children || [])]; + let updatedWidgets: CanvasWidgetsReduxState = Object.assign({}, widgets); + if (direction === LayoutDirection.Vertical) { + updatedWidgets = updateAutoLayoutLayers( + selectedWidgets, + widgets, + parentId, + wrapperType, + isNewLayer, + index, + ); + } + const items = [...(widgets[parentId].children || [])]; // remove moved widegts from children - const newItems = items.filter((item) => newMovedWidgets.indexOf(item) === -1); + const newItems = items.filter((item) => movedWidgets.indexOf(item) === -1); // calculate valid position for drop const pos = index > newItems.length ? newItems.length : index; updatedWidgets[parentId] = { ...updatedWidgets[parentId], children: [ ...newItems.slice(0, pos), - ...newMovedWidgets, + ...movedWidgets, ...newItems.slice(pos), ], }; return updatedWidgets; } +function updateAutoLayoutLayers( + movedWidgets: string[], + allWidgets: CanvasWidgetsReduxState, + parentId: string, + wrapperType: FlexLayerAlignment, + isNewLayer: boolean, + index: number, +) { + const widgets: CanvasWidgetsReduxState = Object.assign({}, allWidgets); + const canvas = widgets[parentId]; + if (!canvas) return widgets; + let hasFillChild = false; + const layerChildren = []; + for (const id of movedWidgets) { + const widget = widgets[id]; + if (!widget) continue; + if (widget.responsiveBehavior === ResponsiveBehavior.Fill) + hasFillChild = true; + layerChildren.push({ + id, + align: wrapperType, + }); + } + const newLayer: FlexLayer = { children: layerChildren, hasFillChild }; + // console.log("#### canvas id", parentId); + // console.log("#### newLayer", newLayer); + const flexLayers = canvas.flexLayers || []; + const pos = index > flexLayers.length ? flexLayers.length : index; + // console.log("#### index", index); + // console.log("#### pos", pos); + const updatedCanvas = { + ...canvas, + flexLayers: [ + ...flexLayers.slice(0, pos), + newLayer, + ...flexLayers.slice(pos), + ], + }; + const updatedWidgets = { + ...widgets, + [parentId]: updatedCanvas, + }; + // console.log("#### updated flex layers", updatedCanvas.flexLayers); + // console.log("#### widgets", updatedWidgets); + return updatedWidgets; +} + function* updateMovedWidgets( movedWidgets: string[], allWidgets: CanvasWidgetsReduxState, @@ -586,6 +638,7 @@ function* addWidgetAndReorderSaga( actionPayload: ReduxAction<{ newWidget: WidgetAddChild; index: number; + isNewLayer: boolean; parentId: string; wrapperType: FlexLayerAlignment; direction: LayoutDirection; @@ -595,25 +648,28 @@ function* addWidgetAndReorderSaga( const { direction, index, + isNewLayer, newWidget, parentId, wrapperType, } = actionPayload.payload; try { - const updatedWidgetsOnAddition: { - widgets: CanvasWidgetsReduxState; - containerId?: string; - } = yield call(addAutoLayoutChild, newWidget, parentId, direction); + const updatedWidgetsOnAddition: CanvasWidgetsReduxState = yield call( + getUpdateDslAfterCreatingChild, + { + ...newWidget, + widgetId: parentId, + }, + ); const updatedWidgetsOnMove: CanvasWidgetsReduxState = yield call( reorderAutolayoutChildren, { - movedWidgets: [ - updatedWidgetsOnAddition.containerId || newWidget.newWidgetId, - ], + movedWidgets: [newWidget.newWidgetId], index, + isNewLayer, parentId, - allWidgets: updatedWidgetsOnAddition.widgets, + allWidgets: updatedWidgetsOnAddition, wrapperType, direction, }, diff --git a/app/client/src/widgets/CanvasWidget.tsx b/app/client/src/widgets/CanvasWidget.tsx index 1b91030f05c3..5036fdc77754 100644 --- a/app/client/src/widgets/CanvasWidget.tsx +++ b/app/client/src/widgets/CanvasWidget.tsx @@ -24,38 +24,6 @@ import { CanvasSelectionArena } from "pages/common/CanvasArenas/CanvasSelectionA import WidgetsMultiSelectBox from "pages/Editor/WidgetsMultiSelectBox"; import FlexBoxComponent from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; -const flexLayers: any[] = [ - { - children: [ - { - id: "2f5yim088g", - align: "start", - }, - { - id: "92hs8bgv4h", - align: "start", - }, - ], - }, - { - children: [ - { - id: "crfmj4atrz", - align: "end", - }, - ], - }, - { - children: [ - { - id: "tbp7meiwtq", - align: "end", - }, - ], - hasFillChild: true, - }, -]; - class CanvasWidget extends ContainerWidget { static getPropertyPaneConfig() { return []; @@ -167,7 +135,7 @@ class CanvasWidget extends ContainerWidget { ): void => { - if (this.props.positioning === Positioning.Fixed) { - this.props.removeWrappers && - this.props.removeWrappers(this.props.widgetId); - } else if (prevProps.positioning === Positioning.Fixed) { + updateWrappers = (): void => { + if (this.props.positioning === Positioning.Vertical) { this.props.addWrappers && this.props.addWrappers( this.props.widgetId, - this.props.positioning === Positioning.Horizontal - ? LayoutDirection.Horizontal - : LayoutDirection.Vertical, - ); - } else - this.props.updateWrappers && - this.props.updateWrappers( - this.props.widgetId, - this.props.positioning === Positioning.Horizontal + (this.props.positioning as Positioning) === Positioning.Horizontal ? LayoutDirection.Horizontal : LayoutDirection.Vertical, ); + } else { + this.props.removeWrappers && + this.props.removeWrappers(this.props.widgetId); + } }; getSnapSpaces = () => { From cb419fe0feea81fe6c2daf7235cd64f647b80194 Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 4 Oct 2022 12:52:17 -0400 Subject: [PATCH 120/708] remove wrapper widget related code --- app/client/src/actions/autoLayoutActions.ts | 11 -- .../src/ce/constants/ReduxActionConstants.tsx | 1 - .../sagas/CanvasSagas/DraggingCanvasSagas.ts | 106 +---------- app/client/src/sagas/LayoutUpdateSagas.tsx | 35 +--- app/client/src/sagas/WidgetAdditionSagas.ts | 81 +-------- app/client/src/sagas/WidgetOperationUtils.ts | 165 ------------------ .../widgets/ContainerWidget/widget/index.tsx | 9 +- 7 files changed, 5 insertions(+), 403 deletions(-) diff --git a/app/client/src/actions/autoLayoutActions.ts b/app/client/src/actions/autoLayoutActions.ts index fcb35a2359d0..2e1310596c35 100644 --- a/app/client/src/actions/autoLayoutActions.ts +++ b/app/client/src/actions/autoLayoutActions.ts @@ -13,14 +13,3 @@ export const addWrappers = (parentId: string, direction: LayoutDirection) => ({ direction, }, }); - -export const updateWrappers = ( - parentId: string, - direction: LayoutDirection, -) => ({ - type: ReduxActionTypes.UPDATE_WRAPPER_DIMENSIONS, - payload: { - parentId, - direction, - }, -}); diff --git a/app/client/src/ce/constants/ReduxActionConstants.tsx b/app/client/src/ce/constants/ReduxActionConstants.tsx index 42666641a175..f2a501eee334 100644 --- a/app/client/src/ce/constants/ReduxActionConstants.tsx +++ b/app/client/src/ce/constants/ReduxActionConstants.tsx @@ -668,7 +668,6 @@ export const ReduxActionTypes = { AUTOLAYOUT_ADD_NEW_WIDGETS: "AUTOLAYOUT_ADD_NEW_WIDGETS", REMOVE_CHILD_WRAPPERS: "REMOVE_CHILD_WRAPPERS", ADD_CHILD_WRAPPERS: "ADD_CHILD_WRAPPERS", - UPDATE_WRAPPER_DIMENSIONS: "UPDATE_WRAPPER_DIMENSIONS", }; export type ReduxActionType = typeof ReduxActionTypes[keyof typeof ReduxActionTypes]; diff --git a/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts b/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts index 966f6dc0350b..46b7cd255aa1 100644 --- a/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts +++ b/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts @@ -26,10 +26,7 @@ import { OccupiedSpace } from "constants/CanvasEditorConstants"; import { collisionCheckPostReflow } from "utils/reflowHookUtils"; import { WidgetDraggingUpdateParams } from "pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas"; import { getWidget, getWidgets } from "sagas/selectors"; -import { - getUpdateDslAfterCreatingAutoLayoutChild, - getUpdateDslAfterCreatingChild, -} from "sagas/WidgetAdditionSagas"; +import { getUpdateDslAfterCreatingChild } from "sagas/WidgetAdditionSagas"; import { MainCanvasReduxState } from "reducers/uiReducers/mainCanvasReducer"; import { CANVAS_DEFAULT_MIN_HEIGHT_PX } from "constants/AppConstants"; import AnalyticsUtil from "utils/AnalyticsUtil"; @@ -38,11 +35,7 @@ import { FlexLayerAlignment, ResponsiveBehavior, } from "components/constants"; -import { - addAndWrapWidget, - purgeEmptyWrappers, - WrappedWidgetPayload, -} from "../WidgetOperationUtils"; +import { purgeEmptyWrappers } from "../WidgetOperationUtils"; import { FlexLayer } from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; export type WidgetMoveParams = { @@ -573,67 +566,6 @@ function updateAutoLayoutLayers( return updatedWidgets; } -function* updateMovedWidgets( - movedWidgets: string[], - allWidgets: CanvasWidgetsReduxState, - parentId: string, - wrapperType: FlexLayerAlignment, - direction: LayoutDirection, -) { - let widgets = { ...allWidgets }; - if (!movedWidgets || !widgets) - return { newMovedWidgets: movedWidgets, updatedWidgets: widgets }; - const stateParent = widgets[parentId]; - if (stateParent.isWrapper || true) { - // If widgets are being dropped in a wrapper, - // then updated the wrapper type and return' - let hasFillChild = false; - for (const each of movedWidgets) { - if (widgets[each].responsiveBehavior === ResponsiveBehavior.Fill) { - hasFillChild = true; - break; - } - widgets[each] = { - ...widgets[each], - wrapperType, - }; - } - if (hasFillChild) { - for (const each of movedWidgets) { - widgets[each] = { - ...widgets[each], - wrapperType: FlexLayerAlignment.Start, - }; - } - } - return { newMovedWidgets: movedWidgets, updatedWidgets: widgets }; - } - const newMovedWidgets: string[] = []; - for (const each of movedWidgets) { - const widget = widgets[each]; - if (!widget) continue; - if (widget.isWrapper) { - newMovedWidgets.push(each); - continue; - } - - const res: WrappedWidgetPayload = yield call( - addAndWrapWidget, - widgets, - parentId, - each, - undefined, - direction, - stateParent?.isWrapper || false, - wrapperType, - ); - - widgets = res.widgets; - newMovedWidgets.push(res.wrapperId); - } - return { newMovedWidgets, updatedWidgets: widgets }; -} - function* addWidgetAndReorderSaga( actionPayload: ReduxAction<{ newWidget: WidgetAddChild; @@ -681,40 +613,6 @@ function* addWidgetAndReorderSaga( } } -function* addAutoLayoutChild( - newWidget: WidgetAddChild, - parentId: string, - direction: LayoutDirection, -) { - const allWidgets: CanvasWidgetsReduxState = yield select(getWidgets); - const stateParent: FlattenedWidgetProps = allWidgets[parentId]; - - if (stateParent?.isWrapper || true) { - const updatedWidgetsOnAddition: CanvasWidgetsReduxState = yield call( - getUpdateDslAfterCreatingChild, - { - ...newWidget, - widgetId: parentId, - }, - ); - return { widgets: updatedWidgetsOnAddition }; - } else { - const updatedWidgetsOnAddition: { - widgets: CanvasWidgetsReduxState; - containerId?: string; - } = yield call( - getUpdateDslAfterCreatingAutoLayoutChild, - { - ...newWidget, - widgetId: parentId, - }, - direction, - ); - - return updatedWidgetsOnAddition; - } -} - export default function* draggingCanvasSagas() { yield all([ takeLatest(ReduxActionTypes.WIDGETS_MOVE, moveWidgetsSaga), diff --git a/app/client/src/sagas/LayoutUpdateSagas.tsx b/app/client/src/sagas/LayoutUpdateSagas.tsx index dd51932121b4..4d9fe676ce0e 100644 --- a/app/client/src/sagas/LayoutUpdateSagas.tsx +++ b/app/client/src/sagas/LayoutUpdateSagas.tsx @@ -9,11 +9,7 @@ import log from "loglevel"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; import { all, call, put, select, takeLatest } from "redux-saga/effects"; import { getWidgets } from "./selectors"; -import { - purgeChildWrappers, - updateWrapperDimensions, - wrapChildren, -} from "./WidgetOperationUtils"; +import { purgeChildWrappers, wrapChildren } from "./WidgetOperationUtils"; type LayoutUpdatePayload = { parentId: string; @@ -65,38 +61,9 @@ function* addChildWrappers(actionPayload: ReduxAction) { } } -function* updateChildWrappers(actionPayload: ReduxAction) { - try { - const start = performance.now(); - const { direction, parentId } = actionPayload.payload; - const allWidgets: CanvasWidgetsReduxState = yield select(getWidgets); - const updatedWidgets: CanvasWidgetsReduxState = yield call( - updateWrapperDimensions, - allWidgets, - parentId, - direction, - ); - yield put(updateAndSaveLayout(updatedWidgets)); - log.debug( - "update wrapper dimensions took", - performance.now() - start, - "ms", - ); - } catch (error) { - yield put({ - type: ReduxActionErrorTypes.WIDGET_OPERATION_ERROR, - payload: { - action: ReduxActionTypes.UPDATE_WRAPPER_DIMENSIONS, - error, - }, - }); - } -} - export default function* layoutUpdateSagas() { yield all([ takeLatest(ReduxActionTypes.ADD_CHILD_WRAPPERS, addChildWrappers), takeLatest(ReduxActionTypes.REMOVE_CHILD_WRAPPERS, removeChildWrappers), - takeLatest(ReduxActionTypes.UPDATE_WRAPPER_DIMENSIONS, updateChildWrappers), ]); } diff --git a/app/client/src/sagas/WidgetAdditionSagas.ts b/app/client/src/sagas/WidgetAdditionSagas.ts index 7d6d2f5cb881..bc1241f6250b 100644 --- a/app/client/src/sagas/WidgetAdditionSagas.ts +++ b/app/client/src/sagas/WidgetAdditionSagas.ts @@ -23,11 +23,7 @@ import { executeWidgetBlueprintOperations, traverseTreeAndExecuteBlueprintChildOperations, } from "./WidgetBlueprintSagas"; -import { - addAndWrapWidget, - getParentBottomRowAfterAddingWidget, - WrappedWidgetPayload, -} from "./WidgetOperationUtils"; +import { getParentBottomRowAfterAddingWidget } from "./WidgetOperationUtils"; import log from "loglevel"; import { getDataTree } from "selectors/dataTreeSelectors"; import { generateReactKey } from "utils/generators"; @@ -40,7 +36,6 @@ import { getSelectedAppThemeStylesheet } from "selectors/appThemingSelectors"; import { getPropertiesToUpdate } from "./WidgetOperationSagas"; import { klona as clone } from "klona/full"; import { DataTree } from "entities/DataTree/dataTreeFactory"; -import { LayoutDirection } from "components/constants"; const WidgetTypes = WidgetFactory.widgetTypes; @@ -326,80 +321,6 @@ export function* getUpdateDslAfterCreatingChild( return updatedWidgets; } -export function* getUpdateDslAfterCreatingAutoLayoutChild( - addChildPayload: WidgetAddChild, - direction: LayoutDirection, -) { - // console.log(addChildPayload); - // NOTE: widgetId here is the parentId of the dropped widget ( we should rename it to avoid confusion ) - const { widgetId: parentId } = addChildPayload; - // Get the current parent widget whose child will be the new widget. - // console.log("State parent"); - // console.log(stateParent); - // const parent = Object.assign({}, stateParent); - // Get all the widgets from the canvasWidgetsReducer - const stateWidgets: CanvasWidgetsReduxState = yield select(getWidgets); - let widgets = Object.assign({}, stateWidgets); - // console.log(widgets); - - const res: WrappedWidgetPayload = yield call( - addAndWrapWidget, - widgets, - parentId, - undefined, - addChildPayload, - direction, - ); - widgets = res.widgets; - - // Update container - canvas relationship - yield put({ - type: WidgetReduxActionTypes.WIDGET_CHILD_ADDED, - payload: { - widgetId: res.wrapperId, - type: "LAYOUT_WRAPPER_WIDGET", - }, - }); - - const parent = widgets[parentId]; - const wrapper = widgets[res.wrapperId]; - const child = widgets[addChildPayload.newWidgetId]; - - const parentBottomRow = getParentBottomRowAfterAddingWidget(parent, wrapper); - widgets[parent.widgetId] = { - ...parent, - bottomRow: parentBottomRow, - }; - - // console.log(widgets); - AppsmithConsole.info({ - text: "Widget was created", - source: { - type: ENTITY_TYPE.WIDGET, - id: child.widgetId, - name: child.widgetName, - }, - }); - yield put({ - type: WidgetReduxActionTypes.WIDGET_CHILD_ADDED, - payload: { - widgetId: child.widgetId, - type: addChildPayload.type, - }, - }); - // some widgets need to update property of parent if the parent have CHILD_OPERATIONS - // so here we are traversing up the tree till we get to MAIN_CONTAINER_WIDGET_ID - // while traversing, if we find any widget which has CHILD_OPERATION, we will call the fn in it - const updatedWidgets: CanvasWidgetsReduxState = yield call( - traverseTreeAndExecuteBlueprintChildOperations, - parent, - addChildPayload.newWidgetId, - widgets, - ); - // console.log(updatedWidgets); - return { widgets: updatedWidgets, containerId: wrapper.widgetId }; -} - /** * this saga is called when we drop a widget on the canvas. * diff --git a/app/client/src/sagas/WidgetOperationUtils.ts b/app/client/src/sagas/WidgetOperationUtils.ts index 971e737024be..474f45519709 100644 --- a/app/client/src/sagas/WidgetOperationUtils.ts +++ b/app/client/src/sagas/WidgetOperationUtils.ts @@ -61,10 +61,6 @@ import { Spacing, ResponsiveBehavior, } from "components/constants"; -import { - generateChildWidgets, - GeneratedWidgetPayload, -} from "./WidgetAdditionSagas"; import { WidgetAddChild } from "actions/pageActions"; import { FlexLayer } from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; @@ -1805,164 +1801,3 @@ export interface WrappedWidgetPayload { widgets: CanvasWidgetsReduxState; wrapperId: string; } - -export function* addAndWrapWidget( - allWidgets: CanvasWidgetsReduxState, - parentId: string, - childId?: string, - addChildPayload?: WidgetAddChild, - direction = LayoutDirection.Horizontal, - isWrapper = false, - wrapperType = FlexLayerAlignment.Start, -) { - let widgets = { ...allWidgets }; - const widgetId: string = childId || addChildPayload?.newWidgetId || ""; - if (!widgetId) return { widgets, wrapperId: parentId }; - let child = widgets[widgetId]; - let parent = widgets[parentId]; - - // remove widget from parent's children - parent = { - ...parent, - children: [...(parent.children || []).filter((each) => each !== widgetId)], - }; - - const payload = childId - ? { - rows: child.rows || child.bottomRow - child.topRow, - columns: child.columns || child.rightColumn - child.leftColumn, - topRow: child.topRow, - leftColumn: child.leftColumn, - parentColumnSpace: child.parentColumnSpace, - parentRowSpace: child.parentRowSpace, - widgetId: parentId, - } - : addChildPayload - ? { - rows: addChildPayload.rows, - columns: addChildPayload.columns, - topRow: addChildPayload.topRow, - leftColumn: addChildPayload.leftColumn, - parentColumnSpace: addChildPayload.parentColumnSpace, - parentRowSpace: addChildPayload.parentRowSpace, - widgetId: parentId, - } - : null; - if (!payload) return { widgets, wrapperId: parentId }; - - // Create new wrapper - const wrapperPayload = getLayoutWrapperPayload( - widgets, - payload, - direction, - isWrapper, - ); - const widgetsAfterCreatingWrapper: GeneratedWidgetPayload = yield generateChildWidgets( - parent, - wrapperPayload, - widgets, - ); - let wrapper = - widgetsAfterCreatingWrapper.widgets[widgetsAfterCreatingWrapper.widgetId]; - - widgets = widgetsAfterCreatingWrapper.widgets; - - // Add child to wrapper's children - wrapper = { - ...wrapper, - children: [...(wrapper.children || []), widgetId], - }; - - // If new widget, then create it. - if (addChildPayload) { - const childPayload: WidgetAddChild = { - ...addChildPayload, - widgetId: wrapper.widgetId, - }; - const widgetsAfterCreatingChild: GeneratedWidgetPayload = yield generateChildWidgets( - wrapper, - childPayload, - widgets, - childPayload?.props?.blueprint, - ); - widgets = widgetsAfterCreatingChild.widgets; - } - - child = widgets[widgetId]; - child = { - ...child, - parentId: wrapper.widgetId, - wrapperType, - }; - - // Update child to fill the width of the parent if it is wrapped in a vertical wrapper. - // if ( - // (!isWrapper && direction === LayoutDirection.Horizontal) || - // (isWrapper && direction === LayoutDirection.Vertical) - // ) { - // child = { - // ...child, - // leftColumn: 0, - // rightColumn: 63, - // }; - // } - - widgets[wrapper.widgetId] = wrapper; - widgets[widgetId] = child; - widgets[parentId] = { - ...parent, - children: [...(parent.children || []), wrapper.widgetId], - }; - return { widgets, wrapperId: wrapper.widgetId }; -} - -export function updateWrapperDimensions( - allWidgets: CanvasWidgetsReduxState, - parentId: string, - direction: LayoutDirection, -): CanvasWidgetsReduxState { - const widgets = { ...allWidgets }; - const container = widgets[parentId]; - if (!container?.children) return widgets; - const canvas = widgets[container.children[0]]; - const wrappers = canvas?.children; - if (!wrappers) return widgets; - if (direction === LayoutDirection.Vertical) { - for (const each of wrappers) { - const wrapper = widgets[each]; - if (!wrapper || !wrapper.children) continue; - const prevRightColumn = wrapper.rightColumn; - widgets[each] = { - ...wrapper, - leftColumn: 0, - rightColumn: 63, - }; - const children = wrapper.children; - for (const child of children) { - const childWidget = widgets[child]; - widgets[child] = { - ...childWidget, - leftColumn: 0, - rightColumn: prevRightColumn ? prevRightColumn - 1 : 0, - }; - } - } - } else { - for (const each of wrappers) { - const wrapper = widgets[each]; - if (!wrapper || !wrapper.children) continue; - const children = wrapper.children; - let max = 0; - for (const child of children) { - const childWidget = widgets[child]; - max = Math.max(max, childWidget.rightColumn - childWidget.leftColumn); - } - widgets[each] = { - ...wrapper, - leftColumn: 0, - rightColumn: max + 1, - }; - } - } - return widgets; -} diff --git a/app/client/src/widgets/ContainerWidget/widget/index.tsx b/app/client/src/widgets/ContainerWidget/widget/index.tsx index 8f80c0ea0b19..0743b9d6755f 100644 --- a/app/client/src/widgets/ContainerWidget/widget/index.tsx +++ b/app/client/src/widgets/ContainerWidget/widget/index.tsx @@ -28,11 +28,7 @@ import { generateResponsiveBehaviorConfig, } from "utils/layoutPropertiesUtils"; import { connect } from "react-redux"; -import { - addWrappers, - removeWrappers, - updateWrappers, -} from "actions/autoLayoutActions"; +import { addWrappers, removeWrappers } from "actions/autoLayoutActions"; export class ContainerWidget extends BaseWidget< ContainerWidgetProps, @@ -420,8 +416,6 @@ const mapDispatchToProps = (dispatch: any) => ({ removeWrappers: (id: string) => dispatch(removeWrappers(id)), addWrappers: (id: string, direction: LayoutDirection) => dispatch(addWrappers(id, direction)), - updateWrappers: (id: string, direction: LayoutDirection) => - dispatch(updateWrappers(id, direction)), }); export interface ContainerWidgetProps @@ -436,7 +430,6 @@ export interface ContainerWidgetProps direction?: LayoutDirection; removeWrappers?: (id: string) => void; addWrappers?: (id: string, direction: LayoutDirection) => void; - updateWrappers?: (id: string, direction: LayoutDirection) => void; } export interface ContainerWidgetState extends WidgetState { From 86d6581ca117a60dfc6fe16cacb93b0e660131ff Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 4 Oct 2022 13:08:31 -0400 Subject: [PATCH 121/708] working reorder at level 1 --- .../hooks/useBlocksToBeDraggedOnCanvas.ts | 1 + .../sagas/CanvasSagas/DraggingCanvasSagas.ts | 35 ++++++++++++++----- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts b/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts index 3349a3895003..a4090a7119d4 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts @@ -293,6 +293,7 @@ export const useBlocksToBeDraggedOnCanvas = ({ parentId: widgetId, wrapperType, direction, + isNewLayer: false, }, }); }; diff --git a/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts b/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts index 46b7cd255aa1..fb4bf4d17b09 100644 --- a/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts +++ b/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts @@ -543,29 +543,46 @@ function updateAutoLayoutLayers( }); } const newLayer: FlexLayer = { children: layerChildren, hasFillChild }; - // console.log("#### canvas id", parentId); - // console.log("#### newLayer", newLayer); + console.log("#### canvas id", parentId); + console.log("#### newLayer", newLayer); const flexLayers = canvas.flexLayers || []; - const pos = index > flexLayers.length ? flexLayers.length : index; - // console.log("#### index", index); - // console.log("#### pos", pos); + const filteredLayers = isNewLayer + ? flexLayers + : filterLayers(flexLayers, movedWidgets); + console.log("#### filteredLayers", filteredLayers); + const pos = index > filteredLayers.length ? filteredLayers.length : index; + console.log("#### index", index); + console.log("#### pos", pos); const updatedCanvas = { ...canvas, flexLayers: [ - ...flexLayers.slice(0, pos), + ...filteredLayers.slice(0, pos), newLayer, - ...flexLayers.slice(pos), + ...filteredLayers.slice(pos), ], }; const updatedWidgets = { ...widgets, [parentId]: updatedCanvas, }; - // console.log("#### updated flex layers", updatedCanvas.flexLayers); - // console.log("#### widgets", updatedWidgets); + console.log("#### updated flex layers", updatedCanvas.flexLayers); + console.log("#### widgets", updatedWidgets); return updatedWidgets; } +function filterLayers( + layers: FlexLayer[], + movedWidgets: string[], +): FlexLayer[] { + const filteredLayers = layers.filter((layer) => { + const children = layer.children.filter((child) => { + return movedWidgets.indexOf(child.id) === -1; + }); + return children.length > 0; + }); + return filteredLayers; +} + function* addWidgetAndReorderSaga( actionPayload: ReduxAction<{ newWidget: WidgetAddChild; From ddf66634b1998224ca6aca0104bedd3387734cca Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 5 Oct 2022 11:42:53 -0400 Subject: [PATCH 122/708] remove layout wrapper widget --- app/client/src/utils/WidgetRegistry.tsx | 4 - .../src/widgets/LayoutWrapperWidget.tsx | 236 ------------------ 2 files changed, 240 deletions(-) delete mode 100644 app/client/src/widgets/LayoutWrapperWidget.tsx diff --git a/app/client/src/utils/WidgetRegistry.tsx b/app/client/src/utils/WidgetRegistry.tsx index 0e1570e107a0..07aafe1bc198 100644 --- a/app/client/src/utils/WidgetRegistry.tsx +++ b/app/client/src/utils/WidgetRegistry.tsx @@ -161,9 +161,6 @@ import CategorySliderWidget, { import CodeScannerWidget, { CONFIG as CODE_SCANNER_WIDGET_CONFIG, } from "widgets/CodeScannerWidget"; -import LayoutWrapperWidget, { - CONFIG as LAYOUT_WRAPPER_CONFIG, -} from "widgets/LayoutWrapperWidget"; export const ALL_WIDGETS_AND_CONFIG = [ [CanvasWidget, CANVAS_WIDGET_CONFIG], @@ -215,7 +212,6 @@ export const ALL_WIDGETS_AND_CONFIG = [ [RangeSliderWidget, RANGE_SLIDER_WIDGET_CONFIG], [CategorySliderWidget, CATEGORY_SLIDER_WIDGET_CONFIG], [CodeScannerWidget, CODE_SCANNER_WIDGET_CONFIG], - [LayoutWrapperWidget, LAYOUT_WRAPPER_CONFIG], //Deprecated Widgets [InputWidget, INPUT_WIDGET_CONFIG], diff --git a/app/client/src/widgets/LayoutWrapperWidget.tsx b/app/client/src/widgets/LayoutWrapperWidget.tsx deleted file mode 100644 index df23fbbad3e6..000000000000 --- a/app/client/src/widgets/LayoutWrapperWidget.tsx +++ /dev/null @@ -1,236 +0,0 @@ -import React, { CSSProperties } from "react"; - -import { WidgetProps } from "widgets/BaseWidget"; -import WidgetFactory, { DerivedPropertiesMap } from "utils/WidgetFactory"; -import { - ContainerWidget, - ContainerWidgetProps, -} from "./ContainerWidget/widget"; -import { DropTargetComponent } from "components/editorComponents/DropTargetComponent"; -import { CANVAS_DEFAULT_MIN_HEIGHT_PX } from "constants/AppConstants"; -import { getCanvasClassName } from "utils/generators"; -import { RenderModes } from "constants/WidgetConstants"; -import { getCanvasSnapRows } from "utils/WidgetPropsUtils"; -import { - AlignItems, - Alignment, - JustifyContent, - LayoutDirection, - Overflow, - Positioning, - ResponsiveBehavior, - Spacing, -} from "components/constants"; -import { CanvasWidgetStructure } from "./constants"; -import ContainerComponent, { LayoutWrapper } from "./ContainerWidget/component"; -import { CanvasDraggingArena } from "pages/common/CanvasArenas/CanvasDraggingArena"; -import { CanvasSelectionArena } from "pages/common/CanvasArenas/CanvasSelectionArena"; -import WidgetsMultiSelectBox from "pages/Editor/WidgetsMultiSelectBox"; -import { AutoLayoutContext } from "utils/autoLayoutContext"; - -class LayoutWrapperWidget extends ContainerWidget { - static getPropertyPaneConfig() { - return []; - } - static getDerivedPropertiesMap(): DerivedPropertiesMap { - return {}; - } - - static getDefaultPropertiesMap(): Record { - return {}; - } - // TODO Find a way to enforce this, (dont let it be set) - static getMetaPropertiesMap(): Record { - return {}; - } - static getWidgetType() { - return "LAYOUT_WRAPPER_WIDGET"; - } - componentDidMount(): void { - super.componentDidMount(); - } - componentDidUpdate(prevProps: ContainerWidgetProps): void { - if (this.props.positioning !== prevProps.positioning) - this.updatePositioningInformation(); - } - updatePositioningInformation = (): void => { - this.setState({ - useAutoLayout: true, - direction: - this.props.positioning === Positioning.Horizontal - ? LayoutDirection.Vertical - : LayoutDirection.Horizontal, - }); - }; - - getCanvasProps(): ContainerWidgetProps { - return { - ...this.props, - parentRowSpace: 1, - parentColumnSpace: 1, - topRow: 0, - leftColumn: 0, - containerStyle: "none", - detachFromLayout: false, - }; - } - renderAsDropTarget() { - const canvasProps = this.getCanvasProps(); - return ( - - {this.renderAsContainerComponent(canvasProps)} - - ); - } - - renderChildWidget(childWidgetData: CanvasWidgetStructure): React.ReactNode { - if (!childWidgetData) return null; - - const childWidget = { ...childWidgetData }; - - const snapSpaces = this.getSnapSpaces(); - childWidget.parentColumnSpace = snapSpaces.snapColumnSpace; - childWidget.parentRowSpace = snapSpaces.snapRowSpace; - if (this.props.noPad) childWidget.noContainerOffset = true; - childWidget.parentId = this.props.widgetId; - // Pass layout controls to children - childWidget.positioning = - childWidget?.positioning || this.props.positioning; - childWidget.useAutoLayout = this.state.useAutoLayout; - childWidget.direction = this.props.direction; - childWidget.justifyContent = this.props.justifyContent; - childWidget.alignItems = this.props.alignItems; - - if ( - childWidget?.responsiveBehavior === ResponsiveBehavior.Fill && - this.state.isMobile - ) { - childWidget.leftColumn = 0; - childWidget.rightColumn = 64; - } - - return WidgetFactory.createWidget(childWidget, this.props.renderMode); - } - - renderAsContainerComponent( - props: ContainerWidgetProps, - ): JSX.Element { - const snapRows = getCanvasSnapRows(props.bottomRow, props.canExtend); - const stretchFlexBox = - !this.props.children || !this.props.children?.length - ? true - : this.props.alignment === Alignment.Bottom || - this.props.positioning === Positioning.Vertical; - return ( - - {props.renderMode === RenderModes.CANVAS && ( - <> - - - - )} - - {/* without the wrapping div onClick events are triggered twice */} - - - {this.renderChildren()} - - - - ); - } - - getPageView() { - const style: CSSProperties = { - width: "100%", - height: "100%", - background: "none", - position: "relative", - }; - // This div is the DropTargetComponent alternative for the page view - // DropTargetComponent and this div are responsible for the canvas height - return ( -
- {this.renderAsContainerComponent(this.getCanvasProps())} -
- ); - } - - getCanvasView() { - if (!this.props.dropDisabled) { - return this.renderAsDropTarget(); - } - return this.getPageView(); - } -} - -export const CONFIG = { - type: LayoutWrapperWidget.getWidgetType(), - name: "LayoutWrapper", - hideCard: true, - defaults: { - rows: 0, - columns: 0, - widgetName: "Canvas", - version: 1, - detachFromLayout: true, - containerStyle: "none", - }, - properties: { - derived: LayoutWrapperWidget.getDerivedPropertiesMap(), - default: LayoutWrapperWidget.getDefaultPropertiesMap(), - meta: LayoutWrapperWidget.getMetaPropertiesMap(), - config: LayoutWrapperWidget.getPropertyPaneConfig(), - }, -}; - -export default LayoutWrapperWidget; From 3d9615418ae38657a24f538160e0f903379c12ad Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 5 Oct 2022 13:41:34 -0400 Subject: [PATCH 123/708] base algo for highlight calculation --- .../pages/common/CanvasArenas/hooks/test.ts | 181 ++++++++++++++++++ 1 file changed, 181 insertions(+) create mode 100644 app/client/src/pages/common/CanvasArenas/hooks/test.ts diff --git a/app/client/src/pages/common/CanvasArenas/hooks/test.ts b/app/client/src/pages/common/CanvasArenas/hooks/test.ts new file mode 100644 index 000000000000..67e8e06f79ee --- /dev/null +++ b/app/client/src/pages/common/CanvasArenas/hooks/test.ts @@ -0,0 +1,181 @@ +import { FlexLayerAlignment, LayoutDirection } from "components/constants"; +import { + FlexLayer, + LayerChild, +} from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; + +export interface HighlightInfo { + isNewLayer: boolean; // determines if a new layer / child has been added directly to the container. + index: number; // index of the child in props.children. + layerIndex?: number; // index of layer in props.flexLayers. + align?: FlexLayerAlignment; // alignment of the child in the layer. + posX?: number; // x position of the highlight. + posY?: number; // y position of the highlight. + width?: number; // width of the highlight. + height?: number; // height of the highlight. +} + +// TODO: Add logic for infering highlight position and size. +// e.g. initial offset for center wrapper => posX = parent.width / 2 + +function generateHighlights( + direction: LayoutDirection, + flexLayers: FlexLayer[], + children: string[], +) { + if (direction === LayoutDirection.Vertical) { + return [ + ...getContainerHighlights(flexLayers), + ...generateHighlightsForLayers(flexLayers), + ]; + } + return generateHighlightsForChildren(children); +} + +/** + * Derive highlights for the container. + */ + +function getContainerHighlights(flexLayers: FlexLayer[]) { + let childCount = 0; + const arr = flexLayers.map((layer, index) => { + const res = { + isNewLayer: true, + index: childCount, + layerIndex: index, + }; + childCount += layer.children.length; + return res; + }); + // Add a final highlight at the end. + arr.push({ + isNewLayer: true, + index: childCount, + layerIndex: arr.length, + }); + return arr; +} + +/** + * Derive highlights for each layer. + */ + +function generateHighlightsForLayers(layers: FlexLayer[]) { + // eslint-disable-next-line prefer-const + let childCount = 0; + const arr: HighlightInfo[] = []; + layers.forEach((layer, index) => { + const { center, end, hasFillChild, start } = spreadLayer(layer); + // process start sub wrapper. + arr.push( + ...getLayerHighlights(start, childCount, index, FlexLayerAlignment.Start), + ); + if (!hasFillChild) { + // process center sub wrapper. + arr.push( + ...getLayerHighlights( + center, + childCount, + index, + FlexLayerAlignment.Center, + ), + ); + // process end sub wrapper. + arr.push( + ...getLayerHighlights( + end, + childCount, + index, + FlexLayerAlignment.Center, + ), + ); + } + }); + return arr; +} + +function spreadLayer(layer: FlexLayer) { + const start: LayerChild[] = [], + center: LayerChild[] = [], + end: LayerChild[] = []; + layer.children.forEach((child: LayerChild) => { + if (layer.hasFillChild) { + start.push(child); + return; + } + if (child.align === FlexLayerAlignment.End) end.push(child); + else if (child.align === FlexLayerAlignment.Center) center.push(child); + else start.push(child); + }); + return { start, center, end, hasFillChild: layer.hasFillChild }; +} + +function getLayerHighlights( + layer: LayerChild[], + childCount: number, + layerIndex: number, + align: FlexLayerAlignment, +): HighlightInfo[] { + const arr: HighlightInfo[] = []; + if (!layer.length) { + arr.push(getInitialOffset(childCount, align, layerIndex)); + return arr; + } + arr.push(...getOffsets(layer, childCount, align, layerIndex)); + return arr; +} + +function getInitialOffset( + childCount: number, + align: FlexLayerAlignment, + layerIndex: number, +): HighlightInfo { + return { + isNewLayer: false, + index: childCount, + layerIndex, + align, + }; +} + +function getOffsets( + layer: LayerChild[], + childCount: number, + align: FlexLayerAlignment, + layerIndex: number, +) { + const arr: HighlightInfo[] = []; + layer.forEach((child, index) => { + // A highlight before each existing child. + arr.push({ + isNewLayer: false, + index: childCount, + layerIndex, + align, + }); + childCount += 1; + }); + // A highlight after the last child. + arr.push({ + isNewLayer: false, + index: childCount, + layerIndex, + align, + }); + return arr; +} + +/** + * Derive highlights for children + */ +function generateHighlightsForChildren(children: string[]): HighlightInfo[] { + const arr: HighlightInfo[] = []; + children.forEach((child, index) => { + arr.push({ + isNewLayer: true, + index, + }); + }); + arr.push({ isNewLayer: true, index: children.length }); + return arr; +} From dd8223c33bb901172ad20d0fc76001c804498b15 Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 5 Oct 2022 13:43:38 -0400 Subject: [PATCH 124/708] move file to utils --- app/client/src/{pages/common/CanvasArenas/hooks => utils}/test.ts | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename app/client/src/{pages/common/CanvasArenas/hooks => utils}/test.ts (100%) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/test.ts b/app/client/src/utils/test.ts similarity index 100% rename from app/client/src/pages/common/CanvasArenas/hooks/test.ts rename to app/client/src/utils/test.ts From 3caac7368e03c1008f2ded43466770133c53cc1e Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 6 Oct 2022 08:23:39 -0400 Subject: [PATCH 125/708] add layer child interface --- .../designSystems/appsmith/autoLayout/FlexBoxComponent.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx index 8982909bffd9..47331b42a54c 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx @@ -27,8 +27,13 @@ export interface FlexBoxProps { flexLayers: FlexLayer[]; } +export interface LayerChild { + id: string; + align: FlexLayerAlignment; +} + export interface FlexLayer { - children: { id: string; align: FlexLayerAlignment }[]; + children: LayerChild[]; hasFillChild?: boolean; } From 02cb6b5f4107b19beedf339793c006874bfcf081 Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 6 Oct 2022 11:08:12 -0400 Subject: [PATCH 126/708] fixed reorder logic --- .../appsmith/autoLayout/FlexBoxComponent.tsx | 7 +- .../editorComponents/ResizableComponent.tsx | 2 +- .../hooks/useAutoLayoutHighlights.ts | 57 +++--- .../hooks/useBlocksToBeDraggedOnCanvas.ts | 15 +- .../CanvasArenas/hooks/useCanvasDragging.ts | 2 +- .../sagas/CanvasSagas/DraggingCanvasSagas.ts | 179 +++++++++++++----- app/client/src/sagas/WidgetOperationUtils.ts | 88 +-------- app/client/src/widgets/CanvasWidget.tsx | 7 +- .../ContainerWidget/component/index.tsx | 4 +- 9 files changed, 177 insertions(+), 184 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx index 8982909bffd9..47331b42a54c 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx @@ -27,8 +27,13 @@ export interface FlexBoxProps { flexLayers: FlexLayer[]; } +export interface LayerChild { + id: string; + align: FlexLayerAlignment; +} + export interface FlexLayer { - children: { id: string; align: FlexLayerAlignment }[]; + children: LayerChild[]; hasFillChild?: boolean; } diff --git a/app/client/src/components/editorComponents/ResizableComponent.tsx b/app/client/src/components/editorComponents/ResizableComponent.tsx index c380f0b6c121..6a4d542cf3aa 100644 --- a/app/client/src/components/editorComponents/ResizableComponent.tsx +++ b/app/client/src/components/editorComponents/ResizableComponent.tsx @@ -302,7 +302,7 @@ export const ResizableComponent = memo(function ResizableComponent( let disabledHorizontalHandles: string[] = []; if ( props.useAutoLayout && - props.direction === LayoutDirection.Horizontal && + props.direction === LayoutDirection.Vertical && props.responsiveBehavior === ResponsiveBehavior.Fill ) { disabledHorizontalHandles = [ diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index db61e37fb1e4..dbc6baefb488 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -6,7 +6,7 @@ import { FlexLayerAlignment, ResponsiveBehavior, } from "components/constants"; -import { isArray, isNaN } from "lodash"; +import { isNaN } from "lodash"; import { WidgetDraggingBlock } from "./useBlocksToBeDraggedOnCanvas"; interface XYCord { @@ -19,7 +19,7 @@ export interface Highlight { y: number; height: number; width: number; - wrapperType: FlexLayerAlignment; + alignment: FlexLayerAlignment; } export interface AutoLayoutHighlightProps { @@ -35,7 +35,7 @@ export interface AutoLayoutHighlightProps { export interface DropPositionPayload { index: number; - wrapperType: FlexLayerAlignment; + alignment: FlexLayerAlignment; } const BASE_OFFSET_SIZE = 100; @@ -108,14 +108,14 @@ export const useAutoLayoutHighlights = ({ y: 8, width: getContainerDimensions()?.width || BASE_OFFSET_SIZE, height: OFFSET_WIDTH, - wrapperType: FlexLayerAlignment.Start, + alignment: FlexLayerAlignment.Start, }, [LayoutDirection.Horizontal]: { x: 8, y: 0, width: OFFSET_WIDTH, height: getContainerDimensions()?.height || BASE_OFFSET_SIZE, - wrapperType: FlexLayerAlignment.Start, + alignment: FlexLayerAlignment.Start, }, }, [FlexLayerAlignment.Center]: { @@ -124,14 +124,14 @@ export const useAutoLayoutHighlights = ({ y: getContainerDimensions()?.height / 2, width: getContainerDimensions()?.width || BASE_OFFSET_SIZE, height: OFFSET_WIDTH, - wrapperType: FlexLayerAlignment.Center, + alignment: FlexLayerAlignment.Center, }, [LayoutDirection.Horizontal]: { x: getContainerDimensions()?.width / 2, y: 0, width: OFFSET_WIDTH, height: getContainerDimensions()?.height || BASE_OFFSET_SIZE, - wrapperType: FlexLayerAlignment.Center, + alignment: FlexLayerAlignment.Center, }, }, [FlexLayerAlignment.End]: { @@ -140,14 +140,14 @@ export const useAutoLayoutHighlights = ({ y: getContainerDimensions()?.bottom, width: getContainerDimensions()?.width || BASE_OFFSET_SIZE, height: OFFSET_WIDTH, - wrapperType: FlexLayerAlignment.End, + alignment: FlexLayerAlignment.End, }, [LayoutDirection.Horizontal]: { x: getContainerDimensions()?.right, y: 0, width: OFFSET_WIDTH, height: getContainerDimensions()?.height || BASE_OFFSET_SIZE, - wrapperType: FlexLayerAlignment.End, + alignment: FlexLayerAlignment.End, }, }, }; @@ -155,10 +155,10 @@ export const useAutoLayoutHighlights = ({ // Create and add an initial offset for an empty canvas const getInitialOffset = ( isWrapper: boolean, - wrapperType: FlexLayerAlignment = FlexLayerAlignment.Start, + alignment: FlexLayerAlignment = FlexLayerAlignment.Start, ): Highlight => { const dir: LayoutDirection = direction || LayoutDirection.Horizontal; - if (isWrapper) return initialOffsets[wrapperType][dir]; + if (isWrapper) return initialOffsets[alignment][dir]; return initialOffsets[FlexLayerAlignment.Start][dir]; }; // Get DOM element for a given widgetId @@ -203,7 +203,7 @@ export const useAutoLayoutHighlights = ({ const getOffset = ( rect: DOMRect, flexOffsetTop: number, - wrapperType: FlexLayerAlignment, + alignment: FlexLayerAlignment, isFinal?: boolean, ): Highlight => { let mOffset: Highlight; @@ -225,7 +225,7 @@ export const useAutoLayoutHighlights = ({ y: rect.top - valueToSubtract.y + valueToAdd.y, width: containerDimensions.width, height: OFFSET_WIDTH, - wrapperType, + alignment, }; } else { mOffset = { @@ -233,7 +233,7 @@ export const useAutoLayoutHighlights = ({ y: rect.top - valueToSubtract.y + valueToAdd.y, height: rect.height, width: OFFSET_WIDTH, - wrapperType, + alignment, }; } // console.log(`#### offset: ${JSON.stringify(mOffset)}`); @@ -322,11 +322,6 @@ export const useAutoLayoutHighlights = ({ const canvasChildren = canvas.children || []; const offsetChildren = canvasChildren.filter((each) => { return blocks.indexOf(each) === -1; - // if (canvas.isWrapper) return blocks.indexOf(each) === -1; - // const children = allWidgets[each].children?.filter( - // (item) => blocks.indexOf(item) === -1, - // ); - // return isArray(children) && children.length > 0; }); // console.log(`#### canvas children: ${JSON.stringify(canvasChildren)}`); // console.log(`#### offset children: ${JSON.stringify(offsetChildren)}`); @@ -340,9 +335,9 @@ export const useAutoLayoutHighlights = ({ center: string[] = [], end: string[] = []; offsetChildren.forEach((each) => { - if (allWidgets[each]?.wrapperType === FlexLayerAlignment.Center) + if (allWidgets[each]?.alignment === FlexLayerAlignment.Center) center.push(each); - else if (allWidgets[each]?.wrapperType === FlexLayerAlignment.End) + else if (allWidgets[each]?.alignment === FlexLayerAlignment.End) end.push(each); else start.push(each); }); @@ -386,7 +381,7 @@ export const useAutoLayoutHighlights = ({ arr: string[], flexOffsetTop: number, isWrapper: boolean, - wrapperType: FlexLayerAlignment, + alignment: FlexLayerAlignment, ): Highlight[] => { let res: Highlight[] = []; const siblings: DOMRect[] = []; @@ -401,7 +396,7 @@ export const useAutoLayoutHighlights = ({ const rect: DOMRect = el.getBoundingClientRect(); // console.log(`#### bounding rect: ${JSON.stringify(rect)}`); // Add a new offset using the current element's dimensions and position - res.push(getOffset(rect, flexOffsetTop, wrapperType, false)); + res.push(getOffset(rect, flexOffsetTop, alignment, false)); siblings.push(rect); siblingElements.push(el); }); @@ -415,13 +410,13 @@ export const useAutoLayoutHighlights = ({ getOffset( siblings[siblings.length - 1], flexOffsetTop, - wrapperType, + alignment, true, ), ); } res = [...new Set(res)]; - } else res = [getInitialOffset(isWrapper, wrapperType)]; + } else res = [getInitialOffset(isWrapper, alignment)]; return res; }; @@ -503,10 +498,10 @@ export const useAutoLayoutHighlights = ({ const getDropPosition = (index: number): number => { if (isNaN(index)) return 0; - const wrapperType: FlexLayerAlignment = - offsets[index]?.wrapperType || FlexLayerAlignment.Start; - if (wrapperType === FlexLayerAlignment.Center) return index - 1; - if (wrapperType === FlexLayerAlignment.End) return index - 2; + const alignment: FlexLayerAlignment = + offsets[index]?.alignment || FlexLayerAlignment.Start; + if (alignment === FlexLayerAlignment.Center) return index - 1; + if (alignment === FlexLayerAlignment.End) return index - 2; return index; }; @@ -514,14 +509,14 @@ export const useAutoLayoutHighlights = ({ if (!isNaN(lastTranslatedIndex) && lastTranslatedIndex >= 0) return { index: getDropPosition(lastTranslatedIndex), - wrapperType: offsets[lastTranslatedIndex]?.wrapperType, + alignment: offsets[lastTranslatedIndex]?.alignment, }; const pos = getHighlightPosition(null, val); if (!pos) return; const dropPos: number = offsets.indexOf(pos); return { index: getDropPosition(dropPos), - wrapperType: offsets[dropPos]?.wrapperType, + alignment: offsets[dropPos]?.alignment, }; }; diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts b/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts index a4090a7119d4..137b9cb6100f 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts @@ -280,10 +280,9 @@ export const useBlocksToBeDraggedOnCanvas = ({ const updateChildrenPositions = ( index: number, drawingBlocks: WidgetDraggingBlock[], - wrapperType: FlexLayerAlignment, + alignment: FlexLayerAlignment, ): void => { - if (isNewWidget) - addNewWidgetToAutoLayout(index, drawingBlocks, wrapperType); + if (isNewWidget) addNewWidgetToAutoLayout(index, drawingBlocks, alignment); else dispatch({ type: ReduxActionTypes.AUTOLAYOUT_REORDER_WIDGETS, @@ -291,7 +290,7 @@ export const useBlocksToBeDraggedOnCanvas = ({ index, movedWidgets: selectedWidgets, parentId: widgetId, - wrapperType, + alignment, direction, isNewLayer: false, }, @@ -300,7 +299,7 @@ export const useBlocksToBeDraggedOnCanvas = ({ const addNewWidgetToAutoLayout = ( index: number, drawingBlocks: WidgetDraggingBlock[], - wrapperType: FlexLayerAlignment, + alignment: FlexLayerAlignment, ) => { logContainerJumpOnDrop(); const blocksToUpdate = drawingBlocks.map((each) => { @@ -323,12 +322,12 @@ export const useBlocksToBeDraggedOnCanvas = ({ updateWidgetParams, }; }); - // Add wrapperType to props of the new widget + // Add alignment to props of the new widget const widgetPayload = { ...blocksToUpdate[0]?.updateWidgetParams?.payload, props: { ...blocksToUpdate[0]?.updateWidgetParams?.payload?.props, - wrapperType, + alignment, }, }; dispatch({ @@ -337,7 +336,7 @@ export const useBlocksToBeDraggedOnCanvas = ({ index, newWidget: widgetPayload, parentId: widgetId, - wrapperType, + alignment, direction, isNewLayer: true, }, diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index 3ecad204fa32..b191d15ca8ae 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -331,7 +331,7 @@ export const useCanvasDragging = ( updateChildrenPositions( pos.index, currentRectanglesToDraw, - pos.wrapperType, + pos.alignment, ); } else onDrop(currentRectanglesToDraw, reflowedPositionsUpdatesWidgets); diff --git a/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts b/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts index fb4bf4d17b09..07af1c40830e 100644 --- a/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts +++ b/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts @@ -35,8 +35,10 @@ import { FlexLayerAlignment, ResponsiveBehavior, } from "components/constants"; -import { purgeEmptyWrappers } from "../WidgetOperationUtils"; -import { FlexLayer } from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; +import { + FlexLayer, + LayerChild, +} from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; export type WidgetMoveParams = { widgetId: string; @@ -302,12 +304,8 @@ function* moveWidgetsSaga( ) { throw Error; } - /** - * Trim empty layout wrappers - * Caused by dragging a widget from auto layout to positioned container - */ - const trimmedWidgets = purgeEmptyWrappers(updatedWidgetsOnMove); - yield put(updateAndSaveLayout(trimmedWidgets)); + + yield put(updateAndSaveLayout(updatedWidgetsOnMove)); const block = draggedBlocksToUpdate[0]; const oldParentId = block.updateWidgetParams.payload.parentId; @@ -405,19 +403,19 @@ function* autolayoutReorderSaga( index: number; isNewLayer: boolean; parentId: string; - wrapperType: FlexLayerAlignment; + alignment: FlexLayerAlignment; direction: LayoutDirection; }>, ) { const start = performance.now(); const { + alignment, direction, index, isNewLayer, movedWidgets, parentId, - wrapperType, } = actionPayload.payload; // console.log(`#### moved widgets: ${JSON.stringify(movedWidgets)}`); // console.log(`#### parentId: ${parentId}`); @@ -432,7 +430,7 @@ function* autolayoutReorderSaga( isNewLayer, parentId, allWidgets, - wrapperType, + alignment, direction, }, ); @@ -450,17 +448,19 @@ function* reorderAutolayoutChildren(params: { isNewLayer: boolean; parentId: string; allWidgets: CanvasWidgetsReduxState; - wrapperType: FlexLayerAlignment; + alignment: FlexLayerAlignment; direction: LayoutDirection; + layerIndex?: number; }) { const { + alignment, allWidgets, direction, index, isNewLayer, + layerIndex, movedWidgets, parentId, - wrapperType, } = params; const widgets = Object.assign({}, allWidgets); if (!movedWidgets) return widgets; @@ -494,17 +494,27 @@ function* reorderAutolayoutChildren(params: { } let updatedWidgets: CanvasWidgetsReduxState = Object.assign({}, widgets); if (direction === LayoutDirection.Vertical) { - updatedWidgets = updateAutoLayoutLayers( - selectedWidgets, - widgets, - parentId, - wrapperType, - isNewLayer, - index, - ); + updatedWidgets = isNewLayer + ? addNewLayer( + selectedWidgets, + widgets, + parentId, + alignment, + isNewLayer, + index, + ) + : updateExistingLayer( + selectedWidgets, + widgets, + parentId, + alignment, + index, + layerIndex || 0, + ); } + const items = [...(widgets[parentId].children || [])]; - // remove moved widegts from children + // remove moved widgets from children const newItems = items.filter((item) => movedWidgets.indexOf(item) === -1); // calculate valid position for drop const pos = index > newItems.length ? newItems.length : index; @@ -519,57 +529,119 @@ function* reorderAutolayoutChildren(params: { return updatedWidgets; } -function updateAutoLayoutLayers( +function addNewLayer( movedWidgets: string[], allWidgets: CanvasWidgetsReduxState, parentId: string, - wrapperType: FlexLayerAlignment, + alignment: FlexLayerAlignment, isNewLayer: boolean, index: number, ) { const widgets: CanvasWidgetsReduxState = Object.assign({}, allWidgets); const canvas = widgets[parentId]; if (!canvas) return widgets; - let hasFillChild = false; - const layerChildren = []; - for (const id of movedWidgets) { - const widget = widgets[id]; - if (!widget) continue; - if (widget.responsiveBehavior === ResponsiveBehavior.Fill) - hasFillChild = true; - layerChildren.push({ - id, - align: wrapperType, - }); - } + + const { hasFillChild, layerChildren } = processLayerChildren( + movedWidgets, + allWidgets, + alignment, + ); + const newLayer: FlexLayer = { children: layerChildren, hasFillChild }; - console.log("#### canvas id", parentId); - console.log("#### newLayer", newLayer); + // console.log("#### canvas id", parentId); + // console.log("#### newLayer", newLayer); const flexLayers = canvas.flexLayers || []; - const filteredLayers = isNewLayer - ? flexLayers - : filterLayers(flexLayers, movedWidgets); - console.log("#### filteredLayers", filteredLayers); - const pos = index > filteredLayers.length ? filteredLayers.length : index; - console.log("#### index", index); - console.log("#### pos", pos); + // console.log("#### filteredLayers", filteredLayers); + const pos = index > flexLayers.length ? flexLayers.length : index; + // console.log("#### index", index); + // console.log("#### pos", pos); const updatedCanvas = { ...canvas, flexLayers: [ - ...filteredLayers.slice(0, pos), + ...flexLayers.slice(0, pos), newLayer, - ...filteredLayers.slice(pos), + ...flexLayers.slice(pos), ], }; const updatedWidgets = { ...widgets, [parentId]: updatedCanvas, }; - console.log("#### updated flex layers", updatedCanvas.flexLayers); - console.log("#### widgets", updatedWidgets); + // console.log("#### updated flex layers", updatedCanvas.flexLayers); + // console.log("#### widgets", updatedWidgets); return updatedWidgets; } +function updateExistingLayer( + movedWidgets: string[], + allWidgets: CanvasWidgetsReduxState, + parentId: string, + alignment: FlexLayerAlignment, + index: number, + layerIndex: number, +): CanvasWidgetsReduxState { + const widgets: CanvasWidgetsReduxState = Object.assign({}, allWidgets); + const canvas = widgets[parentId]; + if (!canvas || !movedWidgets.length) return widgets; + + const layers = canvas.flexLayers || []; + const filteredLayers: FlexLayer[] = filterLayers(layers, movedWidgets); + + const { hasFillChild, layerChildren } = processLayerChildren( + movedWidgets, + allWidgets, + alignment, + ); + + let selectedLayer: FlexLayer = filteredLayers[layerIndex]; + let childCount = 0; + filteredLayers.forEach((layer: FlexLayer, index: number) => { + if (index > layerIndex) return; + childCount += layer.children.length; + }); + const pos = index - childCount; + + selectedLayer = { + ...selectedLayer, + children: [ + ...selectedLayer.children.slice(0, pos), + ...layerChildren, + ...selectedLayer.children.slice(pos), + ], + }; + + const updatedCanvas = { + ...canvas, + flexLayers: [ + ...filteredLayers.slice(0, layerIndex), + selectedLayer, + ...filteredLayers.slice(layerIndex + 1), + ], + }; + + const updatedWidgets = { ...widgets, [parentId]: updatedCanvas }; + return updatedWidgets; +} + +function processLayerChildren( + movedWidgets: string[], + allWidgets: CanvasWidgetsReduxState, + alignment: FlexLayerAlignment, +): { hasFillChild: boolean; layerChildren: LayerChild[] } { + let hasFillChild = false; + const layerChildren = []; + if (movedWidgets && movedWidgets.length) { + for (const id of movedWidgets) { + const widget = allWidgets[id]; + if (!widget) continue; + if (widget.responsiveBehavior === ResponsiveBehavior.Fill) + hasFillChild = true; + layerChildren.push({ id, align: alignment }); + } + } + return { layerChildren, hasFillChild }; +} + function filterLayers( layers: FlexLayer[], movedWidgets: string[], @@ -589,18 +661,20 @@ function* addWidgetAndReorderSaga( index: number; isNewLayer: boolean; parentId: string; - wrapperType: FlexLayerAlignment; + alignment: FlexLayerAlignment; direction: LayoutDirection; + layerIndex?: number; }>, ) { const start = performance.now(); const { + alignment, direction, index, isNewLayer, + layerIndex, newWidget, parentId, - wrapperType, } = actionPayload.payload; try { const updatedWidgetsOnAddition: CanvasWidgetsReduxState = yield call( @@ -619,8 +693,9 @@ function* addWidgetAndReorderSaga( isNewLayer, parentId, allWidgets: updatedWidgetsOnAddition, - wrapperType, + alignment, direction, + layerIndex, }, ); yield put(updateAndSaveLayout(updatedWidgetsOnMove)); diff --git a/app/client/src/sagas/WidgetOperationUtils.ts b/app/client/src/sagas/WidgetOperationUtils.ts index 474f45519709..b0c20b842d76 100644 --- a/app/client/src/sagas/WidgetOperationUtils.ts +++ b/app/client/src/sagas/WidgetOperationUtils.ts @@ -53,15 +53,7 @@ import { reflow } from "reflow"; import { getBottomRowAfterReflow } from "utils/reflowHookUtils"; import { DataTreeWidget } from "entities/DataTree/dataTreeFactory"; import { isWidget } from "../workers/evaluationUtils"; -import { - Alignment, - ButtonBoxShadowTypes, - LayoutDirection, - FlexLayerAlignment, - Spacing, - ResponsiveBehavior, -} from "components/constants"; -import { WidgetAddChild } from "actions/pageActions"; +import { FlexLayerAlignment, ResponsiveBehavior } from "components/constants"; import { FlexLayer } from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; export interface CopiedWidgetGroup { @@ -1680,34 +1672,6 @@ export function mergeDynamicPropertyPaths( return _.unionWith(a, b, (a, b) => a.key === b.key); } -// TODO: check for performance in complex apps. -export function purgeEmptyWrappers(allWidgets: CanvasWidgetsReduxState) { - const widgets = { ...allWidgets }; - // Fetch all empty wrappers - const emptyWrappers = Object.values(widgets).filter( - (each) => each.isWrapper && (!each.children || !each.children?.length), - ); - // Remove wrappers from their parents and then delete them. - emptyWrappers.forEach((each) => { - const parent = each.parentId ? widgets[each.parentId] : null; - if ( - parent && - parent.children && - parent.children?.indexOf(each.widgetId) > -1 - ) { - const updatedParent = { - ...parent, - children: [ - ...(parent.children || []).filter((child) => child !== each.widgetId), - ], - }; - widgets[parent.widgetId] = updatedParent; - } - delete widgets[each.widgetId]; - }); - return widgets; -} - export function purgeChildWrappers( allWidgets: CanvasWidgetsReduxState, containerId: string, @@ -1751,53 +1715,3 @@ export function* wrapChildren( widgets[canvasId] = parent; return widgets; } - -export function getLayoutWrapperName(widgets: CanvasWidgetsReduxState): string { - const arr = - Object.values(widgets) - .filter((each) => each.type === "LAYOUT_WRAPPER_WIDGET") - .map((each) => each.widgetName.split("LayoutWrapper")[1]) - .sort((a, b) => parseInt(b) - parseInt(a)) || []; - const suffix = arr.length > 0 ? parseInt(arr[0]) + 1 : 1; - return `LayoutWrapper${suffix}`; -} - -export function getLayoutWrapperPayload( - widgets: CanvasWidgetsReduxState, - payload: Omit< - WidgetAddChild, - "newWidgetId" | "tabId" | "widgetName" | "type" - >, - direction: LayoutDirection = LayoutDirection.Vertical, - isWrapper = false, -): WidgetAddChild { - // A horizontal wrapper should stretch to occupy the parent's entire width. - const shouldStretch = isWrapper - ? direction === LayoutDirection.Horizontal - : direction === LayoutDirection.Vertical; - return { - ...payload, - newWidgetId: generateReactKey(), - type: "LAYOUT_WRAPPER_WIDGET", - widgetName: getLayoutWrapperName(widgets), - props: { - containerStyle: "none", - canExtend: false, - detachFromLayout: true, - children: [], - alignment: Alignment.Left, - spacing: Spacing.None, - isWrapper: true, - backgroundColor: "transparent", - boxShadow: ButtonBoxShadowTypes.NONE, - borderStyle: "none", - }, - columns: shouldStretch ? 64 : payload.columns + 1, - tabId: "0", - }; -} - -export interface WrappedWidgetPayload { - widgets: CanvasWidgetsReduxState; - wrapperId: string; -} diff --git a/app/client/src/widgets/CanvasWidget.tsx b/app/client/src/widgets/CanvasWidget.tsx index 5036fdc77754..cda211ee4779 100644 --- a/app/client/src/widgets/CanvasWidget.tsx +++ b/app/client/src/widgets/CanvasWidget.tsx @@ -13,6 +13,7 @@ import { CanvasWidgetStructure } from "./constants"; import { CANVAS_DEFAULT_MIN_HEIGHT_PX } from "constants/AppConstants"; import { Alignment, + LayoutDirection, Overflow, Positioning, ResponsiveBehavior, @@ -136,7 +137,11 @@ class CanvasWidget extends ContainerWidget { alignment={this.props.alignment || Alignment.Left} direction={this.props.direction} flexLayers={this.props.flexLayers || []} - overflow={Overflow.NoWrap} + overflow={ + this.props.direction === LayoutDirection.Horizontal + ? Overflow.Wrap + : Overflow.NoWrap + } spacing={this.props.spacing || Spacing.None} stretchHeight={stretchFlexBox} useAutoLayout={this.state.useAutoLayout} diff --git a/app/client/src/widgets/ContainerWidget/component/index.tsx b/app/client/src/widgets/ContainerWidget/component/index.tsx index b9a753f63b65..f47528474f79 100644 --- a/app/client/src/widgets/ContainerWidget/component/index.tsx +++ b/app/client/src/widgets/ContainerWidget/component/index.tsx @@ -145,9 +145,9 @@ export function LayoutWrapper(props: FlexBoxProps): JSX.Element { hasFillChild = true; break; } - if (widget?.wrapperType === FlexLayerAlignment.End) + if (widget?.alignment === FlexLayerAlignment.End) end.push(child as JSX.Element); - else if (widget?.wrapperType === FlexLayerAlignment.Center) + else if (widget?.alignment === FlexLayerAlignment.Center) center.push(child as JSX.Element); else start.push(child as JSX.Element); } From 86646f354740484e154ec1749f5b1dfeb143b7a6 Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 6 Oct 2022 16:56:35 -0400 Subject: [PATCH 127/708] first draft: working highlights and drop --- .../appsmith/autoLayout/AutoLayoutLayer.tsx | 7 +- .../appsmith/autoLayout/FlexBoxComponent.tsx | 2 + .../hooks/useAutoLayoutHighlights.ts | 569 ++++++++++-------- .../CanvasArenas/hooks/useCanvasDragging.ts | 7 +- 4 files changed, 346 insertions(+), 239 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx index f952b7f6e311..941ea0ee5660 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx @@ -15,6 +15,8 @@ export interface AutoLayoutLayerProps { end?: ReactNode; direction: LayoutDirection; hasFillChild?: boolean; + index: number; + widgetId: string; } const LayoutLayerContainer = styled.div<{ @@ -66,7 +68,10 @@ function getInverseDirection(direction: LayoutDirection): LayoutDirection { function AutoLayoutLayer(props: AutoLayoutLayerProps) { const flexDirection = getFlexDirection(getInverseDirection(props.direction)); return ( - + {props.start} ); }); diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index dbc6baefb488..d0dcef166352 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -1,13 +1,13 @@ import { getWidgets } from "sagas/selectors"; import { useSelector } from "store"; -import { - LayoutDirection, - FlexLayerAlignment, - ResponsiveBehavior, -} from "components/constants"; +import { LayoutDirection, FlexLayerAlignment } from "components/constants"; import { isNaN } from "lodash"; import { WidgetDraggingBlock } from "./useBlocksToBeDraggedOnCanvas"; +import { + FlexLayer, + LayerChild, +} from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; interface XYCord { x: number; @@ -22,6 +22,17 @@ export interface Highlight { alignment: FlexLayerAlignment; } +export interface HighlightInfo { + isNewLayer: boolean; // determines if a new layer / child has been added directly to the container. + index: number; // index of the child in props.children. + layerIndex?: number; // index of layer in props.flexLayers. + align: FlexLayerAlignment; // alignment of the child in the layer. + posX: number; // x position of the highlight. + posY: number; // y position of the highlight. + width: number; // width of the highlight. + height: number; // height of the highlight. +} + export interface AutoLayoutHighlightProps { blocksToDraw: WidgetDraggingBlock[]; canvasId: string; @@ -52,10 +63,10 @@ export const useAutoLayoutHighlights = ({ }: AutoLayoutHighlightProps) => { const allWidgets = useSelector(getWidgets); const canvas = allWidgets[canvasId]; - const isCanvasWrapper = canvas.isWrapper || false; + const layers: FlexLayer[] = canvas?.flexLayers || []; const isVertical = direction === LayoutDirection.Vertical; - let offsets: Highlight[] = []; + let highlights: HighlightInfo[] = []; let dragBlocksSize = 0; const siblingElements: any[] = []; let lastTranslatedIndex: number; @@ -153,14 +164,13 @@ export const useAutoLayoutHighlights = ({ }; // Create and add an initial offset for an empty canvas - const getInitialOffset = ( - isWrapper: boolean, - alignment: FlexLayerAlignment = FlexLayerAlignment.Start, - ): Highlight => { - const dir: LayoutDirection = direction || LayoutDirection.Horizontal; - if (isWrapper) return initialOffsets[alignment][dir]; - return initialOffsets[FlexLayerAlignment.Start][dir]; - }; + // const getInitialOffset = ( + // alignment: FlexLayerAlignment = FlexLayerAlignment.Start, + // ): Highlight => { + // const dir: LayoutDirection = direction || LayoutDirection.Horizontal; + // if (false) return initialOffsets[alignment][dir]; + // return initialOffsets[FlexLayerAlignment.Start][dir]; + // }; // Get DOM element for a given widgetId const getDomElement = (widgetId: string): any => document.querySelector(`.auto-layout-child-${widgetId}`); @@ -200,236 +210,318 @@ export const useAutoLayoutHighlights = ({ }); }; - const getOffset = ( - rect: DOMRect, - flexOffsetTop: number, - alignment: FlexLayerAlignment, - isFinal?: boolean, - ): Highlight => { - let mOffset: Highlight; - // Remove the offsets of the canvas and the flex container. - const valueToSubtract = { - x: containerDimensions?.left, - y: containerDimensions?.top + flexOffsetTop, - }; - // For the final offset, include the size of the last sibling and a margin. - const valueToAdd = isFinal - ? { - x: rect.width + 8, - y: isVertical ? rect.height + 8 : 0, - } - : { x: 0, y: 0 }; - if (isVertical) { - mOffset = { - x: 0, - y: rect.top - valueToSubtract.y + valueToAdd.y, - width: containerDimensions.width, - height: OFFSET_WIDTH, - alignment, - }; - } else { - mOffset = { - x: rect.left - valueToSubtract.x + valueToAdd.x, - y: rect.top - valueToSubtract.y + valueToAdd.y, - height: rect.height, - width: OFFSET_WIDTH, - alignment, - }; - } - // console.log(`#### offset: ${JSON.stringify(mOffset)}`); - return mOffset; - }; - - // Get the nearest ancestor that is a wrapper, including itself. - const getNearestWrapperAncestor = (widgetId: string): string => { - const widget = allWidgets[widgetId]; - if (widget?.isWrapper) return widgetId; - const parentId = widget?.parentId; - if (!parentId) return ""; - return getNearestWrapperAncestor(parentId); - }; - - const isWrapperEmpty = ( - widgetId: string, - draggedBlocks: string[], - ): boolean => { - const widget = allWidgets[widgetId]; - if (!widget) return true; - const rest = - widget?.children?.filter((child) => draggedBlocks?.indexOf(child) == -1) - .length || 0; - return rest === 0; - }; - - const hideDraggedItems = (arr: string[]): void => { - arr?.forEach((each) => { - // Get the parent wrapper - const wrapperId = getNearestWrapperAncestor(each); - const isEmpty = isWrapperEmpty(wrapperId, arr); - let el; - if (wrapperId === canvasId) { - if (isEmpty) return; - el = getDomElement(each); - } else { - el = isEmpty ? getDomElement(wrapperId) : getDomElement(each); + // Hide the dragged children of the auto layout container + // to discount them from highlight calculation. + const hideDraggedItems = (draggedBlocks: string[]): void => { + draggedBlocks.forEach((id: string) => { + const el = getDomElement(id); + if (el) { + el.classList.add("auto-temp-no-display"); } - /** - * If the wrapper is not the dragging canvas and is empty, - * then hide it, - * else hide the child element. - */ - el?.classList?.add("auto-temp-no-display"); }); }; - const hasFillChild = (arr: string[]): boolean => { - if (!arr || !arr.length) return false; - let flag = false; - for (const widgetId of arr) { - const widget = allWidgets[widgetId]; - if (widget?.responsiveBehavior === ResponsiveBehavior.Fill) { - flag = true; - break; - } - } - return flag; - }; - - const calculateHighlightOffsets = (): Highlight[] => { - cleanUpTempStyles(); - // console.log( - // `#### ${widgetName} - ${isDragging} - ${isCurrentDraggedCanvas}`, - // ); + const calculateHighlights = (): HighlightInfo[] => { + /** + * 1. Clean up temp styles. + * 2. Get the dragged blocks. + * 3. Discount dragged blocks and empty layers from the calculation. + * 4. hide the dragged blocks and empty layers. + */ + cleanUpTempStyles(); // TODO: is this needed? if (useAutoLayout && isDragging && isCurrentDraggedCanvas) { - // console.log("#### START calculate highlight offsets"); - // console.log(`#### canvas id: ${widgetId} : ${widgetName}`); - // calculate total drag size to translate siblings by calculateDragBlockSize(); - const blocks = getDraggedBlocks(); - - if (!blocks || !blocks.length) return []; - // console.log("#### blocks: ", blocks); + const draggedBlocks = getDraggedBlocks(); + if (!draggedBlocks || !draggedBlocks.length) return []; /** * update dimensions of the current canvas * and break out of the function if returned value is false. * That implies the container is null. */ if (!updateContainerDimensions()) return []; + hideDraggedItems(draggedBlocks); - // Temporarily hide dragged children to discount them from offset calculation - hideDraggedItems(blocks); - // Get all children of current dragging canvas - const canvasChildren = canvas.children || []; - const offsetChildren = canvasChildren.filter((each) => { - return blocks.indexOf(each) === -1; - }); - // console.log(`#### canvas children: ${JSON.stringify(canvasChildren)}`); - // console.log(`#### offset children: ${JSON.stringify(offsetChildren)}`); - const flex = document.querySelector(`.flex-container-${canvasId}`); - const flexOffsetTop = (flex as any)?.offsetTop || 0; - let temp: Highlight[] = []; - const discardExtraWrappers: boolean = - hasFillChild(offsetChildren) || direction === LayoutDirection.Vertical; - if (canvas.isWrapper && !discardExtraWrappers) { - const start: string[] = [], - center: string[] = [], - end: string[] = []; - offsetChildren.forEach((each) => { - if (allWidgets[each]?.alignment === FlexLayerAlignment.Center) - center.push(each); - else if (allWidgets[each]?.alignment === FlexLayerAlignment.End) - end.push(each); - else start.push(each); + if (isVertical) { + highlights = generateContainerHighlights(layers, draggedBlocks); + } else { + const canvasChildren = canvas.children || []; + const offsetChildren = canvasChildren.filter((each) => { + return draggedBlocks.indexOf(each) === -1; }); - const arr1: Highlight[] = evaluateOffsets( - start, - flexOffsetTop, - true, - FlexLayerAlignment.Start, - ); - const arr2: Highlight[] = evaluateOffsets( + highlights = generateHighlightsForChildren(offsetChildren); + } + } + console.log("#### highlights: ", highlights); + return highlights; + }; + + function generateHighlightsForChildren(children: string[]): HighlightInfo[] { + const arr: HighlightInfo[] = []; + const rects: DOMRect[] = []; + children.forEach((child, index) => { + const el = getDomElement(child); + const childRect: DOMRect = el.getBoundingClientRect(); + rects.push(childRect); + arr.push({ + isNewLayer: true, + index, + posX: childRect.x - containerDimensions?.left, + posY: childRect.y - containerDimensions?.top, + width: OFFSET_WIDTH, + height: childRect.height, + align: FlexLayerAlignment.Start, + }); + }); + // TODO: Add check for empty container. + const lastElRect: DOMRect = rects[rects.length - 1]; + arr.push({ + isNewLayer: true, + index: children.length, + posX: lastElRect.x - containerDimensions?.left + lastElRect.width, + posY: lastElRect.y - containerDimensions?.top, + width: OFFSET_WIDTH, + height: lastElRect.height, + align: FlexLayerAlignment.Start, + }); + return arr; + } + + function filterLayers( + flexLayers: FlexLayer[], + draggedBlocks: string[], + ): FlexLayer[] { + return flexLayers.filter((layer) => { + return layer.children?.some((each) => { + return draggedBlocks.indexOf(each.id) === -1; + }); + }); + } + + function isEmptyLayer(layer: FlexLayer, draggedBlocks: string[]): boolean { + return ( + layer.children?.length === 0 || + !layer.children?.some((each) => { + return draggedBlocks.indexOf(each.id) === -1; + }) + ); + } + + function generateContainerHighlights( + flexLayers: FlexLayer[], + draggedBlocks: string[], + ): HighlightInfo[] { + let childCount = 0; + let discardedLayers = 0; + let index = 0; + const arr: HighlightInfo[] = []; + const rects: DOMRect[] = []; + console.log("#### flexLayers: ", flexLayers); + for (const layer of flexLayers) { + if (isEmptyLayer(layer, draggedBlocks)) { + discardedLayers += 1; + continue; + } + const el = document.querySelector( + `.auto-layout-layer-${canvasId}-${index}`, + ); + if (!el) { + discardedLayers += 1; + continue; + } + const rect: DOMRect = el.getBoundingClientRect(); + rects.push(rect); + const info: HighlightInfo = { + isNewLayer: true, + index: childCount, + layerIndex: index - discardedLayers, + posX: 0, + posY: rect.y - containerDimensions?.top, + width: containerDimensions?.width, + height: OFFSET_WIDTH, + align: FlexLayerAlignment.Start, + }; + arr.push(info); + arr.push( + ...generateHighlightsForLayer( + layer, + index - discardedLayers, + rect, + childCount, + ), + ); + console.log("#### arr: ", arr); + index += 1; + childCount += layer.children?.length || 0; + } + console.log("#### rects: ", rects); + const lastElRect: DOMRect = rects[rects.length - 1]; + arr.push({ + isNewLayer: true, + index: childCount, + layerIndex: rects.length, + posX: 0, + posY: lastElRect.y + lastElRect.height - containerDimensions?.top, + width: containerDimensions?.width, + height: OFFSET_WIDTH, + align: FlexLayerAlignment.Start, + }); + console.log("#### final arr: ", arr); + return arr; + } + + function spreadLayer(layer: FlexLayer) { + const start: LayerChild[] = [], + center: LayerChild[] = [], + end: LayerChild[] = []; + layer.children.forEach((child: LayerChild) => { + if (layer.hasFillChild) { + start.push(child); + return; + } + if (child.align === FlexLayerAlignment.End) end.push(child); + else if (child.align === FlexLayerAlignment.Center) center.push(child); + else start.push(child); + }); + return { start, center, end, hasFillChild: layer.hasFillChild }; + } + + function generateHighlightsForLayer( + layer: FlexLayer, + layerIndex: number, + layerRect: DOMRect, + childCount: number, + ): HighlightInfo[] { + const arr: HighlightInfo[] = []; + let curr: number = childCount; + const { center, end, hasFillChild, start } = spreadLayer(layer); + // process start sub wrapper. + arr.push( + ...getLayerHighlights( + start, + curr, + layerIndex, + FlexLayerAlignment.Start, + layerRect, + ), + ); + if (!hasFillChild) { + // process center sub wrapper. + curr += start.length; + arr.push( + ...getLayerHighlights( center, - flexOffsetTop, - true, + curr, + layerIndex, FlexLayerAlignment.Center, - ); - const arr3: Highlight[] = evaluateOffsets( + layerRect, + ), + ); + // process end sub wrapper. + curr += center.length; + arr.push( + ...getLayerHighlights( end, - flexOffsetTop, - true, + curr, + layerIndex, FlexLayerAlignment.End, - ); - temp = [...arr1, ...arr2, ...arr3]; - } else - temp = evaluateOffsets( - offsetChildren, - flexOffsetTop, - false, - FlexLayerAlignment.Start, - ); - offsets = [...offsets, ...temp]; - - if (!offsets || !offsets.length) - offsets = [getInitialOffset(isCanvasWrapper)]; - // console.log(`#### offsets: ${JSON.stringify(offsets)}`); - // console.log(`#### END calculate highlight offsets`); + layerRect, + ), + ); } - return offsets; - }; + return arr; + } + + function getLayerHighlights( + layer: LayerChild[], + childCount: number, + layerIndex: number, + align: FlexLayerAlignment, + layerRect: DOMRect, + ): HighlightInfo[] { + const arr: HighlightInfo[] = []; + if (!layer.length) { + arr.push(getInitialHighlight(childCount, align, layerIndex, layerRect)); + return arr; + } + arr.push(...getHighlights(layer, childCount, align, layerIndex, layerRect)); + return arr; + } + + function getInitialHighlight( + childCount: number, + align: FlexLayerAlignment, + layerIndex: number, + rect: DOMRect, + ): HighlightInfo { + return { + isNewLayer: false, + index: childCount, + layerIndex, + align, + posX: + align === FlexLayerAlignment.Start + ? 0 + : align === FlexLayerAlignment.Center + ? containerDimensions.width / 2 + : containerDimensions?.width, + posY: rect.y - containerDimensions?.top, + width: OFFSET_WIDTH, + height: rect.height, + }; + } - const evaluateOffsets = ( - arr: string[], - flexOffsetTop: number, - isWrapper: boolean, - alignment: FlexLayerAlignment, - ): Highlight[] => { - let res: Highlight[] = []; - const siblings: DOMRect[] = []; - if (arr && arr.length) { - // Get widget ids of all widgets being dragged - arr.forEach((each) => { - const el = getDomElement(each); - if (!el) return; - - // console.log(`#### child: ${el.className}`); - // console.log(`#### offset parent: ${el.offsetParent.className}`); - const rect: DOMRect = el.getBoundingClientRect(); - // console.log(`#### bounding rect: ${JSON.stringify(rect)}`); - // Add a new offset using the current element's dimensions and position - res.push(getOffset(rect, flexOffsetTop, alignment, false)); - siblings.push(rect); - siblingElements.push(el); + function getHighlights( + layer: LayerChild[], + childCount: number, + align: FlexLayerAlignment, + layerIndex: number, + rect: DOMRect, + ) { + const arr: HighlightInfo[] = []; + const childRects: DOMRect[] = []; + layer.forEach((child) => { + const el = getDomElement(child.id); + if (!el) return null; + const childRect: DOMRect = el?.getBoundingClientRect(); + childRects.push(childRect); + // A highlight before each existing child. + arr.push({ + isNewLayer: false, + index: childCount, + layerIndex, + align, + posX: childRect.left - containerDimensions?.left, + posY: childRect.y - containerDimensions?.top, + width: OFFSET_WIDTH, + height: childRect.height, }); - /** - * If the dragged element has siblings, - * then add another offset at the end of the last sibling - * to demarcate the final drop position. - */ - if (siblings.length) { - res.push( - getOffset( - siblings[siblings.length - 1], - flexOffsetTop, - alignment, - true, - ), - ); - } - res = [...new Set(res)]; - } else res = [getInitialOffset(isWrapper, alignment)]; - return res; - }; + childCount += 1; + }); + // A highlight after the last child. + const lastRect: DOMRect = childRects[childRects.length - 1]; + arr.push({ + isNewLayer: false, + index: childCount, + layerIndex, + align, + posX: lastRect.right - containerDimensions?.left, + posY: lastRect.y - containerDimensions?.top, + width: OFFSET_WIDTH, + height: lastRect.height, + }); + return arr; + } /** * END AUTO LAYOUT OFFSET CALCULATION */ - const translateSiblings = (position: Highlight): void => { + const translateSiblings = (position: HighlightInfo): void => { let dropIndex = 0; if (position) - dropIndex = offsets - ?.map((each) => `${each.x},${each.y}`) - .indexOf(`${position.x},${position.y}`); + dropIndex = highlights + ?.map((each) => `${each.posX},${each.posY}`) + .indexOf(`${position.posX},${position.posY}`); if (dropIndex === lastTranslatedIndex) return; lastTranslatedIndex = dropIndex; @@ -453,16 +545,16 @@ export const useAutoLayoutHighlights = ({ const highlightDropPosition = (e: any) => { if (!useAutoLayout) return; - const pos: Highlight | undefined = getHighlightPosition(e); - // console.log(`#### Highlight position: ${JSON.stringify(pos)}`); + const pos: HighlightInfo | undefined = getHighlightPosition(e); + if (!pos) return; if (dropPositionRef && dropPositionRef.current) { dropPositionRef.current.style.opacity = "1"; - dropPositionRef.current.style.top = (pos.y > 6 ? pos.y - 6 : 0) + "px"; + dropPositionRef.current.style.top = (pos.posY || 0) + "px"; dropPositionRef.current.style.left = - (pos.x > 6 + (pos.posX > 6 ? Math.min( - pos.x - 6, + pos.posX - 6, containerDimensions.left + containerDimensions.width - 6, ) : 0) + "px"; @@ -473,11 +565,18 @@ export const useAutoLayoutHighlights = ({ translateSiblings(pos); }; - const getHighlightPosition = (e: any, val?: XYCord): Highlight => { - let base: Highlight[] = []; - if (!offsets || !offsets.length) - offsets = [getInitialOffset(isCanvasWrapper)]; - base = offsets; + const getHighlightPosition = (e: any, val?: XYCord): HighlightInfo => { + let base: HighlightInfo[] = []; + if (!highlights || !highlights.length) + highlights = [ + getInitialHighlight(0, FlexLayerAlignment.Start, 0, { + x: containerDimensions.left, + y: containerDimensions.top, + width: containerDimensions.width, + height: containerDimensions.height, + } as DOMRect), + ]; + base = highlights; const pos: XYCord = { x: e?.offsetX || val?.x, @@ -490,16 +589,16 @@ export const useAutoLayoutHighlights = ({ return arr[0]; }; - const calculateDistance = (a: Highlight, b: XYCord): number => { - const x: number = a.x + a.width / 2 - b.x; - const y: number = a.y + a.height / 2 - b.y; + const calculateDistance = (a: HighlightInfo, b: XYCord): number => { + const x: number = a.posX + a.width / 2 - b.x; + const y: number = a.posY + a.height / 2 - b.y; return Math.abs(Math.sqrt(x * x + y * y)); }; const getDropPosition = (index: number): number => { if (isNaN(index)) return 0; const alignment: FlexLayerAlignment = - offsets[index]?.alignment || FlexLayerAlignment.Start; + highlights[index]?.align || FlexLayerAlignment.Start; if (alignment === FlexLayerAlignment.Center) return index - 1; if (alignment === FlexLayerAlignment.End) return index - 2; return index; @@ -509,19 +608,19 @@ export const useAutoLayoutHighlights = ({ if (!isNaN(lastTranslatedIndex) && lastTranslatedIndex >= 0) return { index: getDropPosition(lastTranslatedIndex), - alignment: offsets[lastTranslatedIndex]?.alignment, + alignment: highlights[lastTranslatedIndex]?.align, }; const pos = getHighlightPosition(null, val); if (!pos) return; - const dropPos: number = offsets.indexOf(pos); + const dropPos: number = highlights.indexOf(pos); return { index: getDropPosition(dropPos), - alignment: offsets[dropPos]?.alignment, + alignment: highlights[dropPos]?.align, }; }; return { - calculateHighlightOffsets, + calculateHighlights, cleanUpTempStyles, getDropInfo, highlightDropPosition, diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index b191d15ca8ae..4ab9e2207f6e 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -31,6 +31,7 @@ import { useCanvasDragToScroll } from "./useCanvasDragToScroll"; import ContainerJumpMetrics from "./ContainerJumpMetric"; import { DropPositionPayload, + HighlightInfo, useAutoLayoutHighlights, } from "./useAutoLayoutHighlights"; @@ -115,7 +116,7 @@ export const useCanvasDragging = ( // eslint-disable-next-line prefer-const const { - calculateHighlightOffsets, + calculateHighlights, cleanUpTempStyles, getDropInfo, highlightDropPosition, @@ -130,7 +131,7 @@ export const useCanvasDragging = ( widgetName, }); - const offsets = calculateHighlightOffsets(); + const highlights: HighlightInfo[] = calculateHighlights(); if (!isDragging || !isCurrentDraggedCanvas) { cleanUpTempStyles(); @@ -586,7 +587,7 @@ export const useCanvasDragging = ( } else if (!isUpdatingRows) { triggerReflow(e, firstMove); isCurrentDraggedCanvas && - offsets.length && + highlights.length && highlightDropPosition(e); renderBlocks(); } From d542241c34553eaa281ccc8fd8f497f07298ad44 Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 6 Oct 2022 19:52:40 -0400 Subject: [PATCH 128/708] remove tests file --- app/client/src/utils/test.ts | 181 ----------------------------------- 1 file changed, 181 deletions(-) delete mode 100644 app/client/src/utils/test.ts diff --git a/app/client/src/utils/test.ts b/app/client/src/utils/test.ts deleted file mode 100644 index 67e8e06f79ee..000000000000 --- a/app/client/src/utils/test.ts +++ /dev/null @@ -1,181 +0,0 @@ -import { FlexLayerAlignment, LayoutDirection } from "components/constants"; -import { - FlexLayer, - LayerChild, -} from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; - -export interface HighlightInfo { - isNewLayer: boolean; // determines if a new layer / child has been added directly to the container. - index: number; // index of the child in props.children. - layerIndex?: number; // index of layer in props.flexLayers. - align?: FlexLayerAlignment; // alignment of the child in the layer. - posX?: number; // x position of the highlight. - posY?: number; // y position of the highlight. - width?: number; // width of the highlight. - height?: number; // height of the highlight. -} - -// TODO: Add logic for infering highlight position and size. -// e.g. initial offset for center wrapper => posX = parent.width / 2 - -function generateHighlights( - direction: LayoutDirection, - flexLayers: FlexLayer[], - children: string[], -) { - if (direction === LayoutDirection.Vertical) { - return [ - ...getContainerHighlights(flexLayers), - ...generateHighlightsForLayers(flexLayers), - ]; - } - return generateHighlightsForChildren(children); -} - -/** - * Derive highlights for the container. - */ - -function getContainerHighlights(flexLayers: FlexLayer[]) { - let childCount = 0; - const arr = flexLayers.map((layer, index) => { - const res = { - isNewLayer: true, - index: childCount, - layerIndex: index, - }; - childCount += layer.children.length; - return res; - }); - // Add a final highlight at the end. - arr.push({ - isNewLayer: true, - index: childCount, - layerIndex: arr.length, - }); - return arr; -} - -/** - * Derive highlights for each layer. - */ - -function generateHighlightsForLayers(layers: FlexLayer[]) { - // eslint-disable-next-line prefer-const - let childCount = 0; - const arr: HighlightInfo[] = []; - layers.forEach((layer, index) => { - const { center, end, hasFillChild, start } = spreadLayer(layer); - // process start sub wrapper. - arr.push( - ...getLayerHighlights(start, childCount, index, FlexLayerAlignment.Start), - ); - if (!hasFillChild) { - // process center sub wrapper. - arr.push( - ...getLayerHighlights( - center, - childCount, - index, - FlexLayerAlignment.Center, - ), - ); - // process end sub wrapper. - arr.push( - ...getLayerHighlights( - end, - childCount, - index, - FlexLayerAlignment.Center, - ), - ); - } - }); - return arr; -} - -function spreadLayer(layer: FlexLayer) { - const start: LayerChild[] = [], - center: LayerChild[] = [], - end: LayerChild[] = []; - layer.children.forEach((child: LayerChild) => { - if (layer.hasFillChild) { - start.push(child); - return; - } - if (child.align === FlexLayerAlignment.End) end.push(child); - else if (child.align === FlexLayerAlignment.Center) center.push(child); - else start.push(child); - }); - return { start, center, end, hasFillChild: layer.hasFillChild }; -} - -function getLayerHighlights( - layer: LayerChild[], - childCount: number, - layerIndex: number, - align: FlexLayerAlignment, -): HighlightInfo[] { - const arr: HighlightInfo[] = []; - if (!layer.length) { - arr.push(getInitialOffset(childCount, align, layerIndex)); - return arr; - } - arr.push(...getOffsets(layer, childCount, align, layerIndex)); - return arr; -} - -function getInitialOffset( - childCount: number, - align: FlexLayerAlignment, - layerIndex: number, -): HighlightInfo { - return { - isNewLayer: false, - index: childCount, - layerIndex, - align, - }; -} - -function getOffsets( - layer: LayerChild[], - childCount: number, - align: FlexLayerAlignment, - layerIndex: number, -) { - const arr: HighlightInfo[] = []; - layer.forEach((child, index) => { - // A highlight before each existing child. - arr.push({ - isNewLayer: false, - index: childCount, - layerIndex, - align, - }); - childCount += 1; - }); - // A highlight after the last child. - arr.push({ - isNewLayer: false, - index: childCount, - layerIndex, - align, - }); - return arr; -} - -/** - * Derive highlights for children - */ -function generateHighlightsForChildren(children: string[]): HighlightInfo[] { - const arr: HighlightInfo[] = []; - children.forEach((child, index) => { - arr.push({ - isNewLayer: true, - index, - }); - }); - arr.push({ isNewLayer: true, index: children.length }); - return arr; -} From 2c1aaf0728ca1a963bacfd12af98f3427be2322e Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 6 Oct 2022 23:27:01 -0400 Subject: [PATCH 129/708] update drop payload --- .../hooks/useAutoLayoutHighlights.ts | 176 +++--------------- .../hooks/useBlocksToBeDraggedOnCanvas.ts | 19 +- .../CanvasArenas/hooks/useCanvasDragging.ts | 11 +- .../sagas/CanvasSagas/DraggingCanvasSagas.ts | 27 +-- 4 files changed, 43 insertions(+), 190 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index d0dcef166352..9cc07266950a 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -2,7 +2,6 @@ import { getWidgets } from "sagas/selectors"; import { useSelector } from "store"; import { LayoutDirection, FlexLayerAlignment } from "components/constants"; -import { isNaN } from "lodash"; import { WidgetDraggingBlock } from "./useBlocksToBeDraggedOnCanvas"; import { FlexLayer, @@ -26,7 +25,7 @@ export interface HighlightInfo { isNewLayer: boolean; // determines if a new layer / child has been added directly to the container. index: number; // index of the child in props.children. layerIndex?: number; // index of layer in props.flexLayers. - align: FlexLayerAlignment; // alignment of the child in the layer. + alignment: FlexLayerAlignment; // alignment of the child in the layer. posX: number; // x position of the highlight. posY: number; // y position of the highlight. width: number; // width of the highlight. @@ -44,11 +43,6 @@ export interface AutoLayoutHighlightProps { widgetName?: string; } -export interface DropPositionPayload { - index: number; - alignment: FlexLayerAlignment; -} - const BASE_OFFSET_SIZE = 100; const OFFSET_WIDTH = 4; @@ -69,6 +63,7 @@ export const useAutoLayoutHighlights = ({ let highlights: HighlightInfo[] = []; let dragBlocksSize = 0; const siblingElements: any[] = []; + let lastActiveHighlight: HighlightInfo | undefined; let lastTranslatedIndex: number; let containerDimensions: { top: number; @@ -104,73 +99,6 @@ export const useAutoLayoutHighlights = ({ return true; }; - const getContainerDimensions = () => { - if (!containerDimensions) updateContainerDimensions(); - return containerDimensions; - }; - - const initialOffsets: Record< - FlexLayerAlignment, - Record - > = { - [FlexLayerAlignment.Start]: { - [LayoutDirection.Vertical]: { - x: 0, - y: 8, - width: getContainerDimensions()?.width || BASE_OFFSET_SIZE, - height: OFFSET_WIDTH, - alignment: FlexLayerAlignment.Start, - }, - [LayoutDirection.Horizontal]: { - x: 8, - y: 0, - width: OFFSET_WIDTH, - height: getContainerDimensions()?.height || BASE_OFFSET_SIZE, - alignment: FlexLayerAlignment.Start, - }, - }, - [FlexLayerAlignment.Center]: { - [LayoutDirection.Vertical]: { - x: 0, - y: getContainerDimensions()?.height / 2, - width: getContainerDimensions()?.width || BASE_OFFSET_SIZE, - height: OFFSET_WIDTH, - alignment: FlexLayerAlignment.Center, - }, - [LayoutDirection.Horizontal]: { - x: getContainerDimensions()?.width / 2, - y: 0, - width: OFFSET_WIDTH, - height: getContainerDimensions()?.height || BASE_OFFSET_SIZE, - alignment: FlexLayerAlignment.Center, - }, - }, - [FlexLayerAlignment.End]: { - [LayoutDirection.Vertical]: { - x: 0, - y: getContainerDimensions()?.bottom, - width: getContainerDimensions()?.width || BASE_OFFSET_SIZE, - height: OFFSET_WIDTH, - alignment: FlexLayerAlignment.End, - }, - [LayoutDirection.Horizontal]: { - x: getContainerDimensions()?.right, - y: 0, - width: OFFSET_WIDTH, - height: getContainerDimensions()?.height || BASE_OFFSET_SIZE, - alignment: FlexLayerAlignment.End, - }, - }, - }; - - // Create and add an initial offset for an empty canvas - // const getInitialOffset = ( - // alignment: FlexLayerAlignment = FlexLayerAlignment.Start, - // ): Highlight => { - // const dir: LayoutDirection = direction || LayoutDirection.Horizontal; - // if (false) return initialOffsets[alignment][dir]; - // return initialOffsets[FlexLayerAlignment.Start][dir]; - // }; // Get DOM element for a given widgetId const getDomElement = (widgetId: string): any => document.querySelector(`.auto-layout-child-${widgetId}`); @@ -187,7 +115,8 @@ export const useAutoLayoutHighlights = ({ // reset state dragBlocksSize = 0; - lastTranslatedIndex = -10; + lastActiveHighlight = undefined; + highlights = []; // Hide the highlight if (dropPositionRef && dropPositionRef.current) { dropPositionRef.current.style.opacity = "0"; @@ -197,6 +126,7 @@ export const useAutoLayoutHighlights = ({ // Get a list of widgetIds that are being dragged. const getDraggedBlocks = (): string[] => { + console.log("#### blocksToDraw", blocksToDraw); const blocks = blocksToDraw.map((block) => block.widgetId); // console.log(`#### blocksToDraw: ${JSON.stringify(blocksToDraw)}`); // console.log(`#### blocks: ${JSON.stringify(blocks)}`); @@ -269,7 +199,7 @@ export const useAutoLayoutHighlights = ({ posY: childRect.y - containerDimensions?.top, width: OFFSET_WIDTH, height: childRect.height, - align: FlexLayerAlignment.Start, + alignment: FlexLayerAlignment.Start, }); }); // TODO: Add check for empty container. @@ -281,22 +211,11 @@ export const useAutoLayoutHighlights = ({ posY: lastElRect.y - containerDimensions?.top, width: OFFSET_WIDTH, height: lastElRect.height, - align: FlexLayerAlignment.Start, + alignment: FlexLayerAlignment.Start, }); return arr; } - function filterLayers( - flexLayers: FlexLayer[], - draggedBlocks: string[], - ): FlexLayer[] { - return flexLayers.filter((layer) => { - return layer.children?.some((each) => { - return draggedBlocks.indexOf(each.id) === -1; - }); - }); - } - function isEmptyLayer(layer: FlexLayer, draggedBlocks: string[]): boolean { return ( layer.children?.length === 0 || @@ -315,7 +234,6 @@ export const useAutoLayoutHighlights = ({ let index = 0; const arr: HighlightInfo[] = []; const rects: DOMRect[] = []; - console.log("#### flexLayers: ", flexLayers); for (const layer of flexLayers) { if (isEmptyLayer(layer, draggedBlocks)) { discardedLayers += 1; @@ -338,7 +256,7 @@ export const useAutoLayoutHighlights = ({ posY: rect.y - containerDimensions?.top, width: containerDimensions?.width, height: OFFSET_WIDTH, - align: FlexLayerAlignment.Start, + alignment: FlexLayerAlignment.Start, }; arr.push(info); arr.push( @@ -349,11 +267,10 @@ export const useAutoLayoutHighlights = ({ childCount, ), ); - console.log("#### arr: ", arr); index += 1; childCount += layer.children?.length || 0; } - console.log("#### rects: ", rects); + const lastElRect: DOMRect = rects[rects.length - 1]; arr.push({ isNewLayer: true, @@ -363,9 +280,9 @@ export const useAutoLayoutHighlights = ({ posY: lastElRect.y + lastElRect.height - containerDimensions?.top, width: containerDimensions?.width, height: OFFSET_WIDTH, - align: FlexLayerAlignment.Start, + alignment: FlexLayerAlignment.Start, }); - console.log("#### final arr: ", arr); + return arr; } @@ -443,13 +360,13 @@ export const useAutoLayoutHighlights = ({ arr.push(getInitialHighlight(childCount, align, layerIndex, layerRect)); return arr; } - arr.push(...getHighlights(layer, childCount, align, layerIndex, layerRect)); + arr.push(...getHighlights(layer, childCount, align, layerIndex)); return arr; } function getInitialHighlight( childCount: number, - align: FlexLayerAlignment, + alignment: FlexLayerAlignment, layerIndex: number, rect: DOMRect, ): HighlightInfo { @@ -457,11 +374,11 @@ export const useAutoLayoutHighlights = ({ isNewLayer: false, index: childCount, layerIndex, - align, + alignment, posX: - align === FlexLayerAlignment.Start + alignment === FlexLayerAlignment.Start ? 0 - : align === FlexLayerAlignment.Center + : alignment === FlexLayerAlignment.Center ? containerDimensions.width / 2 : containerDimensions?.width, posY: rect.y - containerDimensions?.top, @@ -473,9 +390,8 @@ export const useAutoLayoutHighlights = ({ function getHighlights( layer: LayerChild[], childCount: number, - align: FlexLayerAlignment, + alignment: FlexLayerAlignment, layerIndex: number, - rect: DOMRect, ) { const arr: HighlightInfo[] = []; const childRects: DOMRect[] = []; @@ -489,7 +405,7 @@ export const useAutoLayoutHighlights = ({ isNewLayer: false, index: childCount, layerIndex, - align, + alignment, posX: childRect.left - containerDimensions?.left, posY: childRect.y - containerDimensions?.top, width: OFFSET_WIDTH, @@ -503,8 +419,8 @@ export const useAutoLayoutHighlights = ({ isNewLayer: false, index: childCount, layerIndex, - align, - posX: lastRect.right - containerDimensions?.left, + alignment, + posX: lastRect.left + lastRect.width - containerDimensions?.left, posY: lastRect.y - containerDimensions?.top, width: OFFSET_WIDTH, height: lastRect.height, @@ -516,38 +432,12 @@ export const useAutoLayoutHighlights = ({ * END AUTO LAYOUT OFFSET CALCULATION */ - const translateSiblings = (position: HighlightInfo): void => { - let dropIndex = 0; - if (position) - dropIndex = highlights - ?.map((each) => `${each.posX},${each.posY}`) - .indexOf(`${position.posX},${position.posY}`); - if (dropIndex === lastTranslatedIndex) return; - - lastTranslatedIndex = dropIndex; - // console.log(`#### lastTranslatedIndex: ${lastTranslatedIndex}`); - return; - // Get all siblings after the highlighted drop position - const arr = [...siblingElements]; - - // translate each element in the appropriate direction - const x = !isVertical ? dragBlocksSize : 0; - const y = isVertical ? dragBlocksSize : 0; - arr.forEach((each, index) => { - if (index < dropIndex) { - each.style.transform = null; - } else { - each.style.transform = `translate(${x}px, ${y}px)`; - each.style.transitionDuration = "0.2s"; - } - }); - }; - const highlightDropPosition = (e: any) => { if (!useAutoLayout) return; const pos: HighlightInfo | undefined = getHighlightPosition(e); if (!pos) return; + lastActiveHighlight = pos; if (dropPositionRef && dropPositionRef.current) { dropPositionRef.current.style.opacity = "1"; dropPositionRef.current.style.top = (pos.posY || 0) + "px"; @@ -562,7 +452,6 @@ export const useAutoLayoutHighlights = ({ dropPositionRef.current.style.height = pos.height + "px"; dropPositionRef.current.style.display = "block"; } - translateSiblings(pos); }; const getHighlightPosition = (e: any, val?: XYCord): HighlightInfo => { @@ -595,28 +484,13 @@ export const useAutoLayoutHighlights = ({ return Math.abs(Math.sqrt(x * x + y * y)); }; - const getDropPosition = (index: number): number => { - if (isNaN(index)) return 0; - const alignment: FlexLayerAlignment = - highlights[index]?.align || FlexLayerAlignment.Start; - if (alignment === FlexLayerAlignment.Center) return index - 1; - if (alignment === FlexLayerAlignment.End) return index - 2; - return index; - }; + const getDropInfo = (val: XYCord): HighlightInfo | undefined => { + if (lastActiveHighlight) return lastActiveHighlight; - const getDropInfo = (val: XYCord): DropPositionPayload | undefined => { - if (!isNaN(lastTranslatedIndex) && lastTranslatedIndex >= 0) - return { - index: getDropPosition(lastTranslatedIndex), - alignment: highlights[lastTranslatedIndex]?.align, - }; const pos = getHighlightPosition(null, val); if (!pos) return; - const dropPos: number = highlights.indexOf(pos); - return { - index: getDropPosition(dropPos), - alignment: highlights[dropPos]?.align, - }; + lastActiveHighlight = pos; + return pos; }; return { diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts b/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts index 137b9cb6100f..2c49317a89c5 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts @@ -36,6 +36,7 @@ import { LayoutDirection, FlexLayerAlignment, } from "components/constants"; +import { HighlightInfo } from "./useAutoLayoutHighlights"; export interface WidgetDraggingUpdateParams extends WidgetDraggingBlock { updateWidgetParams: WidgetOperationParams; @@ -278,28 +279,24 @@ export const useBlocksToBeDraggedOnCanvas = ({ if (isReflowing) dispatch(stopReflowAction()); }; const updateChildrenPositions = ( - index: number, + dropPayload: HighlightInfo, drawingBlocks: WidgetDraggingBlock[], - alignment: FlexLayerAlignment, ): void => { - if (isNewWidget) addNewWidgetToAutoLayout(index, drawingBlocks, alignment); + if (isNewWidget) addNewWidgetToAutoLayout(dropPayload, drawingBlocks); else dispatch({ type: ReduxActionTypes.AUTOLAYOUT_REORDER_WIDGETS, payload: { - index, + dropPayload, movedWidgets: selectedWidgets, parentId: widgetId, - alignment, direction, - isNewLayer: false, }, }); }; const addNewWidgetToAutoLayout = ( - index: number, + dropPayload: HighlightInfo, drawingBlocks: WidgetDraggingBlock[], - alignment: FlexLayerAlignment, ) => { logContainerJumpOnDrop(); const blocksToUpdate = drawingBlocks.map((each) => { @@ -327,18 +324,16 @@ export const useBlocksToBeDraggedOnCanvas = ({ ...blocksToUpdate[0]?.updateWidgetParams?.payload, props: { ...blocksToUpdate[0]?.updateWidgetParams?.payload?.props, - alignment, + alignment: dropPayload.alignment, }, }; dispatch({ type: ReduxActionTypes.AUTOLAYOUT_ADD_NEW_WIDGETS, payload: { - index, + dropPayload, newWidget: widgetPayload, parentId: widgetId, - alignment, direction, - isNewLayer: true, }, }); }; diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index 4ab9e2207f6e..85bc32e674be 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -30,7 +30,6 @@ import { import { useCanvasDragToScroll } from "./useCanvasDragToScroll"; import ContainerJumpMetrics from "./ContainerJumpMetric"; import { - DropPositionPayload, HighlightInfo, useAutoLayoutHighlights, } from "./useAutoLayoutHighlights"; @@ -323,17 +322,13 @@ export const useCanvasDragging = ( } return each; }); - const pos: DropPositionPayload | undefined = getDropInfo({ + const dropInfo: HighlightInfo | undefined = getDropInfo({ x: currentRectanglesToDraw[0].top, y: currentRectanglesToDraw[0].left, }); - if (pos !== undefined && useAutoLayout) { + if (dropInfo !== undefined && useAutoLayout) { // cleanUpTempStyles(); - updateChildrenPositions( - pos.index, - currentRectanglesToDraw, - pos.alignment, - ); + updateChildrenPositions(dropInfo, currentRectanglesToDraw); } else onDrop(currentRectanglesToDraw, reflowedPositionsUpdatesWidgets); } diff --git a/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts b/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts index 07af1c40830e..869a6aa481ef 100644 --- a/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts +++ b/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts @@ -39,6 +39,7 @@ import { FlexLayer, LayerChild, } from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; +import { HighlightInfo } from "pages/common/CanvasArenas/hooks/useAutoLayoutHighlights"; export type WidgetMoveParams = { widgetId: string; @@ -400,23 +401,20 @@ function moveWidget(widgetMoveParams: WidgetMoveParams) { function* autolayoutReorderSaga( actionPayload: ReduxAction<{ movedWidgets: string[]; - index: number; - isNewLayer: boolean; parentId: string; - alignment: FlexLayerAlignment; direction: LayoutDirection; + dropPayload: HighlightInfo; }>, ) { const start = performance.now(); const { - alignment, direction, - index, - isNewLayer, + dropPayload, movedWidgets, parentId, } = actionPayload.payload; + const { alignment, index, isNewLayer, layerIndex } = dropPayload; // console.log(`#### moved widgets: ${JSON.stringify(movedWidgets)}`); // console.log(`#### parentId: ${parentId}`); try { @@ -432,6 +430,7 @@ function* autolayoutReorderSaga( allWidgets, alignment, direction, + layerIndex, }, ); @@ -658,24 +657,14 @@ function filterLayers( function* addWidgetAndReorderSaga( actionPayload: ReduxAction<{ newWidget: WidgetAddChild; - index: number; - isNewLayer: boolean; parentId: string; - alignment: FlexLayerAlignment; direction: LayoutDirection; - layerIndex?: number; + dropPayload: HighlightInfo; }>, ) { const start = performance.now(); - const { - alignment, - direction, - index, - isNewLayer, - layerIndex, - newWidget, - parentId, - } = actionPayload.payload; + const { direction, dropPayload, newWidget, parentId } = actionPayload.payload; + const { alignment, index, isNewLayer, layerIndex } = dropPayload; try { const updatedWidgetsOnAddition: CanvasWidgetsReduxState = yield call( getUpdateDslAfterCreatingChild, From 23626044b8dd4e1b30579b2a4f59d8633f677bf1 Mon Sep 17 00:00:00 2001 From: Preet Date: Fri, 7 Oct 2022 10:06:37 -0400 Subject: [PATCH 130/708] fix widget_delete to update flex layers --- .../hooks/useAutoLayoutHighlights.ts | 33 ++++++------- .../hooks/useBlocksToBeDraggedOnCanvas.ts | 6 +-- app/client/src/sagas/WidgetDeletionSagas.ts | 46 ++++++++++--------- app/client/src/sagas/WidgetOperationUtils.ts | 36 ++++++++++++++- 4 files changed, 75 insertions(+), 46 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index 9cc07266950a..f61a3af76b49 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -62,9 +62,7 @@ export const useAutoLayoutHighlights = ({ let highlights: HighlightInfo[] = []; let dragBlocksSize = 0; - const siblingElements: any[] = []; let lastActiveHighlight: HighlightInfo | undefined; - let lastTranslatedIndex: number; let containerDimensions: { top: number; bottom: number; @@ -126,7 +124,6 @@ export const useAutoLayoutHighlights = ({ // Get a list of widgetIds that are being dragged. const getDraggedBlocks = (): string[] => { - console.log("#### blocksToDraw", blocksToDraw); const blocks = blocksToDraw.map((block) => block.widgetId); // console.log(`#### blocksToDraw: ${JSON.stringify(blocksToDraw)}`); // console.log(`#### blocks: ${JSON.stringify(blocks)}`); @@ -181,7 +178,7 @@ export const useAutoLayoutHighlights = ({ highlights = generateHighlightsForChildren(offsetChildren); } } - console.log("#### highlights: ", highlights); + // console.log("#### highlights: ", highlights); return highlights; }; @@ -392,12 +389,12 @@ export const useAutoLayoutHighlights = ({ childCount: number, alignment: FlexLayerAlignment, layerIndex: number, - ) { + ): HighlightInfo[] { const arr: HighlightInfo[] = []; const childRects: DOMRect[] = []; - layer.forEach((child) => { + for (const child of layer) { const el = getDomElement(child.id); - if (!el) return null; + if (!el) continue; const childRect: DOMRect = el?.getBoundingClientRect(); childRects.push(childRect); // A highlight before each existing child. @@ -406,13 +403,13 @@ export const useAutoLayoutHighlights = ({ index: childCount, layerIndex, alignment, - posX: childRect.left - containerDimensions?.left, - posY: childRect.y - containerDimensions?.top, + posX: childRect?.left - containerDimensions?.left, + posY: childRect?.y - containerDimensions?.top, width: OFFSET_WIDTH, - height: childRect.height, + height: childRect?.height, }); childCount += 1; - }); + } // A highlight after the last child. const lastRect: DOMRect = childRects[childRects.length - 1]; arr.push({ @@ -420,10 +417,10 @@ export const useAutoLayoutHighlights = ({ index: childCount, layerIndex, alignment, - posX: lastRect.left + lastRect.width - containerDimensions?.left, - posY: lastRect.y - containerDimensions?.top, + posX: lastRect?.left + lastRect?.width - containerDimensions?.left, + posY: lastRect?.y - containerDimensions?.top, width: OFFSET_WIDTH, - height: lastRect.height, + height: lastRect?.height, }); return arr; } @@ -459,10 +456,10 @@ export const useAutoLayoutHighlights = ({ if (!highlights || !highlights.length) highlights = [ getInitialHighlight(0, FlexLayerAlignment.Start, 0, { - x: containerDimensions.left, - y: containerDimensions.top, - width: containerDimensions.width, - height: containerDimensions.height, + x: containerDimensions?.left, + y: containerDimensions?.top, + width: containerDimensions?.width, + height: containerDimensions?.height, } as DOMRect), ]; base = highlights; diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts b/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts index 2c49317a89c5..dc4b9e1ed327 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts @@ -31,11 +31,7 @@ import { DragDetails } from "reducers/uiReducers/dragResizeReducer"; import { getIsReflowing } from "selectors/widgetReflowSelectors"; import { XYCord } from "./useCanvasDragging"; import ContainerJumpMetrics from "./ContainerJumpMetric"; -import { - AlignItems, - LayoutDirection, - FlexLayerAlignment, -} from "components/constants"; +import { AlignItems, LayoutDirection } from "components/constants"; import { HighlightInfo } from "./useAutoLayoutHighlights"; export interface WidgetDraggingUpdateParams extends WidgetDraggingBlock { diff --git a/app/client/src/sagas/WidgetDeletionSagas.ts b/app/client/src/sagas/WidgetDeletionSagas.ts index 288ccdf90bf7..bdaed2fa9285 100644 --- a/app/client/src/sagas/WidgetDeletionSagas.ts +++ b/app/client/src/sagas/WidgetDeletionSagas.ts @@ -30,6 +30,7 @@ import { WidgetProps } from "widgets/BaseWidget"; import { getSelectedWidget, getWidget, getWidgets } from "./selectors"; import { getAllWidgetsInTree, + updateFlexLayersOnDelete, updateListWidgetPropertiesOnChildDelete, WidgetsInTree, } from "./WidgetOperationUtils"; @@ -94,7 +95,14 @@ function* deleteTabChildSaga( tabsObj: updatedObj, }, }; - yield put(updateAndSaveLayout(parentUpdatedWidgets)); + // Update flex layers of a canvas upon deletion of a widget. + const widgetsAfterUpdatingFlexLayers: CanvasWidgetsReduxState = yield call( + updateFlexLayersOnDelete, + parentUpdatedWidgets, + widgetId, + tabWidget.parentId, + ); + yield put(updateAndSaveLayout(widgetsAfterUpdatingFlexLayers)); yield call(postDelete, widgetId, label, otherWidgetsToDelete); } } @@ -199,6 +207,7 @@ function* getUpdatedDslAfterDeletingWidget(widgetId: string, parentId: string) { function* deleteSaga(deleteAction: ReduxAction) { try { let { parentId, widgetId } = deleteAction.payload; + const { disallowUndo, isShortcut } = deleteAction.payload; if (!widgetId) { @@ -217,30 +226,23 @@ function* deleteSaga(deleteAction: ReduxAction) { if (widgetId && parentId) { const stateWidget: WidgetProps = yield select(getWidget, widgetId); const widget = { ...stateWidget }; - const stateParent: WidgetProps = yield select(getWidget, parentId); - const parent = { ...stateParent }; - // Additional check for auto layout children - const shouldDeleteParent = - parent.type === "LAYOUT_WRAPPER_WIDGET" && - !parent.children?.filter((e: string) => e !== widgetId).length; - - let updatedObj: UpdatedDSLPostDelete; - if (shouldDeleteParent) - updatedObj = yield call( - getUpdatedDslAfterDeletingWidget, - parentId, - parent.parentId || "0", - ); - else - updatedObj = yield call( - getUpdatedDslAfterDeletingWidget, - widgetId, - parentId, - ); + + const updatedObj: UpdatedDSLPostDelete = yield call( + getUpdatedDslAfterDeletingWidget, + widgetId, + parentId, + ); if (updatedObj) { const { finalWidgets, otherWidgetsToDelete, widgetName } = updatedObj; - yield put(updateAndSaveLayout(finalWidgets)); + // Update flex layers of a canvas upon deletion of a widget. + const widgetsAfterUpdatingFlexLayers: CanvasWidgetsReduxState = yield call( + updateFlexLayersOnDelete, + finalWidgets, + widgetId, + parentId, + ); + yield put(updateAndSaveLayout(widgetsAfterUpdatingFlexLayers)); const analyticsEvent = isShortcut ? "WIDGET_DELETE_VIA_SHORTCUT" : "WIDGET_DELETE"; diff --git a/app/client/src/sagas/WidgetOperationUtils.ts b/app/client/src/sagas/WidgetOperationUtils.ts index b0c20b842d76..5540e927a089 100644 --- a/app/client/src/sagas/WidgetOperationUtils.ts +++ b/app/client/src/sagas/WidgetOperationUtils.ts @@ -54,7 +54,10 @@ import { getBottomRowAfterReflow } from "utils/reflowHookUtils"; import { DataTreeWidget } from "entities/DataTree/dataTreeFactory"; import { isWidget } from "../workers/evaluationUtils"; import { FlexLayerAlignment, ResponsiveBehavior } from "components/constants"; -import { FlexLayer } from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; +import { + FlexLayer, + LayerChild, +} from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; export interface CopiedWidgetGroup { widgetId: string; @@ -1715,3 +1718,34 @@ export function* wrapChildren( widgets[canvasId] = parent; return widgets; } + +export function* updateFlexLayersOnDelete( + allWidgets: CanvasWidgetsReduxState, + widgetId: string, + parentId: string, +) { + const widgets = { ...allWidgets }; + let parent = widgets[parentId]; + if (!parent) return widgets; + + const flexLayers = parent.flexLayers || []; + if (!flexLayers.length) return widgets; + for (const layer of flexLayers) { + const children = layer.children || []; + if (!children.length) continue; + const index = children.findIndex( + (each: LayerChild) => each.id === widgetId, + ); + if (index === -1) continue; + children.splice(index, 1); + layer.children = children; + } + parent = { + ...parent, + flexLayers: flexLayers.filter( + (layer: FlexLayer) => layer?.children?.length, + ), + }; + widgets[parentId] = parent; + return widgets; +} From 8fa143c466946daa8996a5522172c2672e3c79ad Mon Sep 17 00:00:00 2001 From: Preet Date: Fri, 7 Oct 2022 10:25:28 -0400 Subject: [PATCH 131/708] fix drop in empty container --- .../hooks/useAutoLayoutHighlights.ts | 90 +++++++++++++------ 1 file changed, 64 insertions(+), 26 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index f61a3af76b49..ad918a01301d 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -61,7 +61,6 @@ export const useAutoLayoutHighlights = ({ const isVertical = direction === LayoutDirection.Vertical; let highlights: HighlightInfo[] = []; - let dragBlocksSize = 0; let lastActiveHighlight: HighlightInfo | undefined; let containerDimensions: { top: number; @@ -97,6 +96,16 @@ export const useAutoLayoutHighlights = ({ return true; }; + const getContainerDimensionsAsDomRect = (): DOMRect => { + if (!containerDimensions) updateContainerDimensions(); + return { + x: containerDimensions?.left, + y: containerDimensions?.top, + width: containerDimensions?.width, + height: containerDimensions?.height, + } as DOMRect; + }; + // Get DOM element for a given widgetId const getDomElement = (widgetId: string): any => document.querySelector(`.auto-layout-child-${widgetId}`); @@ -112,7 +121,6 @@ export const useAutoLayoutHighlights = ({ } // reset state - dragBlocksSize = 0; lastActiveHighlight = undefined; highlights = []; // Hide the highlight @@ -130,13 +138,6 @@ export const useAutoLayoutHighlights = ({ return blocks; }; - // Get the total 1D size of the drag block. - const calculateDragBlockSize = (): void => { - blocksToDraw?.forEach((each) => { - dragBlocksSize += isVertical ? each.height : each.width; - }); - }; - // Hide the dragged children of the auto layout container // to discount them from highlight calculation. const hideDraggedItems = (draggedBlocks: string[]): void => { @@ -157,7 +158,6 @@ export const useAutoLayoutHighlights = ({ */ cleanUpTempStyles(); // TODO: is this needed? if (useAutoLayout && isDragging && isCurrentDraggedCanvas) { - calculateDragBlockSize(); const draggedBlocks = getDraggedBlocks(); if (!draggedBlocks || !draggedBlocks.length) return []; /** @@ -183,9 +183,21 @@ export const useAutoLayoutHighlights = ({ }; function generateHighlightsForChildren(children: string[]): HighlightInfo[] { + if (!children || !children.length) + return [ + getInitialHighlight( + 0, + FlexLayerAlignment.Start, + 0, + getContainerDimensionsAsDomRect(), + LayoutDirection.Horizontal, + true, + ), + ]; const arr: HighlightInfo[] = []; const rects: DOMRect[] = []; - children.forEach((child, index) => { + let index = 0; + for (const child of children) { const el = getDomElement(child); const childRect: DOMRect = el.getBoundingClientRect(); rects.push(childRect); @@ -198,16 +210,17 @@ export const useAutoLayoutHighlights = ({ height: childRect.height, alignment: FlexLayerAlignment.Start, }); - }); + index += 1; + } // TODO: Add check for empty container. const lastElRect: DOMRect = rects[rects.length - 1]; arr.push({ isNewLayer: true, index: children.length, - posX: lastElRect.x - containerDimensions?.left + lastElRect.width, - posY: lastElRect.y - containerDimensions?.top, + posX: lastElRect?.x - containerDimensions?.left + lastElRect?.width, + posY: lastElRect?.y - containerDimensions?.top, width: OFFSET_WIDTH, - height: lastElRect.height, + height: lastElRect?.height, alignment: FlexLayerAlignment.Start, }); return arr; @@ -226,6 +239,17 @@ export const useAutoLayoutHighlights = ({ flexLayers: FlexLayer[], draggedBlocks: string[], ): HighlightInfo[] { + if (!flexLayers || !flexLayers.length) + return [ + getInitialHighlight( + 0, + FlexLayerAlignment.Start, + 0, + getContainerDimensionsAsDomRect(), + LayoutDirection.Vertical, + true, + ), + ]; let childCount = 0; let discardedLayers = 0; let index = 0; @@ -274,7 +298,7 @@ export const useAutoLayoutHighlights = ({ index: childCount, layerIndex: rects.length, posX: 0, - posY: lastElRect.y + lastElRect.height - containerDimensions?.top, + posY: lastElRect?.y + lastElRect?.height - containerDimensions?.top, width: containerDimensions?.width, height: OFFSET_WIDTH, alignment: FlexLayerAlignment.Start, @@ -354,7 +378,16 @@ export const useAutoLayoutHighlights = ({ ): HighlightInfo[] { const arr: HighlightInfo[] = []; if (!layer.length) { - arr.push(getInitialHighlight(childCount, align, layerIndex, layerRect)); + arr.push( + getInitialHighlight( + childCount, + align, + layerIndex, + layerRect, + LayoutDirection.Horizontal, + false, + ), + ); return arr; } arr.push(...getHighlights(layer, childCount, align, layerIndex)); @@ -366,9 +399,12 @@ export const useAutoLayoutHighlights = ({ alignment: FlexLayerAlignment, layerIndex: number, rect: DOMRect, + direction: LayoutDirection, + isNewLayer = false, ): HighlightInfo { + const verticalFlex = direction === LayoutDirection.Vertical; return { - isNewLayer: false, + isNewLayer, index: childCount, layerIndex, alignment, @@ -379,8 +415,8 @@ export const useAutoLayoutHighlights = ({ ? containerDimensions.width / 2 : containerDimensions?.width, posY: rect.y - containerDimensions?.top, - width: OFFSET_WIDTH, - height: rect.height, + width: verticalFlex ? rect?.width : OFFSET_WIDTH, + height: verticalFlex ? OFFSET_WIDTH : rect.height, }; } @@ -455,12 +491,14 @@ export const useAutoLayoutHighlights = ({ let base: HighlightInfo[] = []; if (!highlights || !highlights.length) highlights = [ - getInitialHighlight(0, FlexLayerAlignment.Start, 0, { - x: containerDimensions?.left, - y: containerDimensions?.top, - width: containerDimensions?.width, - height: containerDimensions?.height, - } as DOMRect), + getInitialHighlight( + 0, + FlexLayerAlignment.Start, + 0, + getContainerDimensionsAsDomRect(), + direction || LayoutDirection.Vertical, + true, + ), ]; base = highlights; From 0ce135cdf97b6da9dd1ad47301bce872ba9e2b76 Mon Sep 17 00:00:00 2001 From: Preet Date: Fri, 7 Oct 2022 11:21:56 -0400 Subject: [PATCH 132/708] fix improper drop position issue --- .../hooks/useAutoLayoutHighlights.ts | 46 +++++++--------- .../CanvasArenas/hooks/useCanvasDragging.ts | 2 - .../sagas/CanvasSagas/DraggingCanvasSagas.ts | 54 ++++++++++++++++--- 3 files changed, 68 insertions(+), 34 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index ad918a01301d..575245a12f41 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -13,14 +13,6 @@ interface XYCord { y: number; } -export interface Highlight { - x: number; - y: number; - height: number; - width: number; - alignment: FlexLayerAlignment; -} - export interface HighlightInfo { isNewLayer: boolean; // determines if a new layer / child has been added directly to the container. index: number; // index of the child in props.children. @@ -40,10 +32,8 @@ export interface AutoLayoutHighlightProps { isCurrentDraggedCanvas: boolean; isDragging: boolean; useAutoLayout?: boolean; - widgetName?: string; } -const BASE_OFFSET_SIZE = 100; const OFFSET_WIDTH = 4; export const useAutoLayoutHighlights = ({ @@ -168,13 +158,14 @@ export const useAutoLayoutHighlights = ({ if (!updateContainerDimensions()) return []; hideDraggedItems(draggedBlocks); + const canvasChildren = canvas.children || []; + const offsetChildren = canvasChildren.filter((each) => { + return draggedBlocks.indexOf(each) === -1; + }); + if (isVertical) { - highlights = generateContainerHighlights(layers, draggedBlocks); + highlights = generateContainerHighlights(layers, offsetChildren); } else { - const canvasChildren = canvas.children || []; - const offsetChildren = canvasChildren.filter((each) => { - return draggedBlocks.indexOf(each) === -1; - }); highlights = generateHighlightsForChildren(offsetChildren); } } @@ -226,18 +217,18 @@ export const useAutoLayoutHighlights = ({ return arr; } - function isEmptyLayer(layer: FlexLayer, draggedBlocks: string[]): boolean { - return ( - layer.children?.length === 0 || - !layer.children?.some((each) => { - return draggedBlocks.indexOf(each.id) === -1; - }) - ); + function filterLayer(layer: FlexLayer, offsetChildren: string[]): FlexLayer { + return { + ...layer, + children: layer.children?.filter((each) => { + return offsetChildren.indexOf(each.id) > -1; + }), + }; } function generateContainerHighlights( flexLayers: FlexLayer[], - draggedBlocks: string[], + offsetChildren: string[], ): HighlightInfo[] { if (!flexLayers || !flexLayers.length) return [ @@ -256,8 +247,10 @@ export const useAutoLayoutHighlights = ({ const arr: HighlightInfo[] = []; const rects: DOMRect[] = []; for (const layer of flexLayers) { - if (isEmptyLayer(layer, draggedBlocks)) { + const filteredLayer = filterLayer(layer, offsetChildren); + if (!filteredLayer?.children?.length) { discardedLayers += 1; + index += 1; continue; } const el = document.querySelector( @@ -265,6 +258,7 @@ export const useAutoLayoutHighlights = ({ ); if (!el) { discardedLayers += 1; + index += 1; continue; } const rect: DOMRect = el.getBoundingClientRect(); @@ -282,14 +276,14 @@ export const useAutoLayoutHighlights = ({ arr.push(info); arr.push( ...generateHighlightsForLayer( - layer, + filteredLayer, index - discardedLayers, rect, childCount, ), ); index += 1; - childCount += layer.children?.length || 0; + childCount += filteredLayer.children?.length || 0; } const lastElRect: DOMRect = rects[rects.length - 1]; diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index 85bc32e674be..e9c646471a0d 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -127,7 +127,6 @@ export const useCanvasDragging = ( isCurrentDraggedCanvas, isDragging, useAutoLayout, - widgetName, }); const highlights: HighlightInfo[] = calculateHighlights(); @@ -327,7 +326,6 @@ export const useCanvasDragging = ( y: currentRectanglesToDraw[0].left, }); if (dropInfo !== undefined && useAutoLayout) { - // cleanUpTempStyles(); updateChildrenPositions(dropInfo, currentRectanglesToDraw); } else onDrop(currentRectanglesToDraw, reflowedPositionsUpdatesWidgets); diff --git a/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts b/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts index 869a6aa481ef..6cf7098eb343 100644 --- a/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts +++ b/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts @@ -414,6 +414,7 @@ function* autolayoutReorderSaga( movedWidgets, parentId, } = actionPayload.payload; + const { alignment, index, isNewLayer, layerIndex } = dropPayload; // console.log(`#### moved widgets: ${JSON.stringify(movedWidgets)}`); // console.log(`#### parentId: ${parentId}`); @@ -471,7 +472,6 @@ function* reorderAutolayoutChildren(params: { if (orphans && orphans.length) { //parent has changed // console.log(`#### orphans ${JSON.stringify(orphans)}`); - // update parent for children orphans.forEach((item) => { const prevParentId = widgets[item].parentId; if (prevParentId !== undefined) { @@ -525,6 +525,7 @@ function* reorderAutolayoutChildren(params: { ...newItems.slice(pos), ], }; + return updatedWidgets; } @@ -550,16 +551,21 @@ function addNewLayer( // console.log("#### canvas id", parentId); // console.log("#### newLayer", newLayer); const flexLayers = canvas.flexLayers || []; + const updatedLayers = removeWidgetsFromCurrentLayers( + widgets, + movedWidgets, + flexLayers, + ); // console.log("#### filteredLayers", filteredLayers); - const pos = index > flexLayers.length ? flexLayers.length : index; + const pos = index > updatedLayers.length ? updatedLayers.length : index; // console.log("#### index", index); // console.log("#### pos", pos); const updatedCanvas = { ...canvas, flexLayers: [ - ...flexLayers.slice(0, pos), + ...updatedLayers.slice(0, pos), newLayer, - ...flexLayers.slice(pos), + ...updatedLayers.slice(pos), ], }; const updatedWidgets = { @@ -584,7 +590,13 @@ function updateExistingLayer( if (!canvas || !movedWidgets.length) return widgets; const layers = canvas.flexLayers || []; - const filteredLayers: FlexLayer[] = filterLayers(layers, movedWidgets); + const updatedLayers = removeWidgetsFromCurrentLayers( + allWidgets, + movedWidgets, + layers, + ); + // Remove empty layers + const filteredLayers: FlexLayer[] = filterLayers(updatedLayers, movedWidgets); const { hasFillChild, layerChildren } = processLayerChildren( movedWidgets, @@ -595,7 +607,7 @@ function updateExistingLayer( let selectedLayer: FlexLayer = filteredLayers[layerIndex]; let childCount = 0; filteredLayers.forEach((layer: FlexLayer, index: number) => { - if (index > layerIndex) return; + if (index >= layerIndex) return; childCount += layer.children.length; }); const pos = index - childCount; @@ -607,6 +619,7 @@ function updateExistingLayer( ...layerChildren, ...selectedLayer.children.slice(pos), ], + hasFillChild: hasFillChild || selectedLayer.hasFillChild, }; const updatedCanvas = { @@ -622,6 +635,35 @@ function updateExistingLayer( return updatedWidgets; } +function removeWidgetsFromCurrentLayers( + allWidgets: CanvasWidgetsReduxState, + movedWidgets: string[], + flexLayers: FlexLayer[], +): FlexLayer[] { + const updatedLayers = flexLayers.map((layer: FlexLayer) => { + const updatedChildren = layer.children.filter( + (child: LayerChild) => movedWidgets.indexOf(child.id) === -1, + ); + return { + ...layer, + children: updatedChildren, + hasFillChild: updatedChildren.some( + (child: LayerChild) => + allWidgets[child.id].responsiveBehavior === ResponsiveBehavior.Fill, + ), + }; + }); + return updatedLayers; +} + +/** + * Transform movedWidgets to FlexLayer format, + * and determine if the new widgets have a fill child. + * @param movedWidgets + * @param allWidgets + * @param alignment + * @returns hasFillChild: boolean, layerChildren: string[] + */ function processLayerChildren( movedWidgets: string[], allWidgets: CanvasWidgetsReduxState, From 622689e42cd800e3349c7e90cb00e98a5f21f684 Mon Sep 17 00:00:00 2001 From: Preet Date: Fri, 7 Oct 2022 12:39:02 -0400 Subject: [PATCH 133/708] clean up useAutoHighlights --- .../hooks/useAutoLayoutHighlights.ts | 143 +++++++----------- 1 file changed, 56 insertions(+), 87 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index 575245a12f41..38421e3de934 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -80,9 +80,6 @@ export const useAutoLayoutHighlights = ({ width: containerRect?.width, height: containerRect?.height, }; - // console.log( - // `#### container dimensions: ${JSON.stringify(containerDimensions)}`, - // ); return true; }; @@ -123,8 +120,6 @@ export const useAutoLayoutHighlights = ({ // Get a list of widgetIds that are being dragged. const getDraggedBlocks = (): string[] => { const blocks = blocksToDraw.map((block) => block.widgetId); - // console.log(`#### blocksToDraw: ${JSON.stringify(blocksToDraw)}`); - // console.log(`#### blocks: ${JSON.stringify(blocks)}`); return blocks; }; @@ -159,64 +154,29 @@ export const useAutoLayoutHighlights = ({ hideDraggedItems(draggedBlocks); const canvasChildren = canvas.children || []; + // Get the list of children that are not being dragged. const offsetChildren = canvasChildren.filter((each) => { return draggedBlocks.indexOf(each) === -1; }); if (isVertical) { - highlights = generateContainerHighlights(layers, offsetChildren); + highlights = calculateVerticalStackHighlights(layers, offsetChildren); } else { - highlights = generateHighlightsForChildren(offsetChildren); - } - } - // console.log("#### highlights: ", highlights); - return highlights; - }; - - function generateHighlightsForChildren(children: string[]): HighlightInfo[] { - if (!children || !children.length) - return [ - getInitialHighlight( + highlights = calculateRowHighlights( + offsetChildren, 0, - FlexLayerAlignment.Start, 0, + FlexLayerAlignment.Start, getContainerDimensionsAsDomRect(), - LayoutDirection.Horizontal, true, - ), - ]; - const arr: HighlightInfo[] = []; - const rects: DOMRect[] = []; - let index = 0; - for (const child of children) { - const el = getDomElement(child); - const childRect: DOMRect = el.getBoundingClientRect(); - rects.push(childRect); - arr.push({ - isNewLayer: true, - index, - posX: childRect.x - containerDimensions?.left, - posY: childRect.y - containerDimensions?.top, - width: OFFSET_WIDTH, - height: childRect.height, - alignment: FlexLayerAlignment.Start, - }); - index += 1; + ); + } } - // TODO: Add check for empty container. - const lastElRect: DOMRect = rects[rects.length - 1]; - arr.push({ - isNewLayer: true, - index: children.length, - posX: lastElRect?.x - containerDimensions?.left + lastElRect?.width, - posY: lastElRect?.y - containerDimensions?.top, - width: OFFSET_WIDTH, - height: lastElRect?.height, - alignment: FlexLayerAlignment.Start, - }); - return arr; - } + // console.log("#### highlights: ", highlights); + return highlights; + }; + // Remove dragged blocks from the list of children. function filterLayer(layer: FlexLayer, offsetChildren: string[]): FlexLayer { return { ...layer, @@ -226,10 +186,11 @@ export const useAutoLayoutHighlights = ({ }; } - function generateContainerHighlights( + function calculateVerticalStackHighlights( flexLayers: FlexLayer[], offsetChildren: string[], ): HighlightInfo[] { + // If container is empty, return a highlight for the first position. if (!flexLayers || !flexLayers.length) return [ getInitialHighlight( @@ -241,12 +202,15 @@ export const useAutoLayoutHighlights = ({ true, ), ]; + let childCount = 0; let discardedLayers = 0; let index = 0; const arr: HighlightInfo[] = []; const rects: DOMRect[] = []; + for (const layer of flexLayers) { + // remove dragged blocks from the layer const filteredLayer = filterLayer(layer, offsetChildren); if (!filteredLayer?.children?.length) { discardedLayers += 1; @@ -273,7 +237,9 @@ export const useAutoLayoutHighlights = ({ height: OFFSET_WIDTH, alignment: FlexLayerAlignment.Start, }; + // Add the horizontal highlight before the layer. arr.push(info); + // Add vertical highlights for each child in the layer. arr.push( ...generateHighlightsForLayer( filteredLayer, @@ -286,13 +252,14 @@ export const useAutoLayoutHighlights = ({ childCount += filteredLayer.children?.length || 0; } - const lastElRect: DOMRect = rects[rects.length - 1]; + // Add a highlight for the last position. + const lastRect: DOMRect = rects[rects.length - 1]; arr.push({ isNewLayer: true, index: childCount, layerIndex: rects.length, posX: 0, - posY: lastElRect?.y + lastElRect?.height - containerDimensions?.top, + posY: lastRect?.y + lastRect?.height - containerDimensions?.top, width: containerDimensions?.width, height: OFFSET_WIDTH, alignment: FlexLayerAlignment.Start, @@ -301,18 +268,19 @@ export const useAutoLayoutHighlights = ({ return arr; } + // Extract start, center and end children from the layer. function spreadLayer(layer: FlexLayer) { - const start: LayerChild[] = [], - center: LayerChild[] = [], - end: LayerChild[] = []; + const start: string[] = [], + center: string[] = [], + end: string[] = []; layer.children.forEach((child: LayerChild) => { if (layer.hasFillChild) { - start.push(child); + start.push(child.id); return; } - if (child.align === FlexLayerAlignment.End) end.push(child); - else if (child.align === FlexLayerAlignment.Center) center.push(child); - else start.push(child); + if (child.align === FlexLayerAlignment.End) end.push(child.id); + else if (child.align === FlexLayerAlignment.Center) center.push(child.id); + else start.push(child.id); }); return { start, center, end, hasFillChild: layer.hasFillChild }; } @@ -328,7 +296,7 @@ export const useAutoLayoutHighlights = ({ const { center, end, hasFillChild, start } = spreadLayer(layer); // process start sub wrapper. arr.push( - ...getLayerHighlights( + ...calculateRowHighlights( start, curr, layerIndex, @@ -340,7 +308,7 @@ export const useAutoLayoutHighlights = ({ // process center sub wrapper. curr += start.length; arr.push( - ...getLayerHighlights( + ...calculateRowHighlights( center, curr, layerIndex, @@ -351,7 +319,7 @@ export const useAutoLayoutHighlights = ({ // process end sub wrapper. curr += center.length; arr.push( - ...getLayerHighlights( + ...calculateRowHighlights( end, curr, layerIndex, @@ -363,31 +331,29 @@ export const useAutoLayoutHighlights = ({ return arr; } - function getLayerHighlights( - layer: LayerChild[], + function calculateRowHighlights( + children: string[], childCount: number, layerIndex: number, align: FlexLayerAlignment, layerRect: DOMRect, + isNewLayer = false, ): HighlightInfo[] { - const arr: HighlightInfo[] = []; - if (!layer.length) { - arr.push( + if (!children || !children.length) + return [ getInitialHighlight( childCount, align, layerIndex, layerRect, LayoutDirection.Horizontal, - false, + isNewLayer, ), - ); - return arr; - } - arr.push(...getHighlights(layer, childCount, align, layerIndex)); - return arr; + ]; + return getRowHighlights(children, align, layerIndex, childCount); } + // Initial highlight for an empty container or layer. function getInitialHighlight( childCount: number, alignment: FlexLayerAlignment, @@ -414,40 +380,43 @@ export const useAutoLayoutHighlights = ({ }; } - function getHighlights( - layer: LayerChild[], - childCount: number, - alignment: FlexLayerAlignment, - layerIndex: number, + function getRowHighlights( + children: string[], // Children of the row flex. + alignment: FlexLayerAlignment, // alignment for the highlights. + layerIndex: number, // index of the row flex. + childCount: number, // index of the first child. ): HighlightInfo[] { const arr: HighlightInfo[] = []; const childRects: DOMRect[] = []; - for (const child of layer) { - const el = getDomElement(child.id); + let index = childCount; + + for (const child of children) { + const el = getDomElement(child); if (!el) continue; const childRect: DOMRect = el?.getBoundingClientRect(); childRects.push(childRect); // A highlight before each existing child. arr.push({ isNewLayer: false, - index: childCount, + index, layerIndex, alignment, - posX: childRect?.left - containerDimensions?.left, + posX: childRect?.x - containerDimensions?.left, posY: childRect?.y - containerDimensions?.top, width: OFFSET_WIDTH, height: childRect?.height, }); - childCount += 1; + index += 1; } + // A highlight after the last child. const lastRect: DOMRect = childRects[childRects.length - 1]; arr.push({ isNewLayer: false, - index: childCount, + index, layerIndex, alignment, - posX: lastRect?.left + lastRect?.width - containerDimensions?.left, + posX: lastRect?.x + lastRect?.width - containerDimensions?.left, posY: lastRect?.y - containerDimensions?.top, width: OFFSET_WIDTH, height: lastRect?.height, From acb1ea92e05a9a4cd5a7a960bee86449cdb513a2 Mon Sep 17 00:00:00 2001 From: Preet Date: Fri, 7 Oct 2022 13:24:45 -0400 Subject: [PATCH 134/708] clean up dragging canvas sagas --- .../sagas/CanvasSagas/DraggingCanvasSagas.ts | 225 +++++++++--------- 1 file changed, 110 insertions(+), 115 deletions(-) diff --git a/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts b/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts index 6cf7098eb343..b7b0b53aedbe 100644 --- a/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts +++ b/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts @@ -10,7 +10,7 @@ import { } from "reducers/entityReducers/canvasWidgetsReducer"; import { all, call, put, select, takeLatest } from "redux-saga/effects"; import log from "loglevel"; -import { cloneDeep } from "lodash"; +import { cloneDeep, isArray } from "lodash"; import { updateAndSaveLayout, WidgetAddChild } from "actions/pageActions"; import { calculateDropTargetRows } from "components/editorComponents/DropTargetUtils"; import { @@ -416,8 +416,7 @@ function* autolayoutReorderSaga( } = actionPayload.payload; const { alignment, index, isNewLayer, layerIndex } = dropPayload; - // console.log(`#### moved widgets: ${JSON.stringify(movedWidgets)}`); - // console.log(`#### parentId: ${parentId}`); + try { const allWidgets: CanvasWidgetsReduxState = yield select(getWidgets); if (!parentId || !movedWidgets || !movedWidgets.length) return; @@ -465,53 +464,45 @@ function* reorderAutolayoutChildren(params: { const widgets = Object.assign({}, allWidgets); if (!movedWidgets) return widgets; const selectedWidgets = [...movedWidgets]; - // Check if parent has changed - const orphans = selectedWidgets.filter( - (item) => widgets[item].parentId !== parentId, + + let updatedWidgets: CanvasWidgetsReduxState = updateRelationships( + selectedWidgets, + widgets, + parentId, ); - if (orphans && orphans.length) { - //parent has changed - // console.log(`#### orphans ${JSON.stringify(orphans)}`); - orphans.forEach((item) => { - const prevParentId = widgets[item].parentId; - if (prevParentId !== undefined) { - // console.log(`#### previous parent: ${prevParentId}`); - const prevParent = Object.assign({}, widgets[prevParentId]); - if (prevParent.children && Array.isArray(prevParent.children)) { - const updatedPrevParent = { - ...prevParent, - children: prevParent.children.filter((each) => each !== item), - }; - widgets[prevParentId] = updatedPrevParent; - } - } - widgets[item] = { - ...widgets[item], - parentId: parentId, - }; - }); - } - let updatedWidgets: CanvasWidgetsReduxState = Object.assign({}, widgets); + + // Update flexLayers for a vertical stack. if (direction === LayoutDirection.Vertical) { + const canvas = widgets[parentId]; + if (!canvas) return widgets; + const flexLayers = canvas.flexLayers || []; + // Remove moved widgets from the flex layers. + const filteredLayers = removeWidgetsFromCurrentLayers( + widgets, + selectedWidgets, + flexLayers, + ); + // Create a temporary layer from moved widgets. + const newLayer: FlexLayer = createFlexLayer( + selectedWidgets, + widgets, + alignment, + ); + + // Add the new layer to the flex layers. updatedWidgets = isNewLayer - ? addNewLayer( - selectedWidgets, - widgets, - parentId, - alignment, - isNewLayer, - index, - ) + ? addNewLayer(newLayer, widgets, parentId, filteredLayers, layerIndex) : updateExistingLayer( - selectedWidgets, + newLayer, widgets, parentId, - alignment, + filteredLayers, index, - layerIndex || 0, + layerIndex, ); } + // update children of the parent canvas. const items = [...(widgets[parentId].children || [])]; // remove moved widgets from children const newItems = items.filter((item) => movedWidgets.indexOf(item) === -1); @@ -529,105 +520,113 @@ function* reorderAutolayoutChildren(params: { return updatedWidgets; } -function addNewLayer( +/** + * For all moved widgets, + * delete relationship with previous parent and + * add relationship with new parent + * @param movedWidgets + * @param widgets + * @param parentId + * @returns widgets + */ + +function updateRelationships( movedWidgets: string[], + widgets: CanvasWidgetsReduxState, + parentId: string, +): CanvasWidgetsReduxState { + // Check if parent has changed + const orphans = movedWidgets.filter( + (item) => widgets[item].parentId !== parentId, + ); + if (orphans && orphans.length) { + //parent has changed + orphans.forEach((item) => { + // remove from previous parent + const prevParentId = widgets[item].parentId; + if (prevParentId !== undefined) { + const prevParent = Object.assign({}, widgets[prevParentId]); + if (prevParent.children && isArray(prevParent.children)) { + const updatedPrevParent = { + ...prevParent, + children: prevParent.children.filter((each) => each !== item), + }; + widgets[prevParentId] = updatedPrevParent; + } + } + + // add to new parent + widgets[item] = { + ...widgets[item], + parentId: parentId, + }; + }); + } + return widgets; +} + +function addNewLayer( + newLayer: FlexLayer, allWidgets: CanvasWidgetsReduxState, parentId: string, - alignment: FlexLayerAlignment, - isNewLayer: boolean, - index: number, -) { + layers: FlexLayer[], + layerIndex = 0, +): CanvasWidgetsReduxState { const widgets: CanvasWidgetsReduxState = Object.assign({}, allWidgets); const canvas = widgets[parentId]; - if (!canvas) return widgets; - const { hasFillChild, layerChildren } = processLayerChildren( - movedWidgets, - allWidgets, - alignment, - ); + const pos = layerIndex > layers.length ? layers.length : layerIndex; - const newLayer: FlexLayer = { children: layerChildren, hasFillChild }; - // console.log("#### canvas id", parentId); - // console.log("#### newLayer", newLayer); - const flexLayers = canvas.flexLayers || []; - const updatedLayers = removeWidgetsFromCurrentLayers( - widgets, - movedWidgets, - flexLayers, - ); - // console.log("#### filteredLayers", filteredLayers); - const pos = index > updatedLayers.length ? updatedLayers.length : index; - // console.log("#### index", index); - // console.log("#### pos", pos); const updatedCanvas = { ...canvas, - flexLayers: [ - ...updatedLayers.slice(0, pos), - newLayer, - ...updatedLayers.slice(pos), - ], + flexLayers: [...layers.slice(0, pos), newLayer, ...layers.slice(pos)], }; const updatedWidgets = { ...widgets, [parentId]: updatedCanvas, }; - // console.log("#### updated flex layers", updatedCanvas.flexLayers); - // console.log("#### widgets", updatedWidgets); + return updatedWidgets; } function updateExistingLayer( - movedWidgets: string[], + newLayer: FlexLayer, allWidgets: CanvasWidgetsReduxState, parentId: string, - alignment: FlexLayerAlignment, + layers: FlexLayer[], index: number, - layerIndex: number, + layerIndex = 0, ): CanvasWidgetsReduxState { const widgets: CanvasWidgetsReduxState = Object.assign({}, allWidgets); const canvas = widgets[parentId]; - if (!canvas || !movedWidgets.length) return widgets; - - const layers = canvas.flexLayers || []; - const updatedLayers = removeWidgetsFromCurrentLayers( - allWidgets, - movedWidgets, - layers, - ); - // Remove empty layers - const filteredLayers: FlexLayer[] = filterLayers(updatedLayers, movedWidgets); - - const { hasFillChild, layerChildren } = processLayerChildren( - movedWidgets, - allWidgets, - alignment, - ); + if (!canvas || !newLayer) return widgets; - let selectedLayer: FlexLayer = filteredLayers[layerIndex]; + let selectedLayer: FlexLayer = layers[layerIndex]; + // Calculate the number of children in the container before the selected layer. let childCount = 0; - filteredLayers.forEach((layer: FlexLayer, index: number) => { + layers.forEach((layer: FlexLayer, index: number) => { if (index >= layerIndex) return; childCount += layer.children.length; }); const pos = index - childCount; + // merge the selected layer with the new layer. selectedLayer = { ...selectedLayer, children: [ ...selectedLayer.children.slice(0, pos), - ...layerChildren, + ...newLayer.children, ...selectedLayer.children.slice(pos), ], - hasFillChild: hasFillChild || selectedLayer.hasFillChild, + hasFillChild: newLayer.hasFillChild || selectedLayer.hasFillChild, }; const updatedCanvas = { ...canvas, flexLayers: [ - ...filteredLayers.slice(0, layerIndex), + ...layers.slice(0, layerIndex), selectedLayer, - ...filteredLayers.slice(layerIndex + 1), + ...layers.slice(layerIndex + 1), ], }; @@ -635,6 +634,15 @@ function updateExistingLayer( return updatedWidgets; } +/** + * Remove moved widgets from current layers. + * and update hasFillChild property. + * Return non-empty layers. + * @param allWidgets + * @param movedWidgets + * @param flexLayers + * @returns FlexLayer[] + */ function removeWidgetsFromCurrentLayers( allWidgets: CanvasWidgetsReduxState, movedWidgets: string[], @@ -653,7 +661,7 @@ function removeWidgetsFromCurrentLayers( ), }; }); - return updatedLayers; + return updatedLayers.filter((layer: FlexLayer) => layer.children.length); } /** @@ -664,36 +672,23 @@ function removeWidgetsFromCurrentLayers( * @param alignment * @returns hasFillChild: boolean, layerChildren: string[] */ -function processLayerChildren( +function createFlexLayer( movedWidgets: string[], allWidgets: CanvasWidgetsReduxState, alignment: FlexLayerAlignment, -): { hasFillChild: boolean; layerChildren: LayerChild[] } { +): FlexLayer { let hasFillChild = false; - const layerChildren = []; + const children = []; if (movedWidgets && movedWidgets.length) { for (const id of movedWidgets) { const widget = allWidgets[id]; if (!widget) continue; if (widget.responsiveBehavior === ResponsiveBehavior.Fill) hasFillChild = true; - layerChildren.push({ id, align: alignment }); + children.push({ id, align: alignment }); } } - return { layerChildren, hasFillChild }; -} - -function filterLayers( - layers: FlexLayer[], - movedWidgets: string[], -): FlexLayer[] { - const filteredLayers = layers.filter((layer) => { - const children = layer.children.filter((child) => { - return movedWidgets.indexOf(child.id) === -1; - }); - return children.length > 0; - }); - return filteredLayers; + return { children, hasFillChild }; } function* addWidgetAndReorderSaga( From a5cde1f53a23233d2b24e3f18fab91145f5184b2 Mon Sep 17 00:00:00 2001 From: Preet Date: Fri, 7 Oct 2022 15:57:15 -0400 Subject: [PATCH 135/708] minor updates --- .../hooks/useAutoLayoutHighlights.ts | 20 ++++++++++++++----- .../CanvasArenas/hooks/useCanvasDragging.ts | 1 - .../sagas/CanvasSagas/DraggingCanvasSagas.ts | 4 ++-- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index 38421e3de934..909056d9c731 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -1,7 +1,11 @@ import { getWidgets } from "sagas/selectors"; import { useSelector } from "store"; -import { LayoutDirection, FlexLayerAlignment } from "components/constants"; +import { + LayoutDirection, + FlexLayerAlignment, + ResponsiveBehavior, +} from "components/constants"; import { WidgetDraggingBlock } from "./useBlocksToBeDraggedOnCanvas"; import { FlexLayer, @@ -176,13 +180,18 @@ export const useAutoLayoutHighlights = ({ return highlights; }; - // Remove dragged blocks from the list of children. + // Remove dragged blocks from the list of children and update hasChild. function filterLayer(layer: FlexLayer, offsetChildren: string[]): FlexLayer { + const filteredChildren = layer.children?.filter( + (child: LayerChild) => offsetChildren.indexOf(child.id) !== -1, + ); return { ...layer, - children: layer.children?.filter((each) => { - return offsetChildren.indexOf(each.id) > -1; - }), + children: filteredChildren, + hasFillChild: filteredChildren?.some( + (each) => + allWidgets[each.id]?.responsiveBehavior === ResponsiveBehavior.Fill, + ), }; } @@ -294,6 +303,7 @@ export const useAutoLayoutHighlights = ({ const arr: HighlightInfo[] = []; let curr: number = childCount; const { center, end, hasFillChild, start } = spreadLayer(layer); + // process start sub wrapper. arr.push( ...calculateRowHighlights( diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index e9c646471a0d..8cf7b77c3705 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -63,7 +63,6 @@ export const useCanvasDragging = ( snapRowSpace, useAutoLayout, widgetId, - widgetName, }: CanvasDraggingArenaProps, ) => { const canvasZoomLevel = useSelector(getZoomLevel); diff --git a/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts b/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts index b7b0b53aedbe..4725117a8b8f 100644 --- a/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts +++ b/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts @@ -398,7 +398,7 @@ function moveWidget(widgetMoveParams: WidgetMoveParams) { return widgets; } -function* autolayoutReorderSaga( +function* autoLayoutReorderSaga( actionPayload: ReduxAction<{ movedWidgets: string[]; parentId: string; @@ -740,7 +740,7 @@ export default function* draggingCanvasSagas() { ), takeLatest( ReduxActionTypes.AUTOLAYOUT_REORDER_WIDGETS, - autolayoutReorderSaga, + autoLayoutReorderSaga, ), takeLatest( ReduxActionTypes.AUTOLAYOUT_ADD_NEW_WIDGETS, From 7fa44f4e0955011159766d5c233330c1256e9172 Mon Sep 17 00:00:00 2001 From: Preet Date: Sat, 8 Oct 2022 12:44:39 -0400 Subject: [PATCH 136/708] use move direction to calculate highlight --- .../hooks/useAutoLayoutHighlights.ts | 34 ++++++++++++++++--- .../CanvasArenas/hooks/useCanvasDragging.ts | 4 ++- .../widgets/ContainerWidget/widget/index.tsx | 5 --- 3 files changed, 32 insertions(+), 11 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index 909056d9c731..03de35c23c48 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -11,6 +11,7 @@ import { FlexLayer, LayerChild, } from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; +import { ReflowDirection } from "reflow/reflowTypes"; interface XYCord { x: number; @@ -26,6 +27,7 @@ export interface HighlightInfo { posY: number; // y position of the highlight. width: number; // width of the highlight. height: number; // height of the highlight. + isVertical: boolean; // determines if the highlight is vertical or horizontal. } export interface AutoLayoutHighlightProps { @@ -245,6 +247,7 @@ export const useAutoLayoutHighlights = ({ width: containerDimensions?.width, height: OFFSET_WIDTH, alignment: FlexLayerAlignment.Start, + isVertical: false, }; // Add the horizontal highlight before the layer. arr.push(info); @@ -272,6 +275,7 @@ export const useAutoLayoutHighlights = ({ width: containerDimensions?.width, height: OFFSET_WIDTH, alignment: FlexLayerAlignment.Start, + isVertical: false, }); return arr; @@ -387,6 +391,7 @@ export const useAutoLayoutHighlights = ({ posY: rect.y - containerDimensions?.top, width: verticalFlex ? rect?.width : OFFSET_WIDTH, height: verticalFlex ? OFFSET_WIDTH : rect.height, + isVertical: !verticalFlex, }; } @@ -415,6 +420,7 @@ export const useAutoLayoutHighlights = ({ posY: childRect?.y - containerDimensions?.top, width: OFFSET_WIDTH, height: childRect?.height, + isVertical: true, }); index += 1; } @@ -430,6 +436,7 @@ export const useAutoLayoutHighlights = ({ posY: lastRect?.y - containerDimensions?.top, width: OFFSET_WIDTH, height: lastRect?.height, + isVertical: true, }); return arr; } @@ -438,9 +445,12 @@ export const useAutoLayoutHighlights = ({ * END AUTO LAYOUT OFFSET CALCULATION */ - const highlightDropPosition = (e: any) => { + const highlightDropPosition = (e: any, moveDirection: ReflowDirection) => { if (!useAutoLayout) return; - const pos: HighlightInfo | undefined = getHighlightPosition(e); + const pos: HighlightInfo | undefined = getHighlightPosition( + e, + moveDirection, + ); if (!pos) return; lastActiveHighlight = pos; @@ -460,7 +470,11 @@ export const useAutoLayoutHighlights = ({ } }; - const getHighlightPosition = (e: any, val?: XYCord): HighlightInfo => { + const getHighlightPosition = ( + e: any, + moveDirection?: ReflowDirection, + val?: XYCord, + ): HighlightInfo => { let base: HighlightInfo[] = []; if (!highlights || !highlights.length) highlights = [ @@ -480,7 +494,17 @@ export const useAutoLayoutHighlights = ({ y: e?.offsetY || val?.y, }; - const arr = [...base].sort((a, b) => { + let filteredHighlights: HighlightInfo[] = base; + if (moveDirection) { + const isVerticalDrag = + moveDirection === ReflowDirection.TOP || + moveDirection === ReflowDirection.BOTTOM; + filteredHighlights = base.filter((highlight: HighlightInfo) => + isVerticalDrag ? !highlight.isVertical : highlight.isVertical, + ); + } + + const arr = [...filteredHighlights].sort((a, b) => { return calculateDistance(a, pos) - calculateDistance(b, pos); }); return arr[0]; @@ -495,7 +519,7 @@ export const useAutoLayoutHighlights = ({ const getDropInfo = (val: XYCord): HighlightInfo | undefined => { if (lastActiveHighlight) return lastActiveHighlight; - const pos = getHighlightPosition(null, val); + const pos = getHighlightPosition(null, undefined, val); if (!pos) return; lastActiveHighlight = pos; return pos; diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index 8cf7b77c3705..79445ab6e43c 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -577,10 +577,12 @@ export const useCanvasDragging = ( canScroll.current = false; renderNewRows(delta); } else if (!isUpdatingRows) { + const dir: ReflowDirection = getMouseMoveDirection(e); triggerReflow(e, firstMove); isCurrentDraggedCanvas && highlights.length && - highlightDropPosition(e); + dir !== ReflowDirection.UNSET && + highlightDropPosition(e, dir); renderBlocks(); } scrollObj.lastMouseMoveEvent = { diff --git a/app/client/src/widgets/ContainerWidget/widget/index.tsx b/app/client/src/widgets/ContainerWidget/widget/index.tsx index 0743b9d6755f..d6dc320e643f 100644 --- a/app/client/src/widgets/ContainerWidget/widget/index.tsx +++ b/app/client/src/widgets/ContainerWidget/widget/index.tsx @@ -286,7 +286,6 @@ export class ContainerWidget extends BaseWidget< componentDidMount(): void { super.componentDidMount(); this.updatePositioningInformation(); - this.checkIsMobile(); } componentDidUpdate(prevProps: ContainerWidgetProps): void { @@ -297,10 +296,6 @@ export class ContainerWidget extends BaseWidget< } } - checkIsMobile = (): void => { - if (window.innerWidth < 767) this.setState({ isMobile: true }); - }; - updatePositioningInformation = (): void => { if (!this.props.positioning || this.props.positioning === Positioning.Fixed) this.setState({ useAutoLayout: false }); From ce612cac9c6633be92cd324b124875738b1aef1d Mon Sep 17 00:00:00 2001 From: Preet Date: Sun, 9 Oct 2022 20:12:14 -0400 Subject: [PATCH 137/708] change delta for move direction --- .../CanvasArenas/hooks/useCanvasDragging.ts | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index 79445ab6e43c..00c911336d48 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -397,7 +397,7 @@ export const useCanvasDragging = ( prevSpeed < CONTAINER_JUMP_SPEED_THRESHOLD ); }; - const getMouseMoveDirection = (event: any) => { + const getMouseMoveDirection = (event: any, minDelta = 0) => { if (lastMousePosition) { const deltaX = lastMousePosition.x - event.clientX, deltaY = lastMousePosition.y - event.clientY; @@ -411,9 +411,12 @@ export const useCanvasDragging = ( ) { return currentDirection.current; } - if (Math.abs(deltaY) > Math.abs(deltaX) && deltaY > 0) { + if (Math.abs(deltaY) > Math.abs(deltaX) && deltaY > minDelta) { return ReflowDirection.TOP; - } else if (Math.abs(deltaY) > Math.abs(deltaX) && deltaY < 0) { + } else if ( + Math.abs(deltaY) > Math.abs(deltaX) && + deltaY < -minDelta + ) { return ReflowDirection.BOTTOM; } if ( @@ -422,9 +425,12 @@ export const useCanvasDragging = ( ) { return currentDirection.current; } - if (Math.abs(deltaX) > Math.abs(deltaY) && deltaX > 0) { + if (Math.abs(deltaX) > Math.abs(deltaY) && deltaX > minDelta) { return ReflowDirection.LEFT; - } else if (Math.abs(deltaX) > Math.abs(deltaY) && deltaX < 0) { + } else if ( + Math.abs(deltaX) > Math.abs(deltaY) && + deltaX < -minDelta + ) { return ReflowDirection.RIGHT; } } @@ -577,7 +583,7 @@ export const useCanvasDragging = ( canScroll.current = false; renderNewRows(delta); } else if (!isUpdatingRows) { - const dir: ReflowDirection = getMouseMoveDirection(e); + const dir: ReflowDirection = getMouseMoveDirection(e, 3); triggerReflow(e, firstMove); isCurrentDraggedCanvas && highlights.length && From 3e758c18ae94b7d135268ce96b342cfc9b2bcb04 Mon Sep 17 00:00:00 2001 From: Preet Date: Sun, 9 Oct 2022 21:23:30 -0400 Subject: [PATCH 138/708] change min delta --- .../src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index 00c911336d48..6e5ac4096eb4 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -583,7 +583,7 @@ export const useCanvasDragging = ( canScroll.current = false; renderNewRows(delta); } else if (!isUpdatingRows) { - const dir: ReflowDirection = getMouseMoveDirection(e, 3); + const dir: ReflowDirection = getMouseMoveDirection(e, 2); triggerReflow(e, firstMove); isCurrentDraggedCanvas && highlights.length && From 9fe892a3ccfe360dfd128e2987e5d6ef68d9c142 Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 10 Oct 2022 13:06:54 -0400 Subject: [PATCH 139/708] improve highlight selection logic --- .../hooks/useAutoLayoutHighlights.ts | 23 +++++++++++++++---- .../CanvasArenas/hooks/useCanvasDragging.ts | 2 +- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index 03de35c23c48..db7f338fb088 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -495,13 +495,28 @@ export const useAutoLayoutHighlights = ({ }; let filteredHighlights: HighlightInfo[] = base; - if (moveDirection) { + // For vertical stacks, filter out the highlights based on drag direction and y position. + if (moveDirection && direction === LayoutDirection.Vertical) { const isVerticalDrag = moveDirection === ReflowDirection.TOP || moveDirection === ReflowDirection.BOTTOM; - filteredHighlights = base.filter((highlight: HighlightInfo) => - isVerticalDrag ? !highlight.isVertical : highlight.isVertical, - ); + + filteredHighlights = base.filter((highlight: HighlightInfo) => { + // Return only horizontal highlights for vertical drag. + if (isVerticalDrag) return !highlight.isVertical; + // Return only vertical highlights for horizontal drag, if they lie in the same x plane. + return ( + highlight.isVertical && + (pos.y >= highlight.posY || + pos.y <= highlight.posY + highlight.height) + ); + }); + // Additional redundancy check. + // For horizontal drag, if no vertical highlight exists in the same x plane, return the nearest horizontal highlight. + if (!isVerticalDrag && !filteredHighlights.length) + filteredHighlights = base.filter( + (highlight: HighlightInfo) => !highlight.isVertical, + ); } const arr = [...filteredHighlights].sort((a, b) => { diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index 6e5ac4096eb4..409b10412e91 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -583,7 +583,7 @@ export const useCanvasDragging = ( canScroll.current = false; renderNewRows(delta); } else if (!isUpdatingRows) { - const dir: ReflowDirection = getMouseMoveDirection(e, 2); + const dir: ReflowDirection = getMouseMoveDirection(e, 1); triggerReflow(e, firstMove); isCurrentDraggedCanvas && highlights.length && From 22084443b70ebc681f6017e34e30dd0f7196e30a Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 10 Oct 2022 13:42:28 -0400 Subject: [PATCH 140/708] update flex layers when dragged out of flex container --- .../sagas/CanvasSagas/DraggingCanvasSagas.ts | 53 ++++++++++++++----- 1 file changed, 39 insertions(+), 14 deletions(-) diff --git a/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts b/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts index 4725117a8b8f..4ce95b28397c 100644 --- a/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts +++ b/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts @@ -305,10 +305,27 @@ function* moveWidgetsSaga( ) { throw Error; } + // TODO: Put this piece of code in a proper place. + const prevParentId = + draggedBlocksToUpdate[0].updateWidgetParams.payload.parentId; + const prevParent = updatedWidgetsOnMove[prevParentId]; + + const updatedWidgets = { + ...updatedWidgetsOnMove, + [prevParent.widgetId]: { + ...prevParent, + flexLayers: removeWidgetsFromCurrentLayers( + updatedWidgetsOnMove, + draggedBlocksToUpdate.map((each) => each.widgetId), + prevParent.flexLayers, + ), + }, + }; - yield put(updateAndSaveLayout(updatedWidgetsOnMove)); + yield put(updateAndSaveLayout(updatedWidgets)); const block = draggedBlocksToUpdate[0]; + const oldParentId = block.updateWidgetParams.payload.parentId; const newParentId = block.updateWidgetParams.payload.newParentId; @@ -550,6 +567,11 @@ function updateRelationships( const updatedPrevParent = { ...prevParent, children: prevParent.children.filter((each) => each !== item), + flexLayers: removeWidgetsFromCurrentLayers( + widgets, + movedWidgets, + prevParent.flexLayers, + ), }; widgets[prevParentId] = updatedPrevParent; } @@ -648,20 +670,23 @@ function removeWidgetsFromCurrentLayers( movedWidgets: string[], flexLayers: FlexLayer[], ): FlexLayer[] { - const updatedLayers = flexLayers.map((layer: FlexLayer) => { - const updatedChildren = layer.children.filter( - (child: LayerChild) => movedWidgets.indexOf(child.id) === -1, + if (!flexLayers || !flexLayers.length) return []; + return flexLayers?.reduce((acc: FlexLayer[], layer: FlexLayer) => { + const children = layer.children.filter( + (each: LayerChild) => movedWidgets.indexOf(each.id) === -1, ); - return { - ...layer, - children: updatedChildren, - hasFillChild: updatedChildren.some( - (child: LayerChild) => - allWidgets[child.id].responsiveBehavior === ResponsiveBehavior.Fill, - ), - }; - }); - return updatedLayers.filter((layer: FlexLayer) => layer.children.length); + if (children.length) { + acc.push({ + ...layer, + children, + hasFillChild: children.some( + (each: LayerChild) => + allWidgets[each.id].responsiveBehavior === ResponsiveBehavior.Fill, + ), + }); + } + return acc; + }, []); } /** From 4d0f76db94ebc8625d1c5f2eb102faac5a480620 Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 10 Oct 2022 17:31:43 -0400 Subject: [PATCH 141/708] fix ui flicker issue --- .../CanvasArenas/hooks/useCanvasDragging.ts | 34 ++++++++++++++----- app/client/src/utils/hooks/useTrace.tsx | 17 ++++++++++ app/client/src/widgets/CanvasWidget.tsx | 1 + 3 files changed, 43 insertions(+), 9 deletions(-) create mode 100644 app/client/src/utils/hooks/useTrace.tsx diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index 409b10412e91..5e35be3d1d38 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -333,16 +333,32 @@ export const useCanvasDragging = ( startPoints.left = defaultHandlePositions.left; resetCanvasState(); - if (isCurrentDraggedCanvas) { - if (isNewWidget) { - setDraggingNewWidget(false, undefined); - } else { - setDraggingState({ - isDragging: false, - }); + // if (isCurrentDraggedCanvas) { + // if (isNewWidget) { + // setDraggingNewWidget(false, undefined); + // } else { + // setDraggingState({ + // isDragging: false, + // }); + // } + // setDraggingCanvas(); + // } + resetDragging(); + }; + + const resetDragging = () => { + setTimeout(() => { + if (isCurrentDraggedCanvas) { + if (isNewWidget) { + setDraggingNewWidget(false, undefined); + } else { + setDraggingState({ + isDragging: false, + }); + } + setDraggingCanvas(); } - setDraggingCanvas(); - } + }, 0); }; const onFirstMoveOnCanvas = (e: any, over = false) => { diff --git a/app/client/src/utils/hooks/useTrace.tsx b/app/client/src/utils/hooks/useTrace.tsx new file mode 100644 index 000000000000..3b3267f7173d --- /dev/null +++ b/app/client/src/utils/hooks/useTrace.tsx @@ -0,0 +1,17 @@ +import { useEffect, useRef } from "react"; + +export function useTraceUpdate(props: any, name?: string) { + const prev = useRef(props); + useEffect(() => { + const changedProps = Object.entries(props).reduce((ps: any, [k, v]) => { + if (prev.current[k] !== v) { + ps[k] = [prev.current[k], v]; + } + return ps; + }, {}); + if (Object.keys(changedProps).length > 0) { + console.log("#### Changed props:", props.widgetId, name, changedProps); + } + prev.current = props; + }); +} diff --git a/app/client/src/widgets/CanvasWidget.tsx b/app/client/src/widgets/CanvasWidget.tsx index cda211ee4779..39387d40f7d6 100644 --- a/app/client/src/widgets/CanvasWidget.tsx +++ b/app/client/src/widgets/CanvasWidget.tsx @@ -206,6 +206,7 @@ export const CONFIG = { widgetName: "Canvas", version: 1, detachFromLayout: true, + flexLayers: [], }, properties: { derived: CanvasWidget.getDerivedPropertiesMap(), From d665d17c778e6a4afe803addd0b6d349e5b628b0 Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 10 Oct 2022 17:34:58 -0400 Subject: [PATCH 142/708] clean up --- .../common/CanvasArenas/hooks/useCanvasDragging.ts | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index 5e35be3d1d38..c0f5ec17cdd0 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -333,16 +333,6 @@ export const useCanvasDragging = ( startPoints.left = defaultHandlePositions.left; resetCanvasState(); - // if (isCurrentDraggedCanvas) { - // if (isNewWidget) { - // setDraggingNewWidget(false, undefined); - // } else { - // setDraggingState({ - // isDragging: false, - // }); - // } - // setDraggingCanvas(); - // } resetDragging(); }; From 2e6a49292d70c300788c67778ac23409c23043f7 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Tue, 11 Oct 2022 20:31:07 +0530 Subject: [PATCH 143/708] Adding Drag block preview for autolayout. --- .../src/ce/constants/ReduxActionConstants.tsx | 15 ++-- .../appsmith/autoLayout/FlexBoxComponent.tsx | 80 +++++++++++++++++-- .../appsmith/autoLayout/FlexComponent.tsx | 6 +- .../editorComponents/ResizableComponent.tsx | 79 +++++++++--------- .../hooks/useAutoLayoutHighlights.ts | 17 +++- .../hooks/useBlocksToBeDraggedOnCanvas.ts | 42 +++++----- .../CanvasArenas/hooks/useCanvasDragging.ts | 22 +++-- .../reducers/uiReducers/dragResizeReducer.ts | 15 +++- app/client/src/selectors/editorSelectors.tsx | 28 +++---- 9 files changed, 203 insertions(+), 101 deletions(-) diff --git a/app/client/src/ce/constants/ReduxActionConstants.tsx b/app/client/src/ce/constants/ReduxActionConstants.tsx index f2a501eee334..ed0c2e93edef 100644 --- a/app/client/src/ce/constants/ReduxActionConstants.tsx +++ b/app/client/src/ce/constants/ReduxActionConstants.tsx @@ -1,16 +1,16 @@ -import { WidgetCardProps, WidgetProps } from "widgets/BaseWidget"; +import { ERROR_CODES } from "@appsmith/constants/ApiConstants"; +import { ApplicationVersion } from "actions/applicationActions"; +import { + ApplicationPagePayload, + GitApplicationMetadata, +} from "api/ApplicationApi"; import { LayoutOnLoadActionErrors, PageAction, } from "constants/AppsmithActionConstants/ActionConstants"; import { Workspace } from "constants/workspaceConstants"; -import { ERROR_CODES } from "@appsmith/constants/ApiConstants"; import { AppLayoutConfig } from "reducers/entityReducers/pageListReducer"; -import { - ApplicationPagePayload, - GitApplicationMetadata, -} from "api/ApplicationApi"; -import { ApplicationVersion } from "actions/applicationActions"; +import { WidgetCardProps, WidgetProps } from "widgets/BaseWidget"; export const ReduxSagaChannels = { WEBSOCKET_APP_LEVEL_WRITE_CHANNEL: "WEBSOCKET_APP_LEVEL_WRITE_CHANNEL", @@ -18,6 +18,7 @@ export const ReduxSagaChannels = { }; export const ReduxActionTypes = { + SET_AUTOLAYOUT_HIGHLIGHTS: "SET_AUTOLAYOUT_HIGHLIGHTS", GIT_DISCARD_CHANGES_SUCCESS: "GIT_DISCARD_CHANGES_SUCCESS", GIT_DISCARD_CHANGES: "GIT_DISCARD_CHANGES", DELETE_BRANCH_INIT: "DELETE_BRANCH_INIT", diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx index a2902c0c27b4..d607d3351751 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx @@ -1,7 +1,8 @@ +import { isArray } from "lodash"; import React, { ReactNode, useMemo } from "react"; import styled from "styled-components"; -import { isArray } from "lodash"; +import { AppState } from "ce/reducers"; import { AlignItems, Alignment, @@ -12,6 +13,7 @@ import { Overflow, Spacing, } from "components/constants"; +import { useSelector } from "react-redux"; import { getLayoutProperties } from "utils/layoutPropertiesUtils"; import AutoLayoutLayer from "./AutoLayoutLayer"; @@ -68,6 +70,28 @@ function FlexBoxComponent(props: FlexBoxProps) { () => getLayoutProperties(props.direction, props.alignment, props.spacing), [props.direction, props.alignment, props.spacing], ); + const { autoLayoutDragDetails, dragDetails, flexHighlight } = useSelector( + (state: AppState) => state.ui.widgetDragResize, + ); + + const getPreviewNode = () => { + return React.createElement(() => { + const height = autoLayoutDragDetails + ? autoLayoutDragDetails[0].height + : 0; + const width = autoLayoutDragDetails ? autoLayoutDragDetails[0].width : 0; + + return ( +
+ ); + }); + }; const renderChildren = () => { if (!props.children) return null; @@ -75,8 +99,21 @@ function FlexBoxComponent(props: FlexBoxProps) { !props.useAutoLayout || direction === LayoutDirection.Horizontal || !(props.flexLayers && props.flexLayers.length) - ) - return props.children; + ) { + if (flexHighlight && dragDetails.draggedOn === props.widgetId) { + const previewNode = getPreviewNode(); + const totalChildren = (props.children as any).length; + const allChildren = [ + ...(props.children as any).slice(0, flexHighlight?.index), + previewNode, + ...(props.children as any).slice(flexHighlight?.index, totalChildren), + ]; + return allChildren; + } else { + return props.children; + } + } + /** * Wrap children of a Vertical Stack in a flex layer. */ @@ -86,13 +123,46 @@ function FlexBoxComponent(props: FlexBoxProps) { map[(child as JSX.Element).props?.widgetId] = child; } } - return props.flexLayers.map((layer: FlexLayer, index: number) => { + + const layers = + flexHighlight?.isNewLayer && flexHighlight?.layerIndex + ? [ + ...props.flexLayers.slice(0, flexHighlight?.index), + { children: [], hasFillChild: false }, + ...props.flexLayers.slice( + flexHighlight?.index, + props.flexLayers.length, + ), + ] + : props.flexLayers; + let childCount = 0; + return layers.map((layer: FlexLayer, index: number) => { const { children, hasFillChild } = layer; - if (!children || !children.length) return null; const start = [], center = [], end = []; + if (!children || !children.length) { + const previewNode = getPreviewNode(); + start.push(previewNode); + } + for (const child of children) { + if ( + flexHighlight && + index === flexHighlight.layerIndex && + childCount === flexHighlight.index && + dragDetails.draggedOn === props.widgetId + ) { + const previewNode = getPreviewNode(); + if (flexHighlight.alignment === "start") { + start.push(previewNode); + } else if (flexHighlight.alignment === "center") { + center.push(previewNode); + } else { + end.push(previewNode); + } + } + childCount++; const widget = map[child.id]; if (hasFillChild) { start.push(widget); diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx index 6f36e88bdb8f..bf7da3f51824 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx @@ -7,11 +7,11 @@ import { widgetTypeClassname, WIDGET_PADDING, } from "constants/WidgetConstants"; +import { snipingModeSelector } from "selectors/editorSelectors"; +import { useSelector } from "store"; import { useClickToSelectWidget } from "utils/hooks/useClickToSelectWidget"; import { usePositionedContainerZIndex } from "utils/hooks/usePositionedContainerZIndex"; import { checkIsDropTarget } from "../PositionedContainer"; -import { useSelector } from "store"; -import { snipingModeSelector } from "selectors/editorSelectors"; export type AutoLayoutProps = { children: ReactNode; @@ -73,7 +73,6 @@ export function FlexComponent(props: AutoLayoutProps) { const stopEventPropagation = (e: any) => { !isSnipingMode && e.stopPropagation(); }; - /** * In a vertical stack, * Fill widgets grow / shrink to take up all the available space. @@ -91,6 +90,7 @@ export function FlexComponent(props: AutoLayoutProps) { className={className} componentHeight={props.componentHeight} componentWidth={props.componentWidth} + id={props.widgetId} isFillWidget={isFillWidget} minWidth={props.minWidth} onClick={stopEventPropagation} diff --git a/app/client/src/components/editorComponents/ResizableComponent.tsx b/app/client/src/components/editorComponents/ResizableComponent.tsx index 6a4d542cf3aa..e359a80ac803 100644 --- a/app/client/src/components/editorComponents/ResizableComponent.tsx +++ b/app/client/src/components/editorComponents/ResizableComponent.tsx @@ -1,59 +1,58 @@ -import React, { useContext, useEffect, memo, useMemo, useState } from "react"; +import { AppState } from "@appsmith/reducers"; +import { focusWidget } from "actions/widgetActions"; import { - WidgetOperations, - WidgetRowCols, - WidgetProps, -} from "widgets/BaseWidget"; + AlignItems, + LayoutDirection, + ResponsiveBehavior, +} from "components/constants"; import { EditorContext } from "components/editorComponents/EditorContextProvider"; +import { GridDefaults } from "constants/WidgetConstants"; +import { get, omit } from "lodash"; +import { XYCord } from "pages/common/CanvasArenas/hooks/useCanvasDragging"; +import React, { memo, useContext, useEffect, useMemo, useState } from "react"; +import { useSelector } from "react-redux"; +import Resizable from "resizable/resizenreflow"; import { - UIElementSize, - computeFinalRowCols, - computeRowCols, -} from "./ResizableUtils"; + previewModeSelector, + snipingModeSelector, +} from "selectors/editorSelectors"; +import { + getParentToOpenSelector, + isCurrentWidgetFocused, + isCurrentWidgetLastSelected, + isMultiSelectedWidget, + isWidgetSelected, +} from "selectors/widgetSelectors"; +import AnalyticsUtil from "utils/AnalyticsUtil"; import { useShowPropertyPane, useShowTableFilterPane, useWidgetDragResize, } from "utils/hooks/dragResizeHooks"; -import { useSelector } from "react-redux"; -import { AppState } from "@appsmith/reducers"; -import Resizable from "resizable/resizenreflow"; -import { omit, get } from "lodash"; +import { useWidgetSelection } from "utils/hooks/useWidgetSelection"; import { getSnapColumns } from "utils/WidgetPropsUtils"; import { - VisibilityContainer, + WidgetOperations, + WidgetProps, + WidgetRowCols, +} from "widgets/BaseWidget"; +import { DropTargetContext } from "./DropTargetComponent"; +import { + computeFinalRowCols, + computeRowCols, + UIElementSize, +} from "./ResizableUtils"; +import { + BottomHandleStyles, + BottomLeftHandleStyles, + BottomRightHandleStyles, LeftHandleStyles, RightHandleStyles, TopHandleStyles, - BottomHandleStyles, TopLeftHandleStyles, TopRightHandleStyles, - BottomLeftHandleStyles, - BottomRightHandleStyles, + VisibilityContainer, } from "./ResizeStyledComponents"; -import AnalyticsUtil from "utils/AnalyticsUtil"; -import { - previewModeSelector, - snipingModeSelector, -} from "selectors/editorSelectors"; -import { useWidgetSelection } from "utils/hooks/useWidgetSelection"; -import { focusWidget } from "actions/widgetActions"; -import { GridDefaults } from "constants/WidgetConstants"; -import { DropTargetContext } from "./DropTargetComponent"; -import { XYCord } from "pages/common/CanvasArenas/hooks/useCanvasDragging"; -import { - AlignItems, - LayoutDirection, - ResponsiveBehavior, -} from "components/constants"; -import { AutoLayoutContext } from "utils/autoLayoutContext"; -import { getParentToOpenSelector } from "selectors/widgetSelectors"; -import { - isCurrentWidgetFocused, - isCurrentWidgetLastSelected, - isWidgetSelected, - isMultiSelectedWidget, -} from "selectors/widgetSelectors"; export type ResizableComponentProps = WidgetProps & { paddingOffset: number; diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index db7f338fb088..e2eec62141db 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -1,17 +1,19 @@ import { getWidgets } from "sagas/selectors"; import { useSelector } from "store"; +import { ReduxActionTypes } from "ce/constants/ReduxActionConstants"; import { - LayoutDirection, FlexLayerAlignment, + LayoutDirection, ResponsiveBehavior, } from "components/constants"; -import { WidgetDraggingBlock } from "./useBlocksToBeDraggedOnCanvas"; import { FlexLayer, LayerChild, } from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; +import { useDispatch } from "react-redux"; import { ReflowDirection } from "reflow/reflowTypes"; +import { WidgetDraggingBlock } from "./useBlocksToBeDraggedOnCanvas"; interface XYCord { x: number; @@ -55,7 +57,7 @@ export const useAutoLayoutHighlights = ({ const canvas = allWidgets[canvasId]; const layers: FlexLayer[] = canvas?.flexLayers || []; const isVertical = direction === LayoutDirection.Vertical; - + const dispatch = useDispatch(); let highlights: HighlightInfo[] = []; let lastActiveHighlight: HighlightInfo | undefined; let containerDimensions: { @@ -178,6 +180,7 @@ export const useAutoLayoutHighlights = ({ ); } } + console.log({ highlights }); // console.log("#### highlights: ", highlights); return highlights; }; @@ -451,6 +454,14 @@ export const useAutoLayoutHighlights = ({ e, moveDirection, ); + dispatch({ + type: ReduxActionTypes.SET_AUTOLAYOUT_HIGHLIGHTS, + payload: { + flexHighlight: pos, + blocksToDraw, + }, + }); + // console.log({ pos }); if (!pos) return; lastActiveHighlight = pos; diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts b/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts index dc4b9e1ed327..13412c533f68 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts @@ -1,38 +1,38 @@ -import { useContext, useEffect, useRef } from "react"; +import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants"; +import { AppState } from "@appsmith/reducers"; +import { stopReflowAction } from "actions/reflowActions"; +import { AlignItems, LayoutDirection } from "components/constants"; +import { DropTargetContext } from "components/editorComponents/DropTargetComponent"; +import { EditorContext } from "components/editorComponents/EditorContextProvider"; +import { OccupiedSpace } from "constants/CanvasEditorConstants"; import { CONTAINER_GRID_PADDING, GridDefaults, MAIN_CONTAINER_WIDGET_ID, } from "constants/WidgetConstants"; -import { useSelector } from "store"; -import { AppState } from "@appsmith/reducers"; -import { getSelectedWidgets } from "selectors/ui"; +import equal from "fast-deep-equal/es6"; +import { isEmpty } from "lodash"; +import { CanvasDraggingArenaProps } from "pages/common/CanvasArenas/CanvasDraggingArena"; +import { useContext, useEffect, useRef } from "react"; +import { useDispatch } from "react-redux"; +import { DragDetails } from "reducers/uiReducers/dragResizeReducer"; +import { getDragDetails, getWidgetByID, getWidgets } from "sagas/selectors"; import { getOccupiedSpacesWhileMoving } from "selectors/editorSelectors"; import { getTableFilterState } from "selectors/tableFilterSelectors"; -import { OccupiedSpace } from "constants/CanvasEditorConstants"; -import { getDragDetails, getWidgetByID, getWidgets } from "sagas/selectors"; +import { getSelectedWidgets } from "selectors/ui"; +import { getIsReflowing } from "selectors/widgetReflowSelectors"; +import { useSelector } from "store"; +import AnalyticsUtil from "utils/AnalyticsUtil"; +import { snapToGrid } from "utils/helpers"; +import { useWidgetSelection } from "utils/hooks/useWidgetSelection"; import { getDropZoneOffsets, WidgetOperationParams, widgetOperationParams, } from "utils/WidgetPropsUtils"; -import { DropTargetContext } from "components/editorComponents/DropTargetComponent"; -import { isEmpty } from "lodash"; -import equal from "fast-deep-equal/es6"; -import { CanvasDraggingArenaProps } from "pages/common/CanvasArenas/CanvasDraggingArena"; -import { useDispatch } from "react-redux"; -import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants"; -import { EditorContext } from "components/editorComponents/EditorContextProvider"; -import { useWidgetSelection } from "utils/hooks/useWidgetSelection"; -import AnalyticsUtil from "utils/AnalyticsUtil"; -import { snapToGrid } from "utils/helpers"; -import { stopReflowAction } from "actions/reflowActions"; -import { DragDetails } from "reducers/uiReducers/dragResizeReducer"; -import { getIsReflowing } from "selectors/widgetReflowSelectors"; -import { XYCord } from "./useCanvasDragging"; import ContainerJumpMetrics from "./ContainerJumpMetric"; -import { AlignItems, LayoutDirection } from "components/constants"; import { HighlightInfo } from "./useAutoLayoutHighlights"; +import { XYCord } from "./useCanvasDragging"; export interface WidgetDraggingUpdateParams extends WidgetDraggingBlock { updateWidgetParams: WidgetOperationParams; diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index c0f5ec17cdd0..af7b4080367f 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -1,3 +1,4 @@ +import { ReduxActionTypes } from "ce/constants/ReduxActionConstants"; import { OccupiedSpace } from "constants/CanvasEditorConstants"; import { CONTAINER_GRID_PADDING, @@ -6,13 +7,14 @@ import { import { debounce, isEmpty, throttle } from "lodash"; import { CanvasDraggingArenaProps } from "pages/common/CanvasArenas/CanvasDraggingArena"; import React, { useEffect, useRef } from "react"; -import { useSelector } from "store"; +import { useDispatch } from "react-redux"; import { MovementLimitMap, ReflowDirection, ReflowedSpaceMap, } from "reflow/reflowTypes"; import { getZoomLevel } from "selectors/editorSelectors"; +import { useSelector } from "store"; import { getNearestParentCanvas } from "utils/generators"; import { getAbsolutePixels } from "utils/helpers"; import { useWidgetDragResize } from "utils/hooks/dragResizeHooks"; @@ -23,16 +25,16 @@ import { getMousePositionsOnCanvas, noCollision, } from "utils/WidgetPropsUtils"; -import { - useBlocksToBeDraggedOnCanvas, - WidgetDraggingBlock, -} from "./useBlocksToBeDraggedOnCanvas"; -import { useCanvasDragToScroll } from "./useCanvasDragToScroll"; import ContainerJumpMetrics from "./ContainerJumpMetric"; import { HighlightInfo, useAutoLayoutHighlights, } from "./useAutoLayoutHighlights"; +import { + useBlocksToBeDraggedOnCanvas, + WidgetDraggingBlock, +} from "./useBlocksToBeDraggedOnCanvas"; +import { useCanvasDragToScroll } from "./useCanvasDragToScroll"; export interface XYCord { x: number; @@ -127,6 +129,7 @@ export const useCanvasDragging = ( isDragging, useAutoLayout, }); + const dispatch = useDispatch(); const highlights: HighlightInfo[] = calculateHighlights(); @@ -347,6 +350,13 @@ export const useCanvasDragging = ( }); } setDraggingCanvas(); + dispatch({ + type: ReduxActionTypes.SET_AUTOLAYOUT_HIGHLIGHTS, + payload: { + flexHighlight: undefined, + blocksToDraw: undefined, + }, + }); } }, 0); }; diff --git a/app/client/src/reducers/uiReducers/dragResizeReducer.ts b/app/client/src/reducers/uiReducers/dragResizeReducer.ts index e93b31c29150..cc71e66320f6 100644 --- a/app/client/src/reducers/uiReducers/dragResizeReducer.ts +++ b/app/client/src/reducers/uiReducers/dragResizeReducer.ts @@ -1,15 +1,17 @@ -import { areArraysEqual } from "utils/AppsmithUtils"; -import { createImmerReducer } from "utils/ReducerUtils"; import { ReduxAction, ReduxActionTypes, } from "@appsmith/constants/ReduxActionConstants"; import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; +import { HighlightInfo } from "pages/common/CanvasArenas/hooks/useAutoLayoutHighlights"; +import { areArraysEqual } from "utils/AppsmithUtils"; +import { createImmerReducer } from "utils/ReducerUtils"; const initialState: WidgetDragResizeState = { isDraggingDisabled: false, isDragging: false, dragDetails: {}, + autoLayoutDragDetails: {}, isResizing: false, lastSelectedWidget: undefined, selectedWidgets: [], @@ -142,6 +144,13 @@ export const widgetDraggingReducer = createImmerReducer(initialState, { ) => { state.selectedWidgetAncestry = action.payload; }, + [ReduxActionTypes.SET_AUTOLAYOUT_HIGHLIGHTS]: ( + state: WidgetDragResizeState, + action: ReduxAction<{ flexHighlight: HighlightInfo; blocksToDraw: any }>, + ) => { + state.flexHighlight = action.payload.flexHighlight; + state.autoLayoutDragDetails = action.payload.blocksToDraw; + }, }); type DraggingGroupCenter = { @@ -161,6 +170,8 @@ export type WidgetDragResizeState = { isDraggingDisabled: boolean; isDragging: boolean; dragDetails: DragDetails; + autoLayoutDragDetails: any; + flexHighlight?: HighlightInfo; isResizing: boolean; lastSelectedWidget?: string; focusedWidget?: string; diff --git a/app/client/src/selectors/editorSelectors.tsx b/app/client/src/selectors/editorSelectors.tsx index 18f6f7b2b52b..6de439f562d3 100644 --- a/app/client/src/selectors/editorSelectors.tsx +++ b/app/client/src/selectors/editorSelectors.tsx @@ -1,39 +1,39 @@ import { createSelector } from "reselect"; import { AppState } from "@appsmith/reducers"; -import { WidgetConfigReducerState } from "reducers/entityReducers/widgetConfigReducer"; -import { WidgetCardProps, WidgetProps } from "widgets/BaseWidget"; import { CanvasWidgetsReduxState, FlattenedWidgetProps, } from "reducers/entityReducers/canvasWidgetsReducer"; import { PageListReduxState } from "reducers/entityReducers/pageListReducer"; +import { WidgetConfigReducerState } from "reducers/entityReducers/widgetConfigReducer"; +import { WidgetCardProps, WidgetProps } from "widgets/BaseWidget"; +import { Page } from "@appsmith/constants/ReduxActionConstants"; +import { ApplicationVersion } from "actions/applicationActions"; import { OccupiedSpace, WidgetSpace } from "constants/CanvasEditorConstants"; -import { - getActions, - getCanvasWidgets, - getJSCollections, -} from "selectors/entitiesSelector"; +import { PLACEHOLDER_APP_SLUG, PLACEHOLDER_PAGE_SLUG } from "constants/routes"; import { MAIN_CONTAINER_WIDGET_ID, RenderModes, } from "constants/WidgetConstants"; -import CanvasWidgetsNormalizer from "normalizers/CanvasWidgetsNormalizer"; +import { APP_MODE } from "entities/App"; import { DataTree, DataTreeWidget } from "entities/DataTree/dataTreeFactory"; -import { ContainerWidgetProps } from "widgets/ContainerWidget/widget"; import { find, sortBy } from "lodash"; -import { APP_MODE } from "entities/App"; -import { getDataTree, getLoadingEntities } from "selectors/dataTreeSelectors"; -import { Page } from "@appsmith/constants/ReduxActionConstants"; -import { PLACEHOLDER_APP_SLUG, PLACEHOLDER_PAGE_SLUG } from "constants/routes"; -import { ApplicationVersion } from "actions/applicationActions"; +import CanvasWidgetsNormalizer from "normalizers/CanvasWidgetsNormalizer"; import { MainCanvasReduxState } from "reducers/uiReducers/mainCanvasReducer"; +import { getDataTree, getLoadingEntities } from "selectors/dataTreeSelectors"; +import { + getActions, + getCanvasWidgets, + getJSCollections, +} from "selectors/entitiesSelector"; import { buildChildWidgetTree, createCanvasWidget, createLoadingWidget, } from "utils/widgetRenderUtils"; +import { ContainerWidgetProps } from "widgets/ContainerWidget/widget"; const getIsDraggingOrResizing = (state: AppState) => state.ui.widgetDragResize.isResizing || state.ui.widgetDragResize.isDragging; From 428945c3712ae91d93418c6d0d921ac8aae41dcb Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 11 Oct 2022 11:17:09 -0400 Subject: [PATCH 144/708] clean up unused imports --- .../common/CanvasArenas/hooks/useAutoLayoutHighlights.ts | 1 - .../src/widgets/ChartWidget/widget/propertyConfig.ts | 1 + .../src/widgets/CheckboxGroupWidget/widget/index.tsx | 1 + .../src/widgets/CurrencyInputWidget/widget/index.tsx | 2 -- app/client/src/widgets/InputWidgetV2/widget/index.tsx | 2 -- .../src/widgets/JSONFormWidget/widget/propertyConfig.ts | 1 + app/client/src/widgets/MapWidget/widget/index.tsx | 1 + app/client/src/widgets/ModalWidget/widget/index.tsx | 5 +---- app/client/src/widgets/PhoneInputWidget/widget/index.tsx | 2 -- app/client/src/widgets/TabsWidget/widget/index.tsx | 8 +------- 10 files changed, 6 insertions(+), 18 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index e2eec62141db..cbf4318c3ec0 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -180,7 +180,6 @@ export const useAutoLayoutHighlights = ({ ); } } - console.log({ highlights }); // console.log("#### highlights: ", highlights); return highlights; }; diff --git a/app/client/src/widgets/ChartWidget/widget/propertyConfig.ts b/app/client/src/widgets/ChartWidget/widget/propertyConfig.ts index 1e7ca0f55021..eca8d41ad5fd 100644 --- a/app/client/src/widgets/ChartWidget/widget/propertyConfig.ts +++ b/app/client/src/widgets/ChartWidget/widget/propertyConfig.ts @@ -243,6 +243,7 @@ export const contentConfig = [ x.chartType === "CUSTOM_FUSION_CHART" || x.chartType === "PIE_CHART", dependencies: ["chartType"], }, + generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill), ], }, { diff --git a/app/client/src/widgets/CheckboxGroupWidget/widget/index.tsx b/app/client/src/widgets/CheckboxGroupWidget/widget/index.tsx index 54f9666c6a4d..ba833c9f4293 100644 --- a/app/client/src/widgets/CheckboxGroupWidget/widget/index.tsx +++ b/app/client/src/widgets/CheckboxGroupWidget/widget/index.tsx @@ -275,6 +275,7 @@ class CheckboxGroupWidget extends BaseWidget< isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, + generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill), ], }, { diff --git a/app/client/src/widgets/CurrencyInputWidget/widget/index.tsx b/app/client/src/widgets/CurrencyInputWidget/widget/index.tsx index 0af0dbef148f..9c06d16c99c8 100644 --- a/app/client/src/widgets/CurrencyInputWidget/widget/index.tsx +++ b/app/client/src/widgets/CurrencyInputWidget/widget/index.tsx @@ -33,8 +33,6 @@ import { } from "../component/utilities"; import { mergeWidgetConfig } from "utils/helpers"; import { GRID_DENSITY_MIGRATION_V1 } from "widgets/constants"; -import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; -import { ResponsiveBehavior } from "components/constants"; export function defaultValueValidation( value: any, diff --git a/app/client/src/widgets/InputWidgetV2/widget/index.tsx b/app/client/src/widgets/InputWidgetV2/widget/index.tsx index b1c48ffb70f3..6c62f2e73d9b 100644 --- a/app/client/src/widgets/InputWidgetV2/widget/index.tsx +++ b/app/client/src/widgets/InputWidgetV2/widget/index.tsx @@ -25,8 +25,6 @@ import { BaseInputWidgetProps } from "widgets/BaseInputWidget/widget"; import { mergeWidgetConfig } from "utils/helpers"; import { InputTypes } from "widgets/BaseInputWidget/constants"; import { getParsedText } from "./Utilities"; -import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; -import { ResponsiveBehavior } from "components/constants"; export function defaultValueValidation( value: any, diff --git a/app/client/src/widgets/JSONFormWidget/widget/propertyConfig.ts b/app/client/src/widgets/JSONFormWidget/widget/propertyConfig.ts index 4e01025d95b5..f4a613191cbd 100644 --- a/app/client/src/widgets/JSONFormWidget/widget/propertyConfig.ts +++ b/app/client/src/widgets/JSONFormWidget/widget/propertyConfig.ts @@ -267,6 +267,7 @@ export const contentConfig = [ isTriggerProperty: false, validation: { type: ValidationTypes.TEXT }, }, + generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill), ], }, { diff --git a/app/client/src/widgets/MapWidget/widget/index.tsx b/app/client/src/widgets/MapWidget/widget/index.tsx index e4795bb69791..23b93e584ebb 100644 --- a/app/client/src/widgets/MapWidget/widget/index.tsx +++ b/app/client/src/widgets/MapWidget/widget/index.tsx @@ -200,6 +200,7 @@ class MapWidget extends BaseWidget { isBindProperty: false, isTriggerProperty: false, }, + generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill), ], }, { diff --git a/app/client/src/widgets/ModalWidget/widget/index.tsx b/app/client/src/widgets/ModalWidget/widget/index.tsx index a3d32a6e3e26..f25f5ee7bb17 100644 --- a/app/client/src/widgets/ModalWidget/widget/index.tsx +++ b/app/client/src/widgets/ModalWidget/widget/index.tsx @@ -16,10 +16,7 @@ import { getCanvasWidth, snipingModeSelector } from "selectors/editorSelectors"; import { deselectAllInitAction } from "actions/widgetSelectionActions"; import { ValidationTypes } from "constants/WidgetValidation"; import { Alignment, Positioning, Spacing } from "components/constants"; -import { - generatePositioningConfig, - getLayoutConfig, -} from "utils/layoutPropertiesUtils"; +import { generatePositioningConfig } from "utils/layoutPropertiesUtils"; const minSize = 100; diff --git a/app/client/src/widgets/PhoneInputWidget/widget/index.tsx b/app/client/src/widgets/PhoneInputWidget/widget/index.tsx index 96d88d1111da..3521cc468d61 100644 --- a/app/client/src/widgets/PhoneInputWidget/widget/index.tsx +++ b/app/client/src/widgets/PhoneInputWidget/widget/index.tsx @@ -30,8 +30,6 @@ import { import * as Sentry from "@sentry/react"; import log from "loglevel"; import { GRID_DENSITY_MIGRATION_V1 } from "widgets/constants"; -import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; -import { ResponsiveBehavior } from "components/constants"; export function defaultValueValidation( value: any, diff --git a/app/client/src/widgets/TabsWidget/widget/index.tsx b/app/client/src/widgets/TabsWidget/widget/index.tsx index afba9abcefc1..1238d27a8776 100644 --- a/app/client/src/widgets/TabsWidget/widget/index.tsx +++ b/app/client/src/widgets/TabsWidget/widget/index.tsx @@ -13,16 +13,10 @@ import { AutocompleteDataType } from "utils/autocomplete/TernServer"; import { WidgetProperties } from "selectors/propertyPaneSelectors"; import { WIDGET_PADDING } from "constants/WidgetConstants"; import derivedProperties from "./parseDerivedProperties"; -import { - Alignment, - Positioning, - ResponsiveBehavior, - Spacing, -} from "components/constants"; +import { Positioning, ResponsiveBehavior } from "components/constants"; import { generatePositioningConfig, generateResponsiveBehaviorConfig, - getLayoutConfig, } from "utils/layoutPropertiesUtils"; export function selectedTabValidation( From 0c175d4f01f96ed0a1711f58b97d51e0079a3e07 Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 11 Oct 2022 11:41:16 -0400 Subject: [PATCH 145/708] fix vertical stack behavior in tabs --- app/client/src/widgets/TabsWidget/widget/index.tsx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/app/client/src/widgets/TabsWidget/widget/index.tsx b/app/client/src/widgets/TabsWidget/widget/index.tsx index 1238d27a8776..54ce3e5e2656 100644 --- a/app/client/src/widgets/TabsWidget/widget/index.tsx +++ b/app/client/src/widgets/TabsWidget/widget/index.tsx @@ -13,7 +13,11 @@ import { AutocompleteDataType } from "utils/autocomplete/TernServer"; import { WidgetProperties } from "selectors/propertyPaneSelectors"; import { WIDGET_PADDING } from "constants/WidgetConstants"; import derivedProperties from "./parseDerivedProperties"; -import { Positioning, ResponsiveBehavior } from "components/constants"; +import { + LayoutDirection, + Positioning, + ResponsiveBehavior, +} from "components/constants"; import { generatePositioningConfig, generateResponsiveBehaviorConfig, @@ -346,6 +350,10 @@ class TabsWidget extends BaseWidget< (item) => item.widgetId === selectedTabWidgetId, )[0]; childWidgetData.positioning = selectedTabProps?.positioning; + childWidgetData.direction = + selectedTabProps?.positioning === Positioning.Vertical + ? LayoutDirection.Vertical + : LayoutDirection.Horizontal; childWidgetData.alignment = selectedTabProps?.alignment; childWidgetData.spacing = selectedTabProps?.spacing; From 87cc1941a0569a88aa04695d2c0841a37f36e0b8 Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 11 Oct 2022 14:34:16 -0400 Subject: [PATCH 146/708] working wrap for fill containers --- .../appsmith/autoLayout/FlexComponent.tsx | 24 +++++++++++++++---- app/client/src/widgets/CanvasWidget.tsx | 12 ++-------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx index bf7da3f51824..b15906135266 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx @@ -12,6 +12,8 @@ import { useSelector } from "store"; import { useClickToSelectWidget } from "utils/hooks/useClickToSelectWidget"; import { usePositionedContainerZIndex } from "utils/hooks/usePositionedContainerZIndex"; import { checkIsDropTarget } from "../PositionedContainer"; +import { useIsMobileDevice } from "utils/hooks/useDeviceDetect"; +import { MOBILE_MAX_WIDTH } from "constants/AppConstants"; export type AutoLayoutProps = { children: ReactNode; @@ -30,8 +32,9 @@ export type AutoLayoutProps = { const FlexWidget = styled.div<{ componentHeight: number; componentWidth: number; + isMobile: boolean; isFillWidget: boolean; - minWidth?: number; + minWidth?: string; padding: number; zIndex: number; zIndexOnHover: number; @@ -41,10 +44,12 @@ const FlexWidget = styled.div<{ width: ${({ componentWidth, isFillWidget }) => isFillWidget ? "auto" : `${Math.floor(componentWidth)}px`}; - height: ${({ componentHeight }) => Math.floor(componentHeight) + "px"}; - min-width: ${({ minWidth }) => minWidth + "px"}; + height: ${({ componentHeight, isMobile }) => + isMobile ? "100%" : Math.floor(componentHeight) + "px"}; + min-width: ${({ minWidth }) => minWidth}; min-height: 30px; - padding: ${({ padding }) => padding + "px"}; + padding: ${({ isMobile, padding }) => + isMobile ? `${padding}px 0 0` : padding + "px"}; flex-grow: ${({ isFillWidget }) => (isFillWidget ? "1" : "0")}; @@ -56,6 +61,7 @@ const FlexWidget = styled.div<{ // TODO: update min width logic. export function FlexComponent(props: AutoLayoutProps) { + const isMobile = useIsMobileDevice(); const isSnipingMode = useSelector(snipingModeSelector); const clickToSelectWidget = useClickToSelectWidget(props.widgetId); const onClickFn = useCallback(() => { @@ -85,6 +91,13 @@ export function FlexComponent(props: AutoLayoutProps) { props.widgetId } ${widgetTypeClassname(props.widgetType)}`; + console.log("#### widget props", props.widgetId, props); + console.log("#### is mobile", isMobile); + const minWidth = + props.responsiveBehavior === ResponsiveBehavior.Fill && isMobile + ? "100%" + : props.minWidth + "px"; + return ( Date: Tue, 11 Oct 2022 15:45:29 -0400 Subject: [PATCH 147/708] bug fixes --- .../appsmith/autoLayout/AutoLayoutLayer.tsx | 1 + .../appsmith/autoLayout/FlexBoxComponent.tsx | 32 +++++++------------ .../appsmith/autoLayout/FlexComponent.tsx | 3 -- .../pages/Editor/CanvasPropertyPane/index.tsx | 6 +++- .../widgets/BaseInputWidget/widget/index.tsx | 2 +- app/client/src/widgets/CanvasWidget.tsx | 7 ++-- .../widgets/ContainerWidget/widget/index.tsx | 2 +- 7 files changed, 23 insertions(+), 30 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx index 941ea0ee5660..1c1aad6005ab 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx @@ -39,6 +39,7 @@ const SubWrapper = styled.div<{ flex-direction: ${({ flexDirection }) => flexDirection || "row"}; align-items: ${({ flexDirection }) => flexDirection === FlexDirection.Column ? "flex-start" : "center"}; + flex-wrap: wrap; `; const StartWrapper = styled(SubWrapper)` diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx index d607d3351751..a6e7519ca901 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx @@ -1,26 +1,18 @@ import { isArray } from "lodash"; -import React, { ReactNode, useMemo } from "react"; +import React, { ReactNode } from "react"; import styled from "styled-components"; import { AppState } from "ce/reducers"; import { - AlignItems, - Alignment, - FlexDirection, FlexLayerAlignment, - JustifyContent, LayoutDirection, Overflow, - Spacing, } from "components/constants"; import { useSelector } from "react-redux"; -import { getLayoutProperties } from "utils/layoutPropertiesUtils"; import AutoLayoutLayer from "./AutoLayoutLayer"; export interface FlexBoxProps { - alignment: Alignment; direction?: LayoutDirection; - spacing: Spacing; stretchHeight: boolean; useAutoLayout: boolean; children?: ReactNode; @@ -41,16 +33,15 @@ export interface FlexLayer { export const FlexContainer = styled.div<{ useAutoLayout?: boolean; - flexDirection?: FlexDirection; - justifyContent?: JustifyContent; - alignItems?: AlignItems; + direction?: LayoutDirection; stretchHeight: boolean; overflow: Overflow; }>` display: ${({ useAutoLayout }) => (useAutoLayout ? "flex" : "block")}; - flex-direction: ${({ flexDirection }) => flexDirection || "row"}; - justify-content: ${({ justifyContent }) => justifyContent || "flex-start"}; - align-items: ${({ alignItems }) => alignItems || "flex-start"}; + flex-direction: ${({ direction }) => + direction === LayoutDirection.Vertical ? "column" : "row"}; + justify-content: flex-start; + align-items: flex-start; flex-wrap: ${({ overflow }) => overflow?.indexOf("wrap") > -1 ? overflow : "nowrap"}; @@ -66,10 +57,10 @@ function FlexBoxComponent(props: FlexBoxProps) { const direction: LayoutDirection = props.direction || LayoutDirection.Horizontal; - const layoutProps = useMemo( - () => getLayoutProperties(props.direction, props.alignment, props.spacing), - [props.direction, props.alignment, props.spacing], - ); + // const layoutProps = useMemo( + // () => getLayoutProperties(props.direction, props.alignment, props.spacing), + // [props.direction, props.alignment, props.spacing], + // ); const { autoLayoutDragDetails, dragDetails, flexHighlight } = useSelector( (state: AppState) => state.ui.widgetDragResize, ); @@ -186,10 +177,11 @@ function FlexBoxComponent(props: FlexBoxProps) { ); }); }; + return ( { updates: { modify: { positioning: (option as DropdownOption).value, + direction: + (option as DropdownOption).value === Positioning.Vertical + ? LayoutDirection.Vertical + : LayoutDirection.Horizontal, }, }, }, diff --git a/app/client/src/widgets/BaseInputWidget/widget/index.tsx b/app/client/src/widgets/BaseInputWidget/widget/index.tsx index 6cf140f2a52a..3f3c0bf3b2a7 100644 --- a/app/client/src/widgets/BaseInputWidget/widget/index.tsx +++ b/app/client/src/widgets/BaseInputWidget/widget/index.tsx @@ -229,7 +229,7 @@ class BaseInputWidget< return props.type !== "PHONE_INPUT_WIDGET"; }, }, - { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, + generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill), ], }, { diff --git a/app/client/src/widgets/CanvasWidget.tsx b/app/client/src/widgets/CanvasWidget.tsx index 429951a89ffc..35ffe6e7cac8 100644 --- a/app/client/src/widgets/CanvasWidget.tsx +++ b/app/client/src/widgets/CanvasWidget.tsx @@ -16,7 +16,7 @@ import { LayoutDirection, Overflow, Positioning, - Spacing, + ResponsiveBehavior, } from "components/constants"; import ContainerComponent from "./ContainerWidget/component"; import { CanvasDraggingArena } from "pages/common/CanvasArenas/CanvasDraggingArena"; @@ -74,7 +74,7 @@ class CanvasWidget extends ContainerWidget { childWidget.positioning = childWidget?.positioning || this.props.positioning; childWidget.useAutoLayout = this.state.useAutoLayout; - childWidget.direction = this.props.direction; + childWidget.direction = childWidget?.direction || this.props.direction; childWidget.justifyContent = this.props.justifyContent; childWidget.alignItems = this.props.alignItems; @@ -125,7 +125,6 @@ class CanvasWidget extends ContainerWidget { /> {/* without the wrapping div onClick events are triggered twice */} Date: Tue, 11 Oct 2022 18:56:39 -0400 Subject: [PATCH 148/708] move layout direction to state variable --- .../pages/Editor/CanvasPropertyPane/index.tsx | 1 - app/client/src/widgets/CanvasWidget.tsx | 8 +- .../ContainerWidget/component/index.tsx | 105 +----------------- .../widgets/ContainerWidget/widget/index.tsx | 12 +- 4 files changed, 14 insertions(+), 112 deletions(-) diff --git a/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx b/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx index 12763095ad04..bde355969c82 100644 --- a/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx +++ b/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx @@ -25,7 +25,6 @@ const PositioningOptions = () => { const widgets = useSelector(getWidgets); const options: DropdownOption[] = [ { label: "Fixed", value: Positioning.Fixed }, - { label: "Horizontal stack", value: Positioning.Horizontal }, { label: "Vertical stack", value: Positioning.Vertical }, ]; const [selectedOption, setSelectedOption] = useState(() => { diff --git a/app/client/src/widgets/CanvasWidget.tsx b/app/client/src/widgets/CanvasWidget.tsx index 35ffe6e7cac8..6d7dcbbf29eb 100644 --- a/app/client/src/widgets/CanvasWidget.tsx +++ b/app/client/src/widgets/CanvasWidget.tsx @@ -74,7 +74,7 @@ class CanvasWidget extends ContainerWidget { childWidget.positioning = childWidget?.positioning || this.props.positioning; childWidget.useAutoLayout = this.state.useAutoLayout; - childWidget.direction = childWidget?.direction || this.props.direction; + childWidget.direction = childWidget?.direction || this.state.direction; childWidget.justifyContent = this.props.justifyContent; childWidget.alignItems = this.props.alignItems; @@ -98,7 +98,7 @@ class CanvasWidget extends ContainerWidget { {...this.getSnapSpaces()} alignItems={props.alignItems} canExtend={props.canExtend} - direction={this.props.direction} + direction={this.state.direction} dropDisabled={!!props.dropDisabled} noPad={this.props.noPad} parentId={props.parentId} @@ -125,10 +125,10 @@ class CanvasWidget extends ContainerWidget { /> {/* without the wrapping div onClick events are triggered twice */} ` - flex: 1 1 33.3%; - display: flex; - flex-direction: ${({ flexDirection }) => flexDirection || "row"}; - align-items: ${({ flexDirection }) => - flexDirection === FlexDirection.Column ? "flex-start" : "center"}; -`; - -const StartWrapper = styled(SubWrapper)` - justify-content: flex-start; -`; - -const EndWrapper = styled(SubWrapper)` - justify-content: flex-end; -`; - -const CenterWrapper = styled(SubWrapper)` - justify-content: center; -`; - function ContainerComponentWrapper(props: ContainerComponentProps) { const containerStyle = props.containerStyle || "card"; const containerRef: RefObject = useRef(null); @@ -132,70 +95,6 @@ function ContainerComponentWrapper(props: ContainerComponentProps) { ); } -export function LayoutWrapper(props: FlexBoxProps): JSX.Element { - const allWidgets = useSelector(getWidgets); - let start: JSX.Element[] = [], - center: JSX.Element[] = [], - end: JSX.Element[] = []; - let hasFillChild = false; - if (isArray(props.children)) { - for (const child of props.children) { - const widget = allWidgets[(child as JSX.Element).props?.widgetId]; - if (widget.responsiveBehavior === ResponsiveBehavior.Fill) { - hasFillChild = true; - break; - } - if (widget?.alignment === FlexLayerAlignment.End) - end.push(child as JSX.Element); - else if (widget?.alignment === FlexLayerAlignment.Center) - center.push(child as JSX.Element); - else start.push(child as JSX.Element); - } - } - if (hasFillChild) { - start = props.children as JSX.Element[]; - center = []; - end = []; - } - - const layoutProps: LayoutProperties = useMemo( - () => getLayoutProperties(props.direction, props.alignment, props.spacing), - [props.direction, props.alignment, props.spacing], - ); - return ( - - - {start} - - - {center} - - - {end} - - - ); -} - function ContainerComponent(props: ContainerComponentProps) { useCanvasMinHeightUpdateHook(props.widgetId, props.minHeight); return props.widgetId === MAIN_CONTAINER_WIDGET_ID ? ( diff --git a/app/client/src/widgets/ContainerWidget/widget/index.tsx b/app/client/src/widgets/ContainerWidget/widget/index.tsx index 1d2e38f9851c..eef4fd8291bf 100644 --- a/app/client/src/widgets/ContainerWidget/widget/index.tsx +++ b/app/client/src/widgets/ContainerWidget/widget/index.tsx @@ -38,6 +38,7 @@ export class ContainerWidget extends BaseWidget< super(props); this.state = { useAutoLayout: false, + direction: LayoutDirection.Horizontal, }; this.renderChildWidget = this.renderChildWidget.bind(this); } @@ -152,9 +153,7 @@ export class ContainerWidget extends BaseWidget< } static getDerivedPropertiesMap(): DerivedPropertiesMap { - return { - direction: `{{ this.positioning === "vertical" ? "Vertical" : "Horizontal" }}`, - }; + return {}; } static getDefaultPropertiesMap(): Record { return {}; @@ -182,6 +181,10 @@ export class ContainerWidget extends BaseWidget< else this.setState({ useAutoLayout: true, + direction: + this.props.positioning === Positioning.Vertical + ? LayoutDirection.Vertical + : LayoutDirection.Horizontal, }); }; @@ -241,7 +244,7 @@ export class ContainerWidget extends BaseWidget< childWidget.parentId = this.props.widgetId; // Pass layout controls to children childWidget.useAutoLayout = this.state.useAutoLayout; - childWidget.direction = childWidget.direction || this.props.direction; + childWidget.direction = childWidget.direction || this.state.direction; childWidget.positioning = this.props.positioning; childWidget.alignment = this.props.alignment; childWidget.spacing = this.props.spacing; @@ -309,6 +312,7 @@ export interface ContainerWidgetProps export interface ContainerWidgetState extends WidgetState { useAutoLayout: boolean; + direction: LayoutDirection; } export default connect(null, mapDispatchToProps)(ContainerWidget); From 7a2425a9473ec410e94b32f5f760cc4033d94313 Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 11 Oct 2022 21:39:32 -0400 Subject: [PATCH 149/708] add check for mobile viewport --- .../appsmith/autoLayout/AutoLayoutLayer.tsx | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx index 1c1aad6005ab..d8f905917177 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx @@ -2,6 +2,7 @@ import React, { ReactNode } from "react"; import styled from "styled-components"; import { FlexDirection, LayoutDirection } from "components/constants"; +import { useIsMobileDevice } from "utils/hooks/useDeviceDetect"; /** * 1. Given a direction if should employ flex in perpendicular direction. @@ -33,13 +34,14 @@ const LayoutLayerContainer = styled.div<{ const SubWrapper = styled.div<{ flexDirection: FlexDirection; + hasFillChild?: boolean; }>` flex: 1 1 33.3%; display: flex; flex-direction: ${({ flexDirection }) => flexDirection || "row"}; align-items: ${({ flexDirection }) => flexDirection === FlexDirection.Column ? "flex-start" : "center"}; - flex-wrap: wrap; + flex-wrap: ${({ hasFillChild }) => (hasFillChild ? "wrap" : "nowrap")}; `; const StartWrapper = styled(SubWrapper)` @@ -67,13 +69,19 @@ function getInverseDirection(direction: LayoutDirection): LayoutDirection { } function AutoLayoutLayer(props: AutoLayoutLayerProps) { + const isMobile = useIsMobileDevice(); const flexDirection = getFlexDirection(getInverseDirection(props.direction)); return ( - {props.start} + + {props.start} + Date: Tue, 11 Oct 2022 22:46:09 -0400 Subject: [PATCH 150/708] add padding --- .../designSystems/appsmith/autoLayout/FlexComponent.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx index f35a37ad3241..040e7c5ebdb3 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx @@ -48,7 +48,7 @@ const FlexWidget = styled.div<{ min-width: ${({ minWidth }) => minWidth}; min-height: 30px; padding: ${({ isMobile, padding }) => - isMobile ? `${padding}px 0 0` : padding + "px"}; + isMobile ? `${padding}px 1px 0` : padding + "px"}; flex-grow: ${({ isFillWidget }) => (isFillWidget ? "1" : "0")}; From 7202f479dd259834b8f0a984d118fd3d72d1cd13 Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 12 Oct 2022 12:20:06 -0400 Subject: [PATCH 151/708] add layer actions for main container --- app/client/src/actions/autoLayoutActions.ts | 4 +- .../appsmith/autoLayout/AutoLayoutLayer.tsx | 6 +-- .../pages/Editor/CanvasPropertyPane/index.tsx | 15 ++++--- app/client/src/sagas/LayoutUpdateSagas.tsx | 6 +-- app/client/src/sagas/WidgetOperationUtils.ts | 41 +++++++++++-------- .../widgets/ContainerWidget/widget/index.tsx | 13 ++---- 6 files changed, 44 insertions(+), 41 deletions(-) diff --git a/app/client/src/actions/autoLayoutActions.ts b/app/client/src/actions/autoLayoutActions.ts index 2e1310596c35..6eb0833fcae1 100644 --- a/app/client/src/actions/autoLayoutActions.ts +++ b/app/client/src/actions/autoLayoutActions.ts @@ -1,15 +1,13 @@ import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants"; -import { LayoutDirection } from "components/constants"; export const removeWrappers = (parentId: string) => ({ type: ReduxActionTypes.REMOVE_CHILD_WRAPPERS, payload: { parentId }, }); -export const addWrappers = (parentId: string, direction: LayoutDirection) => ({ +export const addWrappers = (parentId: string) => ({ type: ReduxActionTypes.ADD_CHILD_WRAPPERS, payload: { parentId, - direction, }, }); diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx index d8f905917177..cf665fb1676d 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx @@ -34,14 +34,14 @@ const LayoutLayerContainer = styled.div<{ const SubWrapper = styled.div<{ flexDirection: FlexDirection; - hasFillChild?: boolean; + wrap?: boolean; }>` flex: 1 1 33.3%; display: flex; flex-direction: ${({ flexDirection }) => flexDirection || "row"}; align-items: ${({ flexDirection }) => flexDirection === FlexDirection.Column ? "flex-start" : "center"}; - flex-wrap: ${({ hasFillChild }) => (hasFillChild ? "wrap" : "nowrap")}; + flex-wrap: ${({ wrap }) => (wrap ? "wrap" : "nowrap")}; `; const StartWrapper = styled(SubWrapper)` @@ -78,7 +78,7 @@ function AutoLayoutLayer(props: AutoLayoutLayerProps) { > {props.start} diff --git a/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx b/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx index bde355969c82..a5f799ce19ac 100644 --- a/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx +++ b/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx @@ -11,6 +11,7 @@ import { batchUpdateMultipleWidgetProperties } from "actions/controlActions"; import { useSelector } from "store"; import { getWidgets } from "sagas/selectors"; import { Dropdown, DropdownOption, RenderOption } from "design-system"; +import { addWrappers, removeWrappers } from "actions/autoLayoutActions"; const Title = styled.p` color: ${Colors.GRAY_800}; @@ -42,22 +43,26 @@ const PositioningOptions = () => { }`} onClick={() => { setSelectedOption(options.indexOf(option as DropdownOption)); + const isVerticalStack = + (option as DropdownOption).value === Positioning.Vertical; + const widgetId = "0"; dispatch( batchUpdateMultipleWidgetProperties([ { - widgetId: "0", + widgetId, updates: { modify: { positioning: (option as DropdownOption).value, - direction: - (option as DropdownOption).value === Positioning.Vertical - ? LayoutDirection.Vertical - : LayoutDirection.Horizontal, + direction: isVerticalStack + ? LayoutDirection.Vertical + : LayoutDirection.Horizontal, }, }, }, ]), ); + if (isVerticalStack) dispatch(addWrappers(widgetId)); + else removeWrappers(widgetId); }} >
{(option as DropdownOption).label}
diff --git a/app/client/src/sagas/LayoutUpdateSagas.tsx b/app/client/src/sagas/LayoutUpdateSagas.tsx index 4d9fe676ce0e..0c5146a6ea72 100644 --- a/app/client/src/sagas/LayoutUpdateSagas.tsx +++ b/app/client/src/sagas/LayoutUpdateSagas.tsx @@ -4,16 +4,14 @@ import { ReduxActionErrorTypes, ReduxActionTypes, } from "ce/constants/ReduxActionConstants"; -import { LayoutDirection } from "components/constants"; import log from "loglevel"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; import { all, call, put, select, takeLatest } from "redux-saga/effects"; import { getWidgets } from "./selectors"; -import { purgeChildWrappers, wrapChildren } from "./WidgetOperationUtils"; +import { removeChildLayers, wrapChildren } from "./WidgetOperationUtils"; type LayoutUpdatePayload = { parentId: string; - direction: LayoutDirection; }; function* removeChildWrappers(actionPayload: ReduxAction) { @@ -21,7 +19,7 @@ function* removeChildWrappers(actionPayload: ReduxAction) { const start = performance.now(); const { parentId } = actionPayload.payload; const allWidgets: CanvasWidgetsReduxState = yield select(getWidgets); - const updatedWidgets: CanvasWidgetsReduxState = purgeChildWrappers( + const updatedWidgets: CanvasWidgetsReduxState = removeChildLayers( allWidgets, parentId, ); diff --git a/app/client/src/sagas/WidgetOperationUtils.ts b/app/client/src/sagas/WidgetOperationUtils.ts index 85716da80372..245823f978e3 100644 --- a/app/client/src/sagas/WidgetOperationUtils.ts +++ b/app/client/src/sagas/WidgetOperationUtils.ts @@ -1755,18 +1755,29 @@ export function resizePublishedMainCanvasToLowestWidget( ); } -export function purgeChildWrappers( +function getCanvas(widgets: CanvasWidgetsReduxState, containerId: string) { + const container = widgets[containerId]; + if (!container) return; + let canvas; + // True for MainContainer + if (container.type === "CANVAS_WIDGET") canvas = container; + else { + const canvasId = container.children ? container.children[0] : ""; + canvas = widgets[canvasId]; + } + if (!canvas) return; + return canvas; +} + +export function removeChildLayers( allWidgets: CanvasWidgetsReduxState, containerId: string, ): CanvasWidgetsReduxState { const widgets = { ...allWidgets }; - const container = widgets[containerId]; - if (!containerId) return widgets; - const canvasId = container.children ? container.children[0] : ""; - let parent = widgets[canvasId]; - if (!parent) return widgets; - parent = { ...parent, flexLayers: [] }; - widgets[canvasId] = parent; + let canvas = getCanvas(widgets, containerId); + if (!canvas) return widgets; + canvas = { ...canvas, flexLayers: [] }; + widgets[canvas.widgetId] = canvas; return widgets; } @@ -1775,12 +1786,10 @@ export function* wrapChildren( containerId: string, ) { const widgets = { ...allWidgets }; - const container = widgets[containerId]; - if (!container) return widgets; - const canvasId = container.children ? container.children[0] : ""; - let parent = widgets[canvasId]; - if (!parent) return widgets; - const children = parent.children || []; + let canvas = getCanvas(widgets, containerId); + if (!canvas) return widgets; + + const children = canvas.children || []; if (!children.length) return widgets; const flexLayers: FlexLayer[] = []; @@ -1794,8 +1803,8 @@ export function* wrapChildren( child.responsiveBehavior === ResponsiveBehavior.Fill || false, }); } - parent = { ...parent, flexLayers }; - widgets[canvasId] = parent; + canvas = { ...canvas, flexLayers }; + widgets[canvas.widgetId] = canvas; return widgets; } diff --git a/app/client/src/widgets/ContainerWidget/widget/index.tsx b/app/client/src/widgets/ContainerWidget/widget/index.tsx index eef4fd8291bf..8663e07e6640 100644 --- a/app/client/src/widgets/ContainerWidget/widget/index.tsx +++ b/app/client/src/widgets/ContainerWidget/widget/index.tsx @@ -190,13 +190,7 @@ export class ContainerWidget extends BaseWidget< updateWrappers = (): void => { if (this.props.positioning === Positioning.Vertical) { - this.props.addWrappers && - this.props.addWrappers( - this.props.widgetId, - (this.props.positioning as Positioning) === Positioning.Horizontal - ? LayoutDirection.Horizontal - : LayoutDirection.Vertical, - ); + this.props.addWrappers && this.props.addWrappers(this.props.widgetId); } else { this.props.removeWrappers && this.props.removeWrappers(this.props.widgetId); @@ -292,8 +286,7 @@ export class ContainerWidget extends BaseWidget< const mapDispatchToProps = (dispatch: any) => ({ removeWrappers: (id: string) => dispatch(removeWrappers(id)), - addWrappers: (id: string, direction: LayoutDirection) => - dispatch(addWrappers(id, direction)), + addWrappers: (id: string) => dispatch(addWrappers(id)), }); export interface ContainerWidgetProps @@ -307,7 +300,7 @@ export interface ContainerWidgetProps spacing?: Spacing; direction?: LayoutDirection; removeWrappers?: (id: string) => void; - addWrappers?: (id: string, direction: LayoutDirection) => void; + addWrappers?: (id: string) => void; } export interface ContainerWidgetState extends WidgetState { From 2ee76cbca2f851a68dee08ff5a9828a748644cd0 Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 12 Oct 2022 13:10:20 -0400 Subject: [PATCH 152/708] improve highlight selection calculation --- .../hooks/useAutoLayoutHighlights.ts | 48 ++++++++++++++----- 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index cbf4318c3ec0..46ada71b690b 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -508,8 +508,8 @@ export const useAutoLayoutHighlights = ({ // For vertical stacks, filter out the highlights based on drag direction and y position. if (moveDirection && direction === LayoutDirection.Vertical) { const isVerticalDrag = - moveDirection === ReflowDirection.TOP || - moveDirection === ReflowDirection.BOTTOM; + moveDirection && + [ReflowDirection.TOP, ReflowDirection.BOTTOM].includes(moveDirection); filteredHighlights = base.filter((highlight: HighlightInfo) => { // Return only horizontal highlights for vertical drag. @@ -517,28 +517,50 @@ export const useAutoLayoutHighlights = ({ // Return only vertical highlights for horizontal drag, if they lie in the same x plane. return ( highlight.isVertical && - (pos.y >= highlight.posY || - pos.y <= highlight.posY + highlight.height) + pos.y >= highlight.posY && + pos.y <= highlight.posY + highlight.height ); }); - // Additional redundancy check. - // For horizontal drag, if no vertical highlight exists in the same x plane, return the nearest horizontal highlight. - if (!isVerticalDrag && !filteredHighlights.length) - filteredHighlights = base.filter( + + // For horizontal drag, if no vertical highlight exists in the same x plane, + // return the last horizontal highlight. + if (!isVerticalDrag && !filteredHighlights.length) { + const horizontalHighlights = base.filter( (highlight: HighlightInfo) => !highlight.isVertical, ); + filteredHighlights = [ + horizontalHighlights[horizontalHighlights.length - 1], + ]; + } } const arr = [...filteredHighlights].sort((a, b) => { - return calculateDistance(a, pos) - calculateDistance(b, pos); + return ( + calculateDistance(a, pos, moveDirection) - + calculateDistance(b, pos, moveDirection) + ); }); + return arr[0]; }; - const calculateDistance = (a: HighlightInfo, b: XYCord): number => { - const x: number = a.posX + a.width / 2 - b.x; - const y: number = a.posY + a.height / 2 - b.y; - return Math.abs(Math.sqrt(x * x + y * y)); + const calculateDistance = ( + a: HighlightInfo, + b: XYCord, + moveDirection?: ReflowDirection, + ): number => { + /** + * Calculate perpendicular distance of a point from a line. + * If moving vertically, x is fixed (x = 0) and vice versa. + */ + const isVerticalDrag = + moveDirection && + [ReflowDirection.TOP, ReflowDirection.BOTTOM].includes(moveDirection); + + const distX: number = a.isVertical && isVerticalDrag ? 0 : a.posX - b.x; + const distY: number = !a.isVertical && !isVerticalDrag ? 0 : a.posY - b.y; + + return Math.abs(Math.sqrt(distX * distX + distY * distY)); }; const getDropInfo = (val: XYCord): HighlightInfo | undefined => { From 82ab44e1cd14c3f77448bfd0218d037b4e1ae3c6 Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 12 Oct 2022 13:24:18 -0400 Subject: [PATCH 153/708] move auto layout sagas to a separate file --- app/client/src/ce/sagas/index.tsx | 2 + .../CanvasSagas/AutoLayoutDraggingSagas.ts | 374 ++++++++++++++++++ .../sagas/CanvasSagas/DraggingCanvasSagas.ts | 362 +---------------- 3 files changed, 378 insertions(+), 360 deletions(-) create mode 100644 app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts diff --git a/app/client/src/ce/sagas/index.tsx b/app/client/src/ce/sagas/index.tsx index dc7e4cbbabdf..731854dc2a08 100644 --- a/app/client/src/ce/sagas/index.tsx +++ b/app/client/src/ce/sagas/index.tsx @@ -40,6 +40,7 @@ import formEvaluationChangeListener from "sagas/FormEvaluationSaga"; import SuperUserSagas from "@appsmith/sagas/SuperUserSagas"; import PageVisibilitySaga from "sagas/PageVisibilitySagas"; import layoutUpdateSagas from "sagas/LayoutUpdateSagas"; +import autoLayoutDraggingSagas from "sagas/CanvasSagas/AutoLayoutDraggingSagas"; export const sagas = [ initSagas, @@ -84,4 +85,5 @@ export const sagas = [ appThemingSaga, PageVisibilitySaga, layoutUpdateSagas, + autoLayoutDraggingSagas, ]; diff --git a/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts b/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts new file mode 100644 index 000000000000..7d15e2fd59aa --- /dev/null +++ b/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts @@ -0,0 +1,374 @@ +import { isArray } from "lodash"; +import { updateAndSaveLayout, WidgetAddChild } from "actions/pageActions"; +import { + ReduxAction, + ReduxActionTypes, +} from "ce/constants/ReduxActionConstants"; +import { + FlexLayerAlignment, + LayoutDirection, + ResponsiveBehavior, +} from "components/constants"; +import { HighlightInfo } from "pages/common/CanvasArenas/hooks/useAutoLayoutHighlights"; +import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; +import { all, call, put, select, takeLatest } from "redux-saga/effects"; +import log from "loglevel"; +import { getWidgets } from "sagas/selectors"; +import { + FlexLayer, + LayerChild, +} from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; +import { getUpdateDslAfterCreatingChild } from "sagas/WidgetAdditionSagas"; + +function* addWidgetAndReorderSaga( + actionPayload: ReduxAction<{ + newWidget: WidgetAddChild; + parentId: string; + direction: LayoutDirection; + dropPayload: HighlightInfo; + }>, +) { + const start = performance.now(); + const { direction, dropPayload, newWidget, parentId } = actionPayload.payload; + const { alignment, index, isNewLayer, layerIndex } = dropPayload; + try { + const updatedWidgetsOnAddition: CanvasWidgetsReduxState = yield call( + getUpdateDslAfterCreatingChild, + { + ...newWidget, + widgetId: parentId, + }, + ); + + const updatedWidgetsOnMove: CanvasWidgetsReduxState = yield call( + reorderAutolayoutChildren, + { + movedWidgets: [newWidget.newWidgetId], + index, + isNewLayer, + parentId, + allWidgets: updatedWidgetsOnAddition, + alignment, + direction, + layerIndex, + }, + ); + yield put(updateAndSaveLayout(updatedWidgetsOnMove)); + log.debug("reorder computations took", performance.now() - start, "ms"); + } catch (e) { + // console.error(e); + } +} + +function* autoLayoutReorderSaga( + actionPayload: ReduxAction<{ + movedWidgets: string[]; + parentId: string; + direction: LayoutDirection; + dropPayload: HighlightInfo; + }>, +) { + const start = performance.now(); + + const { + direction, + dropPayload, + movedWidgets, + parentId, + } = actionPayload.payload; + + const { alignment, index, isNewLayer, layerIndex } = dropPayload; + + try { + const allWidgets: CanvasWidgetsReduxState = yield select(getWidgets); + if (!parentId || !movedWidgets || !movedWidgets.length) return; + const updatedWidgets: CanvasWidgetsReduxState = yield call( + reorderAutolayoutChildren, + { + movedWidgets, + index, + isNewLayer, + parentId, + allWidgets, + alignment, + direction, + layerIndex, + }, + ); + + yield put(updateAndSaveLayout(updatedWidgets)); + log.debug("reorder computations took", performance.now() - start, "ms"); + } catch (e) { + // console.error(e); + } +} + +function* reorderAutolayoutChildren(params: { + movedWidgets: string[]; + index: number; + isNewLayer: boolean; + parentId: string; + allWidgets: CanvasWidgetsReduxState; + alignment: FlexLayerAlignment; + direction: LayoutDirection; + layerIndex?: number; +}) { + const { + alignment, + allWidgets, + direction, + index, + isNewLayer, + layerIndex, + movedWidgets, + parentId, + } = params; + const widgets = Object.assign({}, allWidgets); + if (!movedWidgets) return widgets; + const selectedWidgets = [...movedWidgets]; + + let updatedWidgets: CanvasWidgetsReduxState = updateRelationships( + selectedWidgets, + widgets, + parentId, + ); + + // Update flexLayers for a vertical stack. + if (direction === LayoutDirection.Vertical) { + const canvas = widgets[parentId]; + if (!canvas) return widgets; + const flexLayers = canvas.flexLayers || []; + // Remove moved widgets from the flex layers. + const filteredLayers = removeWidgetsFromCurrentLayers( + widgets, + selectedWidgets, + flexLayers, + ); + // Create a temporary layer from moved widgets. + const newLayer: FlexLayer = createFlexLayer( + selectedWidgets, + widgets, + alignment, + ); + + // Add the new layer to the flex layers. + updatedWidgets = isNewLayer + ? addNewLayer(newLayer, widgets, parentId, filteredLayers, layerIndex) + : updateExistingLayer( + newLayer, + widgets, + parentId, + filteredLayers, + index, + layerIndex, + ); + } + + // update children of the parent canvas. + const items = [...(widgets[parentId].children || [])]; + // remove moved widgets from children + const newItems = items.filter((item) => movedWidgets.indexOf(item) === -1); + // calculate valid position for drop + const pos = index > newItems.length ? newItems.length : index; + updatedWidgets[parentId] = { + ...updatedWidgets[parentId], + children: [ + ...newItems.slice(0, pos), + ...movedWidgets, + ...newItems.slice(pos), + ], + }; + + return updatedWidgets; +} + +/** + * For all moved widgets, + * delete relationship with previous parent and + * add relationship with new parent + * @param movedWidgets + * @param widgets + * @param parentId + * @returns widgets + */ +function updateRelationships( + movedWidgets: string[], + widgets: CanvasWidgetsReduxState, + parentId: string, +): CanvasWidgetsReduxState { + // Check if parent has changed + const orphans = movedWidgets.filter( + (item) => widgets[item].parentId !== parentId, + ); + if (orphans && orphans.length) { + //parent has changed + orphans.forEach((item) => { + // remove from previous parent + const prevParentId = widgets[item].parentId; + if (prevParentId !== undefined) { + const prevParent = Object.assign({}, widgets[prevParentId]); + if (prevParent.children && isArray(prevParent.children)) { + const updatedPrevParent = { + ...prevParent, + children: prevParent.children.filter((each) => each !== item), + flexLayers: removeWidgetsFromCurrentLayers( + widgets, + movedWidgets, + prevParent.flexLayers, + ), + }; + widgets[prevParentId] = updatedPrevParent; + } + } + + // add to new parent + widgets[item] = { + ...widgets[item], + parentId: parentId, + }; + }); + } + return widgets; +} + +function addNewLayer( + newLayer: FlexLayer, + allWidgets: CanvasWidgetsReduxState, + parentId: string, + layers: FlexLayer[], + layerIndex = 0, +): CanvasWidgetsReduxState { + const widgets: CanvasWidgetsReduxState = Object.assign({}, allWidgets); + const canvas = widgets[parentId]; + + const pos = layerIndex > layers.length ? layers.length : layerIndex; + + const updatedCanvas = { + ...canvas, + flexLayers: [...layers.slice(0, pos), newLayer, ...layers.slice(pos)], + }; + const updatedWidgets = { + ...widgets, + [parentId]: updatedCanvas, + }; + + return updatedWidgets; +} + +function updateExistingLayer( + newLayer: FlexLayer, + allWidgets: CanvasWidgetsReduxState, + parentId: string, + layers: FlexLayer[], + index: number, + layerIndex = 0, +): CanvasWidgetsReduxState { + const widgets: CanvasWidgetsReduxState = Object.assign({}, allWidgets); + const canvas = widgets[parentId]; + if (!canvas || !newLayer) return widgets; + + let selectedLayer: FlexLayer = layers[layerIndex]; + // Calculate the number of children in the container before the selected layer. + let childCount = 0; + layers.forEach((layer: FlexLayer, index: number) => { + if (index >= layerIndex) return; + childCount += layer.children.length; + }); + const pos = index - childCount; + + // merge the selected layer with the new layer. + selectedLayer = { + ...selectedLayer, + children: [ + ...selectedLayer.children.slice(0, pos), + ...newLayer.children, + ...selectedLayer.children.slice(pos), + ], + hasFillChild: newLayer.hasFillChild || selectedLayer.hasFillChild, + }; + + const updatedCanvas = { + ...canvas, + flexLayers: [ + ...layers.slice(0, layerIndex), + selectedLayer, + ...layers.slice(layerIndex + 1), + ], + }; + + const updatedWidgets = { ...widgets, [parentId]: updatedCanvas }; + return updatedWidgets; +} + +/** + * Transform movedWidgets to FlexLayer format, + * and determine if the new widgets have a fill child. + * @param movedWidgets + * @param allWidgets + * @param alignment + * @returns hasFillChild: boolean, layerChildren: string[] + */ +function createFlexLayer( + movedWidgets: string[], + allWidgets: CanvasWidgetsReduxState, + alignment: FlexLayerAlignment, +): FlexLayer { + let hasFillChild = false; + const children = []; + if (movedWidgets && movedWidgets.length) { + for (const id of movedWidgets) { + const widget = allWidgets[id]; + if (!widget) continue; + if (widget.responsiveBehavior === ResponsiveBehavior.Fill) + hasFillChild = true; + children.push({ id, align: alignment }); + } + } + return { children, hasFillChild }; +} + +/** + * Remove moved widgets from current layers. + * and update hasFillChild property. + * Return non-empty layers. + * @param allWidgets + * @param movedWidgets + * @param flexLayers + * @returns FlexLayer[] + */ +export function removeWidgetsFromCurrentLayers( + allWidgets: CanvasWidgetsReduxState, + movedWidgets: string[], + flexLayers: FlexLayer[], +): FlexLayer[] { + if (!flexLayers || !flexLayers.length) return []; + return flexLayers?.reduce((acc: FlexLayer[], layer: FlexLayer) => { + const children = layer.children.filter( + (each: LayerChild) => movedWidgets.indexOf(each.id) === -1, + ); + if (children.length) { + acc.push({ + ...layer, + children, + hasFillChild: children.some( + (each: LayerChild) => + allWidgets[each.id].responsiveBehavior === ResponsiveBehavior.Fill, + ), + }); + } + return acc; + }, []); +} + +export default function* autoLayoutDraggingSagas() { + yield all([ + takeLatest( + ReduxActionTypes.AUTOLAYOUT_REORDER_WIDGETS, + autoLayoutReorderSaga, + ), + takeLatest( + ReduxActionTypes.AUTOLAYOUT_ADD_NEW_WIDGETS, + addWidgetAndReorderSaga, + ), + ]); +} diff --git a/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts b/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts index f629252e10de..927360c9ee0c 100644 --- a/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts +++ b/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts @@ -10,7 +10,7 @@ import { } from "reducers/entityReducers/canvasWidgetsReducer"; import { all, call, put, select, takeLatest } from "redux-saga/effects"; import log from "loglevel"; -import { cloneDeep, isArray } from "lodash"; +import { cloneDeep } from "lodash"; import { updateAndSaveLayout, WidgetAddChild } from "actions/pageActions"; import { calculateDropTargetRows } from "components/editorComponents/DropTargetUtils"; import { @@ -30,16 +30,7 @@ import { getUpdateDslAfterCreatingChild } from "sagas/WidgetAdditionSagas"; import { MainCanvasReduxState } from "reducers/uiReducers/mainCanvasReducer"; import { CANVAS_DEFAULT_MIN_HEIGHT_PX } from "constants/AppConstants"; import AnalyticsUtil from "utils/AnalyticsUtil"; -import { - LayoutDirection, - FlexLayerAlignment, - ResponsiveBehavior, -} from "components/constants"; -import { - FlexLayer, - LayerChild, -} from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; -import { HighlightInfo } from "pages/common/CanvasArenas/hooks/useAutoLayoutHighlights"; +import { removeWidgetsFromCurrentLayers } from "./AutoLayoutDraggingSagas"; export type WidgetMoveParams = { widgetId: string; @@ -416,347 +407,6 @@ function moveWidget(widgetMoveParams: WidgetMoveParams) { return widgets; } -function* autoLayoutReorderSaga( - actionPayload: ReduxAction<{ - movedWidgets: string[]; - parentId: string; - direction: LayoutDirection; - dropPayload: HighlightInfo; - }>, -) { - const start = performance.now(); - - const { - direction, - dropPayload, - movedWidgets, - parentId, - } = actionPayload.payload; - - const { alignment, index, isNewLayer, layerIndex } = dropPayload; - - try { - const allWidgets: CanvasWidgetsReduxState = yield select(getWidgets); - if (!parentId || !movedWidgets || !movedWidgets.length) return; - const updatedWidgets: CanvasWidgetsReduxState = yield call( - reorderAutolayoutChildren, - { - movedWidgets, - index, - isNewLayer, - parentId, - allWidgets, - alignment, - direction, - layerIndex, - }, - ); - - yield put(updateAndSaveLayout(updatedWidgets)); - log.debug("reorder computations took", performance.now() - start, "ms"); - } catch (e) { - // console.error(e); - } -} - -function* reorderAutolayoutChildren(params: { - movedWidgets: string[]; - index: number; - isNewLayer: boolean; - parentId: string; - allWidgets: CanvasWidgetsReduxState; - alignment: FlexLayerAlignment; - direction: LayoutDirection; - layerIndex?: number; -}) { - const { - alignment, - allWidgets, - direction, - index, - isNewLayer, - layerIndex, - movedWidgets, - parentId, - } = params; - const widgets = Object.assign({}, allWidgets); - if (!movedWidgets) return widgets; - const selectedWidgets = [...movedWidgets]; - - let updatedWidgets: CanvasWidgetsReduxState = updateRelationships( - selectedWidgets, - widgets, - parentId, - ); - - // Update flexLayers for a vertical stack. - if (direction === LayoutDirection.Vertical) { - const canvas = widgets[parentId]; - if (!canvas) return widgets; - const flexLayers = canvas.flexLayers || []; - // Remove moved widgets from the flex layers. - const filteredLayers = removeWidgetsFromCurrentLayers( - widgets, - selectedWidgets, - flexLayers, - ); - // Create a temporary layer from moved widgets. - const newLayer: FlexLayer = createFlexLayer( - selectedWidgets, - widgets, - alignment, - ); - - // Add the new layer to the flex layers. - updatedWidgets = isNewLayer - ? addNewLayer(newLayer, widgets, parentId, filteredLayers, layerIndex) - : updateExistingLayer( - newLayer, - widgets, - parentId, - filteredLayers, - index, - layerIndex, - ); - } - - // update children of the parent canvas. - const items = [...(widgets[parentId].children || [])]; - // remove moved widgets from children - const newItems = items.filter((item) => movedWidgets.indexOf(item) === -1); - // calculate valid position for drop - const pos = index > newItems.length ? newItems.length : index; - updatedWidgets[parentId] = { - ...updatedWidgets[parentId], - children: [ - ...newItems.slice(0, pos), - ...movedWidgets, - ...newItems.slice(pos), - ], - }; - - return updatedWidgets; -} - -/** - * For all moved widgets, - * delete relationship with previous parent and - * add relationship with new parent - * @param movedWidgets - * @param widgets - * @param parentId - * @returns widgets - */ - -function updateRelationships( - movedWidgets: string[], - widgets: CanvasWidgetsReduxState, - parentId: string, -): CanvasWidgetsReduxState { - // Check if parent has changed - const orphans = movedWidgets.filter( - (item) => widgets[item].parentId !== parentId, - ); - if (orphans && orphans.length) { - //parent has changed - orphans.forEach((item) => { - // remove from previous parent - const prevParentId = widgets[item].parentId; - if (prevParentId !== undefined) { - const prevParent = Object.assign({}, widgets[prevParentId]); - if (prevParent.children && isArray(prevParent.children)) { - const updatedPrevParent = { - ...prevParent, - children: prevParent.children.filter((each) => each !== item), - flexLayers: removeWidgetsFromCurrentLayers( - widgets, - movedWidgets, - prevParent.flexLayers, - ), - }; - widgets[prevParentId] = updatedPrevParent; - } - } - - // add to new parent - widgets[item] = { - ...widgets[item], - parentId: parentId, - }; - }); - } - return widgets; -} - -function addNewLayer( - newLayer: FlexLayer, - allWidgets: CanvasWidgetsReduxState, - parentId: string, - layers: FlexLayer[], - layerIndex = 0, -): CanvasWidgetsReduxState { - const widgets: CanvasWidgetsReduxState = Object.assign({}, allWidgets); - const canvas = widgets[parentId]; - - const pos = layerIndex > layers.length ? layers.length : layerIndex; - - const updatedCanvas = { - ...canvas, - flexLayers: [...layers.slice(0, pos), newLayer, ...layers.slice(pos)], - }; - const updatedWidgets = { - ...widgets, - [parentId]: updatedCanvas, - }; - - return updatedWidgets; -} - -function updateExistingLayer( - newLayer: FlexLayer, - allWidgets: CanvasWidgetsReduxState, - parentId: string, - layers: FlexLayer[], - index: number, - layerIndex = 0, -): CanvasWidgetsReduxState { - const widgets: CanvasWidgetsReduxState = Object.assign({}, allWidgets); - const canvas = widgets[parentId]; - if (!canvas || !newLayer) return widgets; - - let selectedLayer: FlexLayer = layers[layerIndex]; - // Calculate the number of children in the container before the selected layer. - let childCount = 0; - layers.forEach((layer: FlexLayer, index: number) => { - if (index >= layerIndex) return; - childCount += layer.children.length; - }); - const pos = index - childCount; - - // merge the selected layer with the new layer. - selectedLayer = { - ...selectedLayer, - children: [ - ...selectedLayer.children.slice(0, pos), - ...newLayer.children, - ...selectedLayer.children.slice(pos), - ], - hasFillChild: newLayer.hasFillChild || selectedLayer.hasFillChild, - }; - - const updatedCanvas = { - ...canvas, - flexLayers: [ - ...layers.slice(0, layerIndex), - selectedLayer, - ...layers.slice(layerIndex + 1), - ], - }; - - const updatedWidgets = { ...widgets, [parentId]: updatedCanvas }; - return updatedWidgets; -} - -/** - * Remove moved widgets from current layers. - * and update hasFillChild property. - * Return non-empty layers. - * @param allWidgets - * @param movedWidgets - * @param flexLayers - * @returns FlexLayer[] - */ -function removeWidgetsFromCurrentLayers( - allWidgets: CanvasWidgetsReduxState, - movedWidgets: string[], - flexLayers: FlexLayer[], -): FlexLayer[] { - if (!flexLayers || !flexLayers.length) return []; - return flexLayers?.reduce((acc: FlexLayer[], layer: FlexLayer) => { - const children = layer.children.filter( - (each: LayerChild) => movedWidgets.indexOf(each.id) === -1, - ); - if (children.length) { - acc.push({ - ...layer, - children, - hasFillChild: children.some( - (each: LayerChild) => - allWidgets[each.id].responsiveBehavior === ResponsiveBehavior.Fill, - ), - }); - } - return acc; - }, []); -} - -/** - * Transform movedWidgets to FlexLayer format, - * and determine if the new widgets have a fill child. - * @param movedWidgets - * @param allWidgets - * @param alignment - * @returns hasFillChild: boolean, layerChildren: string[] - */ -function createFlexLayer( - movedWidgets: string[], - allWidgets: CanvasWidgetsReduxState, - alignment: FlexLayerAlignment, -): FlexLayer { - let hasFillChild = false; - const children = []; - if (movedWidgets && movedWidgets.length) { - for (const id of movedWidgets) { - const widget = allWidgets[id]; - if (!widget) continue; - if (widget.responsiveBehavior === ResponsiveBehavior.Fill) - hasFillChild = true; - children.push({ id, align: alignment }); - } - } - return { children, hasFillChild }; -} - -function* addWidgetAndReorderSaga( - actionPayload: ReduxAction<{ - newWidget: WidgetAddChild; - parentId: string; - direction: LayoutDirection; - dropPayload: HighlightInfo; - }>, -) { - const start = performance.now(); - const { direction, dropPayload, newWidget, parentId } = actionPayload.payload; - const { alignment, index, isNewLayer, layerIndex } = dropPayload; - try { - const updatedWidgetsOnAddition: CanvasWidgetsReduxState = yield call( - getUpdateDslAfterCreatingChild, - { - ...newWidget, - widgetId: parentId, - }, - ); - - const updatedWidgetsOnMove: CanvasWidgetsReduxState = yield call( - reorderAutolayoutChildren, - { - movedWidgets: [newWidget.newWidgetId], - index, - isNewLayer, - parentId, - allWidgets: updatedWidgetsOnAddition, - alignment, - direction, - layerIndex, - }, - ); - yield put(updateAndSaveLayout(updatedWidgetsOnMove)); - log.debug("reorder computations took", performance.now() - start, "ms"); - } catch (e) { - // console.error(e); - } -} - export default function* draggingCanvasSagas() { yield all([ takeLatest(ReduxActionTypes.WIDGETS_MOVE, moveWidgetsSaga), @@ -764,13 +414,5 @@ export default function* draggingCanvasSagas() { ReduxActionTypes.WIDGETS_ADD_CHILD_AND_MOVE, addWidgetAndMoveWidgetsSaga, ), - takeLatest( - ReduxActionTypes.AUTOLAYOUT_REORDER_WIDGETS, - autoLayoutReorderSaga, - ), - takeLatest( - ReduxActionTypes.AUTOLAYOUT_ADD_NEW_WIDGETS, - addWidgetAndReorderSaga, - ), ]); } From af115d93ab399573572167bde6aad25c57c3ff16 Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 12 Oct 2022 22:54:07 -0400 Subject: [PATCH 154/708] remove container state --- .../pages/Editor/CanvasPropertyPane/index.tsx | 2 + app/client/src/widgets/BaseWidget.tsx | 9 ++-- app/client/src/widgets/CanvasWidget.tsx | 19 +++---- .../widgets/ContainerWidget/widget/index.tsx | 49 ++++--------------- app/client/src/widgets/constants.ts | 3 +- 5 files changed, 25 insertions(+), 57 deletions(-) diff --git a/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx b/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx index a5f799ce19ac..3d9d7352f4a2 100644 --- a/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx +++ b/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx @@ -53,6 +53,8 @@ const PositioningOptions = () => { updates: { modify: { positioning: (option as DropdownOption).value, + useAutoLayout: + (option as DropdownOption).value !== Positioning.Fixed, direction: isVerticalStack ? LayoutDirection.Vertical : LayoutDirection.Horizontal, diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index b678a31c4d01..80602f21eb0e 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -389,7 +389,7 @@ abstract class BaseWidget< content = this.makeDraggable(content); content = this.makeSnipeable(content); // NOTE: In sniping mode we are not blocking onClick events from PositionWrapper. - if (this.props.useAutoLayout) content = this.makeFlex(content); + if (this.props.isFlexChild) content = this.makeFlex(content); else content = this.makePositioned(content); } return content; @@ -399,7 +399,7 @@ abstract class BaseWidget< content = this.getWidgetComponent(); if (this.props.isVisible) { content = this.addErrorBoundary(content); - if (this.props.useAutoLayout) content = this.makeFlex(content); + if (this.props.isFlexChild) content = this.makeFlex(content); else if (!this.props.detachFromLayout) { content = this.makePositioned(content); } @@ -441,7 +441,7 @@ abstract class BaseWidget< isDeletable: true, resizeDisabled: false, disablePropertyPane: false, - useAutoLayout: false, + isFlexChild: false, }; } @@ -494,9 +494,8 @@ export interface WidgetPositionProps extends WidgetRowCols { // MODAL_WIDGET is also detached from layout. detachFromLayout?: boolean; noContainerOffset?: boolean; // This won't offset the child in parent - useAutoLayout?: boolean; + isFlexChild?: boolean; direction?: LayoutDirection; - alignItems?: AlignItems; responsiveBehavior?: ResponsiveBehavior; } diff --git a/app/client/src/widgets/CanvasWidget.tsx b/app/client/src/widgets/CanvasWidget.tsx index 6d7dcbbf29eb..73e4d99e07e9 100644 --- a/app/client/src/widgets/CanvasWidget.tsx +++ b/app/client/src/widgets/CanvasWidget.tsx @@ -73,10 +73,8 @@ class CanvasWidget extends ContainerWidget { // Pass layout controls to children childWidget.positioning = childWidget?.positioning || this.props.positioning; - childWidget.useAutoLayout = this.state.useAutoLayout; - childWidget.direction = childWidget?.direction || this.state.direction; - childWidget.justifyContent = this.props.justifyContent; - childWidget.alignItems = this.props.alignItems; + childWidget.isFlexChild = this.props.useAutoLayout; + childWidget.direction = this.props.direction; return WidgetFactory.createWidget(childWidget, this.props.renderMode); } @@ -98,12 +96,12 @@ class CanvasWidget extends ContainerWidget { {...this.getSnapSpaces()} alignItems={props.alignItems} canExtend={props.canExtend} - direction={this.state.direction} + direction={this.props.direction} dropDisabled={!!props.dropDisabled} noPad={this.props.noPad} parentId={props.parentId} snapRows={snapRows} - useAutoLayout={this.state.useAutoLayout} + useAutoLayout={this.props.useAutoLayout} widgetId={props.widgetId} widgetName={props.widgetName} /> @@ -125,15 +123,15 @@ class CanvasWidget extends ContainerWidget { /> {/* without the wrapping div onClick events are triggered twice */} {this.renderChildren()} @@ -152,8 +150,7 @@ class CanvasWidget extends ContainerWidget { const style: CSSProperties = { width: "100%", - height: - this.props.positioning !== Positioning.Fixed ? "100%" : `${height}px`, + height: this.props.useAutoLayout ? "100%" : `${height}px`, background: "none", position: "relative", }; diff --git a/app/client/src/widgets/ContainerWidget/widget/index.tsx b/app/client/src/widgets/ContainerWidget/widget/index.tsx index 8663e07e6640..b484aa4878ec 100644 --- a/app/client/src/widgets/ContainerWidget/widget/index.tsx +++ b/app/client/src/widgets/ContainerWidget/widget/index.tsx @@ -17,11 +17,9 @@ import { compact, map, sortBy } from "lodash"; import WidgetsMultiSelectBox from "pages/Editor/WidgetsMultiSelectBox"; import { - Alignment, LayoutDirection, Positioning, ResponsiveBehavior, - Spacing, } from "components/constants"; import { generatePositioningConfig, @@ -32,14 +30,10 @@ import { addWrappers, removeWrappers } from "actions/autoLayoutActions"; export class ContainerWidget extends BaseWidget< ContainerWidgetProps, - ContainerWidgetState + WidgetState > { constructor(props: ContainerWidgetProps) { super(props); - this.state = { - useAutoLayout: false, - direction: LayoutDirection.Horizontal, - }; this.renderChildWidget = this.renderChildWidget.bind(this); } @@ -164,30 +158,13 @@ export class ContainerWidget extends BaseWidget< componentDidMount(): void { super.componentDidMount(); - this.updatePositioningInformation(); } componentDidUpdate(prevProps: ContainerWidgetProps): void { super.componentDidUpdate(prevProps); - if (this.props.positioning !== prevProps.positioning) { - this.updatePositioningInformation(); - this.updateWrappers(); - } + if (this.props.positioning !== prevProps.positioning) this.updateWrappers(); } - updatePositioningInformation = (): void => { - if (!this.props.positioning || this.props.positioning === Positioning.Fixed) - this.setState({ useAutoLayout: false }); - else - this.setState({ - useAutoLayout: true, - direction: - this.props.positioning === Positioning.Vertical - ? LayoutDirection.Vertical - : LayoutDirection.Horizontal, - }); - }; - updateWrappers = (): void => { if (this.props.positioning === Positioning.Vertical) { this.props.addWrappers && this.props.addWrappers(this.props.widgetId); @@ -237,11 +214,13 @@ export class ContainerWidget extends BaseWidget< childWidget.parentId = this.props.widgetId; // Pass layout controls to children - childWidget.useAutoLayout = this.state.useAutoLayout; - childWidget.direction = childWidget.direction || this.state.direction; - childWidget.positioning = this.props.positioning; - childWidget.alignment = this.props.alignment; - childWidget.spacing = this.props.spacing; + childWidget.positioning = + childWidget?.positioning || this.props.positioning; + childWidget.useAutoLayout = this.props.positioning !== Positioning.Fixed; + childWidget.direction = + this.props.positioning === Positioning.Vertical + ? LayoutDirection.Vertical + : LayoutDirection.Horizontal; return WidgetFactory.createWidget(childWidget, this.props.renderMode); } @@ -251,7 +230,7 @@ export class ContainerWidget extends BaseWidget< // sort by row so stacking context is correct // TODO(abhinav): This is hacky. The stacking context should increase for widgets rendered top to bottom, always. // Figure out a way in which the stacking context is consistent. - this.state.useAutoLayout + this.props.positioning !== Positioning.Fixed ? this.props.children : sortBy(compact(this.props.children), (child) => child.topRow), this.renderChildWidget, @@ -296,17 +275,9 @@ export interface ContainerWidgetProps shouldScrollContents?: boolean; noPad?: boolean; positioning?: Positioning; - alignment?: Alignment; - spacing?: Spacing; - direction?: LayoutDirection; removeWrappers?: (id: string) => void; addWrappers?: (id: string) => void; } -export interface ContainerWidgetState extends WidgetState { - useAutoLayout: boolean; - direction: LayoutDirection; -} - export default connect(null, mapDispatchToProps)(ContainerWidget); // export default ContainerWidget; diff --git a/app/client/src/widgets/constants.ts b/app/client/src/widgets/constants.ts index ad27b823cf73..a88336865c65 100644 --- a/app/client/src/widgets/constants.ts +++ b/app/client/src/widgets/constants.ts @@ -58,8 +58,7 @@ interface LayoutProps { positioning?: Positioning; useAutoLayout?: boolean; direction?: LayoutDirection; - justifyContent?: JustifyContent; - alignItems?: AlignItems; + isFlexChild?: boolean; responsiveBehavior?: ResponsiveBehavior; } From 81cc69808278bc2e2b119e67b97d3b9e57a803ed Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 12 Oct 2022 22:54:28 -0400 Subject: [PATCH 155/708] rever resizable component --- .../editorComponents/ResizableComponent.tsx | 210 +++++------------- .../src/resizable/resizenreflow/index.tsx | 28 +-- 2 files changed, 67 insertions(+), 171 deletions(-) diff --git a/app/client/src/components/editorComponents/ResizableComponent.tsx b/app/client/src/components/editorComponents/ResizableComponent.tsx index e359a80ac803..45f63ef18b3b 100644 --- a/app/client/src/components/editorComponents/ResizableComponent.tsx +++ b/app/client/src/components/editorComponents/ResizableComponent.tsx @@ -1,58 +1,53 @@ -import { AppState } from "@appsmith/reducers"; -import { focusWidget } from "actions/widgetActions"; +import React, { useContext, memo, useMemo } from "react"; import { - AlignItems, - LayoutDirection, - ResponsiveBehavior, -} from "components/constants"; + WidgetOperations, + WidgetRowCols, + WidgetProps, +} from "widgets/BaseWidget"; import { EditorContext } from "components/editorComponents/EditorContextProvider"; -import { GridDefaults } from "constants/WidgetConstants"; -import { get, omit } from "lodash"; -import { XYCord } from "pages/common/CanvasArenas/hooks/useCanvasDragging"; -import React, { memo, useContext, useEffect, useMemo, useState } from "react"; -import { useSelector } from "react-redux"; -import Resizable from "resizable/resizenreflow"; import { - previewModeSelector, - snipingModeSelector, -} from "selectors/editorSelectors"; -import { - getParentToOpenSelector, - isCurrentWidgetFocused, - isCurrentWidgetLastSelected, - isMultiSelectedWidget, - isWidgetSelected, -} from "selectors/widgetSelectors"; -import AnalyticsUtil from "utils/AnalyticsUtil"; + UIElementSize, + computeFinalRowCols, + computeRowCols, +} from "./ResizableUtils"; import { useShowPropertyPane, useShowTableFilterPane, useWidgetDragResize, } from "utils/hooks/dragResizeHooks"; -import { useWidgetSelection } from "utils/hooks/useWidgetSelection"; +import { useSelector } from "react-redux"; +import { AppState } from "@appsmith/reducers"; +import Resizable from "resizable/resizenreflow"; +import { omit, get } from "lodash"; import { getSnapColumns } from "utils/WidgetPropsUtils"; import { - WidgetOperations, - WidgetProps, - WidgetRowCols, -} from "widgets/BaseWidget"; -import { DropTargetContext } from "./DropTargetComponent"; -import { - computeFinalRowCols, - computeRowCols, - UIElementSize, -} from "./ResizableUtils"; -import { - BottomHandleStyles, - BottomLeftHandleStyles, - BottomRightHandleStyles, + VisibilityContainer, LeftHandleStyles, RightHandleStyles, TopHandleStyles, + BottomHandleStyles, TopLeftHandleStyles, TopRightHandleStyles, - VisibilityContainer, + BottomLeftHandleStyles, + BottomRightHandleStyles, } from "./ResizeStyledComponents"; +import AnalyticsUtil from "utils/AnalyticsUtil"; +import { + previewModeSelector, + snipingModeSelector, +} from "selectors/editorSelectors"; +import { useWidgetSelection } from "utils/hooks/useWidgetSelection"; +import { focusWidget } from "actions/widgetActions"; +import { GridDefaults } from "constants/WidgetConstants"; +import { DropTargetContext } from "./DropTargetComponent"; +import { XYCord } from "pages/common/CanvasArenas/hooks/useCanvasDragging"; +import { getParentToOpenSelector } from "selectors/widgetSelectors"; +import { + isCurrentWidgetFocused, + isCurrentWidgetLastSelected, + isWidgetSelected, + isMultiSelectedWidget, +} from "selectors/widgetSelectors"; export type ResizableComponentProps = WidgetProps & { paddingOffset: number; @@ -61,15 +56,9 @@ export type ResizableComponentProps = WidgetProps & { export const ResizableComponent = memo(function ResizableComponent( props: ResizableComponentProps, ) { - const [componentWidth, setComponentWidth] = useState(0); - const [componentHeight, setComponentHeight] = useState(0); // Fetch information from the context const { updateWidget } = useContext(EditorContext); - const isHorizontallyStretched = - props.direction === LayoutDirection.Vertical && - props.alignItems === AlignItems.Stretch; - const isSnipingMode = useSelector(snipingModeSelector); const isPreviewMode = useSelector(previewModeSelector); @@ -99,77 +88,18 @@ export const ResizableComponent = memo(function ResizableComponent( const isParentWidgetSelected = useSelector( isCurrentWidgetLastSelected(parentWidgetToSelect?.widgetId || ""), ); - const isWidgetFocused: boolean = isFocused || isLastSelected || isSelected; - - useEffect(() => { - // Set initial dimensions - // if (props.widgetName.toLowerCase().includes("button")) - // console.log(`#### ${props.widgetName} : Initial dimensions`); - setComponentWidth( - props.useAutoLayout && isHorizontallyStretched - ? 64 * props.parentColumnSpace - 2 * props.paddingOffset - : (props.rightColumn - props.leftColumn) * props.parentColumnSpace - - 2 * props.paddingOffset, - ); - setComponentHeight( - (props.bottomRow - props.topRow) * props.parentRowSpace - - 2 * props.paddingOffset, - ); - }, [props.useAutoLayout, props.direction, props.alignItems]); - - useEffect(() => { - // if (props.widgetName.toLowerCase().includes("button")) - // console.log(`#### ${props.widgetName} : Manual resize`); - setComponentWidth( - props.useAutoLayout && isHorizontallyStretched - ? 64 * props.parentColumnSpace - 2 * props.paddingOffset - : (props.rightColumn - props.leftColumn) * props.parentColumnSpace - - 2 * props.paddingOffset, - ); - setComponentHeight( - (props.bottomRow - props.topRow) * props.parentRowSpace - - 2 * props.paddingOffset, - ); - }, [props.topRow, props.bottomRow, props.leftColumn, props.rightColumn]); - - useEffect(() => { - // if (props.widgetName.toLowerCase().includes("button")) - // console.log(`#### ${props.widgetName} : Parent resize`); - if (!props.useAutoLayout) { - setComponentWidth( - (props.rightColumn - props.leftColumn) * props.parentColumnSpace - - 2 * props.paddingOffset, - ); - setComponentHeight( - (props.bottomRow - props.topRow) * props.parentRowSpace - - 2 * props.paddingOffset, - ); - } else { - if (isHorizontallyStretched) { - setComponentWidth( - 64 * props.parentColumnSpace - 2 * props.paddingOffset, - ); - } - } - }, [props.parentColumnSpace, props.parentRowSpace, props.useAutoLayout]); + const isWidgetFocused = isFocused || isLastSelected || isSelected; // Calculate the dimensions of the widget, // The ResizableContainer's size prop is controlled - // const dimensions: UIElementSize = { - // width: - // useAutoLayout && - // direction === LayoutDirection.Vertical && - // alignItems === AlignItems.Stretch - // ? 64 * props.parentColumnSpace - 2 * props.paddingOffset - // : (props.rightColumn - props.leftColumn) * props.parentColumnSpace - - // 2 * props.paddingOffset, - // height: - // (props.bottomRow - props.topRow) * props.parentRowSpace - - // 2 * props.paddingOffset, - // }; - - // console.log(`#### ${props.widgetName} : width - ${componentWidth}`); - // console.log(`#### ${props.widgetName} : bottomRow - ${props.bottomRow}`); + const dimensions: UIElementSize = { + width: + (props.rightColumn - props.leftColumn) * props.parentColumnSpace - + 2 * props.paddingOffset, + height: + (props.bottomRow - props.topRow) * props.parentRowSpace - + 2 * props.paddingOffset, + }; // onResize handler const getResizedPositions = ( @@ -177,10 +107,9 @@ export const ResizableComponent = memo(function ResizableComponent( position: XYCord, ) => { const delta: UIElementSize = { - height: newDimensions.height - componentHeight, - width: newDimensions.width - componentWidth, + height: newDimensions.height - dimensions.height, + width: newDimensions.width - dimensions.width, }; - const newRowCols: WidgetRowCols = computeRowCols(delta, position, props); let canResizeHorizontally = true, canResizeVertically = true; @@ -231,12 +160,11 @@ export const ResizableComponent = memo(function ResizableComponent( // Update widget, if both of the above are true. const updateSize = (newDimensions: UIElementSize, position: XYCord) => { // Get the difference in size of the widget, before and after resizing. - // console.log(`#### ${props.widgetName} : update size`); const delta: UIElementSize = { - height: newDimensions.height - componentHeight, - width: newDimensions.width - componentWidth, + height: newDimensions.height - dimensions.height, + width: newDimensions.width - dimensions.width, }; - // console.log(`#### ${props.widgetName} : delta - ${delta.height}`); + // Get the updated Widget rows and columns props // False, if there is collision // False, if none of the rows and cols have changed. @@ -245,8 +173,7 @@ export const ResizableComponent = memo(function ResizableComponent( position, props, ); - // console.log("#### new row cols"); - // console.log(newRowCols); + if (newRowCols) { updateWidget && updateWidget(WidgetOperations.RESIZE, props.widgetId, { @@ -281,8 +208,8 @@ export const ResizableComponent = memo(function ResizableComponent( AnalyticsUtil.logEvent("WIDGET_RESIZE_END", { widgetName: props.widgetName, widgetType: props.type, - startHeight: componentHeight, - startWidth: componentWidth, + startHeight: dimensions.height, + startWidth: dimensions.width, endHeight: newDimensions.height, endWidth: newDimensions.width, }); @@ -298,21 +225,6 @@ export const ResizableComponent = memo(function ResizableComponent( widgetType: props.type, }); }; - let disabledHorizontalHandles: string[] = []; - if ( - props.useAutoLayout && - props.direction === LayoutDirection.Vertical && - props.responsiveBehavior === ResponsiveBehavior.Fill - ) { - disabledHorizontalHandles = [ - "left", - "right", - "bottomLeft", - "bottomRight", - "topLeft", - "topRight", - ]; - } const handles = useMemo(() => { const allHandles = { left: LeftHandleStyles, @@ -324,14 +236,11 @@ export const ResizableComponent = memo(function ResizableComponent( topRight: TopRightHandleStyles, bottomLeft: BottomLeftHandleStyles, }; - let handlesToOmit = get(props, "disabledResizeHandles", []); - // if (disabledResizeHandles && disabledResizeHandles.length) - // handlesToOmit = [...handlesToOmit, ...disabledResizeHandles]; - handlesToOmit = [...handlesToOmit, ...disabledHorizontalHandles]; - return omit(allHandles, handlesToOmit); - }, [props, disabledHorizontalHandles]); - const isEnabled: boolean = + return omit(allHandles, get(props, "disabledResizeHandles", [])); + }, [props]); + + const isEnabled = !isDragging && isWidgetFocused && !props.resizeDisabled && @@ -359,17 +268,17 @@ export const ResizableComponent = memo(function ResizableComponent( updateDropTargetRows && updateDropTargetRows([props.parentId], bottom); } }; + return ( ` display: block; @@ -156,9 +156,7 @@ type ResizableProps = { gridProps: GridProps; zWidgetType?: string; zWidgetId?: string; - useAutoLayout?: boolean; - isWrapper?: boolean; - direction?: LayoutDirection; + isFlexChild?: boolean; responsiveBehavior?: ResponsiveBehavior; }; @@ -250,13 +248,6 @@ export function ReflowResizable(props: ResizableProps) { bottomMostRow = 0, movementLimitMap: MovementLimitMap | undefined = {}; - if (!props?.useAutoLayout && resizedPositions) { - const isColliding = checkForCollision(resizedPositions); - if (isColliding) { - return prevState; - } - } - if (resizedPositions) { //calling reflow to update movements of reflowing widgets and get movementLimit of current resizing widget ({ bottomMostRow, movementLimitMap } = reflow( @@ -482,16 +473,15 @@ export function ReflowResizable(props: ResizableProps) { snapGrid={props.snapGrid} /> )); - // Don't alter dimensions on temp reflow in children of auto layouts + const widgetWidth = - reflowedPosition?.width === undefined || props?.useAutoLayout + reflowedPosition?.width === undefined ? newDimensions.width : reflowedPosition.width - 2 * WIDGET_PADDING; const widgetHeight = - reflowedPosition?.height === undefined || props?.useAutoLayout + reflowedPosition?.height === undefined ? newDimensions.height : reflowedPosition.height - 2 * WIDGET_PADDING; - return ( From b9eb4f2533d1aac8ec6c18f8b471f8f0e499d9bd Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 12 Oct 2022 23:28:57 -0400 Subject: [PATCH 156/708] bug fixes --- .../designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx | 3 +-- .../designSystems/appsmith/autoLayout/FlexBoxComponent.tsx | 1 + app/client/src/widgets/ContainerWidget/widget/index.tsx | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx index cf665fb1676d..7a8a02b61cc6 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx @@ -39,8 +39,7 @@ const SubWrapper = styled.div<{ flex: 1 1 33.3%; display: flex; flex-direction: ${({ flexDirection }) => flexDirection || "row"}; - align-items: ${({ flexDirection }) => - flexDirection === FlexDirection.Column ? "flex-start" : "center"}; + align-items: "flex-start"; flex-wrap: ${({ wrap }) => (wrap ? "wrap" : "nowrap")}; `; diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx index a6e7519ca901..1457d12bbeee 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx @@ -50,6 +50,7 @@ export const FlexContainer = styled.div<{ overflow: ${({ overflow }) => overflow?.indexOf("wrap") === -1 ? overflow : "hidden"}; + overflow-y: auto; padding: 4px; `; diff --git a/app/client/src/widgets/ContainerWidget/widget/index.tsx b/app/client/src/widgets/ContainerWidget/widget/index.tsx index b484aa4878ec..8f239b29fe74 100644 --- a/app/client/src/widgets/ContainerWidget/widget/index.tsx +++ b/app/client/src/widgets/ContainerWidget/widget/index.tsx @@ -221,6 +221,7 @@ export class ContainerWidget extends BaseWidget< this.props.positioning === Positioning.Vertical ? LayoutDirection.Vertical : LayoutDirection.Horizontal; + childWidget.isFlexChild = this.props.positioning !== Positioning.Fixed; return WidgetFactory.createWidget(childWidget, this.props.renderMode); } From a1eea1a29cde64ecf3b41361db415052fc41a924 Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 13 Oct 2022 00:50:58 -0400 Subject: [PATCH 157/708] minor updates --- .../appsmith/autoLayout/AutoLayoutLayer.tsx | 5 ++--- .../appsmith/autoLayout/FlexBoxComponent.tsx | 11 ++++++++--- .../src/widgets/ContainerWidget/widget/index.tsx | 1 - 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx index 7a8a02b61cc6..fff1a00a0247 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx @@ -2,7 +2,6 @@ import React, { ReactNode } from "react"; import styled from "styled-components"; import { FlexDirection, LayoutDirection } from "components/constants"; -import { useIsMobileDevice } from "utils/hooks/useDeviceDetect"; /** * 1. Given a direction if should employ flex in perpendicular direction. @@ -18,6 +17,7 @@ export interface AutoLayoutLayerProps { hasFillChild?: boolean; index: number; widgetId: string; + isMobile?: boolean; } const LayoutLayerContainer = styled.div<{ @@ -68,7 +68,6 @@ function getInverseDirection(direction: LayoutDirection): LayoutDirection { } function AutoLayoutLayer(props: AutoLayoutLayerProps) { - const isMobile = useIsMobileDevice(); const flexDirection = getFlexDirection(getInverseDirection(props.direction)); return ( {props.start} diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx index 1457d12bbeee..c6ae0d30580e 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx @@ -10,6 +10,7 @@ import { } from "components/constants"; import { useSelector } from "react-redux"; import AutoLayoutLayer from "./AutoLayoutLayer"; +import { useIsMobileDevice } from "utils/hooks/useDeviceDetect"; export interface FlexBoxProps { direction?: LayoutDirection; @@ -36,6 +37,7 @@ export const FlexContainer = styled.div<{ direction?: LayoutDirection; stretchHeight: boolean; overflow: Overflow; + isMobile?: boolean; }>` display: ${({ useAutoLayout }) => (useAutoLayout ? "flex" : "block")}; flex-direction: ${({ direction }) => @@ -48,13 +50,14 @@ export const FlexContainer = styled.div<{ width: 100%; height: ${({ stretchHeight }) => (stretchHeight ? "100%" : "auto")}; - overflow: ${({ overflow }) => - overflow?.indexOf("wrap") === -1 ? overflow : "hidden"}; - overflow-y: auto; + overflow: "hidden"; + overflow-y: ${({ isMobile }) => (isMobile ? "auto" : "hidden")}; padding: 4px; `; function FlexBoxComponent(props: FlexBoxProps) { + // TODO: set isMobile as a prop at the top level + const isMobile = useIsMobileDevice(); const direction: LayoutDirection = props.direction || LayoutDirection.Horizontal; @@ -171,6 +174,7 @@ function FlexBoxComponent(props: FlexBoxProps) { end={end} hasFillChild={layer.hasFillChild} index={index} + isMobile={isMobile} key={index} start={start} widgetId={props.widgetId} @@ -183,6 +187,7 @@ function FlexBoxComponent(props: FlexBoxProps) { Date: Thu, 13 Oct 2022 01:04:54 -0400 Subject: [PATCH 158/708] move direction to canvas --- app/client/src/widgets/CanvasWidget.tsx | 15 +++++++++++---- .../src/widgets/ContainerWidget/widget/index.tsx | 10 +--------- app/client/src/widgets/constants.ts | 2 -- 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/app/client/src/widgets/CanvasWidget.tsx b/app/client/src/widgets/CanvasWidget.tsx index 73e4d99e07e9..f8e402291b25 100644 --- a/app/client/src/widgets/CanvasWidget.tsx +++ b/app/client/src/widgets/CanvasWidget.tsx @@ -74,7 +74,7 @@ class CanvasWidget extends ContainerWidget { childWidget.positioning = childWidget?.positioning || this.props.positioning; childWidget.isFlexChild = this.props.useAutoLayout; - childWidget.direction = this.props.direction; + childWidget.direction = this.getDirection(); return WidgetFactory.createWidget(childWidget, this.props.renderMode); } @@ -82,6 +82,7 @@ class CanvasWidget extends ContainerWidget { renderAsContainerComponent( props: ContainerWidgetProps, ): JSX.Element { + const direction = this.getDirection(); const snapRows = getCanvasSnapRows(props.bottomRow, props.canExtend); const stretchFlexBox = !this.props.children || !this.props.children?.length @@ -96,7 +97,7 @@ class CanvasWidget extends ContainerWidget { {...this.getSnapSpaces()} alignItems={props.alignItems} canExtend={props.canExtend} - direction={this.props.direction} + direction={direction} dropDisabled={!!props.dropDisabled} noPad={this.props.noPad} parentId={props.parentId} @@ -123,10 +124,10 @@ class CanvasWidget extends ContainerWidget { /> {/* without the wrapping div onClick events are triggered twice */} Date: Thu, 13 Oct 2022 15:07:18 +0530 Subject: [PATCH 159/708] Adding debounce and fixing preview block. --- .../appsmith/autoLayout/FlexBoxComponent.tsx | 21 +++++++++- .../hooks/useAutoLayoutHighlights.ts | 18 ++++---- app/client/src/widgets/BaseWidget.tsx | 42 +++++++++---------- 3 files changed, 49 insertions(+), 32 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx index c6ae0d30580e..c7ae3b72fbf2 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx @@ -9,8 +9,8 @@ import { Overflow, } from "components/constants"; import { useSelector } from "react-redux"; -import AutoLayoutLayer from "./AutoLayoutLayer"; import { useIsMobileDevice } from "utils/hooks/useDeviceDetect"; +import AutoLayoutLayer from "./AutoLayoutLayer"; export interface FlexBoxProps { direction?: LayoutDirection; @@ -120,7 +120,9 @@ function FlexBoxComponent(props: FlexBoxProps) { } const layers = - flexHighlight?.isNewLayer && flexHighlight?.layerIndex + flexHighlight?.isNewLayer && + flexHighlight?.layerIndex && + flexHighlight.layerIndex > props.flexLayers.length ? [ ...props.flexLayers.slice(0, flexHighlight?.index), { children: [], hasFillChild: false }, @@ -166,6 +168,21 @@ function FlexBoxComponent(props: FlexBoxProps) { if (child.align === "end") end.push(widget); else if (child.align === "center") center.push(widget); else start.push(widget); + if ( + flexHighlight && + index === flexHighlight.layerIndex && + childCount === flexHighlight.index && + dragDetails.draggedOn === props.widgetId + ) { + const previewNode = getPreviewNode(); + if (flexHighlight.alignment === "start") { + start.push(previewNode); + } else if (flexHighlight.alignment === "center") { + center.push(previewNode); + } else { + end.push(previewNode); + } + } } return ( { + dispatch({ + type: ReduxActionTypes.SET_AUTOLAYOUT_HIGHLIGHTS, + payload: { + flexHighlight: pos, + blocksToDraw, + }, + }); + }, 50); /** * END AUTO LAYOUT OFFSET CALCULATION @@ -453,13 +463,7 @@ export const useAutoLayoutHighlights = ({ e, moveDirection, ); - dispatch({ - type: ReduxActionTypes.SET_AUTOLAYOUT_HIGHLIGHTS, - payload: { - flexHighlight: pos, - blocksToDraw, - }, - }); + debouncedDispatch(pos); // console.log({ pos }); if (!pos) return; diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index 80602f21eb0e..cf39f1904b67 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -3,6 +3,19 @@ * spawing components based on those props * Widgets are also responsible for dispatching actions and updating the state tree */ +import { BatchPropertyUpdatePayload } from "actions/controlActions"; +import { LayoutDirection, ResponsiveBehavior } from "components/constants"; +import FlexComponent from "components/designSystems/appsmith/autoLayout/FlexComponent"; +import PositionedContainer from "components/designSystems/appsmith/PositionedContainer"; +import DraggableComponent from "components/editorComponents/DraggableComponent"; +import { EditorContext } from "components/editorComponents/EditorContextProvider"; +import ErrorBoundary from "components/editorComponents/ErrorBoundry"; +import PreviewModeComponent from "components/editorComponents/PreviewModeComponent"; +import ResizableComponent from "components/editorComponents/ResizableComponent"; +import SnipeableComponent from "components/editorComponents/SnipeableComponent"; +import WidgetNameComponent from "components/editorComponents/WidgetNameComponent"; +import { ExecuteTriggerPayload } from "constants/AppsmithActionConstants/ActionConstants"; +import { PropertyPaneConfig } from "constants/PropertyControlConstants"; import { CSSUnit, PositionType, @@ -10,39 +23,22 @@ import { RenderModes, WidgetType, } from "constants/WidgetConstants"; -import React, { Component, ReactNode } from "react"; +import { ENTITY_TYPE } from "entities/AppsmithConsole"; +import { DataTreeWidget } from "entities/DataTree/dataTreeFactory"; import { get, memoize } from "lodash"; -import DraggableComponent from "components/editorComponents/DraggableComponent"; -import SnipeableComponent from "components/editorComponents/SnipeableComponent"; -import ResizableComponent from "components/editorComponents/ResizableComponent"; -import { ExecuteTriggerPayload } from "constants/AppsmithActionConstants/ActionConstants"; -import PositionedContainer from "components/designSystems/appsmith/PositionedContainer"; -import WidgetNameComponent from "components/editorComponents/WidgetNameComponent"; +import React, { Component, ReactNode } from "react"; import shallowequal from "shallowequal"; -import { EditorContext } from "components/editorComponents/EditorContextProvider"; -import ErrorBoundary from "components/editorComponents/ErrorBoundry"; -import { DerivedPropertiesMap } from "utils/WidgetFactory"; +import AppsmithConsole from "utils/AppsmithConsole"; import { DataTreeEvaluationProps, - EVAL_ERROR_PATH, EvaluationError, + EVAL_ERROR_PATH, PropertyEvaluationErrorType, WidgetDynamicPathListProps, } from "utils/DynamicBindingUtils"; -import { PropertyPaneConfig } from "constants/PropertyControlConstants"; -import { BatchPropertyUpdatePayload } from "actions/controlActions"; -import AppsmithConsole from "utils/AppsmithConsole"; -import { ENTITY_TYPE } from "entities/AppsmithConsole"; -import PreviewModeComponent from "components/editorComponents/PreviewModeComponent"; -import { - AlignItems, - LayoutDirection, - ResponsiveBehavior, -} from "components/constants"; +import { DerivedPropertiesMap } from "utils/WidgetFactory"; import { CanvasWidgetStructure } from "./constants"; -import { DataTreeWidget } from "entities/DataTree/dataTreeFactory"; import Skeleton from "./Skeleton"; -import FlexComponent from "components/designSystems/appsmith/autoLayout/FlexComponent"; /*** * BaseWidget From 9879970fb48f3e3ade3c4885e985d76284ac3f72 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Thu, 13 Oct 2022 15:38:19 +0530 Subject: [PATCH 160/708] fixing preview blocks. --- .../appsmith/autoLayout/FlexBoxComponent.tsx | 22 ++++++------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx index c7ae3b72fbf2..8ee4eb3d40fb 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx @@ -118,22 +118,9 @@ function FlexBoxComponent(props: FlexBoxProps) { map[(child as JSX.Element).props?.widgetId] = child; } } - - const layers = - flexHighlight?.isNewLayer && - flexHighlight?.layerIndex && - flexHighlight.layerIndex > props.flexLayers.length - ? [ - ...props.flexLayers.slice(0, flexHighlight?.index), - { children: [], hasFillChild: false }, - ...props.flexLayers.slice( - flexHighlight?.index, - props.flexLayers.length, - ), - ] - : props.flexLayers; let childCount = 0; - return layers.map((layer: FlexLayer, index: number) => { + let highLightAdded = false; + return props.flexLayers.map((layer: FlexLayer, index: number) => { const { children, hasFillChild } = layer; const start = [], center = [], @@ -146,6 +133,7 @@ function FlexBoxComponent(props: FlexBoxProps) { for (const child of children) { if ( flexHighlight && + !highLightAdded && index === flexHighlight.layerIndex && childCount === flexHighlight.index && dragDetails.draggedOn === props.widgetId @@ -158,6 +146,7 @@ function FlexBoxComponent(props: FlexBoxProps) { } else { end.push(previewNode); } + highLightAdded = true; } childCount++; const widget = map[child.id]; @@ -170,6 +159,8 @@ function FlexBoxComponent(props: FlexBoxProps) { else start.push(widget); if ( flexHighlight && + !highLightAdded && + !flexHighlight.isNewLayer && index === flexHighlight.layerIndex && childCount === flexHighlight.index && dragDetails.draggedOn === props.widgetId @@ -182,6 +173,7 @@ function FlexBoxComponent(props: FlexBoxProps) { } else { end.push(previewNode); } + highLightAdded = true; } } return ( From 8e3dcd7812fe1c0c1506e0926b420ce61010e1ab Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 13 Oct 2022 13:53:29 -0400 Subject: [PATCH 161/708] fix new layer issue for vertical and last child issue for horizontal --- .../appsmith/autoLayout/FlexBoxComponent.tsx | 55 ++++++++++++++++--- .../hooks/useAutoLayoutHighlights.ts | 2 +- 2 files changed, 47 insertions(+), 10 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx index 8ee4eb3d40fb..8b1718186a6d 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx @@ -69,6 +69,10 @@ function FlexBoxComponent(props: FlexBoxProps) { (state: AppState) => state.ui.widgetDragResize, ); + const draggedWidgets: string[] = isArray(autoLayoutDragDetails) + ? autoLayoutDragDetails.map((each) => each.widgetId) + : []; + const getPreviewNode = () => { return React.createElement(() => { const height = autoLayoutDragDetails @@ -97,11 +101,19 @@ function FlexBoxComponent(props: FlexBoxProps) { ) { if (flexHighlight && dragDetails.draggedOn === props.widgetId) { const previewNode = getPreviewNode(); - const totalChildren = (props.children as any).length; + const filteredChildren = (props.children as any)?.filter( + (child: any) => { + return ( + draggedWidgets.indexOf( + (child as JSX.Element)?.props?.widgetId, + ) === -1 + ); + }, + ); const allChildren = [ - ...(props.children as any).slice(0, flexHighlight?.index), + ...filteredChildren.slice(0, flexHighlight?.index), previewNode, - ...(props.children as any).slice(flexHighlight?.index, totalChildren), + ...filteredChildren.slice(flexHighlight?.index), ]; return allChildren; } else { @@ -118,11 +130,14 @@ function FlexBoxComponent(props: FlexBoxProps) { map[(child as JSX.Element).props?.widgetId] = child; } } - let childCount = 0; + let childCount = 0, + index = 0; let highLightAdded = false; - return props.flexLayers.map((layer: FlexLayer, index: number) => { + const layers: any[] = []; + // TODO: Add highlight index within a layer to the data model to simplify this logic. + for (const layer of props.flexLayers) { const { children, hasFillChild } = layer; - const start = [], + let start = [], center = [], end = []; if (!children || !children.length) { @@ -147,6 +162,26 @@ function FlexBoxComponent(props: FlexBoxProps) { end.push(previewNode); } highLightAdded = true; + + if (flexHighlight.isNewLayer) { + layers.push( + , + ); + index += 1; + start = []; + center = []; + end = []; + } } childCount++; const widget = map[child.id]; @@ -176,7 +211,7 @@ function FlexBoxComponent(props: FlexBoxProps) { highLightAdded = true; } } - return ( + layers.push( + />, ); - }); + index += 1; + } + return layers; }; return ( diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index a76bb693d6c5..7db7996ee11d 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -451,7 +451,7 @@ export const useAutoLayoutHighlights = ({ blocksToDraw, }, }); - }, 50); + }, 5); /** * END AUTO LAYOUT OFFSET CALCULATION From 2fdba6c2505f72126fa466600001df22624f33f1 Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 13 Oct 2022 16:17:48 -0400 Subject: [PATCH 162/708] update highlight data model and flex box component --- .../appsmith/autoLayout/FlexBoxComponent.tsx | 180 ++++++++---------- .../editorComponents/ResizableComponent.tsx | 16 +- .../hooks/useAutoLayoutHighlights.ts | 20 +- .../src/resizable/resizenreflow/index.tsx | 6 +- 4 files changed, 110 insertions(+), 112 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx index 8b1718186a6d..ecd78a4dbaaa 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx @@ -1,6 +1,6 @@ -import { isArray } from "lodash"; import React, { ReactNode } from "react"; import styled from "styled-components"; +import { isArray } from "lodash"; import { AppState } from "ce/reducers"; import { @@ -61,14 +61,12 @@ function FlexBoxComponent(props: FlexBoxProps) { const direction: LayoutDirection = props.direction || LayoutDirection.Horizontal; - // const layoutProps = useMemo( - // () => getLayoutProperties(props.direction, props.alignment, props.spacing), - // [props.direction, props.alignment, props.spacing], - // ); const { autoLayoutDragDetails, dragDetails, flexHighlight } = useSelector( (state: AppState) => state.ui.widgetDragResize, ); + const isCurrentCanvasDragging = dragDetails?.draggedOn === props.widgetId; + const draggedWidgets: string[] = isArray(autoLayoutDragDetails) ? autoLayoutDragDetails.map((each) => each.widgetId) : []; @@ -94,12 +92,12 @@ function FlexBoxComponent(props: FlexBoxProps) { const renderChildren = () => { if (!props.children) return null; + if (!props.useAutoLayout) return props.children; if ( - !props.useAutoLayout || direction === LayoutDirection.Horizontal || !(props.flexLayers && props.flexLayers.length) ) { - if (flexHighlight && dragDetails.draggedOn === props.widgetId) { + if (flexHighlight && isCurrentCanvasDragging) { const previewNode = getPreviewNode(); const filteredChildren = (props.children as any)?.filter( (child: any) => { @@ -110,12 +108,11 @@ function FlexBoxComponent(props: FlexBoxProps) { ); }, ); - const allChildren = [ - ...filteredChildren.slice(0, flexHighlight?.index), + return addInPosition( + filteredChildren, + flexHighlight?.index, previewNode, - ...filteredChildren.slice(flexHighlight?.index), - ]; - return allChildren; + ); } else { return props.children; } @@ -130,105 +127,88 @@ function FlexBoxComponent(props: FlexBoxProps) { map[(child as JSX.Element).props?.widgetId] = child; } } - let childCount = 0, - index = 0; - let highLightAdded = false; - const layers: any[] = []; - // TODO: Add highlight index within a layer to the data model to simplify this logic. - for (const layer of props.flexLayers) { - const { children, hasFillChild } = layer; - let start = [], - center = [], - end = []; - if (!children || !children.length) { - const previewNode = getPreviewNode(); - start.push(previewNode); - } - for (const child of children) { - if ( - flexHighlight && - !highLightAdded && - index === flexHighlight.layerIndex && - childCount === flexHighlight.index && - dragDetails.draggedOn === props.widgetId - ) { - const previewNode = getPreviewNode(); - if (flexHighlight.alignment === "start") { - start.push(previewNode); - } else if (flexHighlight.alignment === "center") { - center.push(previewNode); - } else { - end.push(previewNode); - } - highLightAdded = true; - - if (flexHighlight.isNewLayer) { - layers.push( - , - ); - index += 1; - start = []; - center = []; - end = []; - } - } - childCount++; - const widget = map[child.id]; - if (hasFillChild) { - start.push(widget); - continue; - } - if (child.align === "end") end.push(widget); - else if (child.align === "center") center.push(widget); - else start.push(widget); - if ( - flexHighlight && - !highLightAdded && - !flexHighlight.isNewLayer && - index === flexHighlight.layerIndex && - childCount === flexHighlight.index && - dragDetails.draggedOn === props.widgetId - ) { - const previewNode = getPreviewNode(); - if (flexHighlight.alignment === "start") { - start.push(previewNode); - } else if (flexHighlight.alignment === "center") { - center.push(previewNode); - } else { - end.push(previewNode); - } - highLightAdded = true; - } - } - layers.push( + const previewNode = getPreviewNode(); + const layers: any[] = processLayers(map, previewNode); + + if (flexHighlight?.isNewLayer && isCurrentCanvasDragging) { + return addInPosition( + layers, + flexHighlight.layerIndex !== undefined + ? flexHighlight.layerIndex + : layers.length, , ); - index += 1; } + return layers; }; + function processLayers(map: { [key: string]: any }, previewNode: any) { + return props.flexLayers + ?.map((layer: FlexLayer, index: number) => { + const { children, hasFillChild } = layer; + let start = [], + center = [], + end = []; + if (!children || !children.length) return null; + + for (const child of children) { + const widget = map[child.id]; + if (hasFillChild) { + start.push(widget); + continue; + } + if (child.align === "end") end.push(widget); + else if (child.align === "center") center.push(widget); + else start.push(widget); + } + + if ( + isCurrentCanvasDragging && + !flexHighlight?.isNewLayer && + index === flexHighlight?.layerIndex + ) { + const pos = flexHighlight?.rowIndex || 0; + if (flexHighlight?.alignment === "start") + start = addInPosition(start, pos, previewNode); + else if (flexHighlight?.alignment === "center") + center = addInPosition(center, pos, previewNode); + else if (flexHighlight?.alignment === "end") + end = addInPosition(end, pos, previewNode); + } + + return ( + + ); + }) + ?.filter((layer) => layer !== null); + } + + function addInPosition(arr: any[], index: number, item: any): any[] { + return [...arr.slice(0, index), item, ...arr.slice(index)]; + } + return ( { const allHandles = { left: LeftHandleStyles, @@ -236,9 +243,11 @@ export const ResizableComponent = memo(function ResizableComponent( topRight: TopRightHandleStyles, bottomLeft: BottomLeftHandleStyles, }; - - return omit(allHandles, get(props, "disabledResizeHandles", [])); - }, [props]); + let handlesToOmit = get(props, "disabledResizeHandles", []); + if (disabledHorizontalHandles && disabledHorizontalHandles.length) + handlesToOmit = [...handlesToOmit, ...disabledHorizontalHandles]; + return omit(allHandles, handlesToOmit); + }, [props, disabledHorizontalHandles]); const isEnabled = !isDragging && @@ -274,6 +283,7 @@ export const ResizableComponent = memo(function ResizableComponent( allowResize={!isMultiSelected} componentHeight={dimensions.height} componentWidth={dimensions.width} + direction={props.direction} enable={isEnabled} getResizedPositions={getResizedPositions} gridProps={gridProps} diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index 7db7996ee11d..e78ccf32fdf3 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -25,6 +25,7 @@ export interface HighlightInfo { isNewLayer: boolean; // determines if a new layer / child has been added directly to the container. index: number; // index of the child in props.children. layerIndex?: number; // index of layer in props.flexLayers. + rowIndex?: number; // index of highlight within a horizontal layer. alignment: FlexLayerAlignment; // alignment of the child in the layer. posX: number; // x position of the highlight. posY: number; // y position of the highlight. @@ -150,7 +151,7 @@ export const useAutoLayoutHighlights = ({ * 3. Discount dragged blocks and empty layers from the calculation. * 4. hide the dragged blocks and empty layers. */ - cleanUpTempStyles(); // TODO: is this needed? + cleanUpTempStyles(); if (useAutoLayout && isDragging && isCurrentDraggedCanvas) { const draggedBlocks = getDraggedBlocks(); if (!draggedBlocks || !draggedBlocks.length) return []; @@ -181,7 +182,7 @@ export const useAutoLayoutHighlights = ({ ); } } - // console.log("#### highlights: ", highlights); + // console.log("#### highlights", highlights); return highlights; }; @@ -395,6 +396,7 @@ export const useAutoLayoutHighlights = ({ width: verticalFlex ? rect?.width : OFFSET_WIDTH, height: verticalFlex ? OFFSET_WIDTH : rect.height, isVertical: !verticalFlex, + rowIndex: 0, }; } @@ -407,7 +409,7 @@ export const useAutoLayoutHighlights = ({ const arr: HighlightInfo[] = []; const childRects: DOMRect[] = []; let index = childCount; - + let rowIndex = 0; for (const child of children) { const el = getDomElement(child); if (!el) continue; @@ -424,8 +426,10 @@ export const useAutoLayoutHighlights = ({ width: OFFSET_WIDTH, height: childRect?.height, isVertical: true, + rowIndex, }); index += 1; + rowIndex += 1; } // A highlight after the last child. @@ -440,9 +444,15 @@ export const useAutoLayoutHighlights = ({ width: OFFSET_WIDTH, height: lastRect?.height, isVertical: true, + rowIndex, }); return arr; } + + /** + * END AUTO LAYOUT OFFSET CALCULATION + */ + const debouncedDispatch = debounce((pos: HighlightInfo) => { dispatch({ type: ReduxActionTypes.SET_AUTOLAYOUT_HIGHLIGHTS, @@ -453,10 +463,6 @@ export const useAutoLayoutHighlights = ({ }); }, 5); - /** - * END AUTO LAYOUT OFFSET CALCULATION - */ - const highlightDropPosition = (e: any, moveDirection: ReflowDirection) => { if (!useAutoLayout) return; const pos: HighlightInfo | undefined = getHighlightPosition( diff --git a/app/client/src/resizable/resizenreflow/index.tsx b/app/client/src/resizable/resizenreflow/index.tsx index 011232732e7e..50ede0805ddb 100644 --- a/app/client/src/resizable/resizenreflow/index.tsx +++ b/app/client/src/resizable/resizenreflow/index.tsx @@ -19,7 +19,7 @@ import { import { getNearestParentCanvas } from "utils/generators"; import { getContainerOccupiedSpacesSelectorWhileResizing } from "selectors/editorSelectors"; import { isDropZoneOccupied } from "utils/WidgetPropsUtils"; -import { ResponsiveBehavior } from "components/constants"; +import { LayoutDirection, ResponsiveBehavior } from "components/constants"; const ResizeWrapper = styled(animated.div)<{ prevents: boolean }>` display: block; @@ -158,6 +158,7 @@ type ResizableProps = { zWidgetId?: string; isFlexChild?: boolean; responsiveBehavior?: ResponsiveBehavior; + direction?: LayoutDirection; }; export function ReflowResizable(props: ResizableProps) { @@ -497,7 +498,8 @@ export function ReflowResizable(props: ResizableProps) { to={{ width: props.isFlexChild && - props.responsiveBehavior === ResponsiveBehavior.Fill + props.responsiveBehavior === ResponsiveBehavior.Fill && + props.direction === LayoutDirection.Vertical ? "auto" : widgetWidth, height: widgetHeight, From 2b8c7c2bff045cd66912d40fd460c79a50008f0a Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 13 Oct 2022 16:23:36 -0400 Subject: [PATCH 163/708] ignore collision for flex children --- .../src/components/editorComponents/ResizableComponent.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/client/src/components/editorComponents/ResizableComponent.tsx b/app/client/src/components/editorComponents/ResizableComponent.tsx index 224a9e975644..d5184fd9abe0 100644 --- a/app/client/src/components/editorComponents/ResizableComponent.tsx +++ b/app/client/src/components/editorComponents/ResizableComponent.tsx @@ -116,7 +116,7 @@ export const ResizableComponent = memo(function ResizableComponent( canResizeVertically = true; // this is required for list widget so that template have no collision - if (props.ignoreCollision) + if (props.ignoreCollision || props.isFlexChild) return { canResizeHorizontally, canResizeVertically, From 8591d0c9b220fb05d7493bdd9855cd02f8b9365c Mon Sep 17 00:00:00 2001 From: Preet Date: Fri, 14 Oct 2022 08:28:35 -0400 Subject: [PATCH 164/708] fix tabs widget issue --- app/client/src/widgets/TabsWidget/widget/index.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/client/src/widgets/TabsWidget/widget/index.tsx b/app/client/src/widgets/TabsWidget/widget/index.tsx index 54ce3e5e2656..3747bb0968d9 100644 --- a/app/client/src/widgets/TabsWidget/widget/index.tsx +++ b/app/client/src/widgets/TabsWidget/widget/index.tsx @@ -350,6 +350,8 @@ class TabsWidget extends BaseWidget< (item) => item.widgetId === selectedTabWidgetId, )[0]; childWidgetData.positioning = selectedTabProps?.positioning; + childWidgetData.useAutoLayout = + selectedTabProps?.positioning !== Positioning.Fixed; childWidgetData.direction = selectedTabProps?.positioning === Positioning.Vertical ? LayoutDirection.Vertical From 3231b216ac510aa0fdcb4e378b3771788c74e296 Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 17 Oct 2022 13:26:27 -0400 Subject: [PATCH 165/708] add additionalAction prop to PropertyControl Disconnect container from store. --- .../src/ce/constants/ReduxActionConstants.tsx | 1 + .../constants/PropertyControlConstants.tsx | 6 + .../Editor/PropertyPane/PropertyControl.tsx | 26 ++++ app/client/src/sagas/AutoLayoutUtils.ts | 128 ++++++++++++++++++ app/client/src/sagas/LayoutUpdateSagas.tsx | 36 ++++- app/client/src/sagas/WidgetDeletionSagas.ts | 2 +- app/client/src/sagas/WidgetOperationUtils.ts | 89 ------------ app/client/src/utils/layoutPropertiesUtils.ts | 33 ++++- .../widgets/ContainerWidget/widget/index.tsx | 26 +--- 9 files changed, 230 insertions(+), 117 deletions(-) create mode 100644 app/client/src/sagas/AutoLayoutUtils.ts diff --git a/app/client/src/ce/constants/ReduxActionConstants.tsx b/app/client/src/ce/constants/ReduxActionConstants.tsx index fe7d3c0f7845..431ed2f06f18 100644 --- a/app/client/src/ce/constants/ReduxActionConstants.tsx +++ b/app/client/src/ce/constants/ReduxActionConstants.tsx @@ -676,6 +676,7 @@ export const ReduxActionTypes = { AUTOLAYOUT_ADD_NEW_WIDGETS: "AUTOLAYOUT_ADD_NEW_WIDGETS", REMOVE_CHILD_WRAPPERS: "REMOVE_CHILD_WRAPPERS", ADD_CHILD_WRAPPERS: "ADD_CHILD_WRAPPERS", + UPDATE_FILL_CHILD_LAYER: "UPDATE_FILL_CHILD_LAYER", }; export type ReduxActionType = typeof ReduxActionTypes[keyof typeof ReduxActionTypes]; diff --git a/app/client/src/constants/PropertyControlConstants.tsx b/app/client/src/constants/PropertyControlConstants.tsx index 4a171b060bce..6cee2280787e 100644 --- a/app/client/src/constants/PropertyControlConstants.tsx +++ b/app/client/src/constants/PropertyControlConstants.tsx @@ -8,6 +8,7 @@ import { CodeEditorExpected } from "components/editorComponents/CodeEditor"; import { UpdateWidgetPropertyPayload } from "actions/controlActions"; import { AppTheme } from "entities/AppTheming"; import { WidgetProps } from "widgets/BaseWidget"; +import { ReduxAction } from "ce/constants/ReduxActionConstants"; const ControlTypes = getPropertyControlTypes(); export type ControlType = typeof ControlTypes[keyof typeof ControlTypes]; @@ -89,6 +90,11 @@ export type PropertyPaneControlConfig = { // TODO(abhinav): To fix this, rename the options property of the controls which use this // Alternatively, create a new structure options?: any; + additionalAction?: ( + props: any, + propertyName?: string, + propertyValue?: any, + ) => ReduxAction; }; type ValidationConfigParams = { diff --git a/app/client/src/pages/Editor/PropertyPane/PropertyControl.tsx b/app/client/src/pages/Editor/PropertyPane/PropertyControl.tsx index b887d28f1818..11dba48ad188 100644 --- a/app/client/src/pages/Editor/PropertyPane/PropertyControl.tsx +++ b/app/client/src/pages/Editor/PropertyPane/PropertyControl.tsx @@ -51,6 +51,7 @@ import { ReactComponent as ResetIcon } from "assets/icons/control/undo_2.svg"; import { AppTheme } from "entities/AppTheming"; import { JS_TOGGLE_DISABLED_MESSAGE } from "@appsmith/constants/messages"; import PropertyPaneHelperText from "./PropertyPaneHelperText"; +import { ReduxAction } from "ce/constants/ReduxActionConstants"; type Props = PropertyPaneControlConfig & { panel: IPanelProps; @@ -194,6 +195,20 @@ const PropertyControl = memo((props: Props) => { [], ); + const getAdditionalActionsToDispatch = ( + propertyName: string, + propertyValue: any, + ): ReduxAction | null => { + if (props.additionalAction) { + return props.additionalAction( + widgetProperties, + propertyName, + propertyValue, + ); + } + return null; + }; + const getWidgetsOwnUpdatesOnPropertyChange = ( propertyName: string, propertyValue: any, @@ -379,10 +394,21 @@ const PropertyControl = memo((props: Props) => { ); } } + const additionalAction = getAdditionalActionsToDispatch( + propertyName, + propertyValue, + ); if (allPropertiesToUpdates && allPropertiesToUpdates.length) { // updating properties of a widget(s) should be done only once when property value changes. // to make sure dsl updates are atomic which is a necessity for undo/redo. onBatchUpdatePropertiesOfMultipleWidgets(allPropertiesToUpdates); + // TODO: This is a temporary implementation. + // Replace it with Abhinav's implementation of the same functionality on dynamic height, when available. + if (additionalAction) { + setTimeout(() => { + dispatch(additionalAction); + }, 0); + } } }, [widgetProperties], diff --git a/app/client/src/sagas/AutoLayoutUtils.ts b/app/client/src/sagas/AutoLayoutUtils.ts new file mode 100644 index 000000000000..1bb3a0a161b9 --- /dev/null +++ b/app/client/src/sagas/AutoLayoutUtils.ts @@ -0,0 +1,128 @@ +import { FlexLayerAlignment, ResponsiveBehavior } from "components/constants"; +import { + FlexLayer, + LayerChild, +} from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; +import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; + +function getCanvas(widgets: CanvasWidgetsReduxState, containerId: string) { + const container = widgets[containerId]; + if (!container) return; + let canvas; + // True for MainContainer + if (container.type === "CANVAS_WIDGET") canvas = container; + else { + const canvasId = container.children ? container.children[0] : ""; + canvas = widgets[canvasId]; + } + if (!canvas) return; + return canvas; +} + +export function removeChildLayers( + allWidgets: CanvasWidgetsReduxState, + containerId: string, +): CanvasWidgetsReduxState { + const widgets = { ...allWidgets }; + let canvas = getCanvas(widgets, containerId); + if (!canvas) return widgets; + canvas = { ...canvas, flexLayers: [] }; + widgets[canvas.widgetId] = canvas; + return widgets; +} + +export function* wrapChildren( + allWidgets: CanvasWidgetsReduxState, + containerId: string, +) { + const widgets = { ...allWidgets }; + let canvas = getCanvas(widgets, containerId); + if (!canvas) return widgets; + + const children = canvas.children || []; + if (!children.length) return widgets; + + const flexLayers: FlexLayer[] = []; + + for (const each of children) { + const child = widgets[each]; + if (!child) continue; + flexLayers.push({ + children: [{ id: child.widgetId, align: FlexLayerAlignment.Start }], + hasFillChild: + child.responsiveBehavior === ResponsiveBehavior.Fill || false, + }); + } + canvas = { ...canvas, flexLayers }; + widgets[canvas.widgetId] = canvas; + return widgets; +} + +export function* updateFlexLayersOnDelete( + allWidgets: CanvasWidgetsReduxState, + widgetId: string, + parentId: string, +) { + const widgets = { ...allWidgets }; + let parent = widgets[parentId]; + if (!parent) return widgets; + + const flexLayers = parent.flexLayers || []; + if (!flexLayers.length) return widgets; + for (const layer of flexLayers) { + const children = layer.children || []; + if (!children.length) continue; + const index = children.findIndex( + (each: LayerChild) => each.id === widgetId, + ); + if (index === -1) continue; + children.splice(index, 1); + layer.children = children; + } + parent = { + ...parent, + flexLayers: flexLayers.filter( + (layer: FlexLayer) => layer?.children?.length, + ), + }; + widgets[parentId] = parent; + return widgets; +} + +export function updateFillChildStatus( + allWidgets: CanvasWidgetsReduxState, + widgetId: string, + fill: boolean, +) { + const widgets = { ...allWidgets }; + const widget = widgets[widgetId]; + if (!widget || !widget.parentId) return widgets; + let canvas = getCanvas(widgets, widget.parentId); + if (!canvas) return widgets; + + const flexLayers: FlexLayer[] = canvas.flexLayers || []; + if (!flexLayers.length) return widgets; + + const updatedLayers = flexLayers?.map((layer) => { + const children = layer.children || []; + const selectedWidgetIndex: number = children.findIndex( + (each: LayerChild) => each.id === widgetId, + ); + if (selectedWidgetIndex === -1) return layer; + return { + ...layer, + hasFillChild: children.reduce((acc, each, index) => { + const widget = widgets[each.id]; + if (index === selectedWidgetIndex) return acc || fill; + return acc || widget?.responsiveBehavior === ResponsiveBehavior.Fill; + }, false), + }; + }); + + canvas = { + ...canvas, + flexLayers: updatedLayers, + }; + widgets[canvas.widgetId] = canvas; + return widgets; +} diff --git a/app/client/src/sagas/LayoutUpdateSagas.tsx b/app/client/src/sagas/LayoutUpdateSagas.tsx index 0c5146a6ea72..137e75ea300f 100644 --- a/app/client/src/sagas/LayoutUpdateSagas.tsx +++ b/app/client/src/sagas/LayoutUpdateSagas.tsx @@ -4,11 +4,16 @@ import { ReduxActionErrorTypes, ReduxActionTypes, } from "ce/constants/ReduxActionConstants"; +import { ResponsiveBehavior } from "components/constants"; import log from "loglevel"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; import { all, call, put, select, takeLatest } from "redux-saga/effects"; +import { + removeChildLayers, + updateFillChildStatus, + wrapChildren, +} from "./AutoLayoutUtils"; import { getWidgets } from "./selectors"; -import { removeChildLayers, wrapChildren } from "./WidgetOperationUtils"; type LayoutUpdatePayload = { parentId: string; @@ -59,9 +64,38 @@ function* addChildWrappers(actionPayload: ReduxAction) { } } +export function* updateFillChildInfo( + actionPayload: ReduxAction<{ + widgetId: string; + responsiveBehavior: ResponsiveBehavior; + }>, +) { + try { + const start = performance.now(); + const { responsiveBehavior, widgetId } = actionPayload.payload; + const allWidgets: CanvasWidgetsReduxState = yield select(getWidgets); + const updatedWidgets: CanvasWidgetsReduxState = updateFillChildStatus( + allWidgets, + widgetId, + responsiveBehavior === ResponsiveBehavior.Fill, + ); + yield put(updateAndSaveLayout(updatedWidgets)); + log.debug("updating fill child info took", performance.now() - start, "ms"); + } catch (error) { + yield put({ + type: ReduxActionErrorTypes.WIDGET_OPERATION_ERROR, + payload: { + action: ReduxActionTypes.UPDATE_FILL_CHILD_LAYER, + error, + }, + }); + } +} + export default function* layoutUpdateSagas() { yield all([ takeLatest(ReduxActionTypes.ADD_CHILD_WRAPPERS, addChildWrappers), takeLatest(ReduxActionTypes.REMOVE_CHILD_WRAPPERS, removeChildWrappers), + takeLatest(ReduxActionTypes.UPDATE_FILL_CHILD_LAYER, updateFillChildInfo), ]); } diff --git a/app/client/src/sagas/WidgetDeletionSagas.ts b/app/client/src/sagas/WidgetDeletionSagas.ts index d681a0cc504d..ba7b49e74eab 100644 --- a/app/client/src/sagas/WidgetDeletionSagas.ts +++ b/app/client/src/sagas/WidgetDeletionSagas.ts @@ -28,7 +28,6 @@ import { getSelectedWidget, getWidget, getWidgets } from "./selectors"; import { getAllWidgetsInTree, resizeCanvasToLowestWidget, - updateFlexLayersOnDelete, updateListWidgetPropertiesOnChildDelete, WidgetsInTree, } from "./WidgetOperationUtils"; @@ -41,6 +40,7 @@ import { import { toggleShowDeviationDialog } from "actions/onboardingActions"; import { getMainCanvasProps } from "selectors/editorSelectors"; import { MainCanvasReduxState } from "reducers/uiReducers/mainCanvasReducer"; +import { updateFlexLayersOnDelete } from "./AutoLayoutUtils"; const WidgetTypes = WidgetFactory.widgetTypes; type WidgetDeleteTabChild = { diff --git a/app/client/src/sagas/WidgetOperationUtils.ts b/app/client/src/sagas/WidgetOperationUtils.ts index 245823f978e3..fb5851726232 100644 --- a/app/client/src/sagas/WidgetOperationUtils.ts +++ b/app/client/src/sagas/WidgetOperationUtils.ts @@ -54,11 +54,6 @@ import { getBottomRowAfterReflow } from "utils/reflowHookUtils"; import { DataTreeWidget } from "entities/DataTree/dataTreeFactory"; import { isWidget } from "../workers/evaluationUtils"; import { CANVAS_DEFAULT_MIN_HEIGHT_PX } from "constants/AppConstants"; -import { FlexLayerAlignment, ResponsiveBehavior } from "components/constants"; -import { - FlexLayer, - LayerChild, -} from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; export interface CopiedWidgetGroup { widgetId: string; @@ -1754,87 +1749,3 @@ export function resizePublishedMainCanvasToLowestWidget( GridDefaults.DEFAULT_GRID_ROW_HEIGHT, ); } - -function getCanvas(widgets: CanvasWidgetsReduxState, containerId: string) { - const container = widgets[containerId]; - if (!container) return; - let canvas; - // True for MainContainer - if (container.type === "CANVAS_WIDGET") canvas = container; - else { - const canvasId = container.children ? container.children[0] : ""; - canvas = widgets[canvasId]; - } - if (!canvas) return; - return canvas; -} - -export function removeChildLayers( - allWidgets: CanvasWidgetsReduxState, - containerId: string, -): CanvasWidgetsReduxState { - const widgets = { ...allWidgets }; - let canvas = getCanvas(widgets, containerId); - if (!canvas) return widgets; - canvas = { ...canvas, flexLayers: [] }; - widgets[canvas.widgetId] = canvas; - return widgets; -} - -export function* wrapChildren( - allWidgets: CanvasWidgetsReduxState, - containerId: string, -) { - const widgets = { ...allWidgets }; - let canvas = getCanvas(widgets, containerId); - if (!canvas) return widgets; - - const children = canvas.children || []; - if (!children.length) return widgets; - - const flexLayers: FlexLayer[] = []; - - for (const each of children) { - const child = widgets[each]; - if (!child) continue; - flexLayers.push({ - children: [{ id: child.widgetId, align: FlexLayerAlignment.Start }], - hasFillChild: - child.responsiveBehavior === ResponsiveBehavior.Fill || false, - }); - } - canvas = { ...canvas, flexLayers }; - widgets[canvas.widgetId] = canvas; - return widgets; -} - -export function* updateFlexLayersOnDelete( - allWidgets: CanvasWidgetsReduxState, - widgetId: string, - parentId: string, -) { - const widgets = { ...allWidgets }; - let parent = widgets[parentId]; - if (!parent) return widgets; - - const flexLayers = parent.flexLayers || []; - if (!flexLayers.length) return widgets; - for (const layer of flexLayers) { - const children = layer.children || []; - if (!children.length) continue; - const index = children.findIndex( - (each: LayerChild) => each.id === widgetId, - ); - if (index === -1) continue; - children.splice(index, 1); - layer.children = children; - } - parent = { - ...parent, - flexLayers: flexLayers.filter( - (layer: FlexLayer) => layer?.children?.length, - ), - }; - widgets[parentId] = parent; - return widgets; -} diff --git a/app/client/src/utils/layoutPropertiesUtils.ts b/app/client/src/utils/layoutPropertiesUtils.ts index 97b93e4b6061..8d70d93eea69 100644 --- a/app/client/src/utils/layoutPropertiesUtils.ts +++ b/app/client/src/utils/layoutPropertiesUtils.ts @@ -1,3 +1,4 @@ +import { ReduxActionTypes } from "ce/constants/ReduxActionConstants"; import { AlignItems, Alignment, @@ -96,10 +97,22 @@ export const generateResponsiveBehaviorConfig = ( { label: "Fill", value: ResponsiveBehavior.Fill }, { label: "Hug", value: ResponsiveBehavior.Hug }, ], - isJSConvertible: true, + isJSConvertible: false, isBindProperty: false, isTriggerProperty: true, validation: { type: ValidationTypes.TEXT }, + additionalAction: ( + props: any, + propertyName?: string, + propertyValue?: any, + ) => ({ + type: ReduxActionTypes.UPDATE_FILL_CHILD_LAYER, + payload: { + widgetId: props.widgetId, + responsiveBehavior: propertyValue, + }, + }), + dependencies: ["widgetId"], }; }; @@ -165,6 +178,24 @@ export const generatePositioningConfig = ( isBindProperty: true, isTriggerProperty: true, validation: { type: ValidationTypes.TEXT }, + additionalAction: ( + props: any, + propertyName?: string, + propertyValue?: any, + ) => { + if (!propertyName || !propertyValue) return; + const positioning: Positioning = propertyValue as Positioning; + return { + type: + positioning === Positioning.Vertical + ? ReduxActionTypes.ADD_CHILD_WRAPPERS + : ReduxActionTypes.REMOVE_CHILD_WRAPPERS, + payload: { + parentId: props.widgetId, + }, + }; + }, + dependencies: ["widgetId"], }; }; diff --git a/app/client/src/widgets/ContainerWidget/widget/index.tsx b/app/client/src/widgets/ContainerWidget/widget/index.tsx index 98b0bb9f9ed4..6636ddd86725 100644 --- a/app/client/src/widgets/ContainerWidget/widget/index.tsx +++ b/app/client/src/widgets/ContainerWidget/widget/index.tsx @@ -21,8 +21,6 @@ import { generatePositioningConfig, generateResponsiveBehaviorConfig, } from "utils/layoutPropertiesUtils"; -import { connect } from "react-redux"; -import { addWrappers, removeWrappers } from "actions/autoLayoutActions"; export class ContainerWidget extends BaseWidget< ContainerWidgetProps, @@ -156,20 +154,6 @@ export class ContainerWidget extends BaseWidget< super.componentDidMount(); } - componentDidUpdate(prevProps: ContainerWidgetProps): void { - super.componentDidUpdate(prevProps); - if (this.props.positioning !== prevProps.positioning) this.updateWrappers(); - } - - updateWrappers = (): void => { - if (this.props.positioning === Positioning.Vertical) { - this.props.addWrappers && this.props.addWrappers(this.props.widgetId); - } else { - this.props.removeWrappers && - this.props.removeWrappers(this.props.widgetId); - } - }; - getSnapSpaces = () => { const { componentWidth } = this.getComponentDimensions(); // For all widgets inside a container, we remove both container padding as well as widget padding from component width @@ -255,11 +239,6 @@ export class ContainerWidget extends BaseWidget< } } -const mapDispatchToProps = (dispatch: any) => ({ - removeWrappers: (id: string) => dispatch(removeWrappers(id)), - addWrappers: (id: string) => dispatch(addWrappers(id)), -}); - export interface ContainerWidgetProps extends WidgetProps { children?: T[]; @@ -267,9 +246,6 @@ export interface ContainerWidgetProps shouldScrollContents?: boolean; noPad?: boolean; positioning?: Positioning; - removeWrappers?: (id: string) => void; - addWrappers?: (id: string) => void; } -export default connect(null, mapDispatchToProps)(ContainerWidget); -// export default ContainerWidget; +export default ContainerWidget; From 341291ad281b110dcacab6da2d02de78717bcafa Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 17 Oct 2022 13:32:09 -0400 Subject: [PATCH 166/708] remove debounce dispatch --- .../common/CanvasArenas/hooks/useAutoLayoutHighlights.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index e78ccf32fdf3..ff4c94b794c5 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -454,6 +454,10 @@ export const useAutoLayoutHighlights = ({ */ const debouncedDispatch = debounce((pos: HighlightInfo) => { + dispatchTempHighlight(pos); + }, 5); + + const dispatchTempHighlight = (pos: HighlightInfo) => { dispatch({ type: ReduxActionTypes.SET_AUTOLAYOUT_HIGHLIGHTS, payload: { @@ -461,7 +465,7 @@ export const useAutoLayoutHighlights = ({ blocksToDraw, }, }); - }, 5); + }; const highlightDropPosition = (e: any, moveDirection: ReflowDirection) => { if (!useAutoLayout) return; @@ -469,7 +473,7 @@ export const useAutoLayoutHighlights = ({ e, moveDirection, ); - debouncedDispatch(pos); + dispatchTempHighlight(pos); // console.log({ pos }); if (!pos) return; From bcfdbeb6d52e9233a7dc5f406d24a27197583d8a Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 17 Oct 2022 13:32:36 -0400 Subject: [PATCH 167/708] comment unused function --- .../common/CanvasArenas/hooks/useAutoLayoutHighlights.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index ff4c94b794c5..265cc66b18e0 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -453,9 +453,9 @@ export const useAutoLayoutHighlights = ({ * END AUTO LAYOUT OFFSET CALCULATION */ - const debouncedDispatch = debounce((pos: HighlightInfo) => { - dispatchTempHighlight(pos); - }, 5); + // const debouncedDispatch = debounce((pos: HighlightInfo) => { + // dispatchTempHighlight(pos); + // }, 5); const dispatchTempHighlight = (pos: HighlightInfo) => { dispatch({ From eeb9cf252e3f5b3299a3cded1337dcc6db7fef4d Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 18 Oct 2022 08:31:56 -0400 Subject: [PATCH 168/708] disable preview --- .../hooks/useAutoLayoutHighlights.ts | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index 265cc66b18e0..71a09519d994 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -1,7 +1,6 @@ import { getWidgets } from "sagas/selectors"; import { useSelector } from "store"; -import { ReduxActionTypes } from "ce/constants/ReduxActionConstants"; import { FlexLayerAlignment, LayoutDirection, @@ -11,7 +10,6 @@ import { FlexLayer, LayerChild, } from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; -import { debounce } from "lodash"; import { useDispatch } from "react-redux"; import { ReflowDirection } from "reflow/reflowTypes"; import { WidgetDraggingBlock } from "./useBlocksToBeDraggedOnCanvas"; @@ -457,15 +455,15 @@ export const useAutoLayoutHighlights = ({ // dispatchTempHighlight(pos); // }, 5); - const dispatchTempHighlight = (pos: HighlightInfo) => { - dispatch({ - type: ReduxActionTypes.SET_AUTOLAYOUT_HIGHLIGHTS, - payload: { - flexHighlight: pos, - blocksToDraw, - }, - }); - }; + // const dispatchTempHighlight = (pos: HighlightInfo) => { + // dispatch({ + // type: ReduxActionTypes.SET_AUTOLAYOUT_HIGHLIGHTS, + // payload: { + // flexHighlight: pos, + // blocksToDraw, + // }, + // }); + // }; const highlightDropPosition = (e: any, moveDirection: ReflowDirection) => { if (!useAutoLayout) return; @@ -473,7 +471,7 @@ export const useAutoLayoutHighlights = ({ e, moveDirection, ); - dispatchTempHighlight(pos); + // dispatchTempHighlight(pos); // console.log({ pos }); if (!pos) return; From 22b9699f4dd50f8007e7a82339da510358e83da5 Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 18 Oct 2022 08:44:39 -0400 Subject: [PATCH 169/708] re-enable preview --- .../hooks/useAutoLayoutHighlights.ts | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index 71a09519d994..e63b8c98bb93 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -1,6 +1,7 @@ import { getWidgets } from "sagas/selectors"; import { useSelector } from "store"; +import { ReduxActionTypes } from "ce/constants/ReduxActionConstants"; import { FlexLayerAlignment, LayoutDirection, @@ -455,15 +456,15 @@ export const useAutoLayoutHighlights = ({ // dispatchTempHighlight(pos); // }, 5); - // const dispatchTempHighlight = (pos: HighlightInfo) => { - // dispatch({ - // type: ReduxActionTypes.SET_AUTOLAYOUT_HIGHLIGHTS, - // payload: { - // flexHighlight: pos, - // blocksToDraw, - // }, - // }); - // }; + const dispatchTempHighlight = (pos: HighlightInfo) => { + dispatch({ + type: ReduxActionTypes.SET_AUTOLAYOUT_HIGHLIGHTS, + payload: { + flexHighlight: pos, + blocksToDraw, + }, + }); + }; const highlightDropPosition = (e: any, moveDirection: ReflowDirection) => { if (!useAutoLayout) return; @@ -471,7 +472,7 @@ export const useAutoLayoutHighlights = ({ e, moveDirection, ); - // dispatchTempHighlight(pos); + dispatchTempHighlight(pos); // console.log({ pos }); if (!pos) return; From 242091e2bfdf2244138100ed6e3179a7ccbc70fb Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 18 Oct 2022 09:03:08 -0400 Subject: [PATCH 170/708] fix main container overflow --- .../designSystems/appsmith/autoLayout/FlexBoxComponent.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx index ecd78a4dbaaa..321ee5d57926 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx @@ -38,6 +38,7 @@ export const FlexContainer = styled.div<{ stretchHeight: boolean; overflow: Overflow; isMobile?: boolean; + isMainContainer: boolean; }>` display: ${({ useAutoLayout }) => (useAutoLayout ? "flex" : "block")}; flex-direction: ${({ direction }) => @@ -51,7 +52,8 @@ export const FlexContainer = styled.div<{ height: ${({ stretchHeight }) => (stretchHeight ? "100%" : "auto")}; overflow: "hidden"; - overflow-y: ${({ isMobile }) => (isMobile ? "auto" : "hidden")}; + overflow-y: ${({ isMainContainer, isMobile }) => + isMainContainer || isMobile ? "auto" : "hidden"}; padding: 4px; `; @@ -213,6 +215,7 @@ function FlexBoxComponent(props: FlexBoxProps) { Date: Tue, 18 Oct 2022 17:05:10 -0400 Subject: [PATCH 171/708] draw highlights on canvas --- .../src/ce/constants/ReduxActionConstants.tsx | 3 +- .../appsmith/autoLayout/FlexComponent.tsx | 12 +++ .../hooks/useAutoLayoutHighlights.ts | 87 +++++++++------- .../CanvasArenas/hooks/useCanvasDragging.ts | 98 +++++++++++++------ .../reducers/uiReducers/dragResizeReducer.ts | 9 +- .../src/widgets/ContainerWidget/index.ts | 2 +- .../widgets/ContainerWidget/widget/index.tsx | 2 +- .../src/widgets/TabsWidget/widget/index.tsx | 2 +- 8 files changed, 144 insertions(+), 71 deletions(-) diff --git a/app/client/src/ce/constants/ReduxActionConstants.tsx b/app/client/src/ce/constants/ReduxActionConstants.tsx index 431ed2f06f18..5973bb1d0cc7 100644 --- a/app/client/src/ce/constants/ReduxActionConstants.tsx +++ b/app/client/src/ce/constants/ReduxActionConstants.tsx @@ -18,7 +18,8 @@ export const ReduxSagaChannels = { }; export const ReduxActionTypes = { - SET_AUTOLAYOUT_HIGHLIGHTS: "SET_AUTOLAYOUT_HIGHLIGHTS", + SELECT_AUTOLAYOUT_HIGHLIGHT: "SELECT_AUTOLAYOUT_HIGHLIGHT", + CLEAR_HIGHLIGHT_SELECTION: "CLEAR_HIGHLIGHT_SELECTION", GIT_DISCARD_CHANGES_SUCCESS: "GIT_DISCARD_CHANGES_SUCCESS", GIT_DISCARD_CHANGES: "GIT_DISCARD_CHANGES", DELETE_BRANCH_INIT: "DELETE_BRANCH_INIT", diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx index 040e7c5ebdb3..1008106e70a8 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx @@ -13,6 +13,7 @@ import { useClickToSelectWidget } from "utils/hooks/useClickToSelectWidget"; import { usePositionedContainerZIndex } from "utils/hooks/usePositionedContainerZIndex"; import { checkIsDropTarget } from "../PositionedContainer"; import { useIsMobileDevice } from "utils/hooks/useDeviceDetect"; +import { AppState } from "ce/reducers"; export type AutoLayoutProps = { children: ReactNode; @@ -37,6 +38,7 @@ const FlexWidget = styled.div<{ padding: number; zIndex: number; zIndexOnHover: number; + isCurrentCanvasDragging: boolean; }>` position: relative; z-index: ${({ zIndex }) => zIndex}; @@ -55,6 +57,9 @@ const FlexWidget = styled.div<{ &:hover { z-index: ${({ zIndexOnHover }) => zIndexOnHover} !important; } + margin: ${({ isCurrentCanvasDragging }) => + isCurrentCanvasDragging ? "6px" : 0}; + transition: margin 10ms; `; // TODO: update min width logic. @@ -67,6 +72,12 @@ export function FlexComponent(props: AutoLayoutProps) { clickToSelectWidget(props.widgetId); }, [props.widgetId, clickToSelectWidget]); + const { dragDetails } = useSelector( + (state: AppState) => state.ui.widgetDragResize, + ); + + const isCurrentCanvasDragging = dragDetails?.draggedOn !== undefined; + const isDropTarget = checkIsDropTarget(props.widgetType); const { onHoverZIndex, zIndex } = usePositionedContainerZIndex( isDropTarget, @@ -101,6 +112,7 @@ export function FlexComponent(props: AutoLayoutProps) { componentHeight={props.componentHeight} componentWidth={props.componentWidth} id={props.widgetId} + isCurrentCanvasDragging={isCurrentCanvasDragging} isFillWidget={isFillWidget} isMobile={isMobile} minWidth={minWidth} diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index e63b8c98bb93..b0f732fbed45 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -43,7 +43,12 @@ export interface AutoLayoutHighlightProps { useAutoLayout?: boolean; } -const OFFSET_WIDTH = 4; +export interface HighlightSelectionPayload { + highlights: HighlightInfo[]; + selectedHighlight: HighlightInfo; +} + +const OFFSET_WIDTH = 8; export const useAutoLayoutHighlights = ({ blocksToDraw, @@ -246,7 +251,7 @@ export const useAutoLayoutHighlights = ({ index: childCount, layerIndex: index - discardedLayers, posX: 0, - posY: rect.y - containerDimensions?.top, + posY: Math.max(rect.y - containerDimensions?.top - 4, 0), width: containerDimensions?.width, height: OFFSET_WIDTH, alignment: FlexLayerAlignment.Start, @@ -390,7 +395,7 @@ export const useAutoLayoutHighlights = ({ ? 0 : alignment === FlexLayerAlignment.Center ? containerDimensions.width / 2 - : containerDimensions?.width, + : containerDimensions?.width - 8, posY: rect.y - containerDimensions?.top, width: verticalFlex ? rect?.width : OFFSET_WIDTH, height: verticalFlex ? OFFSET_WIDTH : rect.height, @@ -420,7 +425,7 @@ export const useAutoLayoutHighlights = ({ index, layerIndex, alignment, - posX: childRect?.x - containerDimensions?.left, + posX: Math.max(childRect?.x - containerDimensions?.left - 8, 0), posY: childRect?.y - containerDimensions?.top, width: OFFSET_WIDTH, height: childRect?.height, @@ -456,9 +461,9 @@ export const useAutoLayoutHighlights = ({ // dispatchTempHighlight(pos); // }, 5); - const dispatchTempHighlight = (pos: HighlightInfo) => { + const setTempHighlight = (pos: HighlightInfo) => { dispatch({ - type: ReduxActionTypes.SET_AUTOLAYOUT_HIGHLIGHTS, + type: ReduxActionTypes.SELECT_AUTOLAYOUT_HIGHLIGHT, payload: { flexHighlight: pos, blocksToDraw, @@ -466,38 +471,44 @@ export const useAutoLayoutHighlights = ({ }); }; - const highlightDropPosition = (e: any, moveDirection: ReflowDirection) => { - if (!useAutoLayout) return; - const pos: HighlightInfo | undefined = getHighlightPosition( + const clearTempHighlight = () => { + dispatch({ + type: ReduxActionTypes.CLEAR_HIGHLIGHT_SELECTION, + }); + }; + + const highlightDropPosition = ( + e: any, + moveDirection: ReflowDirection, + acceleration: number, + ): HighlightSelectionPayload | undefined => { + if (!highlights) return; + // let highlightAdded = false; + const payload: HighlightSelectionPayload = getHighlightPayload( e, moveDirection, ); - dispatchTempHighlight(pos); - // console.log({ pos }); - - if (!pos) return; - lastActiveHighlight = pos; - if (dropPositionRef && dropPositionRef.current) { - dropPositionRef.current.style.opacity = "1"; - dropPositionRef.current.style.top = (pos.posY || 0) + "px"; - dropPositionRef.current.style.left = - (pos.posX > 6 - ? Math.min( - pos.posX - 6, - containerDimensions.left + containerDimensions.width - 6, - ) - : 0) + "px"; - dropPositionRef.current.style.width = pos.width + "px"; - dropPositionRef.current.style.height = pos.height + "px"; - dropPositionRef.current.style.display = "block"; - } + if (!payload || !payload.selectedHighlight) return; + lastActiveHighlight = payload.selectedHighlight; + + return payload; + // if (acceleration) { + // console.log("#### acceleration", acceleration, highlightAdded); + // if (acceleration > 0 && highlightAdded) { + // highlightAdded = false; + // clearTempHighlight(); + // } else if (!highlightAdded) { + // highlightAdded = true; + // setTempHighlight(pos); + // } + // } }; - const getHighlightPosition = ( + const getHighlightPayload = ( e: any, moveDirection?: ReflowDirection, val?: XYCord, - ): HighlightInfo => { + ): HighlightSelectionPayload => { let base: HighlightInfo[] = []; if (!highlights || !highlights.length) highlights = [ @@ -553,8 +564,8 @@ export const useAutoLayoutHighlights = ({ calculateDistance(b, pos, moveDirection) ); }); - - return arr[0]; + // console.log("#### selected highlights", arr); + return { highlights: [...arr.slice(1)], selectedHighlight: arr[0] }; }; const calculateDistance = ( @@ -579,10 +590,14 @@ export const useAutoLayoutHighlights = ({ const getDropInfo = (val: XYCord): HighlightInfo | undefined => { if (lastActiveHighlight) return lastActiveHighlight; - const pos = getHighlightPosition(null, undefined, val); - if (!pos) return; - lastActiveHighlight = pos; - return pos; + const payload: HighlightSelectionPayload = getHighlightPayload( + null, + undefined, + val, + ); + if (!payload) return; + lastActiveHighlight = payload.selectedHighlight; + return payload.selectedHighlight; }; return { diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index af7b4080367f..eb806a495426 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -28,6 +28,7 @@ import { import ContainerJumpMetrics from "./ContainerJumpMetric"; import { HighlightInfo, + HighlightSelectionPayload, useAutoLayoutHighlights, } from "./useAutoLayoutHighlights"; import { @@ -131,7 +132,9 @@ export const useCanvasDragging = ( }); const dispatch = useDispatch(); - const highlights: HighlightInfo[] = calculateHighlights(); + setTimeout(() => { + calculateHighlights(); + }, 100); if (!isDragging || !isCurrentDraggedCanvas) { cleanUpTempStyles(); @@ -195,7 +198,6 @@ export const useCanvasDragging = ( ); const speed = 10 * movement; //current speed - const acceleration = 10 * (speed - prevSpeed); mouseAttributesRef.current.prevAcceleration = acceleration; mouseAttributesRef.current.prevSpeed = speed; @@ -351,11 +353,7 @@ export const useCanvasDragging = ( } setDraggingCanvas(); dispatch({ - type: ReduxActionTypes.SET_AUTOLAYOUT_HIGHLIGHTS, - payload: { - flexHighlight: undefined, - blocksToDraw: undefined, - }, + type: ReduxActionTypes.CLEAR_HIGHLIGHT_SELECTION, }); } }, 0); @@ -413,6 +411,11 @@ export const useCanvasDragging = ( prevSpeed < CONTAINER_JUMP_SPEED_THRESHOLD ); }; + // const getFlexMouseMoveDirection = (event: any): ReflowDirection => { + // if (lastMousePosition) { + + // } + // }; const getMouseMoveDirection = (event: any, minDelta = 0) => { if (lastMousePosition) { const deltaX = lastMousePosition.x - event.clientX, @@ -567,7 +570,7 @@ export const useCanvasDragging = ( left: e.offsetX - startPoints.left - parentDiff.left, top: e.offsetY - startPoints.top - parentDiff.top, }; - + // console.log("#### mouse move", delta); const drawingBlocks = blocksToDraw.map((each) => ({ ...each, left: each.left + delta.left, @@ -579,33 +582,47 @@ export const useCanvasDragging = ( currentRectanglesToDraw = drawingBlocks.map((each) => ({ ...each, isNotColliding: - !dropDisabled && - noCollision( - { x: each.left, y: each.top }, - snapColumnSpace, - snapRowSpace, - { x: 0, y: 0 }, - each.columnWidth, - each.rowHeight, - each.widgetId, - occSpaces, - rowRef.current, - GridDefaults.DEFAULT_GRID_COLUMNS, - each.detachFromLayout, - ), + useAutoLayout || + (!dropDisabled && + noCollision( + { x: each.left, y: each.top }, + snapColumnSpace, + snapRowSpace, + { x: 0, y: 0 }, + each.columnWidth, + each.rowHeight, + each.widgetId, + occSpaces, + rowRef.current, + GridDefaults.DEFAULT_GRID_COLUMNS, + each.detachFromLayout, + )), })); - if (rowDelta && slidingArenaRef.current) { + if (rowDelta && slidingArenaRef.current && !useAutoLayout) { isUpdatingRows = true; canScroll.current = false; renderNewRows(delta); } else if (!isUpdatingRows) { - const dir: ReflowDirection = getMouseMoveDirection(e, 1); + currentDirection.current = getMouseMoveDirection(e, 1); + // console.log( + // "#### mouse move dir", + // currentDirection.current, + // mouseAttributesRef?.current.prevSpeed, + // mouseAttributesRef?.current.prevAcceleration, + // ); triggerReflow(e, firstMove); - isCurrentDraggedCanvas && - highlights.length && - dir !== ReflowDirection.UNSET && - highlightDropPosition(e, dir); - renderBlocks(); + let payload: HighlightSelectionPayload | undefined; + if ( + useAutoLayout && + isCurrentDraggedCanvas && + currentDirection.current !== ReflowDirection.UNSET + ) + payload = highlightDropPosition( + e, + currentDirection.current, + mouseAttributesRef?.current.prevAcceleration, + ); + renderBlocks(payload); } scrollObj.lastMouseMoveEvent = { offsetX: e.offsetX, @@ -613,6 +630,8 @@ export const useCanvasDragging = ( }; scrollObj.lastScrollTop = scrollParent?.scrollTop; scrollObj.lastScrollHeight = scrollParent?.scrollHeight; + scrollObj.lastDeltaLeft = delta.left; + scrollObj.lastDeltaTop = delta.top; } else { onFirstMoveOnCanvas(e); } @@ -671,7 +690,7 @@ export const useCanvasDragging = ( }, ); - const renderBlocks = () => { + const renderBlocks = (payload?: HighlightSelectionPayload) => { if ( slidingArenaRef.current && isCurrentDraggedCanvas && @@ -692,6 +711,25 @@ export const useCanvasDragging = ( currentRectanglesToDraw.forEach((each) => { drawBlockOnCanvas(each); }); + if (payload) { + console.log("#### highlights", payload.highlights); + canvasCtx.fillStyle = "rgba(217, 89, 183, 0.3)"; + payload.highlights.forEach((each) => { + canvasCtx.fillRect( + each.posX, + each.posY, + each.width, + each.height, + ); + }); + canvasCtx.fillStyle = "rgba(217, 89, 183, 1)"; + canvasCtx.fillRect( + payload.selectedHighlight.posX, + payload.selectedHighlight.posY, + payload.selectedHighlight.width, + payload.selectedHighlight.height, + ); + } } canvasCtx.restore(); } diff --git a/app/client/src/reducers/uiReducers/dragResizeReducer.ts b/app/client/src/reducers/uiReducers/dragResizeReducer.ts index cc71e66320f6..ba5d01eecf15 100644 --- a/app/client/src/reducers/uiReducers/dragResizeReducer.ts +++ b/app/client/src/reducers/uiReducers/dragResizeReducer.ts @@ -144,13 +144,20 @@ export const widgetDraggingReducer = createImmerReducer(initialState, { ) => { state.selectedWidgetAncestry = action.payload; }, - [ReduxActionTypes.SET_AUTOLAYOUT_HIGHLIGHTS]: ( + [ReduxActionTypes.SELECT_AUTOLAYOUT_HIGHLIGHT]: ( state: WidgetDragResizeState, action: ReduxAction<{ flexHighlight: HighlightInfo; blocksToDraw: any }>, ) => { state.flexHighlight = action.payload.flexHighlight; state.autoLayoutDragDetails = action.payload.blocksToDraw; }, + [ReduxActionTypes.CLEAR_HIGHLIGHT_SELECTION]: ( + state: WidgetDragResizeState, + action: ReduxAction, + ) => { + state.flexHighlight = undefined; + state.autoLayoutDragDetails = undefined; + }, }); type DraggingGroupCenter = { diff --git a/app/client/src/widgets/ContainerWidget/index.ts b/app/client/src/widgets/ContainerWidget/index.ts index 5e87a0f4d9ad..123544ce3875 100644 --- a/app/client/src/widgets/ContainerWidget/index.ts +++ b/app/client/src/widgets/ContainerWidget/index.ts @@ -39,7 +39,7 @@ export const CONFIG = { ], }, version: 1, - positioning: Positioning.Fixed, + positioning: Positioning.Vertical, responsiveBehavior: ResponsiveBehavior.Fill, }, properties: { diff --git a/app/client/src/widgets/ContainerWidget/widget/index.tsx b/app/client/src/widgets/ContainerWidget/widget/index.tsx index 6636ddd86725..dc87179f92b9 100644 --- a/app/client/src/widgets/ContainerWidget/widget/index.tsx +++ b/app/client/src/widgets/ContainerWidget/widget/index.tsx @@ -65,7 +65,7 @@ export class ContainerWidget extends BaseWidget< isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, - generatePositioningConfig(), + generatePositioningConfig(Positioning.Vertical), { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, ], }, diff --git a/app/client/src/widgets/TabsWidget/widget/index.tsx b/app/client/src/widgets/TabsWidget/widget/index.tsx index 3747bb0968d9..3b64affa8eef 100644 --- a/app/client/src/widgets/TabsWidget/widget/index.tsx +++ b/app/client/src/widgets/TabsWidget/widget/index.tsx @@ -110,7 +110,7 @@ class TabsWidget extends BaseWidget< isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, - { ...generatePositioningConfig(Positioning.Fixed) }, + { ...generatePositioningConfig(Positioning.Vertical) }, ], }, ], From 1a341551b061c451e96543a9529edae6028cc01d Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 18 Oct 2022 17:14:15 -0400 Subject: [PATCH 172/708] use direction in distance calculation --- .../common/CanvasArenas/hooks/useAutoLayoutHighlights.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index b0f732fbed45..676412508817 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -581,8 +581,12 @@ export const useAutoLayoutHighlights = ({ moveDirection && [ReflowDirection.TOP, ReflowDirection.BOTTOM].includes(moveDirection); - const distX: number = a.isVertical && isVerticalDrag ? 0 : a.posX - b.x; - const distY: number = !a.isVertical && !isVerticalDrag ? 0 : a.posY - b.y; + let distX: number = a.isVertical && isVerticalDrag ? 0 : a.posX - b.x; + let distY: number = !a.isVertical && !isVerticalDrag ? 0 : a.posY - b.y; + if (moveDirection === ReflowDirection.LEFT && distX > 0) distX += 2000; + if (moveDirection === ReflowDirection.RIGHT && distX < 0) distX -= 2000; + if (moveDirection === ReflowDirection.TOP && distY > 0) distY += 2000; + if (moveDirection === ReflowDirection.BOTTOM && distY < 0) distY -= 2000; return Math.abs(Math.sqrt(distX * distX + distY * distY)); }; From d1d63fc24300f3155edacfce3440a52e0b33e8b0 Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 19 Oct 2022 00:17:29 -0400 Subject: [PATCH 173/708] clean up --- .../CanvasArenas/hooks/useAutoLayoutHighlights.ts | 13 +------------ .../common/CanvasArenas/hooks/useCanvasDragging.ts | 1 - 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index 676412508817..2970121f450c 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -483,25 +483,14 @@ export const useAutoLayoutHighlights = ({ acceleration: number, ): HighlightSelectionPayload | undefined => { if (!highlights) return; - // let highlightAdded = false; const payload: HighlightSelectionPayload = getHighlightPayload( e, moveDirection, ); + if (!payload || !payload.selectedHighlight) return; lastActiveHighlight = payload.selectedHighlight; - return payload; - // if (acceleration) { - // console.log("#### acceleration", acceleration, highlightAdded); - // if (acceleration > 0 && highlightAdded) { - // highlightAdded = false; - // clearTempHighlight(); - // } else if (!highlightAdded) { - // highlightAdded = true; - // setTempHighlight(pos); - // } - // } }; const getHighlightPayload = ( diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index eb806a495426..698d21e63a55 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -712,7 +712,6 @@ export const useCanvasDragging = ( drawBlockOnCanvas(each); }); if (payload) { - console.log("#### highlights", payload.highlights); canvasCtx.fillStyle = "rgba(217, 89, 183, 0.3)"; payload.highlights.forEach((each) => { canvasCtx.fillRect( From 7dc015a00a12a4249038177a001ad7b008429874 Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 19 Oct 2022 00:35:36 -0400 Subject: [PATCH 174/708] build fix --- .../hooks/useAutoLayoutHighlights.ts | 33 +++++++++---------- .../CanvasArenas/hooks/useCanvasDragging.ts | 2 +- .../reducers/uiReducers/dragResizeReducer.ts | 2 +- 3 files changed, 18 insertions(+), 19 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index 2970121f450c..0d5a69ef4329 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -1,7 +1,6 @@ import { getWidgets } from "sagas/selectors"; import { useSelector } from "store"; -import { ReduxActionTypes } from "ce/constants/ReduxActionConstants"; import { FlexLayerAlignment, LayoutDirection, @@ -461,26 +460,26 @@ export const useAutoLayoutHighlights = ({ // dispatchTempHighlight(pos); // }, 5); - const setTempHighlight = (pos: HighlightInfo) => { - dispatch({ - type: ReduxActionTypes.SELECT_AUTOLAYOUT_HIGHLIGHT, - payload: { - flexHighlight: pos, - blocksToDraw, - }, - }); - }; - - const clearTempHighlight = () => { - dispatch({ - type: ReduxActionTypes.CLEAR_HIGHLIGHT_SELECTION, - }); - }; + // const setTempHighlight = (pos: HighlightInfo) => { + // dispatch({ + // type: ReduxActionTypes.SELECT_AUTOLAYOUT_HIGHLIGHT, + // payload: { + // flexHighlight: pos, + // blocksToDraw, + // }, + // }); + // }; + + // const clearTempHighlight = () => { + // dispatch({ + // type: ReduxActionTypes.CLEAR_HIGHLIGHT_SELECTION, + // }); + // }; const highlightDropPosition = ( e: any, moveDirection: ReflowDirection, - acceleration: number, + // acceleration: number, ): HighlightSelectionPayload | undefined => { if (!highlights) return; const payload: HighlightSelectionPayload = getHighlightPayload( diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index 698d21e63a55..ba05c84badac 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -620,7 +620,7 @@ export const useCanvasDragging = ( payload = highlightDropPosition( e, currentDirection.current, - mouseAttributesRef?.current.prevAcceleration, + // mouseAttributesRef?.current.prevAcceleration, ); renderBlocks(payload); } diff --git a/app/client/src/reducers/uiReducers/dragResizeReducer.ts b/app/client/src/reducers/uiReducers/dragResizeReducer.ts index ba5d01eecf15..651749ee7252 100644 --- a/app/client/src/reducers/uiReducers/dragResizeReducer.ts +++ b/app/client/src/reducers/uiReducers/dragResizeReducer.ts @@ -153,7 +153,7 @@ export const widgetDraggingReducer = createImmerReducer(initialState, { }, [ReduxActionTypes.CLEAR_HIGHLIGHT_SELECTION]: ( state: WidgetDragResizeState, - action: ReduxAction, + // action: ReduxAction, ) => { state.flexHighlight = undefined; state.autoLayoutDragDetails = undefined; From 2587065a6fe98d1acf6cd38e2c9fb6e8b3af1415 Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 19 Oct 2022 00:47:27 -0400 Subject: [PATCH 175/708] build fix --- .../common/CanvasArenas/hooks/useAutoLayoutHighlights.ts | 4 ++-- app/client/src/widgets/FilePickerWidgetV2/widget/index.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index 0d5a69ef4329..984710d8dd5c 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -10,7 +10,7 @@ import { FlexLayer, LayerChild, } from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; -import { useDispatch } from "react-redux"; +// import { useDispatch } from "react-redux"; import { ReflowDirection } from "reflow/reflowTypes"; import { WidgetDraggingBlock } from "./useBlocksToBeDraggedOnCanvas"; @@ -62,7 +62,7 @@ export const useAutoLayoutHighlights = ({ const canvas = allWidgets[canvasId]; const layers: FlexLayer[] = canvas?.flexLayers || []; const isVertical = direction === LayoutDirection.Vertical; - const dispatch = useDispatch(); + // const dispatch = useDispatch(); let highlights: HighlightInfo[] = []; let lastActiveHighlight: HighlightInfo | undefined; let containerDimensions: { diff --git a/app/client/src/widgets/FilePickerWidgetV2/widget/index.tsx b/app/client/src/widgets/FilePickerWidgetV2/widget/index.tsx index abe6d0a06076..f178f5bba873 100644 --- a/app/client/src/widgets/FilePickerWidgetV2/widget/index.tsx +++ b/app/client/src/widgets/FilePickerWidgetV2/widget/index.tsx @@ -24,8 +24,8 @@ import { Colors } from "constants/Colors"; import Papa from "papaparse"; import { klona } from "klona"; import { UppyFile } from "@uppy/utils"; -import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; import { ResponsiveBehavior } from "components/constants"; +import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; const CSV_ARRAY_LABEL = "Array (CSVs only)"; const CSV_FILE_TYPE_REGEX = /.+(\/csv)$/; From b427ca7a03e11a092760e7b3f6e6bd4d959ac298 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Thu, 20 Oct 2022 11:28:34 +0530 Subject: [PATCH 176/708] Select new widget on drop --- .../common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts b/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts index 13412c533f68..12e92acb0589 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts @@ -323,6 +323,9 @@ export const useBlocksToBeDraggedOnCanvas = ({ alignment: dropPayload.alignment, }, }; + setTimeout(() => { + selectWidget(widgetPayload.newWidgetId); + }, 100); dispatch({ type: ReduxActionTypes.AUTOLAYOUT_ADD_NEW_WIDGETS, payload: { From 322ce5778115b7952966db8fec44c9f371a39299 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Thu, 20 Oct 2022 11:38:17 +0530 Subject: [PATCH 177/708] Fixing the app level positioning drop down --- .../pages/Editor/CanvasPropertyPane/index.tsx | 62 ++++++++++--------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx b/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx index 3d9d7352f4a2..a2aaee9f3d51 100644 --- a/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx +++ b/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx @@ -1,17 +1,17 @@ -import React, { useState } from "react"; import * as Sentry from "@sentry/react"; +import React, { useState } from "react"; -import { MainContainerLayoutControl } from "../MainContainerLayoutControl"; -import ThemeEditor from "../ThemePropertyPane/ThemeEditor"; -import styled from "styled-components"; -import { Colors } from "constants/Colors"; +import { addWrappers, removeWrappers } from "actions/autoLayoutActions"; +import { batchUpdateMultipleWidgetProperties } from "actions/controlActions"; import { LayoutDirection, Positioning } from "components/constants"; +import { Colors } from "constants/Colors"; +import { Dropdown, DropdownOption, RenderOption } from "design-system"; import { useDispatch } from "react-redux"; -import { batchUpdateMultipleWidgetProperties } from "actions/controlActions"; -import { useSelector } from "store"; import { getWidgets } from "sagas/selectors"; -import { Dropdown, DropdownOption, RenderOption } from "design-system"; -import { addWrappers, removeWrappers } from "actions/autoLayoutActions"; +import { useSelector } from "store"; +import styled from "styled-components"; +import { MainContainerLayoutControl } from "../MainContainerLayoutControl"; +import ThemeEditor from "../ThemePropertyPane/ThemeEditor"; const Title = styled.p` color: ${Colors.GRAY_800}; @@ -42,29 +42,31 @@ const PositioningOptions = () => { isSelectedNode ? "px-2 py-2" : "px-2 py-2 " }`} onClick={() => { - setSelectedOption(options.indexOf(option as DropdownOption)); - const isVerticalStack = - (option as DropdownOption).value === Positioning.Vertical; - const widgetId = "0"; - dispatch( - batchUpdateMultipleWidgetProperties([ - { - widgetId, - updates: { - modify: { - positioning: (option as DropdownOption).value, - useAutoLayout: - (option as DropdownOption).value !== Positioning.Fixed, - direction: isVerticalStack - ? LayoutDirection.Vertical - : LayoutDirection.Horizontal, + if (!isSelectedNode) { + setSelectedOption(options.indexOf(option as DropdownOption)); + const isVerticalStack = + (option as DropdownOption).value === Positioning.Vertical; + const widgetId = "0"; + dispatch( + batchUpdateMultipleWidgetProperties([ + { + widgetId, + updates: { + modify: { + positioning: (option as DropdownOption).value, + useAutoLayout: + (option as DropdownOption).value !== Positioning.Fixed, + direction: isVerticalStack + ? LayoutDirection.Vertical + : LayoutDirection.Horizontal, + }, }, }, - }, - ]), - ); - if (isVerticalStack) dispatch(addWrappers(widgetId)); - else removeWrappers(widgetId); + ]), + ); + if (isVerticalStack) dispatch(addWrappers(widgetId)); + else removeWrappers(widgetId); + } }} >
{(option as DropdownOption).label}
From 02c385c8931aea6d3f57b7987ab51dcb1df33819 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Thu, 20 Oct 2022 12:39:20 +0530 Subject: [PATCH 178/708] Fixing dropdown cosmetics. --- .../src/pages/Editor/CanvasPropertyPane/index.tsx | 12 ++++++++---- .../src/widgets/AudioRecorderWidget/widget/index.tsx | 12 ++++++------ 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx b/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx index a2aaee9f3d51..6b3cbeec79c8 100644 --- a/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx +++ b/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx @@ -36,11 +36,15 @@ const PositioningOptions = () => { } return 0; }); - const renderOption: RenderOption = ({ isSelectedNode, option }) => ( + const renderOption: RenderOption = ({ + isHighlighted, + isSelectedNode, + option, + }) => (
{ if (!isSelectedNode) { setSelectedOption(options.indexOf(option as DropdownOption)); diff --git a/app/client/src/widgets/AudioRecorderWidget/widget/index.tsx b/app/client/src/widgets/AudioRecorderWidget/widget/index.tsx index cf7f2226316e..d86e340a666d 100644 --- a/app/client/src/widgets/AudioRecorderWidget/widget/index.tsx +++ b/app/client/src/widgets/AudioRecorderWidget/widget/index.tsx @@ -1,15 +1,15 @@ import React from "react"; -import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; -import { WidgetType } from "constants/WidgetConstants"; +import { ResponsiveBehavior } from "components/constants"; import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; +import { WidgetType } from "constants/WidgetConstants"; import { ValidationTypes } from "constants/WidgetValidation"; -import AudioRecorderComponent from "../component"; -import { DerivedPropertiesMap } from "utils/WidgetFactory"; import { createBlobUrl } from "utils/AppsmithUtils"; -import { FileDataTypes } from "widgets/constants"; import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; -import { ResponsiveBehavior } from "components/constants"; +import { DerivedPropertiesMap } from "utils/WidgetFactory"; +import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; +import { FileDataTypes } from "widgets/constants"; +import AudioRecorderComponent from "../component"; export interface AudioRecorderWidgetProps extends WidgetProps { accentColor: string; From d5842c85859aa2fef821d0743d1de285f5dd0079 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Thu, 20 Oct 2022 17:36:37 +0530 Subject: [PATCH 179/708] Canvas resizer. --- app/client/src/pages/Editor/MainContainer.tsx | 18 +-- .../Editor/WidgetsEditor/CanvasContainer.tsx | 136 ++++++++++++++++-- .../src/pages/Editor/WidgetsEditor/index.tsx | 40 +++--- .../src/utils/hooks/useDynamicAppLayout.tsx | 42 ++++-- 4 files changed, 184 insertions(+), 52 deletions(-) diff --git a/app/client/src/pages/Editor/MainContainer.tsx b/app/client/src/pages/Editor/MainContainer.tsx index ed0b8b8a282a..904967738abe 100644 --- a/app/client/src/pages/Editor/MainContainer.tsx +++ b/app/client/src/pages/Editor/MainContainer.tsx @@ -1,23 +1,23 @@ -import styled from "styled-components"; import * as Sentry from "@sentry/react"; +import React, { useCallback, useEffect, useState } from "react"; import { useDispatch, useSelector } from "react-redux"; -import React, { useState, useCallback, useEffect } from "react"; import { Route, Switch, useLocation } from "react-router"; +import styled from "styled-components"; -import EditorsRouter from "./routes"; -import BottomBar from "./BottomBar"; -import { DEFAULT_ENTITY_EXPLORER_WIDTH } from "constants/AppConstants"; -import WidgetsEditor from "./WidgetsEditor"; import { updateExplorerWidthAction } from "actions/explorerActions"; +import { routeChanged } from "actions/focusHistoryActions"; +import classNames from "classnames"; +import EntityExplorerSidebar from "components/editorComponents/Sidebar"; +import { DEFAULT_ENTITY_EXPLORER_WIDTH } from "constants/AppConstants"; import { BUILDER_CUSTOM_PATH, BUILDER_PATH, BUILDER_PATH_DEPRECATED, } from "constants/routes"; -import EntityExplorerSidebar from "components/editorComponents/Sidebar"; -import classNames from "classnames"; import { previewModeSelector } from "selectors/editorSelectors"; -import { routeChanged } from "actions/focusHistoryActions"; +import BottomBar from "./BottomBar"; +import EditorsRouter from "./routes"; +import WidgetsEditor from "./WidgetsEditor"; const SentryRoute = Sentry.withSentryRouting(Route); diff --git a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx index 89008e2f3157..c23fbe8bc831 100644 --- a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx +++ b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx @@ -1,34 +1,38 @@ -import React, { ReactNode, useEffect } from "react"; +import React, { ReactNode, useEffect, useRef } from "react"; import { useSelector } from "react-redux"; import { + getCanvasWidth, + getCurrentApplicationLayout, getCurrentPageId, getIsFetchingPage, getViewModePageList, previewModeSelector, - getCanvasWidth, showCanvasTopSectionSelector, } from "selectors/editorSelectors"; import styled from "styled-components"; import { getCanvasClassName } from "utils/generators"; -import Centered from "components/designSystems/appsmith/CenteredWrapper"; -import Canvas from "../Canvas"; -import { useParams } from "react-router"; -import classNames from "classnames"; +import { Icon } from "@blueprintjs/core"; import { forceOpenWidgetPanel } from "actions/widgetSidebarActions"; +import classNames from "classnames"; +import Centered from "components/designSystems/appsmith/CenteredWrapper"; +import { layoutConfigurations } from "constants/WidgetConstants"; +import { IconSize, Spinner } from "design-system"; +import equal from "fast-deep-equal/es6"; +import { WidgetGlobaStyles } from "globalStyles/WidgetGlobalStyles"; import { useDispatch } from "react-redux"; +import { useParams } from "react-router"; import { getAppThemeIsChanging, getSelectedAppTheme, } from "selectors/appThemingSelectors"; -import { Spinner } from "design-system"; -import useGoogleFont from "utils/hooks/useGoogleFont"; -import { IconSize } from "design-system"; -import { useDynamicAppLayout } from "utils/hooks/useDynamicAppLayout"; -import { getCurrentThemeDetails } from "selectors/themeSelectors"; import { getCanvasWidgetsStructure } from "selectors/entitiesSelector"; -import equal from "fast-deep-equal/es6"; -import { WidgetGlobaStyles } from "globalStyles/WidgetGlobalStyles"; +import { getCurrentThemeDetails } from "selectors/themeSelectors"; +import { noop } from "utils/AppsmithUtils"; +import { useDynamicAppLayout } from "utils/hooks/useDynamicAppLayout"; +import useGoogleFont from "utils/hooks/useGoogleFont"; +import useHorizontalResize from "utils/hooks/useHorizontalResize"; +import Canvas from "../Canvas"; const Container = styled.section<{ background: string; @@ -94,6 +98,76 @@ function CanvasContainer() { /> ); } + const appLayout = useSelector(getCurrentApplicationLayout); + useEffect(() => { + if (appLayout?.type === "FLUID") { + const smallestWidth = layoutConfigurations.MOBILE.minWidth; + // Query the element + const ele: any = document.getElementById("main-canvas-container"); + const initialWidth = ele.offsetWidth; + // The current position of mouse + let x = 0; + let y = 0; + + // The dimension of the element + let w = 0; + let h = 0; + let events: any = []; + + // Handle the mousedown event + // that's triggered when user drags the resizer + const mouseDownHandler = function(e: any, rightHandle: boolean) { + // Get the current mouse position + x = e.clientX; + y = e.clientY; + + // Calculate the dimension of element + const styles = window.getComputedStyle(ele); + w = parseInt(styles.width, 10); + h = parseInt(styles.height, 10); + const mouseMove = (e: any) => mouseMoveHandler(e, rightHandle); + events.push(mouseMove); + // Attach the listeners to `document` + document.addEventListener("mousemove", mouseMove); + document.addEventListener("mouseup", mouseUpHandler); + e.stopEventPropagation(); + }; + + const mouseMoveHandler = function(e: any, rightHandle: boolean) { + // How far the mouse has been moved + const multiplier = rightHandle ? 2 : -2; + const dx = (e.clientX - x) * multiplier; + if (initialWidth >= w + dx && smallestWidth <= w + dx) { + // Adjust the dimension of element + ele.style.width = `${w + dx}px`; + } + e.stopEventPropagation(); + }; + + const mouseUpHandler = function() { + // Remove the handlers of `mousemove` and `mouseup` + document.removeEventListener("mousemove", events[0] as any); + document.removeEventListener("mouseup", mouseUpHandler); + events = []; + }; + const rightResizer: any = ele.querySelectorAll(".resizer-right")[0]; + const leftResizer: any = ele.querySelectorAll(".resizer-left")[0]; + + rightResizer.addEventListener("mousedown", (e: any) => + mouseDownHandler(e, true), + ); + + leftResizer.addEventListener("mousedown", (e: any) => + mouseDownHandler(e, false), + ); + } else { + const ele: any = document.getElementById("main-canvas-container"); + ele.style.width = "inherit"; + } + }, [appLayout]); + + const leftResizer = useRef(null); + const resizer = useHorizontalResize(leftResizer, noop, noop); // calculating exact height to not allow scroll at this component, // calculating total height minus margin on top, top bar and bottom bar const heightWithTopMargin = `calc(100vh - 2.25rem - ${theme.smallHeaderHeight} - ${theme.bottomBarHeight})`; @@ -110,12 +184,48 @@ function CanvasContainer() { "mt-4": showCanvasTopSection, "mt-8": shouldHaveTopMargin && !showCanvasTopSection, })} + id="main-canvas-container" key={currentPageId} style={{ height: shouldHaveTopMargin ? heightWithTopMargin : "100vh", fontFamily: fontFamily, }} > + {appLayout?.type === "FLUID" && ( + <> +
+ +
+
+ +
+ + )}
{ calculatedWidth -= explorerWidth; } - + const ele: any = document.getElementById("main-canvas-container"); + const mainCanvasWidth = ele.clientWidth; + if (appLayout?.type === "FLUID" && calculatedWidth > mainCanvasWidth) { + calculatedWidth = mainCanvasWidth; + } switch (true) { case maxWidth < 0: case appLayout?.type === "FLUID": @@ -144,6 +148,24 @@ export const useDynamicAppLayout = () => { screenWidth, ]); + const immediateDebouncedResize = useCallback(debounce(resizeToLayout), [ + mainCanvasProps, + screenWidth, + ]); + + const resizeObserver = new ResizeObserver(immediateDebouncedResize); + useEffect(() => { + const ele: any = document.getElementById("main-canvas-container"); + if (ele && appLayout?.type === "FLUID") { + resizeObserver.observe(ele); + } else { + resizeObserver.unobserve(ele); + } + return () => { + resizeObserver.unobserve(ele); + }; + }, [appLayout]); + /** * when screen height is changed, update canvas layout */ From a0ec446ccf11e9e865b519b4b4dc1aa566de2425 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Thu, 20 Oct 2022 18:27:29 +0530 Subject: [PATCH 180/708] show resize handles for existing apps. --- .../pages/Editor/WidgetsEditor/CanvasContainer.tsx | 12 ++++++------ app/client/src/selectors/editorSelectors.tsx | 4 +++- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx index c23fbe8bc831..e93b48b52b08 100644 --- a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx +++ b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx @@ -193,7 +193,7 @@ function CanvasContainer() { > {appLayout?.type === "FLUID" && ( <> -
-
-
+ -
+ )} - state.ui.applications.currentApplication?.appLayout; + state.ui.applications.currentApplication?.appLayout || { + type: "FLUID", + }; export const getCanvasWidth = (state: AppState) => state.ui.mainCanvas.width; From 43d7b21ee4dec19f18418fe080d6583adf592f19 Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 20 Oct 2022 13:04:23 -0400 Subject: [PATCH 181/708] Change highlight color, size and drag margins --- .../appsmith/autoLayout/FlexComponent.tsx | 2 +- .../hooks/useAutoLayoutHighlights.ts | 100 ++++++++++++------ .../CanvasArenas/hooks/useCanvasDragging.ts | 18 ++-- .../src/widgets/TabsWidget/constants.ts | 1 + app/client/src/widgets/TabsWidget/index.ts | 4 +- 5 files changed, 81 insertions(+), 44 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx index 1008106e70a8..92c7284ae5c6 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx @@ -58,7 +58,7 @@ const FlexWidget = styled.div<{ z-index: ${({ zIndexOnHover }) => zIndexOnHover} !important; } margin: ${({ isCurrentCanvasDragging }) => - isCurrentCanvasDragging ? "6px" : 0}; + isCurrentCanvasDragging ? "4px" : 0}; transition: margin 10ms; `; diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index 984710d8dd5c..2c1772e3192b 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -47,7 +47,8 @@ export interface HighlightSelectionPayload { selectedHighlight: HighlightInfo; } -const OFFSET_WIDTH = 8; +const OFFSET_WIDTH = 4; +const DRAG_MARGIN = 8; export const useAutoLayoutHighlights = ({ blocksToDraw, @@ -394,10 +395,10 @@ export const useAutoLayoutHighlights = ({ ? 0 : alignment === FlexLayerAlignment.Center ? containerDimensions.width / 2 - : containerDimensions?.width - 8, + : containerDimensions?.width - DRAG_MARGIN, posY: rect.y - containerDimensions?.top, width: verticalFlex ? rect?.width : OFFSET_WIDTH, - height: verticalFlex ? OFFSET_WIDTH : rect.height, + height: verticalFlex ? OFFSET_WIDTH : rect.height - DRAG_MARGIN, isVertical: !verticalFlex, rowIndex: 0, }; @@ -516,14 +517,45 @@ export const useAutoLayoutHighlights = ({ y: e?.offsetY || val?.y, }; - let filteredHighlights: HighlightInfo[] = base; - // For vertical stacks, filter out the highlights based on drag direction and y position. - if (moveDirection && direction === LayoutDirection.Vertical) { - const isVerticalDrag = - moveDirection && - [ReflowDirection.TOP, ReflowDirection.BOTTOM].includes(moveDirection); + const filteredHighlights: HighlightInfo[] = getViableDropPositions( + base, + pos, + moveDirection, + ); + + const arr = filteredHighlights.sort((a, b) => { + return ( + calculateDistance(a, pos, moveDirection) - + calculateDistance(b, pos, moveDirection) + ); + }); - filteredHighlights = base.filter((highlight: HighlightInfo) => { + return { highlights: [...arr.slice(1)], selectedHighlight: arr[0] }; + }; + + function getViableDropPositions( + arr: HighlightInfo[], + pos: XYCord, + moveDirection?: ReflowDirection, + ): HighlightInfo[] { + if (!moveDirection || !arr) return arr || []; + const isVerticalDrag = [ + ReflowDirection.TOP, + ReflowDirection.BOTTOM, + ].includes(moveDirection); + return direction === LayoutDirection.Vertical + ? getVerticalStackDropPositions(arr, pos, isVerticalDrag) + : getHorizontalStackDropPositions(arr, pos); + } + + function getVerticalStackDropPositions( + arr: HighlightInfo[], + pos: XYCord, + isVerticalDrag: boolean, + ): HighlightInfo[] { + // For vertical stacks, filter out the highlights based on drag direction and y position. + let filteredHighlights: HighlightInfo[] = arr.filter( + (highlight: HighlightInfo) => { // Return only horizontal highlights for vertical drag. if (isVerticalDrag) return !highlight.isVertical; // Return only vertical highlights for horizontal drag, if they lie in the same x plane. @@ -532,29 +564,34 @@ export const useAutoLayoutHighlights = ({ pos.y >= highlight.posY && pos.y <= highlight.posY + highlight.height ); - }); - - // For horizontal drag, if no vertical highlight exists in the same x plane, - // return the last horizontal highlight. - if (!isVerticalDrag && !filteredHighlights.length) { - const horizontalHighlights = base.filter( - (highlight: HighlightInfo) => !highlight.isVertical, - ); - filteredHighlights = [ - horizontalHighlights[horizontalHighlights.length - 1], - ]; - } + }, + ); + // For horizontal drag, if no vertical highlight exists in the same x plane, + // return the last horizontal highlight. + if (!isVerticalDrag && !filteredHighlights.length) { + const horizontalHighlights = arr.filter( + (highlight: HighlightInfo) => !highlight.isVertical, + ); + filteredHighlights = [ + horizontalHighlights[horizontalHighlights.length - 1], + ]; } + return filteredHighlights; + } - const arr = [...filteredHighlights].sort((a, b) => { - return ( - calculateDistance(a, pos, moveDirection) - - calculateDistance(b, pos, moveDirection) - ); - }); - // console.log("#### selected highlights", arr); - return { highlights: [...arr.slice(1)], selectedHighlight: arr[0] }; - }; + function getHorizontalStackDropPositions( + arr: HighlightInfo[], + pos: XYCord, + ): HighlightInfo[] { + // For horizontal stack, return the highlights that lie in the same x plane. + let filteredHighlights = arr.filter( + (highlight) => + pos.y >= highlight.posY && pos.y <= highlight.posY + highlight.height, + ); + // If no highlight exists in the same x plane, return the last highlight. + if (!filteredHighlights.length) filteredHighlights = [arr[arr.length - 1]]; + return filteredHighlights; + } const calculateDistance = ( a: HighlightInfo, @@ -571,6 +608,7 @@ export const useAutoLayoutHighlights = ({ let distX: number = a.isVertical && isVerticalDrag ? 0 : a.posX - b.x; let distY: number = !a.isVertical && !isVerticalDrag ? 0 : a.posY - b.y; + if (moveDirection === ReflowDirection.LEFT && distX > 0) distX += 2000; if (moveDirection === ReflowDirection.RIGHT && distX < 0) distX -= 2000; if (moveDirection === ReflowDirection.TOP && distY > 0) distY += 2000; diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index ba05c84badac..9b01aecc0a99 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -603,13 +603,7 @@ export const useCanvasDragging = ( canScroll.current = false; renderNewRows(delta); } else if (!isUpdatingRows) { - currentDirection.current = getMouseMoveDirection(e, 1); - // console.log( - // "#### mouse move dir", - // currentDirection.current, - // mouseAttributesRef?.current.prevSpeed, - // mouseAttributesRef?.current.prevAcceleration, - // ); + currentDirection.current = getMouseMoveDirection(e); triggerReflow(e, firstMove); let payload: HighlightSelectionPayload | undefined; if ( @@ -712,7 +706,7 @@ export const useCanvasDragging = ( drawBlockOnCanvas(each); }); if (payload) { - canvasCtx.fillStyle = "rgba(217, 89, 183, 0.3)"; + canvasCtx.fillStyle = "rgba(223, 158, 206, 0.6)"; payload.highlights.forEach((each) => { canvasCtx.fillRect( each.posX, @@ -721,12 +715,14 @@ export const useCanvasDragging = ( each.height, ); }); - canvasCtx.fillStyle = "rgba(217, 89, 183, 1)"; + canvasCtx.fillStyle = "rgba(196, 139, 181, 1)"; canvasCtx.fillRect( payload.selectedHighlight.posX, payload.selectedHighlight.posY, - payload.selectedHighlight.width, - payload.selectedHighlight.height, + payload.selectedHighlight.width * + (payload.selectedHighlight.isVertical ? 1.5 : 1), + payload.selectedHighlight.height * + (!payload.selectedHighlight.isVertical ? 1.5 : 1), ); } } diff --git a/app/client/src/widgets/TabsWidget/constants.ts b/app/client/src/widgets/TabsWidget/constants.ts index f9c5cc20f272..51810a031254 100644 --- a/app/client/src/widgets/TabsWidget/constants.ts +++ b/app/client/src/widgets/TabsWidget/constants.ts @@ -14,6 +14,7 @@ export interface TabsWidgetProps label: string; widgetId: string; isVisible?: boolean; + positioning: Positioning; }>; tabsObj: Record< string, diff --git a/app/client/src/widgets/TabsWidget/index.ts b/app/client/src/widgets/TabsWidget/index.ts index 883868ef395f..7f224bb6be00 100644 --- a/app/client/src/widgets/TabsWidget/index.ts +++ b/app/client/src/widgets/TabsWidget/index.ts @@ -3,7 +3,7 @@ import { WidgetProps } from "widgets/BaseWidget"; import { BlueprintOperationTypes } from "widgets/constants"; import IconSVG from "./icon.svg"; import Widget from "./widget"; -import { ResponsiveBehavior } from "components/constants"; +import { Positioning, ResponsiveBehavior } from "components/constants"; export const CONFIG = { type: Widget.getWidgetType(), @@ -28,6 +28,7 @@ export const CONFIG = { widgetId: "", isVisible: true, index: 0, + positioning: Positioning.Vertical, }, tab2: { label: "Tab 2", @@ -35,6 +36,7 @@ export const CONFIG = { widgetId: "", isVisible: true, index: 1, + positioning: Positioning.Vertical, }, }, shouldShowTabs: true, From 3ffd334d31a0f12afb40185c08681e08bf31aac1 Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 20 Oct 2022 13:33:50 -0400 Subject: [PATCH 182/708] turn main container into vertical stack --- .../pages/Editor/CanvasPropertyPane/index.tsx | 138 +++++++++--------- .../Editor/WidgetsEditor/CanvasContainer.tsx | 8 +- 2 files changed, 76 insertions(+), 70 deletions(-) diff --git a/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx b/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx index 6b3cbeec79c8..4e1b4957214d 100644 --- a/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx +++ b/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx @@ -21,73 +21,73 @@ type Props = { skipThemeEditor?: boolean; }; -const PositioningOptions = () => { - const dispatch = useDispatch(); - const widgets = useSelector(getWidgets); - const options: DropdownOption[] = [ - { label: "Fixed", value: Positioning.Fixed }, - { label: "Vertical stack", value: Positioning.Vertical }, - ]; - const [selectedOption, setSelectedOption] = useState(() => { - if (widgets && widgets["0"].positioning) { - return options - .map((each) => each.value) - .indexOf(widgets["0"].positioning); - } - return 0; - }); - const renderOption: RenderOption = ({ - isHighlighted, - isSelectedNode, - option, - }) => ( -
{ - if (!isSelectedNode) { - setSelectedOption(options.indexOf(option as DropdownOption)); - const isVerticalStack = - (option as DropdownOption).value === Positioning.Vertical; - const widgetId = "0"; - dispatch( - batchUpdateMultipleWidgetProperties([ - { - widgetId, - updates: { - modify: { - positioning: (option as DropdownOption).value, - useAutoLayout: - (option as DropdownOption).value !== Positioning.Fixed, - direction: isVerticalStack - ? LayoutDirection.Vertical - : LayoutDirection.Horizontal, - }, - }, - }, - ]), - ); - if (isVerticalStack) dispatch(addWrappers(widgetId)); - else removeWrappers(widgetId); - } - }} - > -
{(option as DropdownOption).label}
-
- ); - return ( -
- -
- ); -}; +// const PositioningOptions = () => { +// const dispatch = useDispatch(); +// const widgets = useSelector(getWidgets); +// const options: DropdownOption[] = [ +// { label: "Fixed", value: Positioning.Fixed }, +// { label: "Vertical stack", value: Positioning.Vertical }, +// ]; +// const [selectedOption, setSelectedOption] = useState(() => { +// if (widgets && widgets["0"].positioning) { +// return options +// .map((each) => each.value) +// .indexOf(widgets["0"].positioning); +// } +// return 0; +// }); +// const renderOption: RenderOption = ({ +// isHighlighted, +// isSelectedNode, +// option, +// }) => ( +//
{ +// if (!isSelectedNode) { +// setSelectedOption(options.indexOf(option as DropdownOption)); +// const isVerticalStack = +// (option as DropdownOption).value === Positioning.Vertical; +// const widgetId = "0"; +// dispatch( +// batchUpdateMultipleWidgetProperties([ +// { +// widgetId, +// updates: { +// modify: { +// positioning: (option as DropdownOption).value, +// useAutoLayout: +// (option as DropdownOption).value !== Positioning.Fixed, +// direction: isVerticalStack +// ? LayoutDirection.Vertical +// : LayoutDirection.Horizontal, +// }, +// }, +// }, +// ]), +// ); +// if (isVerticalStack) dispatch(addWrappers(widgetId)); +// else removeWrappers(widgetId); +// } +// }} +// > +//
{(option as DropdownOption).label}
+//
+// ); +// return ( +//
+// +//
+// ); +// }; export function CanvasPropertyPane(props: Props) { return ( @@ -99,12 +99,12 @@ export function CanvasPropertyPane(props: Props) { Canvas Size
- {!props.skipThemeEditor && ( + {/* {!props.skipThemeEditor && (

Positioning

- )} + )} */} {!props.skipThemeEditor && }
diff --git a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx index e93b48b52b08..158856032edc 100644 --- a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx +++ b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx @@ -33,6 +33,7 @@ import { useDynamicAppLayout } from "utils/hooks/useDynamicAppLayout"; import useGoogleFont from "utils/hooks/useGoogleFont"; import useHorizontalResize from "utils/hooks/useHorizontalResize"; import Canvas from "../Canvas"; +import { Positioning } from "components/constants"; const Container = styled.section<{ background: string; @@ -90,11 +91,16 @@ function CanvasContainer() { } if (!isPageInitializing && widgetsStructure) { + // TODO: Temporary workaround for positioning. To be removed after testing. node = ( ); } From cc2ac9c81f411ebb396d1fd13d99ae1e67e43540 Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 20 Oct 2022 13:35:23 -0400 Subject: [PATCH 183/708] remove unused imports --- .../pages/Editor/CanvasPropertyPane/index.tsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx b/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx index 4e1b4957214d..58a9984baf27 100644 --- a/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx +++ b/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx @@ -1,14 +1,14 @@ import * as Sentry from "@sentry/react"; -import React, { useState } from "react"; +import React from "react"; -import { addWrappers, removeWrappers } from "actions/autoLayoutActions"; -import { batchUpdateMultipleWidgetProperties } from "actions/controlActions"; -import { LayoutDirection, Positioning } from "components/constants"; +// import { addWrappers, removeWrappers } from "actions/autoLayoutActions"; +// import { batchUpdateMultipleWidgetProperties } from "actions/controlActions"; +// import { LayoutDirection, Positioning } from "components/constants"; import { Colors } from "constants/Colors"; -import { Dropdown, DropdownOption, RenderOption } from "design-system"; -import { useDispatch } from "react-redux"; -import { getWidgets } from "sagas/selectors"; -import { useSelector } from "store"; +// import { Dropdown, DropdownOption, RenderOption } from "design-system"; +// import { useDispatch } from "react-redux"; +// import { getWidgets } from "sagas/selectors"; +// import { useSelector } from "store"; import styled from "styled-components"; import { MainContainerLayoutControl } from "../MainContainerLayoutControl"; import ThemeEditor from "../ThemePropertyPane/ThemeEditor"; From 387d5d0743f6b440ea2c282352d3c2a2be52cd72 Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 20 Oct 2022 13:37:31 -0400 Subject: [PATCH 184/708] remove fixed positioning option --- app/client/src/utils/layoutPropertiesUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/client/src/utils/layoutPropertiesUtils.ts b/app/client/src/utils/layoutPropertiesUtils.ts index 8d70d93eea69..467b7bb4b988 100644 --- a/app/client/src/utils/layoutPropertiesUtils.ts +++ b/app/client/src/utils/layoutPropertiesUtils.ts @@ -170,7 +170,7 @@ export const generatePositioningConfig = ( controlType: "DROP_DOWN", defaultValue: value, options: [ - { label: "Fixed", value: Positioning.Fixed }, + // { label: "Fixed", value: Positioning.Fixed }, { label: "Horizontal stack", value: Positioning.Horizontal }, { label: "Vertical stack", value: Positioning.Vertical }, ], From 665520e7b4b004371187e3f5341ca50c62e32a8c Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 20 Oct 2022 17:05:15 -0400 Subject: [PATCH 185/708] fix widget align in a layer --- .../hooks/useAutoLayoutHighlights.ts | 47 ++++++++++++------- .../CanvasArenas/hooks/useCanvasDragging.ts | 2 +- .../CanvasSagas/AutoLayoutDraggingSagas.ts | 8 ++-- 3 files changed, 35 insertions(+), 22 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index 2c1772e3192b..d8fb05ea61ce 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -139,14 +139,14 @@ export const useAutoLayoutHighlights = ({ // Hide the dragged children of the auto layout container // to discount them from highlight calculation. - const hideDraggedItems = (draggedBlocks: string[]): void => { - draggedBlocks.forEach((id: string) => { - const el = getDomElement(id); - if (el) { - el.classList.add("auto-temp-no-display"); - } - }); - }; + // const hideDraggedItems = (draggedBlocks: string[]): void => { + // draggedBlocks.forEach((id: string) => { + // const el = getDomElement(id); + // if (el) { + // el.classList.add("auto-temp-no-display"); + // } + // }); + // }; const calculateHighlights = (): HighlightInfo[] => { /** @@ -165,7 +165,7 @@ export const useAutoLayoutHighlights = ({ * That implies the container is null. */ if (!updateContainerDimensions()) return []; - hideDraggedItems(draggedBlocks); + // hideDraggedItems(draggedBlocks); const canvasChildren = canvas.children || []; // Get the list of children that are not being dragged. @@ -231,11 +231,12 @@ export const useAutoLayoutHighlights = ({ for (const layer of flexLayers) { // remove dragged blocks from the layer const filteredLayer = filterLayer(layer, offsetChildren); - if (!filteredLayer?.children?.length) { - discardedLayers += 1; - index += 1; - continue; - } + // const isEmpty = !filteredLayer.children.length; + // if (isEmpty) { + // discardedLayers += 1; + // index += 1; + // // continue; + // } const el = document.querySelector( `.auto-layout-layer-${canvasId}-${index}`, ); @@ -291,10 +292,12 @@ export const useAutoLayoutHighlights = ({ // Extract start, center and end children from the layer. function spreadLayer(layer: FlexLayer) { + // const draggedBlocks = getDraggedBlocks(); const start: string[] = [], center: string[] = [], end: string[] = []; layer.children.forEach((child: LayerChild) => { + // if (draggedBlocks.includes(child.id)) return; if (layer.hasFillChild) { start.push(child.id); return; @@ -303,7 +306,13 @@ export const useAutoLayoutHighlights = ({ else if (child.align === FlexLayerAlignment.Center) center.push(child.id); else start.push(child.id); }); - return { start, center, end, hasFillChild: layer.hasFillChild }; + return { + start, + center, + end, + hasFillChild: layer.hasFillChild, + isEmpty: !start.length && !center.length && !end.length, + }; } function generateHighlightsForLayer( @@ -314,7 +323,7 @@ export const useAutoLayoutHighlights = ({ ): HighlightInfo[] { const arr: HighlightInfo[] = []; let curr: number = childCount; - const { center, end, hasFillChild, start } = spreadLayer(layer); + const { center, end, hasFillChild, isEmpty, start } = spreadLayer(layer); // process start sub wrapper. arr.push( @@ -324,6 +333,7 @@ export const useAutoLayoutHighlights = ({ layerIndex, FlexLayerAlignment.Start, layerRect, + isEmpty, ), ); if (!hasFillChild) { @@ -336,6 +346,7 @@ export const useAutoLayoutHighlights = ({ layerIndex, FlexLayerAlignment.Center, layerRect, + isEmpty, ), ); // process end sub wrapper. @@ -347,6 +358,7 @@ export const useAutoLayoutHighlights = ({ layerIndex, FlexLayerAlignment.End, layerRect, + isEmpty, ), ); } @@ -522,7 +534,7 @@ export const useAutoLayoutHighlights = ({ pos, moveDirection, ); - + // console.log("#### filteredHighlights: ", filteredHighlights); const arr = filteredHighlights.sort((a, b) => { return ( calculateDistance(a, pos, moveDirection) - @@ -566,6 +578,7 @@ export const useAutoLayoutHighlights = ({ ); }, ); + // console.log("#### pos", arr, filteredHighlights, isVerticalDrag); // For horizontal drag, if no vertical highlight exists in the same x plane, // return the last horizontal highlight. if (!isVerticalDrag && !filteredHighlights.length) { diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index 9b01aecc0a99..6729b03bb509 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -603,7 +603,7 @@ export const useCanvasDragging = ( canScroll.current = false; renderNewRows(delta); } else if (!isUpdatingRows) { - currentDirection.current = getMouseMoveDirection(e); + currentDirection.current = getMouseMoveDirection(e, 1); triggerReflow(e, firstMove); let payload: HighlightSelectionPayload | undefined; if ( diff --git a/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts b/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts index 7d15e2fd59aa..5baa55d00f2b 100644 --- a/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts +++ b/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts @@ -280,11 +280,11 @@ function updateExistingLayer( selectedLayer = { ...selectedLayer, children: [ - ...selectedLayer.children.slice(0, pos), - ...newLayer.children, - ...selectedLayer.children.slice(pos), + ...selectedLayer?.children?.slice(0, pos), + ...newLayer?.children, + ...selectedLayer?.children?.slice(pos), ], - hasFillChild: newLayer.hasFillChild || selectedLayer.hasFillChild, + hasFillChild: newLayer.hasFillChild || selectedLayer?.hasFillChild, }; const updatedCanvas = { From 603d3a4f5cb7d56624a1be4499d1ab0060a4185e Mon Sep 17 00:00:00 2001 From: Preet Date: Fri, 21 Oct 2022 02:43:32 -0400 Subject: [PATCH 186/708] update resizing to work with existing grid --- .../appsmith/autoLayout/FlexBoxComponent.tsx | 2 +- .../appsmith/autoLayout/FlexComponent.tsx | 10 +- .../editorComponents/ResizableComponent.tsx | 15 ++- .../hooks/useAutoLayoutHighlights.ts | 42 +++++---- .../CanvasArenas/hooks/useCanvasDragging.ts | 2 +- .../src/resizable/resizenreflow/index.tsx | 7 +- app/client/src/sagas/AutoLayoutUtils.ts | 94 ++++++++++++++++++- .../CanvasSagas/AutoLayoutDraggingSagas.ts | 55 +++++++++-- app/client/src/sagas/WidgetAdditionSagas.ts | 5 + app/client/src/sagas/WidgetOperationSagas.tsx | 8 +- app/client/src/widgets/constants.ts | 2 + 11 files changed, 199 insertions(+), 43 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx index 321ee5d57926..9c5ecd9c3a81 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx @@ -51,7 +51,7 @@ export const FlexContainer = styled.div<{ width: 100%; height: ${({ stretchHeight }) => (stretchHeight ? "100%" : "auto")}; - overflow: "hidden"; + overflow: hidden; overflow-y: ${({ isMainContainer, isMobile }) => isMainContainer || isMobile ? "auto" : "hidden"}; padding: 4px; diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx index 92c7284ae5c6..c885d61df521 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx @@ -14,6 +14,7 @@ import { usePositionedContainerZIndex } from "utils/hooks/usePositionedContainer import { checkIsDropTarget } from "../PositionedContainer"; import { useIsMobileDevice } from "utils/hooks/useDeviceDetect"; import { AppState } from "ce/reducers"; +import { DRAG_MARGIN } from "widgets/constants"; export type AutoLayoutProps = { children: ReactNode; @@ -43,8 +44,10 @@ const FlexWidget = styled.div<{ position: relative; z-index: ${({ zIndex }) => zIndex}; - width: ${({ componentWidth, isFillWidget }) => - isFillWidget ? "auto" : `${Math.floor(componentWidth)}px`}; + width: ${({ componentWidth, isCurrentCanvasDragging, isFillWidget }) => + isFillWidget || isCurrentCanvasDragging + ? "auto" + : `${Math.floor(componentWidth)}px`}; height: ${({ componentHeight, isMobile }) => isMobile ? "100%" : Math.floor(componentHeight) + "px"}; min-width: ${({ minWidth }) => minWidth}; @@ -58,8 +61,7 @@ const FlexWidget = styled.div<{ z-index: ${({ zIndexOnHover }) => zIndexOnHover} !important; } margin: ${({ isCurrentCanvasDragging }) => - isCurrentCanvasDragging ? "4px" : 0}; - transition: margin 10ms; + isCurrentCanvasDragging ? `${DRAG_MARGIN}px` : 0}; `; // TODO: update min width logic. diff --git a/app/client/src/components/editorComponents/ResizableComponent.tsx b/app/client/src/components/editorComponents/ResizableComponent.tsx index d5184fd9abe0..fffba657dd26 100644 --- a/app/client/src/components/editorComponents/ResizableComponent.tsx +++ b/app/client/src/components/editorComponents/ResizableComponent.tsx @@ -91,6 +91,13 @@ export const ResizableComponent = memo(function ResizableComponent( ); const isWidgetFocused = isFocused || isLastSelected || isSelected; + const { dragDetails } = useSelector( + (state: AppState) => state.ui.widgetDragResize, + ); + + const isCurrentCanvasDragging = + dragDetails && dragDetails?.draggedOn === props.parentId; + // Calculate the dimensions of the widget, // The ResizableContainer's size prop is controlled const dimensions: UIElementSize = { @@ -277,12 +284,16 @@ export const ResizableComponent = memo(function ResizableComponent( updateDropTargetRows && updateDropTargetRows([props.parentId], bottom); } }; - + const multiplier = props.parentId === "0" ? 0.99 : 0.95; return ( { calculateHighlights(); - }, 100); + }, 0); if (!isDragging || !isCurrentDraggedCanvas) { cleanUpTempStyles(); diff --git a/app/client/src/resizable/resizenreflow/index.tsx b/app/client/src/resizable/resizenreflow/index.tsx index 50ede0805ddb..2a18cdedf65f 100644 --- a/app/client/src/resizable/resizenreflow/index.tsx +++ b/app/client/src/resizable/resizenreflow/index.tsx @@ -496,12 +496,7 @@ export function ReflowResizable(props: ResizableProps) { }} immediate={newDimensions.reset ? true : false} to={{ - width: - props.isFlexChild && - props.responsiveBehavior === ResponsiveBehavior.Fill && - props.direction === LayoutDirection.Vertical - ? "auto" - : widgetWidth, + width: widgetWidth, height: widgetHeight, transform: `translate3d(${newDimensions.x}px,${newDimensions.y}px,0)`, }} diff --git a/app/client/src/sagas/AutoLayoutUtils.ts b/app/client/src/sagas/AutoLayoutUtils.ts index 1bb3a0a161b9..d1fbdd496f25 100644 --- a/app/client/src/sagas/AutoLayoutUtils.ts +++ b/app/client/src/sagas/AutoLayoutUtils.ts @@ -55,7 +55,9 @@ export function* wrapChildren( } canvas = { ...canvas, flexLayers }; widgets[canvas.widgetId] = canvas; - return widgets; + // update size + const updatedWidgets = updateSizeOfAllChildren(widgets, canvas.widgetId); + return updatedWidgets; } export function* updateFlexLayersOnDelete( @@ -69,7 +71,9 @@ export function* updateFlexLayersOnDelete( const flexLayers = parent.flexLayers || []; if (!flexLayers.length) return widgets; + let layerIndex = -1; // Find the layer in which the deleted widget exists. for (const layer of flexLayers) { + layerIndex += 1; const children = layer.children || []; if (!children.length) continue; const index = children.findIndex( @@ -86,7 +90,8 @@ export function* updateFlexLayersOnDelete( ), }; widgets[parentId] = parent; - return widgets; + if (layerIndex === -1) return widgets; + return updateFlexChildColumns(widgets, layerIndex, parentId); } export function updateFillChildStatus( @@ -126,3 +131,88 @@ export function updateFillChildStatus( widgets[canvas.widgetId] = canvas; return widgets; } + +export function updateFlexChildColumns( + allWidgets: CanvasWidgetsReduxState, + layerIndex: number, + parentId: string, +): CanvasWidgetsReduxState { + const widgets = Object.assign({}, allWidgets); + const canvas = widgets[parentId]; + const children = canvas.children; + if (!children || !children.length) return widgets; + + const layer = canvas.flexLayers[layerIndex]; + if (!layer || !layer?.children?.length || !layer.hasFillChild) return widgets; + + const fillChildren: any[] = []; + const hugChildrenColumns = layer?.children?.reduce( + (acc: number, child: LayerChild) => { + const widget = widgets[child.id]; + if (widget.responsiveBehavior === ResponsiveBehavior.Fill) { + fillChildren.push(widget); + return acc; + } + return ( + acc + + (widget.columns + ? widget.columns + : widget.rightColumn - widget.leftColumn) + ); + }, + 0, + ); + if (!fillChildren.length) return widgets; + + const columnsPerFillChild = Math.floor( + (64 - hugChildrenColumns) / fillChildren.length, + ); + + for (const child of fillChildren) { + widgets[child.widgetId] = { + ...child, + rightColumn: child.leftColumn + columnsPerFillChild, + }; + } + return widgets; +} + +export function updateChildrenSize( + allWidgets: CanvasWidgetsReduxState, + parentId: string, + widgetId: string, +): CanvasWidgetsReduxState { + const widgets = Object.assign({}, allWidgets); + const parent = widgets[parentId]; + if (!parent || !parent?.flexLayers || !parent?.flexLayers?.length) + return widgets; + + const layerIndex = parent.flexLayers.reduce( + (acc: number, layer: FlexLayer, index: number) => { + if (layer.children.some((child: LayerChild) => child.id === widgetId)) { + return index; + } + return acc; + }, + -1, + ); + + return updateFlexChildColumns(widgets, layerIndex, parentId); +} + +export function updateSizeOfAllChildren( + allWidgets: CanvasWidgetsReduxState, + parentId: string, +): CanvasWidgetsReduxState { + let widgets = Object.assign({}, allWidgets); + const parent = widgets[parentId]; + + if (!parent || !parent?.flexLayers || !parent?.flexLayers?.length) + return widgets; + + for (let i = 0; i < parent.flexLayers.length; i++) { + widgets = updateFlexChildColumns(widgets, i, parentId); + } + + return widgets; +} diff --git a/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts b/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts index 5baa55d00f2b..bb36defa9ce7 100644 --- a/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts +++ b/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts @@ -19,6 +19,10 @@ import { LayerChild, } from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; import { getUpdateDslAfterCreatingChild } from "sagas/WidgetAdditionSagas"; +import { + updateFlexChildColumns, + updateSizeOfAllChildren, +} from "sagas/AutoLayoutUtils"; function* addWidgetAndReorderSaga( actionPayload: ReduxAction<{ @@ -53,7 +57,19 @@ function* addWidgetAndReorderSaga( layerIndex, }, ); - yield put(updateAndSaveLayout(updatedWidgetsOnMove)); + let updatedWidgetsAfterResizing = updatedWidgetsOnMove; + if ( + !isNewLayer && + direction === LayoutDirection.Vertical && + layerIndex !== undefined + ) + updatedWidgetsAfterResizing = updateFlexChildColumns( + updatedWidgetsOnMove, + layerIndex, + parentId, + ); + + yield put(updateAndSaveLayout(updatedWidgetsAfterResizing)); log.debug("reorder computations took", performance.now() - start, "ms"); } catch (e) { // console.error(e); @@ -95,8 +111,14 @@ function* autoLayoutReorderSaga( layerIndex, }, ); + let updatedWidgetsAfterResizing = updatedWidgets; + if (direction === LayoutDirection.Vertical) + updatedWidgetsAfterResizing = updateSizeOfAllChildren( + updatedWidgets, + parentId, + ); - yield put(updateAndSaveLayout(updatedWidgets)); + yield put(updateAndSaveLayout(updatedWidgetsAfterResizing)); log.debug("reorder computations took", performance.now() - start, "ms"); } catch (e) { // console.error(e); @@ -144,6 +166,7 @@ function* reorderAutolayoutChildren(params: { selectedWidgets, flexLayers, ); + // Create a temporary layer from moved widgets. const newLayer: FlexLayer = createFlexLayer( selectedWidgets, @@ -153,10 +176,16 @@ function* reorderAutolayoutChildren(params: { // Add the new layer to the flex layers. updatedWidgets = isNewLayer - ? addNewLayer(newLayer, widgets, parentId, filteredLayers, layerIndex) + ? addNewLayer( + newLayer, + updatedWidgets, + parentId, + filteredLayers, + layerIndex, + ) : updateExistingLayer( newLayer, - widgets, + updatedWidgets, parentId, filteredLayers, index, @@ -193,18 +222,20 @@ function* reorderAutolayoutChildren(params: { */ function updateRelationships( movedWidgets: string[], - widgets: CanvasWidgetsReduxState, + allWidgets: CanvasWidgetsReduxState, parentId: string, ): CanvasWidgetsReduxState { + const widgets = { ...allWidgets }; // Check if parent has changed const orphans = movedWidgets.filter( (item) => widgets[item].parentId !== parentId, ); + let prevParentId: string | undefined; if (orphans && orphans.length) { //parent has changed orphans.forEach((item) => { // remove from previous parent - const prevParentId = widgets[item].parentId; + prevParentId = widgets[item].parentId; if (prevParentId !== undefined) { const prevParent = Object.assign({}, widgets[prevParentId]); if (prevParent.children && isArray(prevParent.children)) { @@ -228,6 +259,9 @@ function updateRelationships( }; }); } + if (prevParentId) { + return updateSizeOfAllChildren(widgets, prevParentId); + } return widgets; } @@ -247,6 +281,7 @@ function addNewLayer( ...canvas, flexLayers: [...layers.slice(0, pos), newLayer, ...layers.slice(pos)], }; + const updatedWidgets = { ...widgets, [parentId]: updatedCanvas, @@ -272,7 +307,13 @@ function updateExistingLayer( let childCount = 0; layers.forEach((layer: FlexLayer, index: number) => { if (index >= layerIndex) return; - childCount += layer.children.length; + const layerChildren = (layer.children || []).filter( + (child: LayerChild) => + newLayer.children.findIndex( + (each: LayerChild) => each.id === child.id, + ) === -1, + ); + childCount += layerChildren.length; }); const pos = index - childCount; diff --git a/app/client/src/sagas/WidgetAdditionSagas.ts b/app/client/src/sagas/WidgetAdditionSagas.ts index 4f75e999b437..e146b219afd4 100644 --- a/app/client/src/sagas/WidgetAdditionSagas.ts +++ b/app/client/src/sagas/WidgetAdditionSagas.ts @@ -44,6 +44,7 @@ import { klona as clone } from "klona/full"; import { DataTree } from "entities/DataTree/dataTreeFactory"; import { MainCanvasReduxState } from "reducers/uiReducers/mainCanvasReducer"; import { getMainCanvasProps } from "selectors/editorSelectors"; +import { ResponsiveBehavior } from "components/constants"; const WidgetTypes = WidgetFactory.widgetTypes; @@ -143,6 +144,10 @@ function* getChildWidgetProps( } } + // TODO: add check for positioning value in parent. + if (restDefaultConfig?.responsiveBehavior === ResponsiveBehavior.Fill) + columns = 64; + const widgetProps = { ...restDefaultConfig, ...props, diff --git a/app/client/src/sagas/WidgetOperationSagas.tsx b/app/client/src/sagas/WidgetOperationSagas.tsx index 1409d02623eb..d7289750705e 100644 --- a/app/client/src/sagas/WidgetOperationSagas.tsx +++ b/app/client/src/sagas/WidgetOperationSagas.tsx @@ -141,6 +141,7 @@ import { flashElementsById } from "utils/helpers"; import { getSlidingCanvasName } from "constants/componentClassNameConstants"; import { builderURL } from "RouteBuilder"; import history from "utils/history"; +import { updateChildrenSize } from "./AutoLayoutUtils"; export function* resizeSaga(resizeAction: ReduxAction) { try { @@ -186,9 +187,14 @@ export function* resizeSaga(resizeAction: ReduxAction) { bottomRow: updatedCanvasBottomRow, }; } + const updatedWidgetsAfterResizing = updateChildrenSize( + movedWidgets, + parentId, + widgetId, + ); log.debug("resize computations took", performance.now() - start, "ms"); yield put(stopReflowAction()); - yield put(updateAndSaveLayout(movedWidgets)); + yield put(updateAndSaveLayout(updatedWidgetsAfterResizing)); } catch (error) { yield put({ type: ReduxActionErrorTypes.WIDGET_OPERATION_ERROR, diff --git a/app/client/src/widgets/constants.ts b/app/client/src/widgets/constants.ts index 77f9cd4c10b9..2ca435c8a538 100644 --- a/app/client/src/widgets/constants.ts +++ b/app/client/src/widgets/constants.ts @@ -278,3 +278,5 @@ export const dateFormatOptions = [ value: "MM/DD/YY", }, ]; + +export const DRAG_MARGIN = 4; From 743df609debea19fa74d37a14831ee0a18f0b7f8 Mon Sep 17 00:00:00 2001 From: Preet Date: Fri, 21 Oct 2022 02:52:36 -0400 Subject: [PATCH 187/708] fix build issues --- .../pages/Editor/WidgetsEditor/CanvasContainer.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx index 158856032edc..7958921d18b5 100644 --- a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx +++ b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx @@ -28,10 +28,10 @@ import { } from "selectors/appThemingSelectors"; import { getCanvasWidgetsStructure } from "selectors/entitiesSelector"; import { getCurrentThemeDetails } from "selectors/themeSelectors"; -import { noop } from "utils/AppsmithUtils"; +// import { noop } from "utils/AppsmithUtils"; import { useDynamicAppLayout } from "utils/hooks/useDynamicAppLayout"; import useGoogleFont from "utils/hooks/useGoogleFont"; -import useHorizontalResize from "utils/hooks/useHorizontalResize"; +// import useHorizontalResize from "utils/hooks/useHorizontalResize"; import Canvas from "../Canvas"; import { Positioning } from "components/constants"; @@ -113,11 +113,11 @@ function CanvasContainer() { const initialWidth = ele.offsetWidth; // The current position of mouse let x = 0; - let y = 0; + // let y = 0; // The dimension of the element let w = 0; - let h = 0; + // let h = 0; let events: any = []; // Handle the mousedown event @@ -125,12 +125,12 @@ function CanvasContainer() { const mouseDownHandler = function(e: any, rightHandle: boolean) { // Get the current mouse position x = e.clientX; - y = e.clientY; + // y = e.clientY; // Calculate the dimension of element const styles = window.getComputedStyle(ele); w = parseInt(styles.width, 10); - h = parseInt(styles.height, 10); + // h = parseInt(styles.height, 10); const mouseMove = (e: any) => mouseMoveHandler(e, rightHandle); events.push(mouseMove); // Attach the listeners to `document` @@ -173,7 +173,7 @@ function CanvasContainer() { }, [appLayout]); const leftResizer = useRef(null); - const resizer = useHorizontalResize(leftResizer, noop, noop); + // const resizer = useHorizontalResize(leftResizer, noop, noop); // calculating exact height to not allow scroll at this component, // calculating total height minus margin on top, top bar and bottom bar const heightWithTopMargin = `calc(100vh - 2.25rem - ${theme.smallHeaderHeight} - ${theme.bottomBarHeight})`; From 78b691bafc260ba51a540969353a71fd7ef569d7 Mon Sep 17 00:00:00 2001 From: Preet Date: Fri, 21 Oct 2022 03:06:03 -0400 Subject: [PATCH 188/708] add comment --- .../pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index 5d9b7d46ba6a..f354f324ea9d 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -227,7 +227,7 @@ export const useAutoLayoutHighlights = ({ let index = 0; const arr: HighlightInfo[] = []; const rects: DOMRect[] = []; - + // TODO: add documentation for the updated logic. for (const layer of flexLayers) { // remove dragged blocks from the layer const filteredLayer = filterLayer(layer, offsetChildren); From cde68ea423efe8b8209b8eaf8ccd731eec2a7fc3 Mon Sep 17 00:00:00 2001 From: Preet Date: Fri, 21 Oct 2022 03:20:53 -0400 Subject: [PATCH 189/708] fix fill size on prop update --- app/client/src/sagas/AutoLayoutUtils.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/client/src/sagas/AutoLayoutUtils.ts b/app/client/src/sagas/AutoLayoutUtils.ts index d1fbdd496f25..6effc8c130fd 100644 --- a/app/client/src/sagas/AutoLayoutUtils.ts +++ b/app/client/src/sagas/AutoLayoutUtils.ts @@ -106,14 +106,16 @@ export function updateFillChildStatus( if (!canvas) return widgets; const flexLayers: FlexLayer[] = canvas.flexLayers || []; + let layerIndex = -1; if (!flexLayers.length) return widgets; - const updatedLayers = flexLayers?.map((layer) => { + const updatedLayers = flexLayers?.map((layer, index: number) => { const children = layer.children || []; const selectedWidgetIndex: number = children.findIndex( (each: LayerChild) => each.id === widgetId, ); if (selectedWidgetIndex === -1) return layer; + layerIndex = index; return { ...layer, hasFillChild: children.reduce((acc, each, index) => { @@ -129,7 +131,9 @@ export function updateFillChildStatus( flexLayers: updatedLayers, }; widgets[canvas.widgetId] = canvas; - return widgets; + + if (layerIndex === -1) return widgets; + return updateFlexChildColumns(widgets, layerIndex, canvas.widgetId); } export function updateFlexChildColumns( From 92d18c740b24da2389fc9c7989a12600625fd109 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Fri, 21 Oct 2022 14:34:28 +0530 Subject: [PATCH 190/708] Fixing errors in dynamic layout resize --- .../src/pages/Editor/WidgetsEditor/CanvasContainer.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx index 7958921d18b5..be8129eb1434 100644 --- a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx +++ b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx @@ -32,8 +32,8 @@ import { getCurrentThemeDetails } from "selectors/themeSelectors"; import { useDynamicAppLayout } from "utils/hooks/useDynamicAppLayout"; import useGoogleFont from "utils/hooks/useGoogleFont"; // import useHorizontalResize from "utils/hooks/useHorizontalResize"; -import Canvas from "../Canvas"; import { Positioning } from "components/constants"; +import Canvas from "../Canvas"; const Container = styled.section<{ background: string; @@ -136,7 +136,7 @@ function CanvasContainer() { // Attach the listeners to `document` document.addEventListener("mousemove", mouseMove); document.addEventListener("mouseup", mouseUpHandler); - e.stopEventPropagation(); + // e.stopPropagation(); }; const mouseMoveHandler = function(e: any, rightHandle: boolean) { @@ -147,7 +147,7 @@ function CanvasContainer() { // Adjust the dimension of element ele.style.width = `${w + dx}px`; } - e.stopEventPropagation(); + // e.stopPropagation(); }; const mouseUpHandler = function() { From 1547b8386284d982ea75a96cf89908b8869cb9c6 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Fri, 21 Oct 2022 14:46:10 +0530 Subject: [PATCH 191/708] Fixing relative positioning while DnD for autolayout --- .../common/CanvasArenas/hooks/useCanvasDragging.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index 13ab3f5f3fb9..ec7b674ec0c6 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -706,19 +706,25 @@ export const useCanvasDragging = ( drawBlockOnCanvas(each); }); if (payload) { + const topOffset = getAbsolutePixels( + stickyCanvasRef.current.style.top, + ); + const leftOffset = getAbsolutePixels( + stickyCanvasRef.current.style.left, + ); canvasCtx.fillStyle = "rgba(223, 158, 206, 0.6)"; payload.highlights.forEach((each) => { canvasCtx.fillRect( - each.posX, - each.posY, + each.posX - leftOffset, + each.posY - topOffset, each.width, each.height, ); }); canvasCtx.fillStyle = "rgba(196, 139, 181, 1)"; canvasCtx.fillRect( - payload.selectedHighlight.posX, - payload.selectedHighlight.posY, + payload.selectedHighlight.posX - leftOffset, + payload.selectedHighlight.posY - topOffset, payload.selectedHighlight.width * (payload.selectedHighlight.isVertical ? 1.5 : 1), payload.selectedHighlight.height * From 8f6ed921017939642999bfc3342ffc89f3bc1046 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Fri, 21 Oct 2022 15:03:49 +0530 Subject: [PATCH 192/708] Trigger Build From b0dac40dfd4b7118d6a791785c45ad37eac06925 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Fri, 21 Oct 2022 16:14:21 +0530 Subject: [PATCH 193/708] Trigger build --- .../designSystems/appsmith/autoLayout/FlexBoxComponent.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx index 9c5ecd9c3a81..a719bf6b8586 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx @@ -1,6 +1,6 @@ +import { isArray } from "lodash"; import React, { ReactNode } from "react"; import styled from "styled-components"; -import { isArray } from "lodash"; import { AppState } from "ce/reducers"; import { From 87c2e6a4958681453dc8fccba4dcd7df935a5d7e Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 24 Oct 2022 16:50:41 -0400 Subject: [PATCH 194/708] update fill columns for mobile breakpoint --- app/client/src/actions/autoLayoutActions.ts | 11 ++++ .../src/ce/constants/ReduxActionConstants.tsx | 1 + .../appsmith/autoLayout/FlexBoxComponent.tsx | 7 ++- .../appsmith/autoLayout/FlexComponent.tsx | 4 +- .../reducers/uiReducers/mainCanvasReducer.ts | 4 ++ app/client/src/sagas/AutoLayoutUtils.ts | 59 ++++++++++++++++++- app/client/src/sagas/LayoutUpdateSagas.tsx | 36 +++++++++++ .../src/selectors/mainCanvasSelectors.tsx | 2 + .../src/utils/hooks/useDynamicAppLayout.tsx | 13 +++- 9 files changed, 130 insertions(+), 7 deletions(-) diff --git a/app/client/src/actions/autoLayoutActions.ts b/app/client/src/actions/autoLayoutActions.ts index 6eb0833fcae1..f2f0e747d37d 100644 --- a/app/client/src/actions/autoLayoutActions.ts +++ b/app/client/src/actions/autoLayoutActions.ts @@ -11,3 +11,14 @@ export const addWrappers = (parentId: string) => ({ parentId, }, }); + +export const updateLayoutForMobileBreakpoint = ( + parentId: string, + isMobile: boolean, +) => ({ + type: ReduxActionTypes.RECALCULATE_COLUMNS, + payload: { + parentId, + isMobile, + }, +}); diff --git a/app/client/src/ce/constants/ReduxActionConstants.tsx b/app/client/src/ce/constants/ReduxActionConstants.tsx index 693833d944bf..73dd05b5a58f 100644 --- a/app/client/src/ce/constants/ReduxActionConstants.tsx +++ b/app/client/src/ce/constants/ReduxActionConstants.tsx @@ -707,6 +707,7 @@ export const ReduxActionTypes = { REMOVE_CHILD_WRAPPERS: "REMOVE_CHILD_WRAPPERS", ADD_CHILD_WRAPPERS: "ADD_CHILD_WRAPPERS", UPDATE_FILL_CHILD_LAYER: "UPDATE_FILL_CHILD_LAYER", + RECALCULATE_COLUMNS: "RECALCULATE_COLUMNS", }; export type ReduxActionType = typeof ReduxActionTypes[keyof typeof ReduxActionTypes]; diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx index a719bf6b8586..a20b4e2189fa 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx @@ -9,8 +9,8 @@ import { Overflow, } from "components/constants"; import { useSelector } from "react-redux"; -import { useIsMobileDevice } from "utils/hooks/useDeviceDetect"; import AutoLayoutLayer from "./AutoLayoutLayer"; +import { getIsMobile } from "selectors/mainCanvasSelectors"; export interface FlexBoxProps { direction?: LayoutDirection; @@ -49,7 +49,8 @@ export const FlexContainer = styled.div<{ overflow?.indexOf("wrap") > -1 ? overflow : "nowrap"}; width: 100%; - height: ${({ stretchHeight }) => (stretchHeight ? "100%" : "auto")}; + height: ${({ isMainContainer, stretchHeight }) => + isMainContainer || stretchHeight ? "100%" : "auto"}; overflow: hidden; overflow-y: ${({ isMainContainer, isMobile }) => @@ -59,7 +60,7 @@ export const FlexContainer = styled.div<{ function FlexBoxComponent(props: FlexBoxProps) { // TODO: set isMobile as a prop at the top level - const isMobile = useIsMobileDevice(); + const isMobile = useSelector(getIsMobile); const direction: LayoutDirection = props.direction || LayoutDirection.Horizontal; diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx index c885d61df521..df5ae4e96a51 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx @@ -12,9 +12,9 @@ import { useSelector } from "store"; import { useClickToSelectWidget } from "utils/hooks/useClickToSelectWidget"; import { usePositionedContainerZIndex } from "utils/hooks/usePositionedContainerZIndex"; import { checkIsDropTarget } from "../PositionedContainer"; -import { useIsMobileDevice } from "utils/hooks/useDeviceDetect"; import { AppState } from "ce/reducers"; import { DRAG_MARGIN } from "widgets/constants"; +import { getIsMobile } from "selectors/mainCanvasSelectors"; export type AutoLayoutProps = { children: ReactNode; @@ -67,7 +67,7 @@ const FlexWidget = styled.div<{ // TODO: update min width logic. export function FlexComponent(props: AutoLayoutProps) { - const isMobile = useIsMobileDevice(); + const isMobile = useSelector(getIsMobile); const isSnipingMode = useSelector(snipingModeSelector); const clickToSelectWidget = useClickToSelectWidget(props.widgetId); const onClickFn = useCallback(() => { diff --git a/app/client/src/reducers/uiReducers/mainCanvasReducer.ts b/app/client/src/reducers/uiReducers/mainCanvasReducer.ts index 73e45944cad1..399c82699a29 100644 --- a/app/client/src/reducers/uiReducers/mainCanvasReducer.ts +++ b/app/client/src/reducers/uiReducers/mainCanvasReducer.ts @@ -6,11 +6,13 @@ import { } from "@appsmith/constants/ReduxActionConstants"; import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; import { UpdateCanvasLayoutPayload } from "actions/controlActions"; +import { MOBILE_MAX_WIDTH } from "constants/AppConstants"; const initialState: MainCanvasReduxState = { initialized: false, width: 0, height: 0, + isMobile: false, }; const mainCanvasReducer = createImmerReducer(initialState, { @@ -32,6 +34,7 @@ const mainCanvasReducer = createImmerReducer(initialState, { state.width = action.payload.width || state.width; state.height = action.payload.height || state.height; state.initialized = true; + state.isMobile = action.payload.width <= MOBILE_MAX_WIDTH; }, }); @@ -39,6 +42,7 @@ export interface MainCanvasReduxState { width: number; height: number; initialized: boolean; + isMobile: boolean; } export default mainCanvasReducer; diff --git a/app/client/src/sagas/AutoLayoutUtils.ts b/app/client/src/sagas/AutoLayoutUtils.ts index 6effc8c130fd..501594db2869 100644 --- a/app/client/src/sagas/AutoLayoutUtils.ts +++ b/app/client/src/sagas/AutoLayoutUtils.ts @@ -1,4 +1,8 @@ -import { FlexLayerAlignment, ResponsiveBehavior } from "components/constants"; +import { + FlexLayerAlignment, + Positioning, + ResponsiveBehavior, +} from "components/constants"; import { FlexLayer, LayerChild, @@ -220,3 +224,56 @@ export function updateSizeOfAllChildren( return widgets; } + +export function alterLayoutForMobile( + allWidgets: CanvasWidgetsReduxState, + parentId: string, +): CanvasWidgetsReduxState { + let widgets = { ...allWidgets }; + const parent = widgets[parentId]; + const children = parent.children; + + if (checkIsNotVerticalStack(parent) && parent.widgetId !== "0") + return widgets; + if (!children || !children.length) return widgets; + + for (const child of children) { + const widget = { ...widgets[child] }; + + if (widget.responsiveBehavior === ResponsiveBehavior.Fill) { + widget.rightColumn = 64; + widget.leftColumn = 0; + } + + widgets = alterLayoutForMobile(widgets, child); + widgets[child] = widget; + } + return widgets; +} + +export function alterLayoutForDesktop( + allWidgets: CanvasWidgetsReduxState, + parentId: string, +): CanvasWidgetsReduxState { + let widgets = { ...allWidgets }; + const parent = widgets[parentId]; + const children = parent.children; + + if (checkIsNotVerticalStack(parent) && parent.widgetId !== "0") + return widgets; + if (!children || !children.length) return widgets; + + widgets = updateSizeOfAllChildren(widgets, parentId); + for (const child of children) { + widgets = alterLayoutForDesktop(widgets, child); + } + + return widgets; +} + +function checkIsNotVerticalStack(widget: any): boolean { + return ( + widget.positioning !== undefined && + widget.positioning !== Positioning.Vertical + ); +} diff --git a/app/client/src/sagas/LayoutUpdateSagas.tsx b/app/client/src/sagas/LayoutUpdateSagas.tsx index 137e75ea300f..6dc2c59c1743 100644 --- a/app/client/src/sagas/LayoutUpdateSagas.tsx +++ b/app/client/src/sagas/LayoutUpdateSagas.tsx @@ -9,6 +9,8 @@ import log from "loglevel"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; import { all, call, put, select, takeLatest } from "redux-saga/effects"; import { + alterLayoutForDesktop, + alterLayoutForMobile, removeChildLayers, updateFillChildStatus, wrapChildren, @@ -92,10 +94,44 @@ export function* updateFillChildInfo( } } +export function* updateLayoutForMobileCheckpoint( + actionPayload: ReduxAction<{ + parentId: string; + isMobile: boolean; + }>, +) { + try { + const start = performance.now(); + const { isMobile, parentId } = actionPayload.payload; + const allWidgets: CanvasWidgetsReduxState = yield select(getWidgets); + const updatedWidgets: CanvasWidgetsReduxState = isMobile + ? alterLayoutForMobile(allWidgets, parentId) + : alterLayoutForDesktop(allWidgets, parentId); + yield put(updateAndSaveLayout(updatedWidgets)); + log.debug( + "updating layout for mobile viewport took", + performance.now() - start, + "ms", + ); + } catch (error) { + yield put({ + type: ReduxActionErrorTypes.WIDGET_OPERATION_ERROR, + payload: { + action: ReduxActionTypes.RECALCULATE_COLUMNS, + error, + }, + }); + } +} + export default function* layoutUpdateSagas() { yield all([ takeLatest(ReduxActionTypes.ADD_CHILD_WRAPPERS, addChildWrappers), takeLatest(ReduxActionTypes.REMOVE_CHILD_WRAPPERS, removeChildWrappers), takeLatest(ReduxActionTypes.UPDATE_FILL_CHILD_LAYER, updateFillChildInfo), + takeLatest( + ReduxActionTypes.RECALCULATE_COLUMNS, + updateLayoutForMobileCheckpoint, + ), ]); } diff --git a/app/client/src/selectors/mainCanvasSelectors.tsx b/app/client/src/selectors/mainCanvasSelectors.tsx index a9f4f01f64ae..e79e6c436731 100644 --- a/app/client/src/selectors/mainCanvasSelectors.tsx +++ b/app/client/src/selectors/mainCanvasSelectors.tsx @@ -3,3 +3,5 @@ import { AppState } from "@appsmith/reducers"; export const getIsCanvasInitialized = (state: AppState) => { return state.ui.mainCanvas.initialized; }; + +export const getIsMobile = (state: AppState) => state.ui.mainCanvas.isMobile; diff --git a/app/client/src/utils/hooks/useDynamicAppLayout.tsx b/app/client/src/utils/hooks/useDynamicAppLayout.tsx index bf084a16b7f6..eecc8d172972 100644 --- a/app/client/src/utils/hooks/useDynamicAppLayout.tsx +++ b/app/client/src/utils/hooks/useDynamicAppLayout.tsx @@ -23,6 +23,8 @@ import { getIsCanvasInitialized } from "selectors/mainCanvasSelectors"; import { calculateDynamicHeight } from "utils/DSLMigrations"; import { scrollbarWidth } from "utils/helpers"; import { useWindowSizeHooks } from "./dragResizeHooks"; +import { MOBILE_MAX_WIDTH } from "constants/AppConstants"; +import { updateLayoutForMobileBreakpoint } from "actions/autoLayoutActions"; const BORDERS_WIDTH = 2; const GUTTER_WIDTH = 72; @@ -136,10 +138,18 @@ export const useDynamicAppLayout = () => { */ const resizeToLayout = () => { const calculatedWidth = calculateCanvasWidth(); - const { width: rightColumn } = mainCanvasProps || {}; + const { isMobile, width: rightColumn } = mainCanvasProps || {}; if (rightColumn !== calculatedWidth || !isCanvasInitialized) { dispatch(updateCanvasLayoutAction(calculatedWidth, calculatedMinHeight)); + if (calculatedWidth <= MOBILE_MAX_WIDTH) { + (!isMobile || !isCanvasInitialized) && + dispatch(updateLayoutForMobileBreakpoint("0", true)); + } + if (calculatedWidth > MOBILE_MAX_WIDTH) { + (isMobile || !isCanvasInitialized) && + dispatch(updateLayoutForMobileBreakpoint("0", false)); + } } }; @@ -200,6 +210,7 @@ export const useDynamicAppLayout = () => { isPreviewMode, explorerWidth, isExplorerPinned, + mainCanvasProps?.isMobile, ]); return isCanvasInitialized; From cadc717981677f394b80767739dfdd56aad712e1 Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 24 Oct 2022 17:53:37 -0400 Subject: [PATCH 195/708] reduce redux updates at breakpoint --- .../src/utils/hooks/useDynamicAppLayout.tsx | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/app/client/src/utils/hooks/useDynamicAppLayout.tsx b/app/client/src/utils/hooks/useDynamicAppLayout.tsx index eecc8d172972..ad6f7a4d47d2 100644 --- a/app/client/src/utils/hooks/useDynamicAppLayout.tsx +++ b/app/client/src/utils/hooks/useDynamicAppLayout.tsx @@ -138,18 +138,10 @@ export const useDynamicAppLayout = () => { */ const resizeToLayout = () => { const calculatedWidth = calculateCanvasWidth(); - const { isMobile, width: rightColumn } = mainCanvasProps || {}; + const { width: rightColumn } = mainCanvasProps || {}; if (rightColumn !== calculatedWidth || !isCanvasInitialized) { dispatch(updateCanvasLayoutAction(calculatedWidth, calculatedMinHeight)); - if (calculatedWidth <= MOBILE_MAX_WIDTH) { - (!isMobile || !isCanvasInitialized) && - dispatch(updateLayoutForMobileBreakpoint("0", true)); - } - if (calculatedWidth > MOBILE_MAX_WIDTH) { - (isMobile || !isCanvasInitialized) && - dispatch(updateLayoutForMobileBreakpoint("0", false)); - } } }; @@ -210,8 +202,14 @@ export const useDynamicAppLayout = () => { isPreviewMode, explorerWidth, isExplorerPinned, - mainCanvasProps?.isMobile, ]); + useEffect(() => { + function relayoutAtBreakpoint() { + dispatch(updateLayoutForMobileBreakpoint("0", mainCanvasProps?.isMobile)); + } + relayoutAtBreakpoint(); + }, [mainCanvasProps?.isMobile]); + return isCanvasInitialized; }; From 52c08b4905c98d8a592a521a032f8b505f91b390 Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 24 Oct 2022 18:33:36 -0400 Subject: [PATCH 196/708] fix deletion issue --- app/client/src/sagas/AutoLayoutUtils.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/client/src/sagas/AutoLayoutUtils.ts b/app/client/src/sagas/AutoLayoutUtils.ts index 501594db2869..ddecdf6bd9c0 100644 --- a/app/client/src/sagas/AutoLayoutUtils.ts +++ b/app/client/src/sagas/AutoLayoutUtils.ts @@ -86,7 +86,16 @@ export function* updateFlexLayersOnDelete( if (index === -1) continue; children.splice(index, 1); layer.children = children; + break; } + // update hasFillChild property + flexLayers[layerIndex] = { + ...flexLayers[layerIndex], + hasFillChild: flexLayers[layerIndex].children.some( + (each: LayerChild) => + widgets[each.id].responsiveBehavior === ResponsiveBehavior.Fill, + ), + }; parent = { ...parent, flexLayers: flexLayers.filter( @@ -94,6 +103,7 @@ export function* updateFlexLayersOnDelete( ), }; widgets[parentId] = parent; + if (layerIndex === -1) return widgets; return updateFlexChildColumns(widgets, layerIndex, parentId); } From 8bf3fd593709451481e465c5a131ad03afe0a9b6 Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 24 Oct 2022 20:19:28 -0400 Subject: [PATCH 197/708] build fix --- app/client/src/utils/hooks/useDynamicAppLayout.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/app/client/src/utils/hooks/useDynamicAppLayout.tsx b/app/client/src/utils/hooks/useDynamicAppLayout.tsx index ad6f7a4d47d2..0210f9e82be1 100644 --- a/app/client/src/utils/hooks/useDynamicAppLayout.tsx +++ b/app/client/src/utils/hooks/useDynamicAppLayout.tsx @@ -23,7 +23,6 @@ import { getIsCanvasInitialized } from "selectors/mainCanvasSelectors"; import { calculateDynamicHeight } from "utils/DSLMigrations"; import { scrollbarWidth } from "utils/helpers"; import { useWindowSizeHooks } from "./dragResizeHooks"; -import { MOBILE_MAX_WIDTH } from "constants/AppConstants"; import { updateLayoutForMobileBreakpoint } from "actions/autoLayoutActions"; const BORDERS_WIDTH = 2; From ba45042196ede1f53b5fd4b4180cbf945b4aa93c Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 24 Oct 2022 21:17:29 -0400 Subject: [PATCH 198/708] trigger another build --- app/client/src/sagas/AutoLayoutUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/client/src/sagas/AutoLayoutUtils.ts b/app/client/src/sagas/AutoLayoutUtils.ts index ddecdf6bd9c0..12b09e61b580 100644 --- a/app/client/src/sagas/AutoLayoutUtils.ts +++ b/app/client/src/sagas/AutoLayoutUtils.ts @@ -107,7 +107,7 @@ export function* updateFlexLayersOnDelete( if (layerIndex === -1) return widgets; return updateFlexChildColumns(widgets, layerIndex, parentId); } - +// TODO: refactor these implementations export function updateFillChildStatus( allWidgets: CanvasWidgetsReduxState, widgetId: string, From c667dc5edbaa3362f1cc41415028d7d7e3503df8 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Wed, 26 Oct 2022 18:46:18 +0530 Subject: [PATCH 199/708] Fixing view mode crash because of useDynamicAppLayout changes --- .../src/utils/hooks/useDynamicAppLayout.tsx | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/app/client/src/utils/hooks/useDynamicAppLayout.tsx b/app/client/src/utils/hooks/useDynamicAppLayout.tsx index 0210f9e82be1..a553d2351431 100644 --- a/app/client/src/utils/hooks/useDynamicAppLayout.tsx +++ b/app/client/src/utils/hooks/useDynamicAppLayout.tsx @@ -104,9 +104,13 @@ export const useDynamicAppLayout = () => { calculatedWidth -= explorerWidth; } const ele: any = document.getElementById("main-canvas-container"); - const mainCanvasWidth = ele.clientWidth; - if (appLayout?.type === "FLUID" && calculatedWidth > mainCanvasWidth) { - calculatedWidth = mainCanvasWidth; + if ( + appMode === "EDIT" && + appLayout?.type === "FLUID" && + ele && + calculatedWidth > ele.clientWidth + ) { + calculatedWidth = ele.clientWidth; } switch (true) { case maxWidth < 0: @@ -157,13 +161,15 @@ export const useDynamicAppLayout = () => { const resizeObserver = new ResizeObserver(immediateDebouncedResize); useEffect(() => { const ele: any = document.getElementById("main-canvas-container"); - if (ele && appLayout?.type === "FLUID") { - resizeObserver.observe(ele); - } else { - resizeObserver.unobserve(ele); + if (ele) { + if (appLayout?.type === "FLUID") { + resizeObserver.observe(ele); + } else { + resizeObserver.unobserve(ele); + } } return () => { - resizeObserver.unobserve(ele); + ele && resizeObserver.unobserve(ele); }; }, [appLayout]); From 32e63f0a7e12ffdd65c54bad6354a0004dc23d6d Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Wed, 26 Oct 2022 19:42:23 +0530 Subject: [PATCH 200/708] Fixing dynamic resize issues. --- .../Editor/WidgetsEditor/CanvasContainer.tsx | 23 +++++++++++-------- .../src/utils/hooks/useDynamicAppLayout.tsx | 4 +++- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx index be8129eb1434..ab6c480b33e6 100644 --- a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx +++ b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx @@ -1,4 +1,4 @@ -import React, { ReactNode, useEffect, useRef } from "react"; +import React, { ReactNode, useEffect } from "react"; import { useSelector } from "react-redux"; import { getCanvasWidth, @@ -106,7 +106,7 @@ function CanvasContainer() { } const appLayout = useSelector(getCurrentApplicationLayout); useEffect(() => { - if (appLayout?.type === "FLUID") { + if (!isPreviewMode && appLayout?.type === "FLUID") { const smallestWidth = layoutConfigurations.MOBILE.minWidth; // Query the element const ele: any = document.getElementById("main-canvas-container"); @@ -170,10 +170,8 @@ function CanvasContainer() { const ele: any = document.getElementById("main-canvas-container"); ele.style.width = "inherit"; } - }, [appLayout]); + }, [appLayout, isPreviewMode]); - const leftResizer = useRef(null); - // const resizer = useHorizontalResize(leftResizer, noop, noop); // calculating exact height to not allow scroll at this component, // calculating total height minus margin on top, top bar and bottom bar const heightWithTopMargin = `calc(100vh - 2.25rem - ${theme.smallHeaderHeight} - ${theme.bottomBarHeight})`; @@ -197,12 +195,15 @@ function CanvasContainer() { fontFamily: fontFamily, }} > - {appLayout?.type === "FLUID" && ( + {appLayout?.type === "FLUID" && !isPreviewMode && ( <> { + e.preventDefault(); + e.stopPropagation(); + }} style={{ zIndex: 100, position: "sticky", @@ -217,7 +218,11 @@ function CanvasContainer() { { + e.preventDefault(); + e.stopPropagation(); + }} style={{ zIndex: 100, position: "sticky", diff --git a/app/client/src/utils/hooks/useDynamicAppLayout.tsx b/app/client/src/utils/hooks/useDynamicAppLayout.tsx index a553d2351431..dd933c0bc428 100644 --- a/app/client/src/utils/hooks/useDynamicAppLayout.tsx +++ b/app/client/src/utils/hooks/useDynamicAppLayout.tsx @@ -2,6 +2,7 @@ import { debounce, get } from "lodash"; import { useCallback, useEffect, useMemo } from "react"; import { useDispatch, useSelector } from "react-redux"; +import { updateLayoutForMobileBreakpoint } from "actions/autoLayoutActions"; import { updateCanvasLayoutAction } from "actions/editorActions"; import { DefaultLayoutType, @@ -23,7 +24,6 @@ import { getIsCanvasInitialized } from "selectors/mainCanvasSelectors"; import { calculateDynamicHeight } from "utils/DSLMigrations"; import { scrollbarWidth } from "utils/helpers"; import { useWindowSizeHooks } from "./dragResizeHooks"; -import { updateLayoutForMobileBreakpoint } from "actions/autoLayoutActions"; const BORDERS_WIDTH = 2; const GUTTER_WIDTH = 72; @@ -156,6 +156,8 @@ export const useDynamicAppLayout = () => { const immediateDebouncedResize = useCallback(debounce(resizeToLayout), [ mainCanvasProps, screenWidth, + appMode, + appLayout, ]); const resizeObserver = new ResizeObserver(immediateDebouncedResize); From 3210c65666c769343818565e70e32eeaf62b40cd Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 26 Oct 2022 11:56:31 -0400 Subject: [PATCH 201/708] fix flex layers on bulk deletion --- app/client/src/sagas/AutoLayoutUtils.ts | 30 +++++++++++++-------- app/client/src/sagas/WidgetDeletionSagas.ts | 23 ++++++++++++---- 2 files changed, 37 insertions(+), 16 deletions(-) diff --git a/app/client/src/sagas/AutoLayoutUtils.ts b/app/client/src/sagas/AutoLayoutUtils.ts index 12b09e61b580..d3245831ad7e 100644 --- a/app/client/src/sagas/AutoLayoutUtils.ts +++ b/app/client/src/sagas/AutoLayoutUtils.ts @@ -73,9 +73,10 @@ export function* updateFlexLayersOnDelete( let parent = widgets[parentId]; if (!parent) return widgets; - const flexLayers = parent.flexLayers || []; + let flexLayers = [...(parent.flexLayers || [])]; if (!flexLayers.length) return widgets; let layerIndex = -1; // Find the layer in which the deleted widget exists. + let updatedChildren: LayerChild[] = []; for (const layer of flexLayers) { layerIndex += 1; const children = layer.children || []; @@ -84,18 +85,25 @@ export function* updateFlexLayersOnDelete( (each: LayerChild) => each.id === widgetId, ); if (index === -1) continue; - children.splice(index, 1); - layer.children = children; + // children.splice(index, 1); + updatedChildren = children.filter( + (each: LayerChild) => each.id !== widgetId, + ); break; } - // update hasFillChild property - flexLayers[layerIndex] = { - ...flexLayers[layerIndex], - hasFillChild: flexLayers[layerIndex].children.some( - (each: LayerChild) => - widgets[each.id].responsiveBehavior === ResponsiveBehavior.Fill, - ), - }; + + flexLayers = [ + ...flexLayers.slice(0, layerIndex), + { + children: updatedChildren, + hasFillChild: updatedChildren.some( + (each: LayerChild) => + widgets[each.id].responsiveBehavior === ResponsiveBehavior.Fill, + ), + }, + ...flexLayers.slice(layerIndex + 1), + ]; + parent = { ...parent, flexLayers: flexLayers.filter( diff --git a/app/client/src/sagas/WidgetDeletionSagas.ts b/app/client/src/sagas/WidgetDeletionSagas.ts index ba7b49e74eab..e83829eb6240 100644 --- a/app/client/src/sagas/WidgetDeletionSagas.ts +++ b/app/client/src/sagas/WidgetDeletionSagas.ts @@ -311,7 +311,18 @@ function* deleteAllSelectedWidgetsSaga( ); // assuming only widgets with same parent can be selected const parentId = widgets[selectedWidgets[0]].parentId; - + console.log("#### parent id", parentId); + let widgetsAfterUpdatingFlexLayers: CanvasWidgetsReduxState = finalWidgets; + if (parentId) { + for (const widgetId of selectedWidgets) { + widgetsAfterUpdatingFlexLayers = yield call( + updateFlexLayersOnDelete, + widgetsAfterUpdatingFlexLayers, + widgetId, + parentId, + ); + } + } //Main canvas's minheight keeps varying, hence retrieving updated value let mainCanvasMinHeight; if (parentId === MAIN_CONTAINER_WIDGET_ID) { @@ -321,16 +332,18 @@ function* deleteAllSelectedWidgetsSaga( mainCanvasMinHeight = mainCanvasProps?.height; } - if (parentId && finalWidgets[parentId]) { - finalWidgets[parentId].bottomRow = resizeCanvasToLowestWidget( - finalWidgets, + if (parentId && widgetsAfterUpdatingFlexLayers[parentId]) { + widgetsAfterUpdatingFlexLayers[ + parentId + ].bottomRow = resizeCanvasToLowestWidget( + widgetsAfterUpdatingFlexLayers, parentId, finalWidgets[parentId].bottomRow, mainCanvasMinHeight, ); } - yield put(updateAndSaveLayout(finalWidgets)); + yield put(updateAndSaveLayout(widgetsAfterUpdatingFlexLayers)); yield put(selectWidgetInitAction("")); const bulkDeleteKey = selectedWidgets.join(","); if (!disallowUndo) { From c904301f64406e02e084f83af91159b7c70cbb59 Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 26 Oct 2022 12:21:14 -0400 Subject: [PATCH 202/708] reduce widget width on drag --- .../appsmith/autoLayout/FlexComponent.tsx | 15 ++++++++------- .../editorComponents/ResizableComponent.tsx | 5 +++-- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx index df5ae4e96a51..b347deea5280 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx @@ -44,10 +44,7 @@ const FlexWidget = styled.div<{ position: relative; z-index: ${({ zIndex }) => zIndex}; - width: ${({ componentWidth, isCurrentCanvasDragging, isFillWidget }) => - isFillWidget || isCurrentCanvasDragging - ? "auto" - : `${Math.floor(componentWidth)}px`}; + width: ${({ componentWidth }) => `${Math.floor(componentWidth)}px`}; height: ${({ componentHeight, isMobile }) => isMobile ? "100%" : Math.floor(componentHeight) + "px"}; min-width: ${({ minWidth }) => minWidth}; @@ -61,7 +58,7 @@ const FlexWidget = styled.div<{ z-index: ${({ zIndexOnHover }) => zIndexOnHover} !important; } margin: ${({ isCurrentCanvasDragging }) => - isCurrentCanvasDragging ? `${DRAG_MARGIN}px` : 0}; + isCurrentCanvasDragging ? `${DRAG_MARGIN}px` : "0px"}; `; // TODO: update min width logic. @@ -78,7 +75,7 @@ export function FlexComponent(props: AutoLayoutProps) { (state: AppState) => state.ui.widgetDragResize, ); - const isCurrentCanvasDragging = dragDetails?.draggedOn !== undefined; + const isCurrentCanvasDragging = dragDetails?.draggedOn === props.parentId; const isDropTarget = checkIsDropTarget(props.widgetType); const { onHoverZIndex, zIndex } = usePositionedContainerZIndex( @@ -112,7 +109,11 @@ export function FlexComponent(props: AutoLayoutProps) { Date: Wed, 26 Oct 2022 12:43:32 -0400 Subject: [PATCH 203/708] fix highlight selection on proximity --- .../common/CanvasArenas/hooks/useAutoLayoutHighlights.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index f354f324ea9d..60b4cb0b2ea7 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -626,10 +626,10 @@ export const useAutoLayoutHighlights = ({ let distX: number = a.isVertical && isVerticalDrag ? 0 : a.posX - b.x; let distY: number = !a.isVertical && !isVerticalDrag ? 0 : a.posY - b.y; - if (moveDirection === ReflowDirection.LEFT && distX > 0) distX += 2000; - if (moveDirection === ReflowDirection.RIGHT && distX < 0) distX -= 2000; - if (moveDirection === ReflowDirection.TOP && distY > 0) distY += 2000; - if (moveDirection === ReflowDirection.BOTTOM && distY < 0) distY -= 2000; + if (moveDirection === ReflowDirection.LEFT && distX > 20) distX += 2000; + if (moveDirection === ReflowDirection.RIGHT && distX < 20) distX -= 2000; + if (moveDirection === ReflowDirection.TOP && distY > 20) distY += 2000; + if (moveDirection === ReflowDirection.BOTTOM && distY < 20) distY -= 2000; return Math.abs(Math.sqrt(distX * distX + distY * distY)); }; From fc2603b589a9e7e7dfc611ecd684cba9999acc6a Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Thu, 27 Oct 2022 16:10:34 +0530 Subject: [PATCH 204/708] Disabling Draw to select, grouping, multi select --- app/client/src/pages/Editor/Canvas.tsx | 24 ++++----- .../CanvasArenas/CanvasSelectionArena.tsx | 26 +++++----- app/client/src/sagas/WidgetDeletionSagas.ts | 33 ++++++------ app/client/src/sagas/WidgetSelectionSagas.ts | 50 ++++++++++--------- app/client/src/widgets/CanvasWidget.tsx | 40 +++++++-------- .../HorizontalLayoutWidget/widget/index.tsx | 22 ++++---- .../VerticalLayoutWidget/widget/index.tsx | 24 ++++----- 7 files changed, 110 insertions(+), 109 deletions(-) diff --git a/app/client/src/pages/Editor/Canvas.tsx b/app/client/src/pages/Editor/Canvas.tsx index 25c6c874076c..1f8a0385a6aa 100644 --- a/app/client/src/pages/Editor/Canvas.tsx +++ b/app/client/src/pages/Editor/Canvas.tsx @@ -1,25 +1,25 @@ -import log from "loglevel"; import * as Sentry from "@sentry/react"; -import styled from "styled-components"; +import log from "loglevel"; +import React, { memo, useCallback, useEffect } from "react"; import store, { useSelector } from "store"; -import { CanvasWidgetStructure } from "widgets/constants"; +import styled from "styled-components"; import WidgetFactory from "utils/WidgetFactory"; -import React, { memo, useCallback, useEffect } from "react"; +import { CanvasWidgetStructure } from "widgets/constants"; +import { collabShareUserPointerEvent } from "actions/appCollabActions"; +import { initPageLevelSocketConnection } from "actions/websocketActions"; +import { RenderModes } from "constants/WidgetConstants"; +import { throttle } from "lodash"; import CanvasMultiPointerArena, { POINTERS_CANVAS_ID, } from "pages/common/CanvasArenas/CanvasMultiPointerArena"; -import { throttle } from "lodash"; -import { RenderModes } from "constants/WidgetConstants"; -import { isMultiplayerEnabledForUser as isMultiplayerEnabledForUserSelector } from "selectors/appCollabSelectors"; import { useDispatch } from "react-redux"; -import { initPageLevelSocketConnection } from "actions/websocketActions"; -import { collabShareUserPointerEvent } from "actions/appCollabActions"; -import { getIsPageLevelSocketConnected } from "selectors/websocketSelectors"; -import { getCurrentGitBranch } from "selectors/gitSyncSelectors"; -import { getSelectedAppTheme } from "selectors/appThemingSelectors"; import { getPageLevelSocketRoomId } from "sagas/WebsocketSagas/utils"; +import { isMultiplayerEnabledForUser as isMultiplayerEnabledForUserSelector } from "selectors/appCollabSelectors"; +import { getSelectedAppTheme } from "selectors/appThemingSelectors"; import { previewModeSelector } from "selectors/editorSelectors"; +import { getCurrentGitBranch } from "selectors/gitSyncSelectors"; +import { getIsPageLevelSocketConnected } from "selectors/websocketSelectors"; interface CanvasProps { widgetsStructure: CanvasWidgetStructure; diff --git a/app/client/src/pages/common/CanvasArenas/CanvasSelectionArena.tsx b/app/client/src/pages/common/CanvasArenas/CanvasSelectionArena.tsx index 1a3d15e68afe..55b2eadd9745 100644 --- a/app/client/src/pages/common/CanvasArenas/CanvasSelectionArena.tsx +++ b/app/client/src/pages/common/CanvasArenas/CanvasSelectionArena.tsx @@ -1,31 +1,31 @@ +import { AppState } from "@appsmith/reducers"; import { selectAllWidgetsInAreaAction, setCanvasSelectionStateAction, } from "actions/canvasSelectionActions"; +import { + getSlidingCanvasName, + getStickyCanvasName, +} from "constants/componentClassNameConstants"; +import { theme } from "constants/DefaultTheme"; +import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; +import { APP_MODE } from "entities/App"; import { throttle } from "lodash"; -import React, { useEffect, useCallback, useRef } from "react"; +import React, { useCallback, useEffect, useRef } from "react"; import { useDispatch, useSelector } from "react-redux"; -import { AppState } from "@appsmith/reducers"; -import { APP_MODE } from "entities/App"; import { getWidget } from "sagas/selectors"; import { getAppMode } from "selectors/applicationSelectors"; +import { getIsDraggingForSelection } from "selectors/canvasSelectors"; import { getCurrentApplicationLayout, getCurrentPageId, previewModeSelector, } from "selectors/editorSelectors"; import { getNearestParentCanvas } from "utils/generators"; -import { useCanvasDragToScroll } from "./hooks/useCanvasDragToScroll"; -import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; +import { getAbsolutePixels } from "utils/helpers"; import { XYCord } from "./hooks/useCanvasDragging"; -import { theme } from "constants/DefaultTheme"; -import { getIsDraggingForSelection } from "selectors/canvasSelectors"; +import { useCanvasDragToScroll } from "./hooks/useCanvasDragToScroll"; import { StickyCanvasArena } from "./StickyCanvasArena"; -import { getAbsolutePixels } from "utils/helpers"; -import { - getSlidingCanvasName, - getStickyCanvasName, -} from "constants/componentClassNameConstants"; export interface SelectedArenaDimensions { top: number; @@ -75,7 +75,7 @@ export function CanvasSelectionArena({ getWidget(state, widgetId), ); const currentPageId = useSelector(getCurrentPageId); - const appLayout = useSelector(getCurrentApplicationLayout); + const appLayout = useSelector(getCurrentApplicationLayout) || "FLUID"; const throttledWidgetSelection = useCallback( throttle( ( diff --git a/app/client/src/sagas/WidgetDeletionSagas.ts b/app/client/src/sagas/WidgetDeletionSagas.ts index e83829eb6240..38ddf6daaa5b 100644 --- a/app/client/src/sagas/WidgetDeletionSagas.ts +++ b/app/client/src/sagas/WidgetDeletionSagas.ts @@ -1,3 +1,10 @@ +import { + ReduxAction, + ReduxActionErrorTypes, + ReduxActionTypes, + WidgetReduxActionTypes, +} from "@appsmith/constants/ReduxActionConstants"; +import { toggleShowDeviationDialog } from "actions/onboardingActions"; import { MultipleWidgetDeletePayload, updateAndSaveLayout, @@ -5,12 +12,6 @@ import { } from "actions/pageActions"; import { closePropertyPane, closeTableFilterPane } from "actions/widgetActions"; import { selectWidgetInitAction } from "actions/widgetSelectionActions"; -import { - ReduxAction, - ReduxActionErrorTypes, - ReduxActionTypes, - WidgetReduxActionTypes, -} from "@appsmith/constants/ReduxActionConstants"; import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; import { ENTITY_TYPE } from "entities/AppsmithConsole"; import LOG_TYPE from "entities/AppsmithConsole/logtype"; @@ -19,11 +20,20 @@ import { CanvasWidgetsReduxState, FlattenedWidgetProps, } from "reducers/entityReducers/canvasWidgetsReducer"; +import { MainCanvasReduxState } from "reducers/uiReducers/mainCanvasReducer"; import { all, call, put, select, takeEvery } from "redux-saga/effects"; +import { getMainCanvasProps } from "selectors/editorSelectors"; +import { + inGuidedTour, + isExploringSelector, +} from "selectors/onboardingSelectors"; import { getSelectedWidgets } from "selectors/ui"; import AnalyticsUtil from "utils/AnalyticsUtil"; import AppsmithConsole from "utils/AppsmithConsole"; +import { showUndoRedoToast } from "utils/replayHelpers"; +import WidgetFactory from "utils/WidgetFactory"; import { WidgetProps } from "widgets/BaseWidget"; +import { updateFlexLayersOnDelete } from "./AutoLayoutUtils"; import { getSelectedWidget, getWidget, getWidgets } from "./selectors"; import { getAllWidgetsInTree, @@ -31,16 +41,6 @@ import { updateListWidgetPropertiesOnChildDelete, WidgetsInTree, } from "./WidgetOperationUtils"; -import { showUndoRedoToast } from "utils/replayHelpers"; -import WidgetFactory from "utils/WidgetFactory"; -import { - inGuidedTour, - isExploringSelector, -} from "selectors/onboardingSelectors"; -import { toggleShowDeviationDialog } from "actions/onboardingActions"; -import { getMainCanvasProps } from "selectors/editorSelectors"; -import { MainCanvasReduxState } from "reducers/uiReducers/mainCanvasReducer"; -import { updateFlexLayersOnDelete } from "./AutoLayoutUtils"; const WidgetTypes = WidgetFactory.widgetTypes; type WidgetDeleteTabChild = { @@ -311,7 +311,6 @@ function* deleteAllSelectedWidgetsSaga( ); // assuming only widgets with same parent can be selected const parentId = widgets[selectedWidgets[0]].parentId; - console.log("#### parent id", parentId); let widgetsAfterUpdatingFlexLayers: CanvasWidgetsReduxState = finalWidgets; if (parentId) { for (const widgetId of selectedWidgets) { diff --git a/app/client/src/sagas/WidgetSelectionSagas.ts b/app/client/src/sagas/WidgetSelectionSagas.ts index 9f7b6278e8e0..a2d9d9d804ba 100644 --- a/app/client/src/sagas/WidgetSelectionSagas.ts +++ b/app/client/src/sagas/WidgetSelectionSagas.ts @@ -1,16 +1,14 @@ +import { + createMessage, + SELECT_ALL_WIDGETS_MSG, +} from "@appsmith/constants/messages"; import { ReduxAction, ReduxActionErrorTypes, ReduxActionTypes, } from "@appsmith/constants/ReduxActionConstants"; -import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; -import { all, call, fork, put, select, takeLatest } from "redux-saga/effects"; -import { - getWidgetImmediateChildren, - getWidgetMetaProps, - getWidgets, -} from "./selectors"; -import log from "loglevel"; +import { AppState } from "@appsmith/reducers"; +import { showModal } from "actions/widgetActions"; import { deselectMultipleWidgetsAction, selectMultipleWidgetsAction, @@ -18,27 +16,29 @@ import { selectWidgetInitAction, silentAddSelectionsAction, } from "actions/widgetSelectionActions"; -import { Toaster } from "design-system"; -import { - createMessage, - SELECT_ALL_WIDGETS_MSG, -} from "@appsmith/constants/messages"; import { Variant } from "components/ads/common"; -import { getLastSelectedWidget, getSelectedWidgets } from "selectors/ui"; +import { checkIsDropTarget } from "components/designSystems/appsmith/PositionedContainer"; +import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; +import { Toaster } from "design-system"; +import log from "loglevel"; import { CanvasWidgetsReduxState, FlattenedWidgetProps, } from "reducers/entityReducers/canvasWidgetsReducer"; -import { getWidgetChildrenIds } from "./WidgetOperationUtils"; -import { AppState } from "@appsmith/reducers"; -import { checkIsDropTarget } from "components/designSystems/appsmith/PositionedContainer"; -import WidgetFactory from "utils/WidgetFactory"; -import { showModal } from "actions/widgetActions"; -import history from "utils/history"; -import { getCurrentPageId } from "selectors/editorSelectors"; -import { builderURL } from "RouteBuilder"; import { CanvasWidgetsStructureReduxState } from "reducers/entityReducers/canvasWidgetsStructureReducer"; +import { all, call, fork, put, select, takeLatest } from "redux-saga/effects"; +import { builderURL } from "RouteBuilder"; +import { getCurrentPageId } from "selectors/editorSelectors"; import { getCanvasWidgetsWithParentId } from "selectors/entitiesSelector"; +import { getLastSelectedWidget, getSelectedWidgets } from "selectors/ui"; +import history from "utils/history"; +import WidgetFactory from "utils/WidgetFactory"; +import { + getWidgetImmediateChildren, + getWidgetMetaProps, + getWidgets, +} from "./selectors"; +import { getWidgetChildrenIds } from "./WidgetOperationUtils"; const WidgetTypes = WidgetFactory.widgetTypes; // The following is computed to be used in the entity explorer // Every time a widget is selected, we need to expand widget entities @@ -114,14 +114,16 @@ function* getLastSelectedCanvas() { const canvasWidgets: CanvasWidgetsReduxState = yield select(getWidgets); const widgetLastSelected = lastSelectedWidget && canvasWidgets[lastSelectedWidget]; - if (widgetLastSelected) { + if (widgetLastSelected && !widgetLastSelected.flexLayers) { const canvasToSelect: string = yield call( getDroppingCanvasOfWidget, widgetLastSelected, ); return canvasToSelect ? canvasToSelect : MAIN_CONTAINER_WIDGET_ID; } - return MAIN_CONTAINER_WIDGET_ID; + if (!canvasWidgets[MAIN_CONTAINER_WIDGET_ID].flexLayers) { + return MAIN_CONTAINER_WIDGET_ID; + } } // used for List widget cases diff --git a/app/client/src/widgets/CanvasWidget.tsx b/app/client/src/widgets/CanvasWidget.tsx index f8e402291b25..a3c16f29da4f 100644 --- a/app/client/src/widgets/CanvasWidget.tsx +++ b/app/client/src/widgets/CanvasWidget.tsx @@ -1,16 +1,3 @@ -import React, { CSSProperties } from "react"; -import { WidgetProps } from "widgets/BaseWidget"; -import { - ContainerWidget, - ContainerWidgetProps, -} from "widgets/ContainerWidget/widget"; -import { GridDefaults, RenderModes } from "constants/WidgetConstants"; -import DropTargetComponent from "components/editorComponents/DropTargetComponent"; -import { getCanvasSnapRows } from "utils/WidgetPropsUtils"; -import { getCanvasClassName } from "utils/generators"; -import WidgetFactory, { DerivedPropertiesMap } from "utils/WidgetFactory"; -import { CanvasWidgetStructure } from "./constants"; -import { CANVAS_DEFAULT_MIN_HEIGHT_PX } from "constants/AppConstants"; import { Alignment, LayoutDirection, @@ -18,11 +5,22 @@ import { Positioning, ResponsiveBehavior, } from "components/constants"; -import ContainerComponent from "./ContainerWidget/component"; -import { CanvasDraggingArena } from "pages/common/CanvasArenas/CanvasDraggingArena"; -import { CanvasSelectionArena } from "pages/common/CanvasArenas/CanvasSelectionArena"; -import WidgetsMultiSelectBox from "pages/Editor/WidgetsMultiSelectBox"; import FlexBoxComponent from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; +import DropTargetComponent from "components/editorComponents/DropTargetComponent"; +import { CANVAS_DEFAULT_MIN_HEIGHT_PX } from "constants/AppConstants"; +import { GridDefaults, RenderModes } from "constants/WidgetConstants"; +import { CanvasDraggingArena } from "pages/common/CanvasArenas/CanvasDraggingArena"; +import React, { CSSProperties } from "react"; +import { getCanvasClassName } from "utils/generators"; +import WidgetFactory, { DerivedPropertiesMap } from "utils/WidgetFactory"; +import { getCanvasSnapRows } from "utils/WidgetPropsUtils"; +import { WidgetProps } from "widgets/BaseWidget"; +import { + ContainerWidget, + ContainerWidgetProps, +} from "widgets/ContainerWidget/widget"; +import { CanvasWidgetStructure } from "./constants"; +import ContainerComponent from "./ContainerWidget/component"; class CanvasWidget extends ContainerWidget { static getPropertyPaneConfig() { @@ -106,6 +104,8 @@ class CanvasWidget extends ContainerWidget { widgetId={props.widgetId} widgetName={props.widgetName} /> + {/* + // Removing Canvas Selection and grouping in the POC + /> */} )} - + /> */} {/* without the wrapping div onClick events are triggered twice */} , @@ -156,6 +154,8 @@ class HorizontalLayoutWidget extends BaseWidget< useAutoLayout widgetId={this.props.widgetId} /> + {/* + // Removing Canvas Selection and grouping in the POC + /> */} )} - + /> */} {/* without the wrapping div onClick events are triggered twice */} <>{this.renderChildren()} diff --git a/app/client/src/widgets/VerticalLayoutWidget/widget/index.tsx b/app/client/src/widgets/VerticalLayoutWidget/widget/index.tsx index cc8725d4943d..46d3eafca84a 100644 --- a/app/client/src/widgets/VerticalLayoutWidget/widget/index.tsx +++ b/app/client/src/widgets/VerticalLayoutWidget/widget/index.tsx @@ -1,28 +1,26 @@ -import React from "react"; import { map } from "lodash"; +import React from "react"; -import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; import WidgetFactory, { DerivedPropertiesMap } from "utils/WidgetFactory"; +import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; -import VerticalLayoutComponent from "../component"; -import { ContainerStyle } from "widgets/ContainerWidget/component"; import { AlignItems, JustifyContent, LayoutDirection, } from "components/constants"; -import { CanvasDraggingArena } from "pages/common/CanvasArenas/CanvasDraggingArena"; -import { CanvasSelectionArena } from "pages/common/CanvasArenas/CanvasSelectionArena"; import { CONTAINER_GRID_PADDING, GridDefaults, MAIN_CONTAINER_WIDGET_ID, WIDGET_PADDING, } from "constants/WidgetConstants"; -import WidgetsMultiSelectBox from "pages/Editor/WidgetsMultiSelectBox"; -import { getCanvasSnapRows } from "utils/WidgetPropsUtils"; import { ValidationTypes } from "constants/WidgetValidation"; +import { CanvasDraggingArena } from "pages/common/CanvasArenas/CanvasDraggingArena"; import { AutoLayoutContext } from "utils/autoLayoutContext"; +import { getCanvasSnapRows } from "utils/WidgetPropsUtils"; +import { ContainerStyle } from "widgets/ContainerWidget/component"; +import VerticalLayoutComponent from "../component"; class VerticalLayoutWidget extends BaseWidget< VerticalLayoutWidgetProps, @@ -164,22 +162,24 @@ class VerticalLayoutWidget extends BaseWidget< useAutoLayout widgetId={this.props.widgetId} /> - + /> */} )} - + /> */} {/* without the wrapping div onClick events are triggered twice */} <>{this.renderChildren()} From aae21406921eb0de9b64ac71dffbbe52237a93ab Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Thu, 27 Oct 2022 17:39:26 +0530 Subject: [PATCH 205/708] reinitiate canvas resizer on page change --- app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx index ab6c480b33e6..2e380d9fcef0 100644 --- a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx +++ b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx @@ -170,7 +170,7 @@ function CanvasContainer() { const ele: any = document.getElementById("main-canvas-container"); ele.style.width = "inherit"; } - }, [appLayout, isPreviewMode]); + }, [appLayout, isPreviewMode, currentPageId]); // calculating exact height to not allow scroll at this component, // calculating total height minus margin on top, top bar and bottom bar From 9512287edc9350a1168aad6f6edd6591aab9c77e Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Thu, 27 Oct 2022 17:56:55 +0530 Subject: [PATCH 206/708] Rerender hooks on page change --- app/client/src/utils/hooks/useDynamicAppLayout.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/client/src/utils/hooks/useDynamicAppLayout.tsx b/app/client/src/utils/hooks/useDynamicAppLayout.tsx index dd933c0bc428..cb0dba7c6088 100644 --- a/app/client/src/utils/hooks/useDynamicAppLayout.tsx +++ b/app/client/src/utils/hooks/useDynamicAppLayout.tsx @@ -156,6 +156,7 @@ export const useDynamicAppLayout = () => { const immediateDebouncedResize = useCallback(debounce(resizeToLayout), [ mainCanvasProps, screenWidth, + currentPageId, appMode, appLayout, ]); @@ -173,7 +174,7 @@ export const useDynamicAppLayout = () => { return () => { ele && resizeObserver.unobserve(ele); }; - }, [appLayout]); + }, [appLayout, currentPageId]); /** * when screen height is changed, update canvas layout @@ -204,7 +205,6 @@ export const useDynamicAppLayout = () => { resizeToLayout(); }, [ appLayout, - currentPageId, mainCanvasProps?.width, isPreviewMode, explorerWidth, From b95f1c116952c124078f150296143b97d398f999 Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 27 Oct 2022 10:46:43 -0400 Subject: [PATCH 207/708] fix vertical stack paddings --- .../appsmith/autoLayout/FlexComponent.tsx | 12 ++---------- .../CanvasArenas/hooks/useAutoLayoutHighlights.ts | 4 ++-- app/client/src/widgets/BaseWidget.tsx | 2 +- 3 files changed, 5 insertions(+), 13 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx index b347deea5280..e0c76640b10e 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx @@ -2,11 +2,7 @@ import React, { ReactNode, useCallback } from "react"; import styled from "styled-components"; import { LayoutDirection, ResponsiveBehavior } from "components/constants"; -import { - WidgetType, - widgetTypeClassname, - WIDGET_PADDING, -} from "constants/WidgetConstants"; +import { WidgetType, widgetTypeClassname } from "constants/WidgetConstants"; import { snipingModeSelector } from "selectors/editorSelectors"; import { useSelector } from "store"; import { useClickToSelectWidget } from "utils/hooks/useClickToSelectWidget"; @@ -36,7 +32,6 @@ const FlexWidget = styled.div<{ isMobile: boolean; isFillWidget: boolean; minWidth?: string; - padding: number; zIndex: number; zIndexOnHover: number; isCurrentCanvasDragging: boolean; @@ -49,8 +44,6 @@ const FlexWidget = styled.div<{ isMobile ? "100%" : Math.floor(componentHeight) + "px"}; min-width: ${({ minWidth }) => minWidth}; min-height: 30px; - padding: ${({ isMobile, padding }) => - isMobile ? `${padding}px 1px 0` : padding + "px"}; flex-grow: ${({ isFillWidget }) => (isFillWidget ? "1" : "0")}; @@ -58,7 +51,7 @@ const FlexWidget = styled.div<{ z-index: ${({ zIndexOnHover }) => zIndexOnHover} !important; } margin: ${({ isCurrentCanvasDragging }) => - isCurrentCanvasDragging ? `${DRAG_MARGIN}px` : "0px"}; + isCurrentCanvasDragging ? `${DRAG_MARGIN * 2}px` : `${DRAG_MARGIN}px`}; `; // TODO: update min width logic. @@ -121,7 +114,6 @@ export function FlexComponent(props: AutoLayoutProps) { minWidth={minWidth} onClick={stopEventPropagation} onClickCapture={onClickFn} - padding={WIDGET_PADDING} zIndex={zIndex} zIndexOnHover={onHoverZIndex} > diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index 60b4cb0b2ea7..b2743c650faa 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -411,10 +411,10 @@ export const useAutoLayoutHighlights = ({ ? 0 : alignment === FlexLayerAlignment.Center ? containerDimensions.width / 2 - : containerDimensions?.width - DRAG_MARGIN * 2, + : containerDimensions?.width - DRAG_MARGIN * 4, posY: rect.y - containerDimensions?.top, width: verticalFlex ? rect?.width : OFFSET_WIDTH, - height: verticalFlex ? OFFSET_WIDTH : rect.height - DRAG_MARGIN * 2, + height: verticalFlex ? OFFSET_WIDTH : rect.height - DRAG_MARGIN * 4, isVertical: !verticalFlex, rowIndex: 0, }; diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index cf39f1904b67..e394d6aa989e 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -240,7 +240,7 @@ abstract class BaseWidget< return ( {content} From 3c0de502efd0f6de2db2f0f9a50c3055c1fa28c3 Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 27 Oct 2022 10:46:43 -0400 Subject: [PATCH 208/708] Revert "fix vertical stack paddings" This reverts commit b95f1c116952c124078f150296143b97d398f999. --- .../appsmith/autoLayout/FlexComponent.tsx | 12 ++++++++++-- .../CanvasArenas/hooks/useAutoLayoutHighlights.ts | 4 ++-- app/client/src/widgets/BaseWidget.tsx | 2 +- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx index e0c76640b10e..b347deea5280 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx @@ -2,7 +2,11 @@ import React, { ReactNode, useCallback } from "react"; import styled from "styled-components"; import { LayoutDirection, ResponsiveBehavior } from "components/constants"; -import { WidgetType, widgetTypeClassname } from "constants/WidgetConstants"; +import { + WidgetType, + widgetTypeClassname, + WIDGET_PADDING, +} from "constants/WidgetConstants"; import { snipingModeSelector } from "selectors/editorSelectors"; import { useSelector } from "store"; import { useClickToSelectWidget } from "utils/hooks/useClickToSelectWidget"; @@ -32,6 +36,7 @@ const FlexWidget = styled.div<{ isMobile: boolean; isFillWidget: boolean; minWidth?: string; + padding: number; zIndex: number; zIndexOnHover: number; isCurrentCanvasDragging: boolean; @@ -44,6 +49,8 @@ const FlexWidget = styled.div<{ isMobile ? "100%" : Math.floor(componentHeight) + "px"}; min-width: ${({ minWidth }) => minWidth}; min-height: 30px; + padding: ${({ isMobile, padding }) => + isMobile ? `${padding}px 1px 0` : padding + "px"}; flex-grow: ${({ isFillWidget }) => (isFillWidget ? "1" : "0")}; @@ -51,7 +58,7 @@ const FlexWidget = styled.div<{ z-index: ${({ zIndexOnHover }) => zIndexOnHover} !important; } margin: ${({ isCurrentCanvasDragging }) => - isCurrentCanvasDragging ? `${DRAG_MARGIN * 2}px` : `${DRAG_MARGIN}px`}; + isCurrentCanvasDragging ? `${DRAG_MARGIN}px` : "0px"}; `; // TODO: update min width logic. @@ -114,6 +121,7 @@ export function FlexComponent(props: AutoLayoutProps) { minWidth={minWidth} onClick={stopEventPropagation} onClickCapture={onClickFn} + padding={WIDGET_PADDING} zIndex={zIndex} zIndexOnHover={onHoverZIndex} > diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index b2743c650faa..60b4cb0b2ea7 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -411,10 +411,10 @@ export const useAutoLayoutHighlights = ({ ? 0 : alignment === FlexLayerAlignment.Center ? containerDimensions.width / 2 - : containerDimensions?.width - DRAG_MARGIN * 4, + : containerDimensions?.width - DRAG_MARGIN * 2, posY: rect.y - containerDimensions?.top, width: verticalFlex ? rect?.width : OFFSET_WIDTH, - height: verticalFlex ? OFFSET_WIDTH : rect.height - DRAG_MARGIN * 4, + height: verticalFlex ? OFFSET_WIDTH : rect.height - DRAG_MARGIN * 2, isVertical: !verticalFlex, rowIndex: 0, }; diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index e394d6aa989e..cf39f1904b67 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -240,7 +240,7 @@ abstract class BaseWidget< return ( {content} From 5cf642a7e07e490afd45cf4858da1502fc270b32 Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 27 Oct 2022 12:20:45 -0400 Subject: [PATCH 209/708] fix margins --- .../designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx | 2 ++ .../src/components/editorComponents/ResizableComponent.tsx | 1 + .../common/CanvasArenas/hooks/useAutoLayoutHighlights.ts | 7 ++++--- app/client/src/resizable/resizenreflow/index.tsx | 3 ++- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx index fff1a00a0247..6eb31e974606 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx @@ -2,6 +2,7 @@ import React, { ReactNode } from "react"; import styled from "styled-components"; import { FlexDirection, LayoutDirection } from "components/constants"; +import { DRAG_MARGIN } from "widgets/constants"; /** * 1. Given a direction if should employ flex in perpendicular direction. @@ -30,6 +31,7 @@ const LayoutLayerContainer = styled.div<{ width: 100%; height: auto; + margin-top: ${DRAG_MARGIN}px; `; const SubWrapper = styled.div<{ diff --git a/app/client/src/components/editorComponents/ResizableComponent.tsx b/app/client/src/components/editorComponents/ResizableComponent.tsx index cc9b96e96720..b2bb804534e6 100644 --- a/app/client/src/components/editorComponents/ResizableComponent.tsx +++ b/app/client/src/components/editorComponents/ResizableComponent.tsx @@ -304,6 +304,7 @@ export const ResizableComponent = memo(function ResizableComponent( onStart={handleResizeStart} onStop={updateSize} originalPositions={originalPositions} + paddingOffset={props.paddingOffset} parentId={props.parentId} responsiveBehavior={props.responsiveBehavior} snapGrid={{ x: props.parentColumnSpace, y: props.parentRowSpace }} diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index 60b4cb0b2ea7..a16406076c54 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -412,7 +412,8 @@ export const useAutoLayoutHighlights = ({ : alignment === FlexLayerAlignment.Center ? containerDimensions.width / 2 : containerDimensions?.width - DRAG_MARGIN * 2, - posY: rect.y - containerDimensions?.top, + posY: + rect.y - containerDimensions?.top + (verticalFlex ? 0 : DRAG_MARGIN), width: verticalFlex ? rect?.width : OFFSET_WIDTH, height: verticalFlex ? OFFSET_WIDTH : rect.height - DRAG_MARGIN * 2, isVertical: !verticalFlex, @@ -627,9 +628,9 @@ export const useAutoLayoutHighlights = ({ let distY: number = !a.isVertical && !isVerticalDrag ? 0 : a.posY - b.y; if (moveDirection === ReflowDirection.LEFT && distX > 20) distX += 2000; - if (moveDirection === ReflowDirection.RIGHT && distX < 20) distX -= 2000; + if (moveDirection === ReflowDirection.RIGHT && distX < -20) distX -= 2000; if (moveDirection === ReflowDirection.TOP && distY > 20) distY += 2000; - if (moveDirection === ReflowDirection.BOTTOM && distY < 20) distY -= 2000; + if (moveDirection === ReflowDirection.BOTTOM && distY < -20) distY -= 2000; return Math.abs(Math.sqrt(distX * distX + distY * distY)); }; diff --git a/app/client/src/resizable/resizenreflow/index.tsx b/app/client/src/resizable/resizenreflow/index.tsx index 2a18cdedf65f..d21d15483769 100644 --- a/app/client/src/resizable/resizenreflow/index.tsx +++ b/app/client/src/resizable/resizenreflow/index.tsx @@ -159,6 +159,7 @@ type ResizableProps = { isFlexChild?: boolean; responsiveBehavior?: ResponsiveBehavior; direction?: LayoutDirection; + paddingOffset: number; }; export function ReflowResizable(props: ResizableProps) { @@ -303,7 +304,7 @@ export function ReflowResizable(props: ResizableProps) { ...prevDimensions, width: props.componentWidth, height: props.componentHeight, - x: 0, + x: props.isFlexChild ? props.paddingOffset : 0, y: 0, reset: true, }; From 8ea75e20b9ec9cde09ea5636d10668669aae70aa Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 27 Oct 2022 13:34:13 -0400 Subject: [PATCH 210/708] cleanup --- .../appsmith/autoLayout/FlexComponent.tsx | 3 +-- app/client/src/resizable/resizenreflow/index.tsx | 10 ++++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx index b347deea5280..1016cdd3147d 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx @@ -49,8 +49,7 @@ const FlexWidget = styled.div<{ isMobile ? "100%" : Math.floor(componentHeight) + "px"}; min-width: ${({ minWidth }) => minWidth}; min-height: 30px; - padding: ${({ isMobile, padding }) => - isMobile ? `${padding}px 1px 0` : padding + "px"}; + padding: ${({ padding }) => padding + "px"}; flex-grow: ${({ isFillWidget }) => (isFillWidget ? "1" : "0")}; diff --git a/app/client/src/resizable/resizenreflow/index.tsx b/app/client/src/resizable/resizenreflow/index.tsx index d21d15483769..ca541fb51716 100644 --- a/app/client/src/resizable/resizenreflow/index.tsx +++ b/app/client/src/resizable/resizenreflow/index.tsx @@ -1,6 +1,9 @@ import React, { ReactNode, useState, useEffect, useRef } from "react"; import styled, { StyledComponent } from "styled-components"; -import { WIDGET_PADDING } from "constants/WidgetConstants"; +import { + MAIN_CONTAINER_WIDGET_ID, + WIDGET_PADDING, +} from "constants/WidgetConstants"; import { useDrag } from "react-use-gesture"; import { animated, Spring } from "react-spring"; import PerformanceTracker, { @@ -304,7 +307,10 @@ export function ReflowResizable(props: ResizableProps) { ...prevDimensions, width: props.componentWidth, height: props.componentHeight, - x: props.isFlexChild ? props.paddingOffset : 0, + x: + props.isFlexChild && props.parentId === MAIN_CONTAINER_WIDGET_ID + ? props.paddingOffset / 2 + : 0, y: 0, reset: true, }; From c8433dd7f127a3623d8843a0c1c0c8d07c300800 Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 31 Oct 2022 10:31:44 -0400 Subject: [PATCH 211/708] fix crash on bulk delete --- app/client/src/sagas/AutoLayoutUtils.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/client/src/sagas/AutoLayoutUtils.ts b/app/client/src/sagas/AutoLayoutUtils.ts index d3245831ad7e..9f9dd29b6359 100644 --- a/app/client/src/sagas/AutoLayoutUtils.ts +++ b/app/client/src/sagas/AutoLayoutUtils.ts @@ -98,7 +98,8 @@ export function* updateFlexLayersOnDelete( children: updatedChildren, hasFillChild: updatedChildren.some( (each: LayerChild) => - widgets[each.id].responsiveBehavior === ResponsiveBehavior.Fill, + widgets[each.id] && + widgets[each.id]?.responsiveBehavior === ResponsiveBehavior.Fill, ), }, ...flexLayers.slice(layerIndex + 1), From 7e0ac8bcd8e5aebd91a11ee4df2b858e298f1ce5 Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 31 Oct 2022 16:15:00 -0400 Subject: [PATCH 212/708] use column space for resizing and margin --- .../appsmith/autoLayout/FlexComponent.tsx | 33 +++++++++++++------ .../editorComponents/ResizableComponent.tsx | 18 ++++++---- app/client/src/widgets/BaseWidget.tsx | 1 + 3 files changed, 35 insertions(+), 17 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx index 1016cdd3147d..1b94bd0c7608 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx @@ -3,6 +3,7 @@ import styled from "styled-components"; import { LayoutDirection, ResponsiveBehavior } from "components/constants"; import { + MAIN_CONTAINER_WIDGET_ID, WidgetType, widgetTypeClassname, WIDGET_PADDING, @@ -28,6 +29,7 @@ export type AutoLayoutProps = { selected?: boolean; widgetId: string; widgetType: WidgetType; + parentColumnSpace: number; }; const FlexWidget = styled.div<{ @@ -39,7 +41,8 @@ const FlexWidget = styled.div<{ padding: number; zIndex: number; zIndexOnHover: number; - isCurrentCanvasDragging: boolean; + dragMargin: number; + isAffectedByDrag: boolean; }>` position: relative; z-index: ${({ zIndex }) => zIndex}; @@ -56,8 +59,8 @@ const FlexWidget = styled.div<{ &:hover { z-index: ${({ zIndexOnHover }) => zIndexOnHover} !important; } - margin: ${({ isCurrentCanvasDragging }) => - isCurrentCanvasDragging ? `${DRAG_MARGIN}px` : "0px"}; + margin: ${({ dragMargin, isAffectedByDrag }) => + isAffectedByDrag ? `${DRAG_MARGIN}px ${dragMargin}px` : "0px"}; `; // TODO: update min width logic. @@ -73,8 +76,9 @@ export function FlexComponent(props: AutoLayoutProps) { const { dragDetails } = useSelector( (state: AppState) => state.ui.widgetDragResize, ); - - const isCurrentCanvasDragging = dragDetails?.draggedOn === props.parentId; + const isDragging: boolean = dragDetails?.draggedOn !== undefined; + const isCurrentCanvasDragging: boolean = + dragDetails?.draggedOn === props.parentId; const isDropTarget = checkIsDropTarget(props.widgetType); const { onHoverZIndex, zIndex } = usePositionedContainerZIndex( @@ -104,17 +108,26 @@ export function FlexComponent(props: AutoLayoutProps) { ? "100%" : props.minWidth + "px"; + const dragMargin = Math.max(props.parentColumnSpace, DRAG_MARGIN); + const isAffectedByDrag: boolean = + isCurrentCanvasDragging || + (isDragging && props.parentId === MAIN_CONTAINER_WIDGET_ID); + const resizedWidth: number = isAffectedByDrag + ? props.componentWidth - props.parentColumnSpace + : props.componentWidth; + return ( Date: Mon, 31 Oct 2022 22:40:55 -0400 Subject: [PATCH 213/708] more wip --- .../appsmith/autoLayout/FlexComponent.tsx | 21 ++++++++++++------- .../editorComponents/ResizableComponent.tsx | 9 ++++---- .../src/resizable/resizenreflow/index.tsx | 14 ++++++++++--- 3 files changed, 28 insertions(+), 16 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx index 1b94bd0c7608..f22f9cad34f1 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx @@ -43,6 +43,7 @@ const FlexWidget = styled.div<{ zIndexOnHover: number; dragMargin: number; isAffectedByDrag: boolean; + parentId?: string; }>` position: relative; z-index: ${({ zIndex }) => zIndex}; @@ -51,8 +52,13 @@ const FlexWidget = styled.div<{ height: ${({ componentHeight, isMobile }) => isMobile ? "100%" : Math.floor(componentHeight) + "px"}; min-width: ${({ minWidth }) => minWidth}; + max-width: ${({ dragMargin, isAffectedByDrag, parentId }) => + isAffectedByDrag && parentId !== "0" + ? `calc(100% - ${dragMargin}px)` + : "auto"}; min-height: 30px; - padding: ${({ padding }) => padding + "px"}; + padding: ${({ isAffectedByDrag, padding }) => + isAffectedByDrag ? 0 : padding + "px"}; flex-grow: ${({ isFillWidget }) => (isFillWidget ? "1" : "0")}; @@ -60,7 +66,9 @@ const FlexWidget = styled.div<{ z-index: ${({ zIndexOnHover }) => zIndexOnHover} !important; } margin: ${({ dragMargin, isAffectedByDrag }) => - isAffectedByDrag ? `${DRAG_MARGIN}px ${dragMargin}px` : "0px"}; + isAffectedByDrag + ? `${DRAG_MARGIN}px ${Math.floor(dragMargin / 2)}px` + : "0px"}; `; // TODO: update min width logic. @@ -113,7 +121,7 @@ export function FlexComponent(props: AutoLayoutProps) { isCurrentCanvasDragging || (isDragging && props.parentId === MAIN_CONTAINER_WIDGET_ID); const resizedWidth: number = isAffectedByDrag - ? props.componentWidth - props.parentColumnSpace + ? props.componentWidth - dragMargin : props.componentWidth; return ( @@ -121,11 +129,7 @@ export function FlexComponent(props: AutoLayoutProps) { className={className} componentHeight={props.componentHeight} componentWidth={resizedWidth} - dragMargin={ - props.parentId === MAIN_CONTAINER_WIDGET_ID - ? dragMargin / 2 - : dragMargin - } + dragMargin={dragMargin} id={props.widgetId} isAffectedByDrag={isAffectedByDrag} isFillWidget={isFillWidget} @@ -134,6 +138,7 @@ export function FlexComponent(props: AutoLayoutProps) { onClick={stopEventPropagation} onClickCapture={onClickFn} padding={WIDGET_PADDING} + parentId={props.parentId} zIndex={zIndex} zIndexOnHover={onHoverZIndex} > diff --git a/app/client/src/components/editorComponents/ResizableComponent.tsx b/app/client/src/components/editorComponents/ResizableComponent.tsx index 61c91919f132..a7c6300eee9b 100644 --- a/app/client/src/components/editorComponents/ResizableComponent.tsx +++ b/app/client/src/components/editorComponents/ResizableComponent.tsx @@ -288,22 +288,21 @@ export const ResizableComponent = memo(function ResizableComponent( } }; - const newWidth = + const isAffectedByDrag = isCurrentCanvasDragging || - (isDragging && props.parentId === MAIN_CONTAINER_WIDGET_ID) - ? dimensions.width - props.parentColumnSpace - : dimensions.width; + (isDragging && props.parentId === MAIN_CONTAINER_WIDGET_ID); return ( Date: Tue, 1 Nov 2022 01:01:50 -0400 Subject: [PATCH 214/708] fix container dnd experience --- .../appsmith/autoLayout/FlexComponent.tsx | 14 +++----------- app/client/src/resizable/resizenreflow/index.tsx | 11 +++-------- 2 files changed, 6 insertions(+), 19 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx index f22f9cad34f1..08bf204354b4 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx @@ -52,10 +52,7 @@ const FlexWidget = styled.div<{ height: ${({ componentHeight, isMobile }) => isMobile ? "100%" : Math.floor(componentHeight) + "px"}; min-width: ${({ minWidth }) => minWidth}; - max-width: ${({ dragMargin, isAffectedByDrag, parentId }) => - isAffectedByDrag && parentId !== "0" - ? `calc(100% - ${dragMargin}px)` - : "auto"}; + min-height: 30px; padding: ${({ isAffectedByDrag, padding }) => isAffectedByDrag ? 0 : padding + "px"}; @@ -66,13 +63,9 @@ const FlexWidget = styled.div<{ z-index: ${({ zIndexOnHover }) => zIndexOnHover} !important; } margin: ${({ dragMargin, isAffectedByDrag }) => - isAffectedByDrag - ? `${DRAG_MARGIN}px ${Math.floor(dragMargin / 2)}px` - : "0px"}; + isAffectedByDrag ? `${DRAG_MARGIN}px ${dragMargin / 2}px` : "0px"}; `; -// TODO: update min width logic. - export function FlexComponent(props: AutoLayoutProps) { const isMobile = useSelector(getIsMobile); const isSnipingMode = useSelector(snipingModeSelector); @@ -115,7 +108,6 @@ export function FlexComponent(props: AutoLayoutProps) { props.responsiveBehavior === ResponsiveBehavior.Fill && isMobile ? "100%" : props.minWidth + "px"; - const dragMargin = Math.max(props.parentColumnSpace, DRAG_MARGIN); const isAffectedByDrag: boolean = isCurrentCanvasDragging || @@ -131,7 +123,7 @@ export function FlexComponent(props: AutoLayoutProps) { componentWidth={resizedWidth} dragMargin={dragMargin} id={props.widgetId} - isAffectedByDrag={isAffectedByDrag} + isAffectedByDrag={isCurrentCanvasDragging} isFillWidget={isFillWidget} isMobile={isMobile} minWidth={minWidth} diff --git a/app/client/src/resizable/resizenreflow/index.tsx b/app/client/src/resizable/resizenreflow/index.tsx index 16dcfa2b2d12..c01dd7444fc8 100644 --- a/app/client/src/resizable/resizenreflow/index.tsx +++ b/app/client/src/resizable/resizenreflow/index.tsx @@ -1,9 +1,6 @@ import React, { ReactNode, useState, useEffect, useRef } from "react"; import styled, { StyledComponent } from "styled-components"; -import { - MAIN_CONTAINER_WIDGET_ID, - WIDGET_PADDING, -} from "constants/WidgetConstants"; +import { WIDGET_PADDING } from "constants/WidgetConstants"; import { useDrag } from "react-use-gesture"; import { animated, Spring } from "react-spring"; import PerformanceTracker, { @@ -309,10 +306,8 @@ export function ReflowResizable(props: ResizableProps) { width: props.componentWidth, height: props.componentHeight, x: - !props.isAffectedByDrag && - props.isFlexChild && - props.parentId === MAIN_CONTAINER_WIDGET_ID - ? props.paddingOffset / 2 + !props.isAffectedByDrag && props.isFlexChild + ? props.paddingOffset / 4 : 0, y: 0, reset: true, From 310e8122f0408ba5b68185028dffe5dce0009bf3 Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 1 Nov 2022 01:56:25 -0400 Subject: [PATCH 215/708] fix dnd margins --- .../appsmith/autoLayout/FlexComponent.tsx | 19 +++++++-- .../src/selectors/autoLayoutSelectors.tsx | 42 +++++++++++++++++++ 2 files changed, 58 insertions(+), 3 deletions(-) create mode 100644 app/client/src/selectors/autoLayoutSelectors.tsx diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx index 08bf204354b4..411e9710f265 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx @@ -16,6 +16,7 @@ import { checkIsDropTarget } from "../PositionedContainer"; import { AppState } from "ce/reducers"; import { DRAG_MARGIN } from "widgets/constants"; import { getIsMobile } from "selectors/mainCanvasSelectors"; +import { getSiblingCount } from "selectors/autoLayoutSelectors"; export type AutoLayoutProps = { children: ReactNode; @@ -66,6 +67,8 @@ const FlexWidget = styled.div<{ isAffectedByDrag ? `${DRAG_MARGIN}px ${dragMargin / 2}px` : "0px"}; `; +const DEFAULT_MARGIN = 20; + export function FlexComponent(props: AutoLayoutProps) { const isMobile = useSelector(getIsMobile); const isSnipingMode = useSelector(snipingModeSelector); @@ -80,6 +83,9 @@ export function FlexComponent(props: AutoLayoutProps) { const isDragging: boolean = dragDetails?.draggedOn !== undefined; const isCurrentCanvasDragging: boolean = dragDetails?.draggedOn === props.parentId; + const siblingCount = useSelector( + getSiblingCount(props.widgetId, props.parentId || MAIN_CONTAINER_WIDGET_ID), + ); const isDropTarget = checkIsDropTarget(props.widgetType); const { onHoverZIndex, zIndex } = usePositionedContainerZIndex( @@ -108,12 +114,19 @@ export function FlexComponent(props: AutoLayoutProps) { props.responsiveBehavior === ResponsiveBehavior.Fill && isMobile ? "100%" : props.minWidth + "px"; - const dragMargin = Math.max(props.parentColumnSpace, DRAG_MARGIN); + const dragMargin = + props.parentId === MAIN_CONTAINER_WIDGET_ID + ? DEFAULT_MARGIN + : Math.max(props.parentColumnSpace, DRAG_MARGIN); const isAffectedByDrag: boolean = isCurrentCanvasDragging || (isDragging && props.parentId === MAIN_CONTAINER_WIDGET_ID); const resizedWidth: number = isAffectedByDrag - ? props.componentWidth - dragMargin + ? props.componentWidth - + dragMargin - + (props.parentId !== MAIN_CONTAINER_WIDGET_ID && siblingCount > 0 + ? DEFAULT_MARGIN / siblingCount + : 0) : props.componentWidth; return ( @@ -123,7 +136,7 @@ export function FlexComponent(props: AutoLayoutProps) { componentWidth={resizedWidth} dragMargin={dragMargin} id={props.widgetId} - isAffectedByDrag={isCurrentCanvasDragging} + isAffectedByDrag={isAffectedByDrag} isFillWidget={isFillWidget} isMobile={isMobile} minWidth={minWidth} diff --git a/app/client/src/selectors/autoLayoutSelectors.tsx b/app/client/src/selectors/autoLayoutSelectors.tsx new file mode 100644 index 000000000000..2f4414a260cf --- /dev/null +++ b/app/client/src/selectors/autoLayoutSelectors.tsx @@ -0,0 +1,42 @@ +import { createSelector } from "reselect"; +import { getWidgets } from "sagas/selectors"; +import { + FlexLayer, + LayerChild, +} from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; + +export const getFlexLayers = (parentId: string) => { + return createSelector(getWidgets, (widgets): FlexLayer[] => { + const parent = widgets[parentId]; + if (!parent) return []; + return parent?.flexLayers || []; + }); +}; + +export const getSiblingCount = (widgetId: string, parentId: string) => { + return createSelector(getFlexLayers(parentId), (flexLayers): number => { + if (!flexLayers) return -1; + const selectedLayer = flexLayers?.find((layer: FlexLayer) => + layer.children?.some((child: LayerChild) => child.id === widgetId), + ); + if (!selectedLayer) return -1; + return selectedLayer.children?.length; + }); +}; + +export const getLayerIndex = (widgetId: string, parentId: string) => { + return createSelector( + getFlexLayers(parentId), + (layers: FlexLayer[]): number => { + if (!layers) return -1; + const selectedLayer = layers.find((layer: FlexLayer) => + layer.children.some((child: LayerChild) => child.id === widgetId), + ); + console.log("#### layers", layers, selectedLayer); + if (!selectedLayer) return -1; + return selectedLayer.children?.findIndex( + (child: LayerChild) => child.id === widgetId, + ); + }, + ); +}; From 264bfba81080ed936591237da9a3e5c2a08d2bc2 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Tue, 1 Nov 2022 15:18:29 +0530 Subject: [PATCH 216/708] Fixing dynamic layout resizer --- .../src/pages/Editor/WidgetsEditor/CanvasContainer.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx index 2e380d9fcef0..1c8550f394bf 100644 --- a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx +++ b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx @@ -147,6 +147,12 @@ function CanvasContainer() { // Adjust the dimension of element ele.style.width = `${w + dx}px`; } + if (initialWidth < w + dx) { + ele.style.width = `${initialWidth}px`; + } + if (smallestWidth > w + dx) { + ele.style.width = `${smallestWidth}px`; + } // e.stopPropagation(); }; @@ -205,7 +211,6 @@ function CanvasContainer() { e.stopPropagation(); }} style={{ - zIndex: 100, position: "sticky", cursor: "col-resize", width: "16px", @@ -224,7 +229,6 @@ function CanvasContainer() { e.stopPropagation(); }} style={{ - zIndex: 100, position: "sticky", cursor: "col-resize", width: "16px", From 8a25606aaeeede89c321cadf04f2941c9ddae3d1 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Tue, 1 Nov 2022 15:48:45 +0530 Subject: [PATCH 217/708] Fixing showing widgetname in vertical containers --- .../appsmith/autoLayout/FlexBoxComponent.tsx | 2 +- .../WidgetNameComponent/index.tsx | 31 ++++++++++--------- .../src/selectors/autoLayoutSelectors.tsx | 5 ++- app/client/src/widgets/BaseWidget.tsx | 1 + 4 files changed, 20 insertions(+), 19 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx index a20b4e2189fa..cd9512cfc4c3 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx @@ -9,8 +9,8 @@ import { Overflow, } from "components/constants"; import { useSelector } from "react-redux"; -import AutoLayoutLayer from "./AutoLayoutLayer"; import { getIsMobile } from "selectors/mainCanvasSelectors"; +import AutoLayoutLayer from "./AutoLayoutLayer"; export interface FlexBoxProps { direction?: LayoutDirection; diff --git a/app/client/src/components/editorComponents/WidgetNameComponent/index.tsx b/app/client/src/components/editorComponents/WidgetNameComponent/index.tsx index eca357f556a5..6ee62f3a9901 100644 --- a/app/client/src/components/editorComponents/WidgetNameComponent/index.tsx +++ b/app/client/src/components/editorComponents/WidgetNameComponent/index.tsx @@ -1,26 +1,26 @@ +import { AppState } from "@appsmith/reducers"; +import { bindDataToWidget } from "actions/propertyPaneActions"; +import { WidgetType } from "constants/WidgetConstants"; import React from "react"; -import styled from "styled-components"; import { useDispatch, useSelector } from "react-redux"; -import { AppState } from "@appsmith/reducers"; -import SettingsControl, { Activities } from "./SettingsControl"; -import { useShowTableFilterPane } from "utils/hooks/dragResizeHooks"; +import { hideErrors } from "selectors/debuggerSelectors"; +import { + previewModeSelector, + snipingModeSelector, +} from "selectors/editorSelectors"; +import { getIsPropertyPaneVisible } from "selectors/propertyPaneSelectors"; +import { getIsTableFilterPaneVisible } from "selectors/tableFilterSelectors"; +import styled from "styled-components"; import AnalyticsUtil from "utils/AnalyticsUtil"; -import { WidgetType } from "constants/WidgetConstants"; +import { useShowTableFilterPane } from "utils/hooks/dragResizeHooks"; +import { useWidgetSelection } from "utils/hooks/useWidgetSelection"; import PerformanceTracker, { PerformanceTransactionName, } from "utils/PerformanceTracker"; -import { getIsTableFilterPaneVisible } from "selectors/tableFilterSelectors"; -import { useWidgetSelection } from "utils/hooks/useWidgetSelection"; import WidgetFactory from "utils/WidgetFactory"; +import SettingsControl, { Activities } from "./SettingsControl"; const WidgetTypes = WidgetFactory.widgetTypes; -import { - previewModeSelector, - snipingModeSelector, -} from "selectors/editorSelectors"; -import { bindDataToWidget } from "actions/propertyPaneActions"; -import { hideErrors } from "selectors/debuggerSelectors"; -import { getIsPropertyPaneVisible } from "selectors/propertyPaneSelectors"; const PositionStyle = styled.div<{ topRow: number; isSnipingMode: boolean }>` position: absolute; @@ -52,6 +52,7 @@ type WidgetNameComponentProps = { showControls?: boolean; topRow: number; errorCount: number; + isFlexChild: boolean; }; export function WidgetNameComponent(props: WidgetNameComponentProps) { @@ -164,7 +165,7 @@ export function WidgetNameComponent(props: WidgetNameComponentProps) { className={isSnipingMode ? "t--settings-sniping-control" : ""} data-testid="t--settings-controls-positioned-wrapper" isSnipingMode={isSnipingMode} - topRow={props.topRow} + topRow={props.isFlexChild ? 0 : props.topRow} > { return createSelector(getWidgets, (widgets): FlexLayer[] => { @@ -32,7 +32,6 @@ export const getLayerIndex = (widgetId: string, parentId: string) => { const selectedLayer = layers.find((layer: FlexLayer) => layer.children.some((child: LayerChild) => child.id === widgetId), ); - console.log("#### layers", layers, selectedLayer); if (!selectedLayer) return -1; return selectedLayer.children?.findIndex( (child: LayerChild) => child.id === widgetId, diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index 89e06b77c96e..b940e8c22073 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -262,6 +262,7 @@ abstract class BaseWidget< errorCount={this.getErrorCount( get(this.props, EVAL_ERROR_PATH, {}), )} + isFlexChild={!!this.props.isFlexChild} parentId={this.props.parentId} showControls={showControls} topRow={this.props.detachFromLayout ? 4 : this.props.topRow} From 827ba7b0284abd4a3e69266e979eae13d878f85e Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Tue, 1 Nov 2022 16:53:35 +0530 Subject: [PATCH 218/708] fixing the center highlight --- .../hooks/useAutoLayoutHighlights.ts | 49 +++++++++++-------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index a16406076c54..7d0bdf9a64aa 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -12,8 +12,8 @@ import { } from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; // import { useDispatch } from "react-redux"; import { ReflowDirection } from "reflow/reflowTypes"; -import { WidgetDraggingBlock } from "./useBlocksToBeDraggedOnCanvas"; import { DRAG_MARGIN } from "widgets/constants"; +import { WidgetDraggingBlock } from "./useBlocksToBeDraggedOnCanvas"; interface XYCord { x: number; @@ -343,28 +343,37 @@ export const useAutoLayoutHighlights = ({ if (!hasFillChild) { // process center sub wrapper. curr += start.length; - arr.push( - ...calculateRowHighlights( - center, - curr, - layerIndex, - FlexLayerAlignment.Center, - layerRect, - isEmpty, - ), + + const endHighLights = calculateRowHighlights( + end, + curr, + layerIndex, + FlexLayerAlignment.End, + layerRect, + isEmpty, ); + const validEndHighLights = endHighLights.filter((each) => { + return !arr.some((eachHighlight) => { + return eachHighlight.posX > each.posX; + }); + }); + arr.push(...validEndHighLights); + const centerHighlights = calculateRowHighlights( + center, + curr, + layerIndex, + FlexLayerAlignment.Center, + layerRect, + isEmpty, + ); + const validCenterdHighlights = centerHighlights.filter((each) => { + return !arr.some((eachHighlight) => { + return eachHighlight.posX > each.posX; + }); + }); + arr.push(...validCenterdHighlights); // process end sub wrapper. curr += center.length; - arr.push( - ...calculateRowHighlights( - end, - curr, - layerIndex, - FlexLayerAlignment.End, - layerRect, - isEmpty, - ), - ); } return arr; } From 74a06b5c207870f38dc75f8262ace020086c6c99 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Tue, 1 Nov 2022 16:55:00 +0530 Subject: [PATCH 219/708] adding comments --- .../pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index 7d0bdf9a64aa..97584acff252 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -344,6 +344,8 @@ export const useAutoLayoutHighlights = ({ // process center sub wrapper. curr += start.length; + //ToDo(Ashok and Preet): we need a better way to decide how to filter out highlights + // for now i am filtering highlights to not overlap with each other. const endHighLights = calculateRowHighlights( end, curr, From c7dd12abef3cf17b0123964a8f241ecbf7f3882e Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Tue, 1 Nov 2022 17:25:40 +0530 Subject: [PATCH 220/708] Fixing issues with center highlight --- .../hooks/useAutoLayoutHighlights.ts | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index 97584acff252..ec9b2da4638d 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -329,21 +329,31 @@ export const useAutoLayoutHighlights = ({ let curr: number = childCount; const { center, end, hasFillChild, isEmpty, start } = spreadLayer(layer); + const startHighLights = calculateRowHighlights( + start, + curr, + layerIndex, + FlexLayerAlignment.Start, + layerRect, + isEmpty, + ); + // process start sub wrapper. - arr.push( - ...calculateRowHighlights( - start, + arr.push(...startHighLights); + if (!hasFillChild) { + // process center sub wrapper. + curr += start.length; + const centerHighlights = calculateRowHighlights( + center, curr, layerIndex, - FlexLayerAlignment.Start, + FlexLayerAlignment.Center, layerRect, isEmpty, - ), - ); - if (!hasFillChild) { - // process center sub wrapper. - curr += start.length; + ); + // process end sub wrapper. + curr += center.length; //ToDo(Ashok and Preet): we need a better way to decide how to filter out highlights // for now i am filtering highlights to not overlap with each other. const endHighLights = calculateRowHighlights( @@ -360,22 +370,12 @@ export const useAutoLayoutHighlights = ({ }); }); arr.push(...validEndHighLights); - const centerHighlights = calculateRowHighlights( - center, - curr, - layerIndex, - FlexLayerAlignment.Center, - layerRect, - isEmpty, - ); const validCenterdHighlights = centerHighlights.filter((each) => { return !arr.some((eachHighlight) => { return eachHighlight.posX > each.posX; }); }); arr.push(...validCenterdHighlights); - // process end sub wrapper. - curr += center.length; } return arr; } From 3a29643af9d46a8ce6538601302ab597393eb525 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Tue, 1 Nov 2022 19:36:37 +0530 Subject: [PATCH 221/708] fixing resizer for new pages where layout is undefined --- .../src/pages/Editor/WidgetsEditor/CanvasContainer.tsx | 3 +++ .../common/CanvasArenas/hooks/useAutoLayoutHighlights.ts | 4 ++-- app/client/src/selectors/editorSelectors.tsx | 8 +++++--- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx index 1c8550f394bf..16f7c7d50853 100644 --- a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx +++ b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx @@ -254,5 +254,8 @@ function CanvasContainer() { ); } +CanvasContainer.whyDidYouRender = { + logOnDifferentValues: true, +}; export default CanvasContainer; diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index ec9b2da4638d..4a8020c2d2de 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -370,12 +370,12 @@ export const useAutoLayoutHighlights = ({ }); }); arr.push(...validEndHighLights); - const validCenterdHighlights = centerHighlights.filter((each) => { + const validCenteredHighlights = centerHighlights.filter((each) => { return !arr.some((eachHighlight) => { return eachHighlight.posX > each.posX; }); }); - arr.push(...validCenterdHighlights); + arr.push(...validCenteredHighlights); } return arr; } diff --git a/app/client/src/selectors/editorSelectors.tsx b/app/client/src/selectors/editorSelectors.tsx index 921616ad4d47..c8812e8b6277 100644 --- a/app/client/src/selectors/editorSelectors.tsx +++ b/app/client/src/selectors/editorSelectors.tsx @@ -196,10 +196,12 @@ export const getViewModePageList = createSelector( }, ); +const defaultLayout = { + type: "FLUID", +}; + export const getCurrentApplicationLayout = (state: AppState) => - state.ui.applications.currentApplication?.appLayout || { - type: "FLUID", - }; + state.ui.applications.currentApplication?.appLayout || defaultLayout; export const getCanvasWidth = (state: AppState) => state.ui.mainCanvas.width; From 1d766db2bde7a1fba007b879e2741153c320a76d Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Tue, 1 Nov 2022 19:45:47 +0530 Subject: [PATCH 222/708] fixing center align display --- .../hooks/useAutoLayoutHighlights.ts | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index 4a8020c2d2de..b3baf2ef8cf8 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -365,15 +365,24 @@ export const useAutoLayoutHighlights = ({ isEmpty, ); const validEndHighLights = endHighLights.filter((each) => { - return !arr.some((eachHighlight) => { + return !startHighLights.some((eachHighlight) => { return eachHighlight.posX > each.posX; }); }); arr.push(...validEndHighLights); const validCenteredHighlights = centerHighlights.filter((each) => { - return !arr.some((eachHighlight) => { - return eachHighlight.posX > each.posX; - }); + return ( + !startHighLights.length || + !endHighLights.length || + !( + startHighLights.some((eachHighlight) => { + return eachHighlight.posX > each.posX; + }) || + endHighLights.some((eachHighlight) => { + return eachHighlight.posX < each.posX; + }) + ) + ); }); arr.push(...validCenteredHighlights); } From 8cf16386ce3661cfd34b3836db6cd2294ac29556 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Tue, 1 Nov 2022 20:26:50 +0530 Subject: [PATCH 223/708] Fixing deployment failure --- app/client/src/selectors/editorSelectors.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/client/src/selectors/editorSelectors.tsx b/app/client/src/selectors/editorSelectors.tsx index c8812e8b6277..3e9b5bbc47a8 100644 --- a/app/client/src/selectors/editorSelectors.tsx +++ b/app/client/src/selectors/editorSelectors.tsx @@ -5,7 +5,10 @@ import { CanvasWidgetsReduxState, FlattenedWidgetProps, } from "reducers/entityReducers/canvasWidgetsReducer"; -import { PageListReduxState } from "reducers/entityReducers/pageListReducer"; +import { + AppLayoutConfig, + PageListReduxState, +} from "reducers/entityReducers/pageListReducer"; import { WidgetConfigReducerState } from "reducers/entityReducers/widgetConfigReducer"; import { WidgetCardProps, WidgetProps } from "widgets/BaseWidget"; @@ -196,7 +199,7 @@ export const getViewModePageList = createSelector( }, ); -const defaultLayout = { +const defaultLayout: AppLayoutConfig = { type: "FLUID", }; From 390816359ca6957745a611e16a46a7d215ae66dc Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Tue, 1 Nov 2022 21:00:16 +0530 Subject: [PATCH 224/708] Adding space below the layer in horizontal direction for widget name --- .../appsmith/autoLayout/FlexBoxComponent.tsx | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx index cd9512cfc4c3..d9b4b1767f1b 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx @@ -8,7 +8,9 @@ import { LayoutDirection, Overflow, } from "components/constants"; +import { APP_MODE } from "entities/App"; import { useSelector } from "react-redux"; +import { getAppMode } from "selectors/entitiesSelector"; import { getIsMobile } from "selectors/mainCanvasSelectors"; import AutoLayoutLayer from "./AutoLayoutLayer"; @@ -37,6 +39,7 @@ export const FlexContainer = styled.div<{ direction?: LayoutDirection; stretchHeight: boolean; overflow: Overflow; + leaveSpaceForWidgetName: boolean; isMobile?: boolean; isMainContainer: boolean; }>` @@ -55,7 +58,9 @@ export const FlexContainer = styled.div<{ overflow: hidden; overflow-y: ${({ isMainContainer, isMobile }) => isMainContainer || isMobile ? "auto" : "hidden"}; - padding: 4px; + + padding: ${({ leaveSpaceForWidgetName }) => + leaveSpaceForWidgetName ? "4px 4px 22px 4px;" : "4px;"}; `; function FlexBoxComponent(props: FlexBoxProps) { @@ -63,7 +68,8 @@ function FlexBoxComponent(props: FlexBoxProps) { const isMobile = useSelector(getIsMobile); const direction: LayoutDirection = props.direction || LayoutDirection.Horizontal; - + const appMode = useSelector(getAppMode); + const leaveSpaceForWidgetName = appMode === APP_MODE.EDIT; const { autoLayoutDragDetails, dragDetails, flexHighlight } = useSelector( (state: AppState) => state.ui.widgetDragResize, ); @@ -218,6 +224,7 @@ function FlexBoxComponent(props: FlexBoxProps) { direction={direction} isMainContainer={props.widgetId === "0"} isMobile={isMobile} + leaveSpaceForWidgetName={leaveSpaceForWidgetName} overflow={props.overflow} stretchHeight={props.stretchHeight} useAutoLayout={props.useAutoLayout} From b1089901ddff015c05321ee402befcecff5cde35 Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 2 Nov 2022 15:48:58 -0400 Subject: [PATCH 225/708] show drop positions accurately --- .../appsmith/autoLayout/AutoLayoutLayer.tsx | 16 + .../appsmith/autoLayout/FlexBoxComponent.tsx | 279 +++++++++++------- 2 files changed, 190 insertions(+), 105 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx index 6eb31e974606..4d23d9ac9e2e 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx @@ -19,6 +19,8 @@ export interface AutoLayoutLayerProps { index: number; widgetId: string; isMobile?: boolean; + isCurrentCanvasDragging: boolean; + currentChildCount: number; } const LayoutLayerContainer = styled.div<{ @@ -35,6 +37,7 @@ const LayoutLayerContainer = styled.div<{ `; const SubWrapper = styled.div<{ + isCurrentCanvasDragging: boolean; flexDirection: FlexDirection; wrap?: boolean; }>` @@ -43,6 +46,8 @@ const SubWrapper = styled.div<{ flex-direction: ${({ flexDirection }) => flexDirection || "row"}; align-items: "flex-start"; flex-wrap: ${({ wrap }) => (wrap ? "wrap" : "nowrap")}; + height: ${({ isCurrentCanvasDragging }) => + isCurrentCanvasDragging ? "100%" : "auto"}; `; const StartWrapper = styled(SubWrapper)` @@ -71,13 +76,22 @@ function getInverseDirection(direction: LayoutDirection): LayoutDirection { function AutoLayoutLayer(props: AutoLayoutLayerProps) { const flexDirection = getFlexDirection(getInverseDirection(props.direction)); + + const handleMouseMove = (e: any) => { + if (!props.isCurrentCanvasDragging) return; + e.stopPropagation(); + console.log("#### mouse move", props.index, props.widgetId); + }; + return ( handleMouseMove(e)} > {props.start} @@ -85,12 +99,14 @@ function AutoLayoutLayer(props: AutoLayoutLayerProps) { {props.center} {props.end} diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx index d9b4b1767f1b..57d761313e9f 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx @@ -63,6 +63,21 @@ export const FlexContainer = styled.div<{ leaveSpaceForWidgetName ? "4px 4px 22px 4px;" : "4px;"}; `; +const DEFAULT_HIGHLIGHT_SIZE = 4; + +export const DropPosition = styled.div<{ + isDragging: boolean; + isVertical: boolean; +}>` + width: ${({ isVertical }) => + isVertical ? `${DEFAULT_HIGHLIGHT_SIZE}px` : "100%"}; + height: ${({ isVertical }) => + isVertical ? "auto" : `${DEFAULT_HIGHLIGHT_SIZE}px`}; + background-color: rgba(223, 158, 206, 0.6); + margin: 2px; + display: ${({ isDragging }) => (isDragging ? "block" : "none")}; +`; + function FlexBoxComponent(props: FlexBoxProps) { // TODO: set isMobile as a prop at the top level const isMobile = useSelector(getIsMobile); @@ -70,7 +85,7 @@ function FlexBoxComponent(props: FlexBoxProps) { props.direction || LayoutDirection.Horizontal; const appMode = useSelector(getAppMode); const leaveSpaceForWidgetName = appMode === APP_MODE.EDIT; - const { autoLayoutDragDetails, dragDetails, flexHighlight } = useSelector( + const { autoLayoutDragDetails, dragDetails } = useSelector( (state: AppState) => state.ui.widgetDragResize, ); @@ -80,25 +95,6 @@ function FlexBoxComponent(props: FlexBoxProps) { ? autoLayoutDragDetails.map((each) => each.widgetId) : []; - const getPreviewNode = () => { - return React.createElement(() => { - const height = autoLayoutDragDetails - ? autoLayoutDragDetails[0].height - : 0; - const width = autoLayoutDragDetails ? autoLayoutDragDetails[0].width : 0; - - return ( -
- ); - }); - }; - const renderChildren = () => { if (!props.children) return null; if (!props.useAutoLayout) return props.children; @@ -106,25 +102,13 @@ function FlexBoxComponent(props: FlexBoxProps) { direction === LayoutDirection.Horizontal || !(props.flexLayers && props.flexLayers.length) ) { - if (flexHighlight && isCurrentCanvasDragging) { - const previewNode = getPreviewNode(); - const filteredChildren = (props.children as any)?.filter( - (child: any) => { - return ( - draggedWidgets.indexOf( - (child as JSX.Element)?.props?.widgetId, - ) === -1 - ); - }, + if (isCurrentCanvasDragging && draggedWidgets?.length) + return ((props.children as any) || [])?.filter( + (child: any) => + draggedWidgets?.indexOf((child as JSX.Element).props.widgetId) === + -1, ); - return addInPosition( - filteredChildren, - flexHighlight?.index, - previewNode, - ); - } else { - return props.children; - } + return props.children; } /** @@ -137,81 +121,166 @@ function FlexBoxComponent(props: FlexBoxProps) { } } - const previewNode = getPreviewNode(); - const layers: any[] = processLayers(map, previewNode); - - if (flexHighlight?.isNewLayer && isCurrentCanvasDragging) { - return addInPosition( - layers, - flexHighlight.layerIndex !== undefined - ? flexHighlight.layerIndex - : layers.length, - , - ); - } + const layers: any[] = processLayers(map); return layers; }; - function processLayers(map: { [key: string]: any }, previewNode: any) { - return props.flexLayers - ?.map((layer: FlexLayer, index: number) => { - const { children, hasFillChild } = layer; - let start = [], - center = [], - end = []; - if (!children || !children.length) return null; - - for (const child of children) { - const widget = map[child.id]; - if (hasFillChild) { - start.push(widget); - continue; - } - if (child.align === "end") end.push(widget); - else if (child.align === "center") center.push(widget); - else start.push(widget); - } + function DropPositionComponent(props: { + isVertical: boolean; + alignment: FlexLayerAlignment; + layerIndex: number; + childIndex: number; + widgetId: string; + }) { + return ( + + ); + } - if ( - isCurrentCanvasDragging && - !flexHighlight?.isNewLayer && - index === flexHighlight?.layerIndex - ) { - const pos = flexHighlight?.rowIndex || 0; - if (flexHighlight?.alignment === "start") - start = addInPosition(start, pos, previewNode); - else if (flexHighlight?.alignment === "center") - center = addInPosition(center, pos, previewNode); - else if (flexHighlight?.alignment === "end") - end = addInPosition(end, pos, previewNode); - } + const getDropPositionKey = ( + index: number, + alignment: FlexLayerAlignment, + layerIndex: number, + ): string => + `drop-layer-${props.widgetId}-${layerIndex}-${alignment}-${index}`; - return ( - { + const res = [ + , + ]; + let count = 0; + if (arr) { + for (const item of arr) { + count += 1; + res.push(item); + res.push( + + />, ); - }) - ?.filter((layer) => layer !== null); + } + } + return res; + }; + + function processLayers(map: { [key: string]: any }) { + let childCount = 0; + const layers = [ + , + ]; + props.flexLayers?.map((layer: FlexLayer, index: number) => { + const { children, hasFillChild } = layer; + let count = 0; + let start = [], + center = [], + end = []; + if (!children || !children.length) return null; + + for (const child of children) { + count += 1; + const widget = map[child.id]; + if (hasFillChild) { + start.push(widget); + continue; + } + if (child.align === "end") end.push(widget); + else if (child.align === "center") center.push(widget); + else start.push(widget); + } + /** + * Add drop positions + */ + start = addDropPositions( + start, + childCount, + index, + FlexLayerAlignment.Start, + true, + ); + center = addDropPositions( + center, + childCount, + index, + FlexLayerAlignment.Center, + true, + ); + end = addDropPositions( + end, + childCount, + index, + FlexLayerAlignment.End, + true, + ); + + const res = ( + + ); + childCount += count; + layers.push(res); + layers.push( + , + ); + return res; + }); + return layers; + // ?.filter((layer) => layer !== null); } function addInPosition(arr: any[], index: number, item: any): any[] { From 590d4f258e7db072f3ed4d644cd18c27156167a9 Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 2 Nov 2022 20:47:51 -0400 Subject: [PATCH 226/708] use new highlights for vertical stacks --- .../appsmith/autoLayout/FlexBoxComponent.tsx | 36 ++++++++++++--- .../appsmith/autoLayout/FlexComponent.tsx | 3 +- .../hooks/useAutoLayoutHighlights.ts | 44 +++++++++++++++++-- 3 files changed, 74 insertions(+), 9 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx index 57d761313e9f..c5124f0bf02f 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx @@ -75,7 +75,7 @@ export const DropPosition = styled.div<{ isVertical ? "auto" : `${DEFAULT_HIGHLIGHT_SIZE}px`}; background-color: rgba(223, 158, 206, 0.6); margin: 2px; - display: ${({ isDragging }) => (isDragging ? "block" : "none")}; + display: ${({ isDragging }) => (isDragging || true ? "block" : "none")}; `; function FlexBoxComponent(props: FlexBoxProps) { @@ -121,21 +121,37 @@ function FlexBoxComponent(props: FlexBoxProps) { } } - const layers: any[] = processLayers(map); + const layers: any[] = cleanLayers(processLayers(map)); return layers; }; + function cleanLayers(layers: any[]): any[] { + if (!layers) return []; + const set = new Set(); + return layers.filter((layer) => { + const key = (layer as JSX.Element).key as string; + const flag = set.has(key); + set.add(key); + return !flag; + }); + } + function DropPositionComponent(props: { isVertical: boolean; alignment: FlexLayerAlignment; layerIndex: number; childIndex: number; widgetId: string; + isNewLayer: boolean; }) { return ( @@ -155,11 +171,13 @@ function FlexBoxComponent(props: FlexBoxProps) { layerIndex: number, alignment: FlexLayerAlignment, isVertical: boolean, + isNewLayer: boolean, ): any[] => { const res = [ zIndex}; - width: ${({ componentWidth }) => `${Math.floor(componentWidth)}px`}; + width: ${({ componentWidth, isAffectedByDrag }) => + isAffectedByDrag ? "auto" : `${Math.floor(componentWidth)}px`}; height: ${({ componentHeight, isMobile }) => isMobile ? "100%" : Math.floor(componentHeight) + "px"}; min-width: ${({ minWidth }) => minWidth}; diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index b3baf2ef8cf8..2f9c9fe82959 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -147,6 +147,44 @@ export const useAutoLayoutHighlights = ({ // } // }); // }; + const getDropPositions = () => { + const els = document.querySelectorAll(`.t--drop-position-${canvasId}`); + const highlights: HighlightInfo[] = []; + for (const el of els) { + const rect: DOMRect = el.getBoundingClientRect(); + console.log("rect", rect); + const classList = Array.from(el.classList); + + const highlight: HighlightInfo = classList.reduce( + (acc: HighlightInfo, curr) => { + if (curr.indexOf("alignment") > -1) + acc.alignment = curr.split("-")[1] as FlexLayerAlignment; + else if (curr.indexOf("layer-index") > -1) + acc.layerIndex = parseInt(curr.split("layer-index-")[1]); + else if (curr.indexOf("child-index") > -1) + acc.index = parseInt(curr.split("child-index-")[1]); + else if (curr.indexOf("isNewLayer") > -1) acc.isNewLayer = true; + else if (curr.indexOf("isVertical") > -1) acc.isVertical = true; + + return acc; + }, + { + isNewLayer: false, + index: 0, + layerIndex: 0, + alignment: FlexLayerAlignment.Start, + posX: rect.x - containerDimensions.left, + posY: rect.y - containerDimensions?.top, + width: rect.width, + height: rect.height, + isVertical: false, + }, + ); + highlights.push(highlight); + } + + return highlights; + }; const calculateHighlights = (): HighlightInfo[] => { /** @@ -166,7 +204,7 @@ export const useAutoLayoutHighlights = ({ */ if (!updateContainerDimensions()) return []; // hideDraggedItems(draggedBlocks); - + getDropPositions(); const canvasChildren = canvas.children || []; // Get the list of children that are not being dragged. const offsetChildren = canvasChildren.filter((each) => { @@ -174,7 +212,7 @@ export const useAutoLayoutHighlights = ({ }); if (isVertical) { - highlights = calculateVerticalStackHighlights(layers, offsetChildren); + highlights = getDropPositions(); } else { highlights = calculateRowHighlights( offsetChildren, @@ -559,7 +597,7 @@ export const useAutoLayoutHighlights = ({ pos, moveDirection, ); - // console.log("#### filteredHighlights: ", filteredHighlights); + console.log("#### filteredHighlights: ", filteredHighlights, base); const arr = filteredHighlights.sort((a, b) => { return ( calculateDistance(a, pos, moveDirection) - From 8f75bd1fd12f11726885156a637c9102973f02f5 Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 3 Nov 2022 08:33:10 -0400 Subject: [PATCH 227/708] wip --- .../appsmith/autoLayout/FlexBoxComponent.tsx | 264 ++++++++++++------ .../hooks/useAutoLayoutHighlights.ts | 3 +- 2 files changed, 185 insertions(+), 82 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx index c5124f0bf02f..ab4e7483855d 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx @@ -70,12 +70,31 @@ export const DropPosition = styled.div<{ isVertical: boolean; }>` width: ${({ isVertical }) => - isVertical ? `${DEFAULT_HIGHLIGHT_SIZE}px` : "100%"}; + isVertical ? `${DEFAULT_HIGHLIGHT_SIZE}px` : "calc(100% - 4px)"}; height: ${({ isVertical }) => isVertical ? "auto" : `${DEFAULT_HIGHLIGHT_SIZE}px`}; background-color: rgba(223, 158, 206, 0.6); margin: 2px; - display: ${({ isDragging }) => (isDragging || true ? "block" : "none")}; + display: ${({ isDragging }) => (isDragging ? "block" : "none")}; +`; + +export const NewLayerStyled = styled.div<{ + isDragging: boolean; +}>` + width: 100%; + + div:nth-child(1) { + display: ${({ isDragging }) => (isDragging ? "block" : "none")}; + } + div:nth-child(2) { + display: none; + } + .selected div:nth-child(1) { + display: none; + } + .selected div:nth-child(2) { + display: ${({ isDragging }) => (isDragging ? "block" : "none")}; + } `; function FlexBoxComponent(props: FlexBoxProps) { @@ -85,16 +104,14 @@ function FlexBoxComponent(props: FlexBoxProps) { props.direction || LayoutDirection.Horizontal; const appMode = useSelector(getAppMode); const leaveSpaceForWidgetName = appMode === APP_MODE.EDIT; - const { autoLayoutDragDetails, dragDetails } = useSelector( + const { dragDetails } = useSelector( (state: AppState) => state.ui.widgetDragResize, ); + // TODO: Add support for multiple dragged widgets + const draggedWidget = dragDetails?.draggingGroupCenter?.widgetId; const isCurrentCanvasDragging = dragDetails?.draggedOn === props.widgetId; - const draggedWidgets: string[] = isArray(autoLayoutDragDetails) - ? autoLayoutDragDetails.map((each) => each.widgetId) - : []; - const renderChildren = () => { if (!props.children) return null; if (!props.useAutoLayout) return props.children; @@ -102,12 +119,12 @@ function FlexBoxComponent(props: FlexBoxProps) { direction === LayoutDirection.Horizontal || !(props.flexLayers && props.flexLayers.length) ) { - if (isCurrentCanvasDragging && draggedWidgets?.length) + if (isCurrentCanvasDragging && draggedWidget) return ((props.children as any) || [])?.filter( (child: any) => - draggedWidgets?.indexOf((child as JSX.Element).props.widgetId) === - -1, + draggedWidget !== (child as JSX.Element)?.props?.widgetId, ); + return props.children; } @@ -150,7 +167,7 @@ function FlexBoxComponent(props: FlexBoxProps) { className={`t--drop-position-${props.widgetId} alignment-${ props.alignment } layer-index-${props.layerIndex} child-index-${props.childIndex} ${ - props.isVertical ? "isVertical" : "" + props.isVertical ? "isVertical" : "isHorizontal" } ${props.isNewLayer ? "isNewLayer" : ""}`} isDragging={isCurrentCanvasDragging} isVertical={props.isVertical} @@ -158,12 +175,65 @@ function FlexBoxComponent(props: FlexBoxProps) { ); } + function NewLayerComponent(props: { + alignment: FlexLayerAlignment; + childCount: number; + layerIndex: number; + isDragging: boolean; + isNewLayer: boolean; + isVertical: boolean; + map: { [key: string]: any }; + widgetId: string; + }): JSX.Element { + const { + alignment, + childCount, + isDragging, + isNewLayer, + isVertical, + layerIndex, + map, + widgetId, + } = props; + return ( + + +
+ { + processIndividualLayer( + { children: [], hasFillChild: false }, + childCount, + layerIndex, + map, + true, + ).element + } +
+
+ ); + } + const getDropPositionKey = ( index: number, alignment: FlexLayerAlignment, layerIndex: number, + isVertical: boolean, ): string => - `drop-layer-${props.widgetId}-${layerIndex}-${alignment}-${index}`; + `drop-layer-${props.widgetId}-${layerIndex}-${alignment}-${index}-${ + isVertical ? "vertical" : "horizontal" + }-${Math.random()}`; const addDropPositions = ( arr: any[], @@ -179,7 +249,7 @@ function FlexBoxComponent(props: FlexBoxProps) { childIndex={childCount} isNewLayer={isNewLayer} isVertical={isVertical} - key={getDropPositionKey(0, alignment, layerIndex)} + key={getDropPositionKey(0, alignment, layerIndex, true)} layerIndex={layerIndex} widgetId={props.widgetId} />, @@ -187,6 +257,10 @@ function FlexBoxComponent(props: FlexBoxProps) { let count = 0; if (arr) { for (const item of arr) { + const widgetId = item + ? (item as JSX.Element)?.props.widgetId + : undefined; + // if (draggedWidget && widgetId && draggedWidget === widgetId) continue; count += 1; res.push(item); res.push( @@ -195,7 +269,7 @@ function FlexBoxComponent(props: FlexBoxProps) { childIndex={childCount + count} isNewLayer={isNewLayer} isVertical={isVertical} - key={getDropPositionKey(0, alignment, layerIndex)} + key={getDropPositionKey(0, alignment, layerIndex, true)} layerIndex={layerIndex} widgetId={props.widgetId} />, @@ -208,107 +282,137 @@ function FlexBoxComponent(props: FlexBoxProps) { function processLayers(map: { [key: string]: any }) { let childCount = 0; const layers = [ - , ]; props.flexLayers?.map((layer: FlexLayer, index: number) => { - const { children, hasFillChild } = layer; - let count = 0; - let start = [], - center = [], - end = []; - if (!children || !children.length) return null; - - for (const child of children) { - count += 1; - const widget = map[child.id]; - if (hasFillChild) { - start.push(widget); - continue; - } - if (child.align === "end") end.push(widget); - else if (child.align === "center") center.push(widget); - else start.push(widget); - } - /** - * Add drop positions - */ - const startLength = start.length, - centerLength = center.length; - start = addDropPositions( - start, + const { count, element } = processIndividualLayer( + layer, childCount, index, - FlexLayerAlignment.Start, - true, - false, - ); - center = addDropPositions( - center, - childCount + startLength, - index, - FlexLayerAlignment.Center, - true, - false, - ); - end = addDropPositions( - end, - childCount + startLength + centerLength, - index, - FlexLayerAlignment.End, - true, - false, - ); - - const res = ( - + map, ); + index === 1 && + props.widgetId !== "0" && + console.log("#### element", element); + if (element === null) return null; childCount += count; - layers.push(res); + layers.push(element); layers.push( - , ); - return res; + return element; }); return layers; // ?.filter((layer) => layer !== null); } + function processIndividualLayer( + layer: FlexLayer, + childCount: number, + index: number, + map: { [key: string]: any }, + bypassEmptyCheck = false, + ) { + const { children, hasFillChild } = layer; + props.widgetId !== "0" && console.log("#### children", children); + let count = 0; + let start = [], + center = [], + end = []; + if ((!children || !children.length) && !bypassEmptyCheck) + return { element: null, count }; + + for (const child of children) { + count += 1; + const widget = map[child.id]; + if (hasFillChild) { + start.push(widget); + continue; + } + if (child.align === "end") end.push(widget); + else if (child.align === "center") center.push(widget); + else start.push(widget); + } + /** + * Add drop positions + */ + const startLength = start.length, + centerLength = center.length; + start = addDropPositions( + start, + childCount, + index, + FlexLayerAlignment.Start, + true, + false, + ); + center = addDropPositions( + center, + childCount + startLength, + index, + FlexLayerAlignment.Center, + true, + false, + ); + end = addDropPositions( + end, + childCount + startLength + centerLength, + index, + FlexLayerAlignment.End, + true, + false, + ); + + return { + element: ( + + ), + count, + }; + } + function addInPosition(arr: any[], index: number, item: any): any[] { return [...arr.slice(0, index), item, ...arr.slice(index)]; } diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index 2f9c9fe82959..891f9b51c355 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -152,7 +152,6 @@ export const useAutoLayoutHighlights = ({ const highlights: HighlightInfo[] = []; for (const el of els) { const rect: DOMRect = el.getBoundingClientRect(); - console.log("rect", rect); const classList = Array.from(el.classList); const highlight: HighlightInfo = classList.reduce( @@ -597,7 +596,7 @@ export const useAutoLayoutHighlights = ({ pos, moveDirection, ); - console.log("#### filteredHighlights: ", filteredHighlights, base); + // console.log("#### filteredHighlights: ", filteredHighlights, base); const arr = filteredHighlights.sort((a, b) => { return ( calculateDistance(a, pos, moveDirection) - From 592a4bd96678a4ff23f5efdf8d5545b255508091 Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 3 Nov 2022 09:04:52 -0400 Subject: [PATCH 228/708] fix canvas height on mobile viewport --- .../appsmith/autoLayout/FlexComponent.tsx | 2 +- .../editorComponents/DropTargetComponent.tsx | 25 +++++-------------- .../editorComponents/ResizableComponent.tsx | 3 +++ .../src/resizable/resizenreflow/index.tsx | 3 ++- 4 files changed, 12 insertions(+), 21 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx index 411e9710f265..1400fba18708 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx @@ -51,7 +51,7 @@ const FlexWidget = styled.div<{ width: ${({ componentWidth }) => `${Math.floor(componentWidth)}px`}; height: ${({ componentHeight, isMobile }) => - isMobile ? "100%" : Math.floor(componentHeight) + "px"}; + isMobile ? "auto" : Math.floor(componentHeight) + "px"}; min-width: ${({ minWidth }) => minWidth}; min-height: 30px; diff --git a/app/client/src/components/editorComponents/DropTargetComponent.tsx b/app/client/src/components/editorComponents/DropTargetComponent.tsx index b1f0852c386f..b4885b9f2da1 100644 --- a/app/client/src/components/editorComponents/DropTargetComponent.tsx +++ b/app/client/src/components/editorComponents/DropTargetComponent.tsx @@ -27,7 +27,7 @@ import { import { getOccupiedSpacesSelectorForContainer } from "selectors/editorSelectors"; import { useWidgetSelection } from "utils/hooks/useWidgetSelection"; import { getDragDetails } from "sagas/selectors"; -import { LayoutDirection } from "components/constants"; +import { getIsMobile } from "selectors/mainCanvasSelectors"; type DropTargetComponentProps = WidgetProps & { children?: ReactNode; @@ -38,23 +38,13 @@ type DropTargetComponentProps = WidgetProps & { isWrapper?: boolean; }; -const StyledDropTarget = styled.div<{ - direction?: LayoutDirection; - isDragging: boolean; - isWrapper: boolean; -}>` +const StyledDropTarget = styled.div` transition: height 100ms ease-in; width: 100%; position: relative; background: none; user-select: none; - z-index: ${({ isWrapper }) => (isWrapper ? 2 : 1)}; - margin-top: ${({ direction, isDragging, isWrapper }) => - isWrapper && isDragging && direction === LayoutDirection.Horizontal - ? "8px" - : 0}; - margin-right: ${({ direction, isWrapper }) => - isWrapper && direction === LayoutDirection.Vertical ? "6px" : 0}; + z-index: 1; `; function Onboarding() { @@ -87,6 +77,8 @@ export function DropTargetComponent(props: DropTargetComponentProps) { (state: AppState) => state.ui.widgetDragResize.isDragging, ); + const isMobile = useSelector(getIsMobile); + // dragDetails contains of info needed for a container jump: // which parent the dragging widget belongs, // which canvas is active(being dragged on), @@ -138,7 +130,7 @@ export function DropTargetComponent(props: DropTargetComponentProps) { const updateHeight = () => { if (dropTargetRef.current) { - const height = props.isWrapper + const height = isMobile ? "auto" : canDropTargetExtend ? `${Math.max(rowRef.current * props.snapRowSpace, props.minHeight)}px` @@ -205,15 +197,10 @@ export function DropTargetComponent(props: DropTargetComponentProps) { height, boxShadow, }; - if (props.isWrapper && props.direction === LayoutDirection.Vertical) - style["width"] = "auto"; return ( From 6f1fb9f7aac29e306857e4932ed7d0f6f7d4a321 Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 3 Nov 2022 11:30:08 -0400 Subject: [PATCH 229/708] working reveal and hide --- .../appsmith/autoLayout/FlexBoxComponent.tsx | 44 +++++-------- .../appsmith/autoLayout/FlexComponent.tsx | 3 +- .../hooks/useAutoLayoutHighlights.ts | 61 ++++++++++++++++++- 3 files changed, 74 insertions(+), 34 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx index ab4e7483855d..0e2950127ca0 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx @@ -82,19 +82,6 @@ export const NewLayerStyled = styled.div<{ isDragging: boolean; }>` width: 100%; - - div:nth-child(1) { - display: ${({ isDragging }) => (isDragging ? "block" : "none")}; - } - div:nth-child(2) { - display: none; - } - .selected div:nth-child(1) { - display: none; - } - .selected div:nth-child(2) { - display: ${({ isDragging }) => (isDragging ? "block" : "none")}; - } `; function FlexBoxComponent(props: FlexBoxProps) { @@ -195,6 +182,15 @@ function FlexBoxComponent(props: FlexBoxProps) { map, widgetId, } = props; + + const { element: verticalHighlights } = processIndividualLayer( + { children: [], hasFillChild: false }, + childCount, + layerIndex, + map, + true, + ); + return ( -
- { - processIndividualLayer( - { children: [], hasFillChild: false }, - childCount, - layerIndex, - map, - true, - ).element - } -
+ {verticalHighlights}
); } @@ -306,9 +292,7 @@ function FlexBoxComponent(props: FlexBoxProps) { index, map, ); - index === 1 && - props.widgetId !== "0" && - console.log("#### element", element); + if (element === null) return null; childCount += count; layers.push(element); @@ -322,10 +306,10 @@ function FlexBoxComponent(props: FlexBoxProps) { key={getDropPositionKey( Math.ceil(Math.random() * 100), FlexLayerAlignment.Start, - index, + index + 1, false, )} - layerIndex={index} + layerIndex={index + 1} map={map} widgetId={props.widgetId} />, @@ -344,7 +328,7 @@ function FlexBoxComponent(props: FlexBoxProps) { bypassEmptyCheck = false, ) { const { children, hasFillChild } = layer; - props.widgetId !== "0" && console.log("#### children", children); + let count = 0; let start = [], center = [], diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx index 80d073c4d562..411e9710f265 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx @@ -49,8 +49,7 @@ const FlexWidget = styled.div<{ position: relative; z-index: ${({ zIndex }) => zIndex}; - width: ${({ componentWidth, isAffectedByDrag }) => - isAffectedByDrag ? "auto" : `${Math.floor(componentWidth)}px`}; + width: ${({ componentWidth }) => `${Math.floor(componentWidth)}px`}; height: ${({ componentHeight, isMobile }) => isMobile ? "100%" : Math.floor(componentHeight) + "px"}; min-width: ${({ minWidth }) => minWidth}; diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index 891f9b51c355..b6143818757b 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -31,6 +31,7 @@ export interface HighlightInfo { width: number; // width of the highlight. height: number; // height of the highlight. isVertical: boolean; // determines if the highlight is vertical or horizontal. + el?: Element; // dom node of the highlight. } export interface AutoLayoutHighlightProps { @@ -46,6 +47,7 @@ export interface AutoLayoutHighlightProps { export interface HighlightSelectionPayload { highlights: HighlightInfo[]; selectedHighlight: HighlightInfo; + showNewLayerAlignments?: boolean; } const OFFSET_WIDTH = 4; @@ -66,6 +68,7 @@ export const useAutoLayoutHighlights = ({ // const dispatch = useDispatch(); let highlights: HighlightInfo[] = []; let lastActiveHighlight: HighlightInfo | undefined; + let isNewLayerExpanded = false; let containerDimensions: { top: number; bottom: number; @@ -177,6 +180,7 @@ export const useAutoLayoutHighlights = ({ width: rect.width, height: rect.height, isVertical: false, + el, }, ); highlights.push(highlight); @@ -203,7 +207,7 @@ export const useAutoLayoutHighlights = ({ */ if (!updateContainerDimensions()) return []; // hideDraggedItems(draggedBlocks); - getDropPositions(); + const canvasChildren = canvas.children || []; // Get the list of children that are not being dragged. const offsetChildren = canvasChildren.filter((each) => { @@ -551,6 +555,25 @@ export const useAutoLayoutHighlights = ({ // }); // }; + const toggleNewLayerAlignments = ( + el: Element | undefined, + reveal: boolean, + ): void => { + if (!el) return; + const horizontalElement = el as HTMLElement; + const verticalElement = el?.nextSibling; + if (verticalElement) { + if (reveal) { + horizontalElement.style.display = "none"; + (verticalElement as HTMLElement).style.display = "flex"; + (verticalElement as HTMLElement).style.height = "40px"; + } else { + (horizontalElement as HTMLElement).style.display = "block"; + (verticalElement as HTMLElement).style.display = "none"; + } + } + }; + const highlightDropPosition = ( e: any, moveDirection: ReflowDirection, @@ -563,7 +586,22 @@ export const useAutoLayoutHighlights = ({ ); if (!payload || !payload.selectedHighlight) return; + + if ( + payload.showNewLayerAlignments && + (payload.selectedHighlight.layerIndex !== + lastActiveHighlight?.layerIndex || + !isNewLayerExpanded) + ) { + toggleNewLayerAlignments(payload.selectedHighlight.el, true); + isNewLayerExpanded = true; + } else if (!payload.showNewLayerAlignments && isNewLayerExpanded) { + toggleNewLayerAlignments(lastActiveHighlight?.el, false); + isNewLayerExpanded = false; + } + lastActiveHighlight = payload.selectedHighlight; + return payload; }; @@ -604,7 +642,26 @@ export const useAutoLayoutHighlights = ({ ); }); - return { highlights: [...arr.slice(1)], selectedHighlight: arr[0] }; + const isVerticalDrag = + moveDirection && + [ReflowDirection.TOP, ReflowDirection.BOTTOM].includes(moveDirection); + let distance: number | undefined = undefined; + if (isVerticalDrag) { + distance = calculateDistance( + { ...arr[0], posX: 0 }, + { ...pos, x: 0 }, + moveDirection, + ); + } + + return { + highlights: [...arr.slice(1)], + selectedHighlight: arr[0], + showNewLayerAlignments: + isVerticalDrag && distance !== undefined + ? Math.abs(distance) < 15 + : false, + }; }; function getViableDropPositions( From 23585614564ae5e4819884231a5abec1962f80e1 Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 3 Nov 2022 13:31:08 -0400 Subject: [PATCH 230/708] new layer alignment selection --- .../appsmith/autoLayout/AutoLayoutLayer.tsx | 16 +-- .../appsmith/autoLayout/FlexBoxComponent.tsx | 101 ++++++++++-------- .../hooks/useAutoLayoutHighlights.ts | 44 ++++++-- .../CanvasArenas/hooks/useCanvasDragging.ts | 18 ++-- 4 files changed, 105 insertions(+), 74 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx index 4d23d9ac9e2e..8ea680a3c635 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx @@ -21,18 +21,23 @@ export interface AutoLayoutLayerProps { isMobile?: boolean; isCurrentCanvasDragging: boolean; currentChildCount: number; + hideOnLoad?: boolean; } const LayoutLayerContainer = styled.div<{ flexDirection: FlexDirection; + hideOnLoad?: boolean; + isCurrentCanvasDragging: boolean; }>` - display: flex; + display: ${({ hideOnLoad }) => (hideOnLoad ? "none" : "flex")}; flex-direction: ${({ flexDirection }) => flexDirection || FlexDirection.Row}; justify-content: flex-start; align-items: flex-start; width: 100%; height: auto; + min-height: ${({ isCurrentCanvasDragging }) => + isCurrentCanvasDragging ? "40px" : "auto"}; margin-top: ${DRAG_MARGIN}px; `; @@ -77,17 +82,12 @@ function getInverseDirection(direction: LayoutDirection): LayoutDirection { function AutoLayoutLayer(props: AutoLayoutLayerProps) { const flexDirection = getFlexDirection(getInverseDirection(props.direction)); - const handleMouseMove = (e: any) => { - if (!props.isCurrentCanvasDragging) return; - e.stopPropagation(); - console.log("#### mouse move", props.index, props.widgetId); - }; - return ( handleMouseMove(e)} + hideOnLoad={props.hideOnLoad} + isCurrentCanvasDragging={props.isCurrentCanvasDragging} > child.id !== draggedWidget, + ).length === 0; + + !isEmpty && + layers.push( + , + ); + + const { count, element } = processIndividualLayer( + layer, + childCount, + layerIndex, + map, + ); + childCount += count; + if (!isEmpty) { + layerIndex += 1; + layers.push(element); + } + } + layers.push( , - ]; - props.flexLayers?.map((layer: FlexLayer, index: number) => { - const { count, element } = processIndividualLayer( - layer, - childCount, - index, - map, - ); - - if (element === null) return null; - childCount += count; - layers.push(element); - layers.push( - , - ); - return element; - }); + ); return layers; - // ?.filter((layer) => layer !== null); } function processIndividualLayer( @@ -325,7 +334,8 @@ function FlexBoxComponent(props: FlexBoxProps) { childCount: number, index: number, map: { [key: string]: any }, - bypassEmptyCheck = false, + allowEmptyLayer = false, + isNewLayer = false, ) { const { children, hasFillChild } = layer; @@ -333,8 +343,6 @@ function FlexBoxComponent(props: FlexBoxProps) { let start = [], center = [], end = []; - if ((!children || !children.length) && !bypassEmptyCheck) - return { element: null, count }; for (const child of children) { count += 1; @@ -358,7 +366,7 @@ function FlexBoxComponent(props: FlexBoxProps) { index, FlexLayerAlignment.Start, true, - false, + isNewLayer, ); center = addDropPositions( center, @@ -366,7 +374,7 @@ function FlexBoxComponent(props: FlexBoxProps) { index, FlexLayerAlignment.Center, true, - false, + isNewLayer, ); end = addDropPositions( end, @@ -374,7 +382,7 @@ function FlexBoxComponent(props: FlexBoxProps) { index, FlexLayerAlignment.End, true, - false, + isNewLayer, ); return { @@ -385,6 +393,7 @@ function FlexBoxComponent(props: FlexBoxProps) { direction={direction} end={end} hasFillChild={layer.hasFillChild} + hideOnLoad={allowEmptyLayer} index={index} isCurrentCanvasDragging={isCurrentCanvasDragging} isMobile={isMobile} @@ -397,9 +406,9 @@ function FlexBoxComponent(props: FlexBoxProps) { }; } - function addInPosition(arr: any[], index: number, item: any): any[] { - return [...arr.slice(0, index), item, ...arr.slice(index)]; - } + // function addInPosition(arr: any[], index: number, item: any): any[] { + // return [...arr.slice(0, index), item, ...arr.slice(index)]; + // } return ( { const allWidgets = useSelector(getWidgets); const canvas = allWidgets[canvasId]; - const layers: FlexLayer[] = canvas?.flexLayers || []; const isVertical = direction === LayoutDirection.Vertical; - // const dispatch = useDispatch(); let highlights: HighlightInfo[] = []; let lastActiveHighlight: HighlightInfo | undefined; let isNewLayerExpanded = false; @@ -189,6 +187,18 @@ export const useAutoLayoutHighlights = ({ return highlights; }; + const updateHighlight = (index: number): HighlightInfo => { + const highlight = highlights[index]; + if (!highlight || !highlight.el) return highlight; + const rect: DOMRect = highlight.el.getBoundingClientRect(); + console.log("#### rect"); + highlight.posX = rect.x - containerDimensions.left; + highlight.posY = rect.y - containerDimensions.top; + highlight.width = rect.width; + highlight.height = rect.height; + return highlight; + }; + const calculateHighlights = (): HighlightInfo[] => { /** * 1. Clean up temp styles. @@ -561,15 +571,15 @@ export const useAutoLayoutHighlights = ({ ): void => { if (!el) return; const horizontalElement = el as HTMLElement; - const verticalElement = el?.nextSibling; + const verticalElement = el?.nextSibling as HTMLElement; if (verticalElement) { if (reveal) { horizontalElement.style.display = "none"; - (verticalElement as HTMLElement).style.display = "flex"; - (verticalElement as HTMLElement).style.height = "40px"; + verticalElement.style.display = "flex"; + verticalElement.style.height = "40px"; } else { (horizontalElement as HTMLElement).style.display = "block"; - (verticalElement as HTMLElement).style.display = "none"; + verticalElement.style.display = "none"; } } }; @@ -594,6 +604,14 @@ export const useAutoLayoutHighlights = ({ !isNewLayerExpanded) ) { toggleNewLayerAlignments(payload.selectedHighlight.el, true); + const selectedIndex = highlights.findIndex( + (each) => each === payload.selectedHighlight, + ); + if (highlights[selectedIndex + 1].height === 0) { + highlights[selectedIndex + 1] = updateHighlight(selectedIndex + 1); + highlights[selectedIndex + 2] = updateHighlight(selectedIndex + 2); + highlights[selectedIndex + 3] = updateHighlight(selectedIndex + 3); + } isNewLayerExpanded = true; } else if (!payload.showNewLayerAlignments && isNewLayerExpanded) { toggleNewLayerAlignments(lastActiveHighlight?.el, false); @@ -634,7 +652,12 @@ export const useAutoLayoutHighlights = ({ pos, moveDirection, ); - // console.log("#### filteredHighlights: ", filteredHighlights, base); + // console.log( + // "#### filteredHighlights: ", + // filteredHighlights, + // base, + // moveDirection, + // ); const arr = filteredHighlights.sort((a, b) => { return ( calculateDistance(a, pos, moveDirection) - @@ -657,10 +680,9 @@ export const useAutoLayoutHighlights = ({ return { highlights: [...arr.slice(1)], selectedHighlight: arr[0], - showNewLayerAlignments: - isVerticalDrag && distance !== undefined - ? Math.abs(distance) < 15 - : false, + showNewLayerAlignments: isVerticalDrag + ? distance !== undefined && Math.abs(distance) < 15 + : isNewLayerExpanded, }; }; diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index ec7b674ec0c6..37010f39facb 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -712,15 +712,15 @@ export const useCanvasDragging = ( const leftOffset = getAbsolutePixels( stickyCanvasRef.current.style.left, ); - canvasCtx.fillStyle = "rgba(223, 158, 206, 0.6)"; - payload.highlights.forEach((each) => { - canvasCtx.fillRect( - each.posX - leftOffset, - each.posY - topOffset, - each.width, - each.height, - ); - }); + // canvasCtx.fillStyle = "rgba(223, 158, 206, 0.6)"; + // payload.highlights.forEach((each) => { + // canvasCtx.fillRect( + // each.posX - leftOffset, + // each.posY - topOffset, + // each.width, + // each.height, + // ); + // }); canvasCtx.fillStyle = "rgba(196, 139, 181, 1)"; canvasCtx.fillRect( payload.selectedHighlight.posX - leftOffset, From 79f69ea27b5bf56fa6a0473968dcc905d5e6ebd6 Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 3 Nov 2022 15:30:19 -0400 Subject: [PATCH 231/708] fix horizontal highlights --- .../appsmith/autoLayout/FlexBoxComponent.tsx | 40 +- .../hooks/useAutoLayoutHighlights.ts | 404 +----------------- .../src/selectors/autoLayoutSelectors.tsx | 10 + 3 files changed, 34 insertions(+), 420 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx index ec5d455143de..c7a1fbc7964f 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx @@ -13,6 +13,7 @@ import { useSelector } from "react-redux"; import { getAppMode } from "selectors/entitiesSelector"; import { getIsMobile } from "selectors/mainCanvasSelectors"; import AutoLayoutLayer from "./AutoLayoutLayer"; +import { isCurrentCanvasDragging } from "selectors/autoLayoutSelectors"; export interface FlexBoxProps { direction?: LayoutDirection; @@ -76,6 +77,7 @@ export const DropPosition = styled.div<{ background-color: rgba(223, 158, 206, 0.6); margin: 2px; display: ${({ isDragging }) => (isDragging ? "block" : "none")}; + align-self: stretch; `; export const NewLayerStyled = styled.div<{ @@ -91,28 +93,26 @@ function FlexBoxComponent(props: FlexBoxProps) { props.direction || LayoutDirection.Horizontal; const appMode = useSelector(getAppMode); const leaveSpaceForWidgetName = appMode === APP_MODE.EDIT; - const { dragDetails } = useSelector( - (state: AppState) => state.ui.widgetDragResize, - ); // TODO: Add support for multiple dragged widgets - const draggedWidget = dragDetails?.draggingGroupCenter?.widgetId; + const draggedWidget = useSelector( + (state: AppState) => + state.ui.widgetDragResize?.dragDetails?.draggingGroupCenter?.widgetId, + ); - const isCurrentCanvasDragging = dragDetails?.draggedOn === props.widgetId; + const isDragging = useSelector(isCurrentCanvasDragging(props.widgetId)); const renderChildren = () => { if (!props.children) return null; if (!props.useAutoLayout) return props.children; - if ( - direction === LayoutDirection.Horizontal || - !(props.flexLayers && props.flexLayers.length) - ) { - if (isCurrentCanvasDragging && draggedWidget) - return ((props.children as any) || [])?.filter( - (child: any) => - draggedWidget !== (child as JSX.Element)?.props?.widgetId, - ); - - return props.children; + if (direction === LayoutDirection.Horizontal) { + return addDropPositions( + props.children as any, + 0, + 0, + FlexLayerAlignment.Start, + true, + true, + ); } /** @@ -156,7 +156,7 @@ function FlexBoxComponent(props: FlexBoxProps) { } layer-index-${props.layerIndex} child-index-${props.childIndex} ${ props.isVertical ? "isVertical" : "isHorizontal" } ${props.isNewLayer ? "isNewLayer" : ""}`} - isDragging={isCurrentCanvasDragging} + isDragging={isDragging} isVertical={props.isVertical} /> ); @@ -281,7 +281,7 @@ function FlexBoxComponent(props: FlexBoxProps) { { - const allWidgets = useSelector(getWidgets); - const canvas = allWidgets[canvasId]; - const isVertical = direction === LayoutDirection.Vertical; let highlights: HighlightInfo[] = []; let lastActiveHighlight: HighlightInfo | undefined; let isNewLayerExpanded = false; @@ -98,20 +80,6 @@ export const useAutoLayoutHighlights = ({ return true; }; - const getContainerDimensionsAsDomRect = (): DOMRect => { - if (!containerDimensions) updateContainerDimensions(); - return { - x: containerDimensions?.left, - y: containerDimensions?.top, - width: containerDimensions?.width, - height: containerDimensions?.height, - } as DOMRect; - }; - - // Get DOM element for a given widgetId - const getDomElement = (widgetId: string): any => - document.querySelector(`.auto-layout-child-${widgetId}`); - const cleanUpTempStyles = () => { // reset display of all dragged blocks const els = document.querySelectorAll(`.auto-layout-parent-${canvasId}`); @@ -138,16 +106,6 @@ export const useAutoLayoutHighlights = ({ return blocks; }; - // Hide the dragged children of the auto layout container - // to discount them from highlight calculation. - // const hideDraggedItems = (draggedBlocks: string[]): void => { - // draggedBlocks.forEach((id: string) => { - // const el = getDomElement(id); - // if (el) { - // el.classList.add("auto-temp-no-display"); - // } - // }); - // }; const getDropPositions = () => { const els = document.querySelectorAll(`.t--drop-position-${canvasId}`); const highlights: HighlightInfo[] = []; @@ -191,7 +149,7 @@ export const useAutoLayoutHighlights = ({ const highlight = highlights[index]; if (!highlight || !highlight.el) return highlight; const rect: DOMRect = highlight.el.getBoundingClientRect(); - console.log("#### rect"); + highlight.posX = rect.x - containerDimensions.left; highlight.posY = rect.y - containerDimensions.top; highlight.width = rect.width; @@ -200,12 +158,6 @@ export const useAutoLayoutHighlights = ({ }; const calculateHighlights = (): HighlightInfo[] => { - /** - * 1. Clean up temp styles. - * 2. Get the dragged blocks. - * 3. Discount dragged blocks and empty layers from the calculation. - * 4. hide the dragged blocks and empty layers. - */ cleanUpTempStyles(); if (useAutoLayout && isDragging && isCurrentDraggedCanvas) { const draggedBlocks = getDraggedBlocks(); @@ -216,355 +168,17 @@ export const useAutoLayoutHighlights = ({ * That implies the container is null. */ if (!updateContainerDimensions()) return []; - // hideDraggedItems(draggedBlocks); - - const canvasChildren = canvas.children || []; - // Get the list of children that are not being dragged. - const offsetChildren = canvasChildren.filter((each) => { - return draggedBlocks.indexOf(each) === -1; - }); - if (isVertical) { - highlights = getDropPositions(); - } else { - highlights = calculateRowHighlights( - offsetChildren, - 0, - 0, - FlexLayerAlignment.Start, - getContainerDimensionsAsDomRect(), - true, - ); - } + highlights = getDropPositions(); } // console.log("#### highlights", highlights); return highlights; }; - // Remove dragged blocks from the list of children and update hasChild. - function filterLayer(layer: FlexLayer, offsetChildren: string[]): FlexLayer { - const filteredChildren = layer.children?.filter( - (child: LayerChild) => offsetChildren.indexOf(child.id) !== -1, - ); - return { - ...layer, - children: filteredChildren, - hasFillChild: filteredChildren?.some( - (each) => - allWidgets[each.id]?.responsiveBehavior === ResponsiveBehavior.Fill, - ), - }; - } - - function calculateVerticalStackHighlights( - flexLayers: FlexLayer[], - offsetChildren: string[], - ): HighlightInfo[] { - // If container is empty, return a highlight for the first position. - if (!flexLayers || !flexLayers.length) - return [ - getInitialHighlight( - 0, - FlexLayerAlignment.Start, - 0, - getContainerDimensionsAsDomRect(), - LayoutDirection.Vertical, - true, - ), - ]; - - let childCount = 0; - let discardedLayers = 0; - let index = 0; - const arr: HighlightInfo[] = []; - const rects: DOMRect[] = []; - // TODO: add documentation for the updated logic. - for (const layer of flexLayers) { - // remove dragged blocks from the layer - const filteredLayer = filterLayer(layer, offsetChildren); - const isEmpty = !filteredLayer.children.length; - // if (isEmpty) { - // discardedLayers += 1; - // // index += 1; - // // continue; - // } - const el = document.querySelector( - `.auto-layout-layer-${canvasId}-${index}`, - ); - if (!el) { - discardedLayers += 1; - index += 1; - continue; - } - const rect: DOMRect = el.getBoundingClientRect(); - // Add a horizontal highlight if the layer is not empty. - if (!isEmpty) { - rects.push(rect); - const info: HighlightInfo = { - isNewLayer: true, - index: childCount, - layerIndex: index - discardedLayers, - posX: 0, - posY: Math.max(rect.y - containerDimensions?.top - 4, 0), - width: containerDimensions?.width, - height: OFFSET_WIDTH, - alignment: FlexLayerAlignment.Start, - isVertical: false, - }; - // Add the horizontal highlight before the layer. - arr.push(info); - } - // Add vertical highlights for each child in the layer. - arr.push( - ...generateHighlightsForLayer( - filteredLayer, - index - discardedLayers, - rect, - childCount, - ), - ); - if (isEmpty) discardedLayers += 1; - index += 1; - childCount += filteredLayer.children?.length || 0; - } - - // Add a highlight for the last position. - const lastRect: DOMRect = rects[rects.length - 1]; - arr.push({ - isNewLayer: true, - index: childCount, - layerIndex: rects.length, - posX: 0, - posY: lastRect?.y + lastRect?.height - containerDimensions?.top, - width: containerDimensions?.width, - height: OFFSET_WIDTH, - alignment: FlexLayerAlignment.Start, - isVertical: false, - }); - - return arr; - } - - // Extract start, center and end children from the layer. - function spreadLayer(layer: FlexLayer) { - // const draggedBlocks = getDraggedBlocks(); - const start: string[] = [], - center: string[] = [], - end: string[] = []; - layer.children.forEach((child: LayerChild) => { - // if (draggedBlocks.includes(child.id)) return; - if (layer.hasFillChild) { - start.push(child.id); - return; - } - if (child.align === FlexLayerAlignment.End) end.push(child.id); - else if (child.align === FlexLayerAlignment.Center) center.push(child.id); - else start.push(child.id); - }); - return { - start, - center, - end, - hasFillChild: layer.hasFillChild, - isEmpty: !start.length && !center.length && !end.length, - }; - } - - function generateHighlightsForLayer( - layer: FlexLayer, - layerIndex: number, - layerRect: DOMRect, - childCount: number, - ): HighlightInfo[] { - const arr: HighlightInfo[] = []; - let curr: number = childCount; - const { center, end, hasFillChild, isEmpty, start } = spreadLayer(layer); - - const startHighLights = calculateRowHighlights( - start, - curr, - layerIndex, - FlexLayerAlignment.Start, - layerRect, - isEmpty, - ); - - // process start sub wrapper. - arr.push(...startHighLights); - if (!hasFillChild) { - // process center sub wrapper. - curr += start.length; - const centerHighlights = calculateRowHighlights( - center, - curr, - layerIndex, - FlexLayerAlignment.Center, - layerRect, - isEmpty, - ); - - // process end sub wrapper. - curr += center.length; - //ToDo(Ashok and Preet): we need a better way to decide how to filter out highlights - // for now i am filtering highlights to not overlap with each other. - const endHighLights = calculateRowHighlights( - end, - curr, - layerIndex, - FlexLayerAlignment.End, - layerRect, - isEmpty, - ); - const validEndHighLights = endHighLights.filter((each) => { - return !startHighLights.some((eachHighlight) => { - return eachHighlight.posX > each.posX; - }); - }); - arr.push(...validEndHighLights); - const validCenteredHighlights = centerHighlights.filter((each) => { - return ( - !startHighLights.length || - !endHighLights.length || - !( - startHighLights.some((eachHighlight) => { - return eachHighlight.posX > each.posX; - }) || - endHighLights.some((eachHighlight) => { - return eachHighlight.posX < each.posX; - }) - ) - ); - }); - arr.push(...validCenteredHighlights); - } - return arr; - } - - function calculateRowHighlights( - children: string[], - childCount: number, - layerIndex: number, - align: FlexLayerAlignment, - layerRect: DOMRect, - isNewLayer = false, - ): HighlightInfo[] { - if (!children || !children.length) - return [ - getInitialHighlight( - childCount, - align, - layerIndex, - layerRect, - LayoutDirection.Horizontal, - isNewLayer, - ), - ]; - return getRowHighlights(children, align, layerIndex, childCount); - } - - // Initial highlight for an empty container or layer. - function getInitialHighlight( - childCount: number, - alignment: FlexLayerAlignment, - layerIndex: number, - rect: DOMRect, - direction: LayoutDirection, - isNewLayer = false, - ): HighlightInfo { - const verticalFlex = direction === LayoutDirection.Vertical; - return { - isNewLayer, - index: childCount, - layerIndex, - alignment, - posX: - alignment === FlexLayerAlignment.Start - ? 0 - : alignment === FlexLayerAlignment.Center - ? containerDimensions.width / 2 - : containerDimensions?.width - DRAG_MARGIN * 2, - posY: - rect.y - containerDimensions?.top + (verticalFlex ? 0 : DRAG_MARGIN), - width: verticalFlex ? rect?.width : OFFSET_WIDTH, - height: verticalFlex ? OFFSET_WIDTH : rect.height - DRAG_MARGIN * 2, - isVertical: !verticalFlex, - rowIndex: 0, - }; - } - - function getRowHighlights( - children: string[], // Children of the row flex. - alignment: FlexLayerAlignment, // alignment for the highlights. - layerIndex: number, // index of the row flex. - childCount: number, // index of the first child. - ): HighlightInfo[] { - const arr: HighlightInfo[] = []; - const childRects: DOMRect[] = []; - let index = childCount; - let rowIndex = 0; - for (const child of children) { - const el = getDomElement(child); - if (!el) continue; - const childRect: DOMRect = el?.getBoundingClientRect(); - childRects.push(childRect); - // A highlight before each existing child. - arr.push({ - isNewLayer: false, - index, - layerIndex, - alignment, - posX: Math.max(childRect?.x - containerDimensions?.left - 8, 0), - posY: childRect?.y - containerDimensions?.top, - width: OFFSET_WIDTH, - height: childRect?.height, - isVertical: true, - rowIndex, - }); - index += 1; - rowIndex += 1; - } - - // A highlight after the last child. - const lastRect: DOMRect = childRects[childRects.length - 1]; - arr.push({ - isNewLayer: false, - index, - layerIndex, - alignment, - posX: lastRect?.x + lastRect?.width - containerDimensions?.left, - posY: lastRect?.y - containerDimensions?.top, - width: OFFSET_WIDTH, - height: lastRect?.height, - isVertical: true, - rowIndex, - }); - return arr; - } - /** * END AUTO LAYOUT OFFSET CALCULATION */ - // const debouncedDispatch = debounce((pos: HighlightInfo) => { - // dispatchTempHighlight(pos); - // }, 5); - - // const setTempHighlight = (pos: HighlightInfo) => { - // dispatch({ - // type: ReduxActionTypes.SELECT_AUTOLAYOUT_HIGHLIGHT, - // payload: { - // flexHighlight: pos, - // blocksToDraw, - // }, - // }); - // }; - - // const clearTempHighlight = () => { - // dispatch({ - // type: ReduxActionTypes.CLEAR_HIGHLIGHT_SELECTION, - // }); - // }; - const toggleNewLayerAlignments = ( el: Element | undefined, reveal: boolean, @@ -629,17 +243,7 @@ export const useAutoLayoutHighlights = ({ val?: XYCord, ): HighlightSelectionPayload => { let base: HighlightInfo[] = []; - if (!highlights || !highlights.length) - highlights = [ - getInitialHighlight( - 0, - FlexLayerAlignment.Start, - 0, - getContainerDimensionsAsDomRect(), - direction || LayoutDirection.Vertical, - true, - ), - ]; + if (!highlights || !highlights.length) highlights = getDropPositions(); base = highlights; const pos: XYCord = { diff --git a/app/client/src/selectors/autoLayoutSelectors.tsx b/app/client/src/selectors/autoLayoutSelectors.tsx index 2030a77be0d6..b3255fbb6ef7 100644 --- a/app/client/src/selectors/autoLayoutSelectors.tsx +++ b/app/client/src/selectors/autoLayoutSelectors.tsx @@ -1,3 +1,4 @@ +import { AppState } from "ce/reducers"; import { FlexLayer, LayerChild, @@ -39,3 +40,12 @@ export const getLayerIndex = (widgetId: string, parentId: string) => { }, ); }; + +export const isCurrentCanvasDragging = (widgetId: string) => { + return createSelector( + (state: AppState) => state.ui.widgetDragResize.dragDetails, + (dragDetails): boolean => { + return dragDetails?.draggedOn === widgetId; + }, + ); +}; From 23b67e4dd32bce7ae21f5bf2a0fffe1a1d3cf493 Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 3 Nov 2022 15:43:45 -0400 Subject: [PATCH 232/708] selective visibility of highlights --- .../appsmith/autoLayout/FlexBoxComponent.tsx | 1 + .../hooks/useAutoLayoutHighlights.ts | 20 +++++++++++++------ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx index c7a1fbc7964f..e0082b4403c5 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx @@ -78,6 +78,7 @@ export const DropPosition = styled.div<{ margin: 2px; display: ${({ isDragging }) => (isDragging ? "block" : "none")}; align-self: stretch; + opacity: 0; `; export const NewLayerStyled = styled.div<{ diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index d039fc1f16d0..0ccff9f348e0 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -49,6 +49,7 @@ export const useAutoLayoutHighlights = ({ let highlights: HighlightInfo[] = []; let lastActiveHighlight: HighlightInfo | undefined; let isNewLayerExpanded = false; + const isVerticalStack = direction === LayoutDirection.Vertical; let containerDimensions: { top: number; bottom: number; @@ -198,6 +199,17 @@ export const useAutoLayoutHighlights = ({ } }; + const toggleHighlightVisibility = ( + arr: HighlightInfo[], + selected: HighlightInfo[], + ): void => { + arr.forEach((each: HighlightInfo) => { + const el = each.el as HTMLElement; + if (!isVerticalStack) el.style.opacity = "1"; + else el.style.opacity = selected.includes(each) ? "1" : "0"; + }); + }; + const highlightDropPosition = ( e: any, moveDirection: ReflowDirection, @@ -256,18 +268,14 @@ export const useAutoLayoutHighlights = ({ pos, moveDirection, ); - // console.log( - // "#### filteredHighlights: ", - // filteredHighlights, - // base, - // moveDirection, - // ); + const arr = filteredHighlights.sort((a, b) => { return ( calculateDistance(a, pos, moveDirection) - calculateDistance(b, pos, moveDirection) ); }); + toggleHighlightVisibility(base, filteredHighlights); const isVerticalDrag = moveDirection && From 1f8de9428dd64eb11e5a82ac778eb71cc8b60821 Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 3 Nov 2022 16:16:31 -0400 Subject: [PATCH 233/708] fix selected highlight style and remove drop position ref usage --- .../appsmith/autoLayout/FlexBoxComponent.tsx | 2 +- .../CanvasArenas/CanvasDraggingArena.tsx | 37 +++++-------- .../common/CanvasArenas/StickyCanvasArena.tsx | 24 +------- .../hooks/useAutoLayoutHighlights.ts | 55 ++++++++++++++----- .../CanvasArenas/hooks/useCanvasDragging.ts | 40 +------------- 5 files changed, 60 insertions(+), 98 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx index e0082b4403c5..68194c0d7cdd 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx @@ -64,7 +64,7 @@ export const FlexContainer = styled.div<{ leaveSpaceForWidgetName ? "4px 4px 22px 4px;" : "4px;"}; `; -const DEFAULT_HIGHLIGHT_SIZE = 4; +export const DEFAULT_HIGHLIGHT_SIZE = 4; export const DropPosition = styled.div<{ isDragging: boolean; diff --git a/app/client/src/pages/common/CanvasArenas/CanvasDraggingArena.tsx b/app/client/src/pages/common/CanvasArenas/CanvasDraggingArena.tsx index 4fcafb45749a..32f2a52a31f4 100644 --- a/app/client/src/pages/common/CanvasArenas/CanvasDraggingArena.tsx +++ b/app/client/src/pages/common/CanvasArenas/CanvasDraggingArena.tsx @@ -49,28 +49,21 @@ export function CanvasDraggingArena({ const slidingArenaRef = React.useRef(null); const stickyCanvasRef = React.useRef(null); - const dropPositionRef = React.useRef(null); - const { showCanvas } = useCanvasDragging( - dropPositionRef, - slidingArenaRef, - stickyCanvasRef, - { - alignItems, - canExtend, - direction, - dropDisabled, - noPad, - parentId, - snapColumnSpace, - snapRows, - snapRowSpace, - useAutoLayout, - widgetId, - widgetName, - }, - ); + const { showCanvas } = useCanvasDragging(slidingArenaRef, stickyCanvasRef, { + alignItems, + canExtend, + direction, + dropDisabled, + noPad, + parentId, + snapColumnSpace, + snapRows, + snapRowSpace, + useAutoLayout, + widgetId, + widgetName, + }); const canvasRef = React.useRef({ - dropPositionRef, stickyCanvasRef, slidingArenaRef, }); @@ -79,7 +72,6 @@ export function CanvasDraggingArena({ canExtend={canExtend} canvasId={`canvas-dragging-${widgetId}`} canvasPadding={needsPadding ? theme.canvasBottomPadding : 0} - direction={direction} getRelativeScrollingParent={getNearestParentCanvas} id={`div-dragarena-${widgetId}`} ref={canvasRef} @@ -87,7 +79,6 @@ export function CanvasDraggingArena({ snapColSpace={snapColumnSpace} snapRowSpace={snapRowSpace} snapRows={snapRows} - useAutoLayout={useAutoLayout} /> ) : null; } diff --git a/app/client/src/pages/common/CanvasArenas/StickyCanvasArena.tsx b/app/client/src/pages/common/CanvasArenas/StickyCanvasArena.tsx index 1198c089c7ef..a57341c755a3 100644 --- a/app/client/src/pages/common/CanvasArenas/StickyCanvasArena.tsx +++ b/app/client/src/pages/common/CanvasArenas/StickyCanvasArena.tsx @@ -1,12 +1,10 @@ import styled from "constants/DefaultTheme"; import React, { forwardRef, RefObject, useEffect, useRef } from "react"; import ResizeObserver from "resize-observer-polyfill"; -import { LayoutDirection } from "components/constants"; interface StickyCanvasArenaProps { showCanvas: boolean; canvasId: string; - direction?: LayoutDirection; id: string; canvasPadding: number; snapRows: number; @@ -15,11 +13,9 @@ interface StickyCanvasArenaProps { getRelativeScrollingParent: (child: HTMLDivElement) => Element | null; canExtend: boolean; ref: StickyCanvasArenaRef; - useAutoLayout?: boolean; } interface StickyCanvasArenaRef { - dropPositionRef: RefObject; stickyCanvasRef: RefObject; slidingArenaRef: RefObject; } @@ -37,19 +33,6 @@ const StyledCanvasSlider = styled.div<{ paddingBottom: number }>` overflow-y: auto; `; -const Highlight = styled.div<{ - direction?: LayoutDirection; -}>` - width: ${({ direction }) => - direction === LayoutDirection.Vertical ? "100%" : "4px"}; - height: ${({ direction }) => - direction === LayoutDirection.Horizontal ? "100%" : "4px"}; - background-color: rgba(217, 89, 183, 0.8); - position: absolute; - opacity: 0; - z-index: 4; -`; - const StickyCanvas = styled.canvas` position: absolute; `; @@ -60,16 +43,14 @@ export const StickyCanvasArena = forwardRef( canExtend, canvasId, canvasPadding, - direction, getRelativeScrollingParent, id, showCanvas, snapColSpace, snapRows, snapRowSpace, - useAutoLayout, } = props; - const { dropPositionRef, slidingArenaRef, stickyCanvasRef } = ref.current; + const { slidingArenaRef, stickyCanvasRef } = ref.current; const interSectionObserver = useRef( new IntersectionObserver((entries) => { @@ -156,9 +137,6 @@ export const StickyCanvasArena = forwardRef( paddingBottom={canvasPadding} ref={slidingArenaRef} /> - {useAutoLayout && ( - - )} ); }, diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index 0ccff9f348e0..cf00b9862b22 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -1,4 +1,6 @@ import { FlexLayerAlignment, LayoutDirection } from "components/constants"; +import { DEFAULT_HIGHLIGHT_SIZE } from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; +import { isEqual } from "lodash"; import { ReflowDirection } from "reflow/reflowTypes"; import { WidgetDraggingBlock } from "./useBlocksToBeDraggedOnCanvas"; @@ -41,14 +43,14 @@ export const useAutoLayoutHighlights = ({ blocksToDraw, canvasId, direction, - dropPositionRef, isCurrentDraggedCanvas, isDragging, useAutoLayout, }: AutoLayoutHighlightProps) => { let highlights: HighlightInfo[] = []; let lastActiveHighlight: HighlightInfo | undefined; - let isNewLayerExpanded = false; + let expandedNewLayer: number | undefined; + const isVerticalStack = direction === LayoutDirection.Vertical; let containerDimensions: { top: number; @@ -63,6 +65,8 @@ export const useAutoLayoutHighlights = ({ * START AUTO LAYOUT OFFSET CALCULATION */ + const isNewLayerExpanded = (): boolean => expandedNewLayer !== undefined; + // Fetch and update the dimensions of the containing canvas. const updateContainerDimensions = (): boolean => { const container = document.querySelector(`.appsmith_widget_${canvasId}`); @@ -93,12 +97,8 @@ export const useAutoLayoutHighlights = ({ // reset state lastActiveHighlight = undefined; + expandedNewLayer = undefined; highlights = []; - // Hide the highlight - if (dropPositionRef && dropPositionRef.current) { - dropPositionRef.current.style.opacity = "0"; - dropPositionRef.current.style.display = "none"; - } }; // Get a list of widgetIds that are being dragged. @@ -210,6 +210,26 @@ export const useAutoLayoutHighlights = ({ }); }; + const updateSelection = (highlight: HighlightInfo): void => { + if (lastActiveHighlight) { + const lastEl = lastActiveHighlight?.el as HTMLElement; + if (lastEl) { + lastActiveHighlight.isVertical + ? (lastEl.style.width = `${DEFAULT_HIGHLIGHT_SIZE}px`) + : (lastEl.style.height = `${DEFAULT_HIGHLIGHT_SIZE}px`); + lastEl.style.backgroundColor = "rgba(223, 158, 206, 0.6)"; + } + } + const el = highlight.el as HTMLElement; + if (el) { + highlight.isVertical + ? (el.style.width = `${DEFAULT_HIGHLIGHT_SIZE * 1.5}px`) + : (el.style.height = `${DEFAULT_HIGHLIGHT_SIZE * 1.5}px`); + el.style.backgroundColor = "rgba(196, 139, 181, 1)"; + } + lastActiveHighlight = highlight; + }; + const highlightDropPosition = ( e: any, moveDirection: ReflowDirection, @@ -221,13 +241,18 @@ export const useAutoLayoutHighlights = ({ moveDirection, ); - if (!payload || !payload.selectedHighlight) return; + if ( + !payload || + !payload.selectedHighlight || + isEqual(payload.selectedHighlight, lastActiveHighlight) + ) + return; if ( payload.showNewLayerAlignments && (payload.selectedHighlight.layerIndex !== lastActiveHighlight?.layerIndex || - !isNewLayerExpanded) + !isNewLayerExpanded()) ) { toggleNewLayerAlignments(payload.selectedHighlight.el, true); const selectedIndex = highlights.findIndex( @@ -238,13 +263,15 @@ export const useAutoLayoutHighlights = ({ highlights[selectedIndex + 2] = updateHighlight(selectedIndex + 2); highlights[selectedIndex + 3] = updateHighlight(selectedIndex + 3); } - isNewLayerExpanded = true; - } else if (!payload.showNewLayerAlignments && isNewLayerExpanded) { + expandedNewLayer = selectedIndex; + updateSelection(highlights[selectedIndex + 1]); + return { ...payload, selectedHighlight: highlights[selectedIndex + 1] }; + } else if (!payload.showNewLayerAlignments && isNewLayerExpanded()) { toggleNewLayerAlignments(lastActiveHighlight?.el, false); - isNewLayerExpanded = false; + expandedNewLayer = undefined; } - lastActiveHighlight = payload.selectedHighlight; + updateSelection(payload.selectedHighlight); return payload; }; @@ -294,7 +321,7 @@ export const useAutoLayoutHighlights = ({ selectedHighlight: arr[0], showNewLayerAlignments: isVerticalDrag ? distance !== undefined && Math.abs(distance) < 15 - : isNewLayerExpanded, + : isNewLayerExpanded(), }; }; diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index 37010f39facb..59da6d6843f0 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -28,7 +28,6 @@ import { import ContainerJumpMetrics from "./ContainerJumpMetric"; import { HighlightInfo, - HighlightSelectionPayload, useAutoLayoutHighlights, } from "./useAutoLayoutHighlights"; import { @@ -52,7 +51,6 @@ const containerJumpThresholdMetrics = new ContainerJumpMetrics<{ }>(); export const useCanvasDragging = ( - dropPositionRef: React.RefObject, slidingArenaRef: React.RefObject, stickyCanvasRef: React.RefObject, { @@ -125,7 +123,6 @@ export const useCanvasDragging = ( blocksToDraw, canvasId: widgetId, direction, - dropPositionRef, isCurrentDraggedCanvas, isDragging, useAutoLayout, @@ -605,18 +602,13 @@ export const useCanvasDragging = ( } else if (!isUpdatingRows) { currentDirection.current = getMouseMoveDirection(e, 1); triggerReflow(e, firstMove); - let payload: HighlightSelectionPayload | undefined; if ( useAutoLayout && isCurrentDraggedCanvas && currentDirection.current !== ReflowDirection.UNSET ) - payload = highlightDropPosition( - e, - currentDirection.current, - // mouseAttributesRef?.current.prevAcceleration, - ); - renderBlocks(payload); + highlightDropPosition(e, currentDirection.current); + renderBlocks(); } scrollObj.lastMouseMoveEvent = { offsetX: e.offsetX, @@ -684,7 +676,7 @@ export const useCanvasDragging = ( }, ); - const renderBlocks = (payload?: HighlightSelectionPayload) => { + const renderBlocks = () => { if ( slidingArenaRef.current && isCurrentDraggedCanvas && @@ -705,32 +697,6 @@ export const useCanvasDragging = ( currentRectanglesToDraw.forEach((each) => { drawBlockOnCanvas(each); }); - if (payload) { - const topOffset = getAbsolutePixels( - stickyCanvasRef.current.style.top, - ); - const leftOffset = getAbsolutePixels( - stickyCanvasRef.current.style.left, - ); - // canvasCtx.fillStyle = "rgba(223, 158, 206, 0.6)"; - // payload.highlights.forEach((each) => { - // canvasCtx.fillRect( - // each.posX - leftOffset, - // each.posY - topOffset, - // each.width, - // each.height, - // ); - // }); - canvasCtx.fillStyle = "rgba(196, 139, 181, 1)"; - canvasCtx.fillRect( - payload.selectedHighlight.posX - leftOffset, - payload.selectedHighlight.posY - topOffset, - payload.selectedHighlight.width * - (payload.selectedHighlight.isVertical ? 1.5 : 1), - payload.selectedHighlight.height * - (!payload.selectedHighlight.isVertical ? 1.5 : 1), - ); - } } canvasCtx.restore(); } From 6091f024388319a1c566098533ccd1b873dbbdb7 Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 3 Nov 2022 16:25:39 -0400 Subject: [PATCH 234/708] bug fix --- .../pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index cf00b9862b22..f72114ae4421 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -27,7 +27,6 @@ export interface AutoLayoutHighlightProps { blocksToDraw: WidgetDraggingBlock[]; canvasId: string; direction?: LayoutDirection; - dropPositionRef: React.RefObject; isCurrentDraggedCanvas: boolean; isDragging: boolean; useAutoLayout?: boolean; From d19886f80f39fd53d820d5658cba94dfbf69326d Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 3 Nov 2022 23:01:32 -0400 Subject: [PATCH 235/708] working new layer alignments --- .../hooks/useAutoLayoutHighlights.ts | 99 +++++++++---------- 1 file changed, 49 insertions(+), 50 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index f72114ae4421..ff9542ecda7f 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -47,6 +47,7 @@ export const useAutoLayoutHighlights = ({ useAutoLayout, }: AutoLayoutHighlightProps) => { let highlights: HighlightInfo[] = []; + let newLayers: { [key: string]: number }; let lastActiveHighlight: HighlightInfo | undefined; let expandedNewLayer: number | undefined; @@ -98,6 +99,7 @@ export const useAutoLayoutHighlights = ({ lastActiveHighlight = undefined; expandedNewLayer = undefined; highlights = []; + newLayers = {}; }; // Get a list of widgetIds that are being dragged. @@ -139,6 +141,7 @@ export const useAutoLayoutHighlights = ({ el, }, ); + if (!highlight.isVertical) newLayers[highlights.length] = highlight.posY; highlights.push(highlight); } @@ -154,6 +157,7 @@ export const useAutoLayoutHighlights = ({ highlight.posY = rect.y - containerDimensions.top; highlight.width = rect.width; highlight.height = rect.height; + highlights[index] = highlight; return highlight; }; @@ -191,6 +195,7 @@ export const useAutoLayoutHighlights = ({ horizontalElement.style.display = "none"; verticalElement.style.display = "flex"; verticalElement.style.height = "40px"; + verticalElement.style.border = "1px dotted rgba(223, 158, 206, 0.6)"; } else { (horizontalElement as HTMLElement).style.display = "block"; verticalElement.style.display = "none"; @@ -240,35 +245,7 @@ export const useAutoLayoutHighlights = ({ moveDirection, ); - if ( - !payload || - !payload.selectedHighlight || - isEqual(payload.selectedHighlight, lastActiveHighlight) - ) - return; - - if ( - payload.showNewLayerAlignments && - (payload.selectedHighlight.layerIndex !== - lastActiveHighlight?.layerIndex || - !isNewLayerExpanded()) - ) { - toggleNewLayerAlignments(payload.selectedHighlight.el, true); - const selectedIndex = highlights.findIndex( - (each) => each === payload.selectedHighlight, - ); - if (highlights[selectedIndex + 1].height === 0) { - highlights[selectedIndex + 1] = updateHighlight(selectedIndex + 1); - highlights[selectedIndex + 2] = updateHighlight(selectedIndex + 2); - highlights[selectedIndex + 3] = updateHighlight(selectedIndex + 3); - } - expandedNewLayer = selectedIndex; - updateSelection(highlights[selectedIndex + 1]); - return { ...payload, selectedHighlight: highlights[selectedIndex + 1] }; - } else if (!payload.showNewLayerAlignments && isNewLayerExpanded()) { - toggleNewLayerAlignments(lastActiveHighlight?.el, false); - expandedNewLayer = undefined; - } + if (!payload || !payload.selectedHighlight) return; updateSelection(payload.selectedHighlight); @@ -289,41 +266,63 @@ export const useAutoLayoutHighlights = ({ y: e?.offsetY || val?.y, }; - const filteredHighlights: HighlightInfo[] = getViableDropPositions( - base, - pos, - moveDirection, - ); + let filteredHighlights: HighlightInfo[] = []; + const newLayerIndex = isInNewLayerRange(pos.y); + if (newLayerIndex > -1) { + filteredHighlights = [ + ...base.slice( + newLayerIndex + 1, + Math.min(newLayerIndex + 4, base.length), + ), + ]; + if (filteredHighlights[0].height === 0) { + filteredHighlights[0] = updateHighlight(newLayerIndex + 1); + filteredHighlights[1] = updateHighlight(newLayerIndex + 2); + filteredHighlights[2] = updateHighlight(newLayerIndex + 3); + } + expandedNewLayer !== undefined && + toggleNewLayerAlignments(highlights[expandedNewLayer]?.el, false); + toggleNewLayerAlignments(highlights[newLayerIndex].el, true); + expandedNewLayer = newLayerIndex; + } else { + filteredHighlights = getViableDropPositions(base, pos, moveDirection); + expandedNewLayer !== undefined && + toggleNewLayerAlignments(highlights[expandedNewLayer]?.el, false); + expandedNewLayer = undefined; + } + toggleHighlightVisibility(base, filteredHighlights); const arr = filteredHighlights.sort((a, b) => { return ( calculateDistance(a, pos, moveDirection) - calculateDistance(b, pos, moveDirection) ); }); - toggleHighlightVisibility(base, filteredHighlights); - - const isVerticalDrag = - moveDirection && - [ReflowDirection.TOP, ReflowDirection.BOTTOM].includes(moveDirection); - let distance: number | undefined = undefined; - if (isVerticalDrag) { - distance = calculateDistance( - { ...arr[0], posX: 0 }, - { ...pos, x: 0 }, - moveDirection, - ); - } return { highlights: [...arr.slice(1)], selectedHighlight: arr[0], - showNewLayerAlignments: isVerticalDrag - ? distance !== undefined && Math.abs(distance) < 15 - : isNewLayerExpanded(), }; }; + function isInNewLayerRange(y: number): number { + const positions: number[] = Object.values(newLayers); + const keys: string[] = Object.keys(newLayers); + if (!positions || !positions.length) return -1; + const index: number = positions.findIndex((each: number, index: number) => { + const lower: number = + expandedNewLayer !== undefined ? each : Math.max(each - 5, 0); + const upper: number = + expandedNewLayer !== undefined && + expandedNewLayer === parseInt(keys[index]) + ? each + 40 + : each + 14; + return y >= lower && (y <= upper || index === positions.length - 1); + }); + if (index === -1) return -1; + return parseInt(keys[index]); + } + function getViableDropPositions( arr: HighlightInfo[], pos: XYCord, From 2ccd897e31ba578ed47d6dc7aff61e683a7cb349 Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 3 Nov 2022 23:06:05 -0400 Subject: [PATCH 236/708] remove unused code --- .../CanvasArenas/hooks/useAutoLayoutHighlights.ts | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index ff9542ecda7f..efce34efb4d6 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -1,6 +1,5 @@ import { FlexLayerAlignment, LayoutDirection } from "components/constants"; import { DEFAULT_HIGHLIGHT_SIZE } from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; -import { isEqual } from "lodash"; import { ReflowDirection } from "reflow/reflowTypes"; import { WidgetDraggingBlock } from "./useBlocksToBeDraggedOnCanvas"; @@ -20,7 +19,7 @@ export interface HighlightInfo { width: number; // width of the highlight. height: number; // height of the highlight. isVertical: boolean; // determines if the highlight is vertical or horizontal. - el?: Element; // dom node of the highlight. + el: Element; // dom node of the highlight. } export interface AutoLayoutHighlightProps { @@ -65,8 +64,6 @@ export const useAutoLayoutHighlights = ({ * START AUTO LAYOUT OFFSET CALCULATION */ - const isNewLayerExpanded = (): boolean => expandedNewLayer !== undefined; - // Fetch and update the dimensions of the containing canvas. const updateContainerDimensions = (): boolean => { const container = document.querySelector(`.appsmith_widget_${canvasId}`); @@ -102,12 +99,6 @@ export const useAutoLayoutHighlights = ({ newLayers = {}; }; - // Get a list of widgetIds that are being dragged. - const getDraggedBlocks = (): string[] => { - const blocks = blocksToDraw.map((block) => block.widgetId); - return blocks; - }; - const getDropPositions = () => { const els = document.querySelectorAll(`.t--drop-position-${canvasId}`); const highlights: HighlightInfo[] = []; @@ -164,8 +155,7 @@ export const useAutoLayoutHighlights = ({ const calculateHighlights = (): HighlightInfo[] => { cleanUpTempStyles(); if (useAutoLayout && isDragging && isCurrentDraggedCanvas) { - const draggedBlocks = getDraggedBlocks(); - if (!draggedBlocks || !draggedBlocks.length) return []; + if (!blocksToDraw || !blocksToDraw.length) return []; /** * update dimensions of the current canvas * and break out of the function if returned value is false. From 2e2913b9613a7b22d0b9404c41606d8c75d80522 Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 3 Nov 2022 23:23:58 -0400 Subject: [PATCH 237/708] fix width width on drag --- .../designSystems/appsmith/autoLayout/FlexBoxComponent.tsx | 3 +++ .../designSystems/appsmith/autoLayout/FlexComponent.tsx | 4 +++- .../common/CanvasArenas/hooks/useAutoLayoutHighlights.ts | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx index 68194c0d7cdd..59e5e72d56ff 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx @@ -85,6 +85,9 @@ export const NewLayerStyled = styled.div<{ isDragging: boolean; }>` width: 100%; + > div { + transition: 500ms ease-in-out; + } `; function FlexBoxComponent(props: FlexBoxProps) { diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx index 411e9710f265..981f2e2c93b8 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx @@ -121,12 +121,14 @@ export function FlexComponent(props: AutoLayoutProps) { const isAffectedByDrag: boolean = isCurrentCanvasDragging || (isDragging && props.parentId === MAIN_CONTAINER_WIDGET_ID); + // TODO: Simplify this logic. const resizedWidth: number = isAffectedByDrag ? props.componentWidth - dragMargin - (props.parentId !== MAIN_CONTAINER_WIDGET_ID && siblingCount > 0 ? DEFAULT_MARGIN / siblingCount - : 0) + : 0) - + ((siblingCount || 0) + 1) * 6 : props.componentWidth; return ( diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index efce34efb4d6..a21597d34ffb 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -305,7 +305,7 @@ export const useAutoLayoutHighlights = ({ const upper: number = expandedNewLayer !== undefined && expandedNewLayer === parseInt(keys[index]) - ? each + 40 + ? each + 35 : each + 14; return y >= lower && (y <= upper || index === positions.length - 1); }); From 1e40e5ba16e1278d8e7638cd4511b335df752b14 Mon Sep 17 00:00:00 2001 From: Preet Date: Fri, 4 Nov 2022 00:34:35 -0400 Subject: [PATCH 238/708] fix undefined newLayers --- .../pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index a21597d34ffb..6f26f3750d2b 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -46,7 +46,7 @@ export const useAutoLayoutHighlights = ({ useAutoLayout, }: AutoLayoutHighlightProps) => { let highlights: HighlightInfo[] = []; - let newLayers: { [key: string]: number }; + let newLayers: { [key: string]: number } = {}; let lastActiveHighlight: HighlightInfo | undefined; let expandedNewLayer: number | undefined; From a45d706d888d5efc36cc01f311e56e4dedbae907 Mon Sep 17 00:00:00 2001 From: Preet Date: Fri, 4 Nov 2022 01:30:46 -0400 Subject: [PATCH 239/708] hide center alignments on encroachment --- .../appsmith/autoLayout/FlexBoxComponent.tsx | 182 +++++++++++------- 1 file changed, 110 insertions(+), 72 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx index 59e5e72d56ff..c237ee83f3be 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx @@ -14,6 +14,7 @@ import { getAppMode } from "selectors/entitiesSelector"; import { getIsMobile } from "selectors/mainCanvasSelectors"; import AutoLayoutLayer from "./AutoLayoutLayer"; import { isCurrentCanvasDragging } from "selectors/autoLayoutSelectors"; +import { getWidgets } from "sagas/selectors"; export interface FlexBoxProps { direction?: LayoutDirection; @@ -35,6 +36,26 @@ export interface FlexLayer { hasFillChild?: boolean; } +interface DropPositionProps { + isVertical: boolean; + alignment: FlexLayerAlignment; + layerIndex: number; + childIndex: number; + widgetId: string; + isNewLayer: boolean; +} + +interface NewLayerProps { + alignment: FlexLayerAlignment; + childCount: number; + layerIndex: number; + isDragging: boolean; + isNewLayer: boolean; + isVertical: boolean; + map: { [key: string]: any }; + widgetId: string; +} + export const FlexContainer = styled.div<{ useAutoLayout?: boolean; direction?: LayoutDirection; @@ -93,6 +114,7 @@ export const NewLayerStyled = styled.div<{ function FlexBoxComponent(props: FlexBoxProps) { // TODO: set isMobile as a prop at the top level const isMobile = useSelector(getIsMobile); + const allWidgets = useSelector(getWidgets); const direction: LayoutDirection = props.direction || LayoutDirection.Horizontal; const appMode = useSelector(getAppMode); @@ -109,14 +131,15 @@ function FlexBoxComponent(props: FlexBoxProps) { if (!props.children) return null; if (!props.useAutoLayout) return props.children; if (direction === LayoutDirection.Horizontal) { - return addDropPositions( - props.children as any, - 0, - 0, - FlexLayerAlignment.Start, - true, - true, - ); + return addDropPositions({ + arr: props.children as any, + childCount: 0, + layerIndex: 0, + alignment: FlexLayerAlignment.Start, + isVertical: true, + isNewLayer: true, + addInitialHighlight: true, + }); } /** @@ -145,14 +168,7 @@ function FlexBoxComponent(props: FlexBoxProps) { }); } - function DropPositionComponent(props: { - isVertical: boolean; - alignment: FlexLayerAlignment; - layerIndex: number; - childIndex: number; - widgetId: string; - isNewLayer: boolean; - }) { + function DropPositionComponent(props: DropPositionProps) { return ( { - const res = [ - , - ]; + const addDropPositions = (data: { + arr: any[]; + childCount: number; + layerIndex: number; + alignment: FlexLayerAlignment; + isVertical: boolean; + isNewLayer: boolean; + addInitialHighlight: boolean; + }): any[] => { + const { + addInitialHighlight, + alignment, + arr, + childCount, + isNewLayer, + isVertical, + layerIndex, + } = data; + const res = addInitialHighlight + ? [ + , + ] + : []; let count = 0; if (arr) { for (const item of arr) { @@ -347,47 +366,70 @@ function FlexBoxComponent(props: FlexBoxProps) { let start = [], center = [], end = []; + let startColumns = 0, + centerColumns = 0, + endColumns = 0; for (const child of children) { count += 1; const widget = map[child.id]; if (hasFillChild) { start.push(widget); + startColumns += allWidgets[child.id].rightColumn; continue; } - if (child.align === "end") end.push(widget); - else if (child.align === "center") center.push(widget); - else start.push(widget); + if (child.align === "end") { + end.push(widget); + endColumns += allWidgets[child.id] + ? allWidgets[child.id]?.rightColumn + : 0; + } else if (child.align === "center") { + center.push(widget); + centerColumns += allWidgets[child.id] + ? allWidgets[child.id]?.rightColumn + : 0; + } else { + start.push(widget); + startColumns += allWidgets[child.id] + ? allWidgets[child.id]?.rightColumn + : 0; + } } /** * Add drop positions */ const startLength = start.length, centerLength = center.length; - start = addDropPositions( - start, + + start = addDropPositions({ + arr: start, childCount, - index, - FlexLayerAlignment.Start, - true, + layerIndex: index, + alignment: FlexLayerAlignment.Start, + isVertical: true, isNewLayer, - ); - center = addDropPositions( - center, - childCount + startLength, - index, - FlexLayerAlignment.Center, - true, + addInitialHighlight: !( + start.length === 0 && centerColumns + endColumns > 60 + ), + }); + center = addDropPositions({ + arr: center, + childCount: childCount + startLength, + layerIndex: index, + alignment: FlexLayerAlignment.Center, + isVertical: true, isNewLayer, - ); - end = addDropPositions( - end, - childCount + startLength + centerLength, - index, - FlexLayerAlignment.End, - true, + addInitialHighlight: !(startColumns > 25 || endColumns > 25), + }); + end = addDropPositions({ + arr: end, + childCount: childCount + startLength + centerLength, + layerIndex: index, + alignment: FlexLayerAlignment.End, + isVertical: true, isNewLayer, - ); + addInitialHighlight: !(centerColumns > 25 || startColumns > 60), + }); return { element: ( @@ -410,10 +452,6 @@ function FlexBoxComponent(props: FlexBoxProps) { }; } - // function addInPosition(arr: any[], index: number, item: any): any[] { - // return [...arr.slice(0, index), item, ...arr.slice(index)]; - // } - return ( Date: Fri, 4 Nov 2022 02:33:57 -0400 Subject: [PATCH 240/708] merge fixes --- app/client/src/ce/constants/ReduxActionConstants.tsx | 1 - app/client/src/widgets/BaseWidget.tsx | 1 - 2 files changed, 2 deletions(-) diff --git a/app/client/src/ce/constants/ReduxActionConstants.tsx b/app/client/src/ce/constants/ReduxActionConstants.tsx index 5159f5db1949..bf6adf78904a 100644 --- a/app/client/src/ce/constants/ReduxActionConstants.tsx +++ b/app/client/src/ce/constants/ReduxActionConstants.tsx @@ -1,4 +1,3 @@ -import { ERROR_CODES } from "@appsmith/constants/ApiConstants"; import { ApplicationVersion } from "actions/applicationActions"; import { ApplicationPagePayload, diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index 0cee5c78e29b..d196daa90045 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -33,7 +33,6 @@ import { DataTreeEvaluationProps, EvaluationError, EVAL_ERROR_PATH, - EvaluationError, WidgetDynamicPathListProps, } from "utils/DynamicBindingUtils"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; From 1a984abe34c58d2015dcbd5323b94d3d66bbc841 Mon Sep 17 00:00:00 2001 From: Preet Date: Fri, 4 Nov 2022 03:01:54 -0400 Subject: [PATCH 241/708] start alignment for new layer fill widgets --- .../appsmith/autoLayout/FlexBoxComponent.tsx | 19 +++++++++---------- .../CanvasSagas/AutoLayoutDraggingSagas.ts | 13 ++++++++++++- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx index c237ee83f3be..0d768000cf13 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx @@ -289,6 +289,11 @@ function FlexBoxComponent(props: FlexBoxProps) { return res; }; + function getColumns(id: string): number { + if (!allWidgets[id]) return 0; + return allWidgets[id].rightColumn; + } + function processLayers(map: { [key: string]: any }) { const layers = []; let childCount = 0; @@ -375,24 +380,18 @@ function FlexBoxComponent(props: FlexBoxProps) { const widget = map[child.id]; if (hasFillChild) { start.push(widget); - startColumns += allWidgets[child.id].rightColumn; + startColumns += getColumns(child.id); continue; } if (child.align === "end") { end.push(widget); - endColumns += allWidgets[child.id] - ? allWidgets[child.id]?.rightColumn - : 0; + endColumns += getColumns(child.id); } else if (child.align === "center") { center.push(widget); - centerColumns += allWidgets[child.id] - ? allWidgets[child.id]?.rightColumn - : 0; + centerColumns += getColumns(child.id); } else { start.push(widget); - startColumns += allWidgets[child.id] - ? allWidgets[child.id]?.rightColumn - : 0; + startColumns += getColumns(child.id); } } /** diff --git a/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts b/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts index bb36defa9ce7..377142d990b9 100644 --- a/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts +++ b/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts @@ -136,7 +136,6 @@ function* reorderAutolayoutChildren(params: { layerIndex?: number; }) { const { - alignment, allWidgets, direction, index, @@ -145,6 +144,7 @@ function* reorderAutolayoutChildren(params: { movedWidgets, parentId, } = params; + let alignment = params.alignment; const widgets = Object.assign({}, allWidgets); if (!movedWidgets) return widgets; const selectedWidgets = [...movedWidgets]; @@ -160,6 +160,17 @@ function* reorderAutolayoutChildren(params: { const canvas = widgets[parentId]; if (!canvas) return widgets; const flexLayers = canvas.flexLayers || []; + + // TODO: temporary hack. Remove this once we have a better way to handle this. + if (isNewLayer) { + const hasFillChild = selectedWidgets.some( + (id: string) => + widgets[id] && + widgets[id].responsiveBehavior === ResponsiveBehavior.Fill, + ); + if (hasFillChild) alignment = FlexLayerAlignment.Start; + } + // Remove moved widgets from the flex layers. const filteredLayers = removeWidgetsFromCurrentLayers( widgets, From ef6a08933f33c63e17f7169fe9c26d7a348e8cad Mon Sep 17 00:00:00 2001 From: Preet Date: Fri, 4 Nov 2022 10:56:41 -0400 Subject: [PATCH 242/708] debounce highlight calculation --- .../src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index 59da6d6843f0..f86b11b725fb 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -607,7 +607,9 @@ export const useCanvasDragging = ( isCurrentDraggedCanvas && currentDirection.current !== ReflowDirection.UNSET ) - highlightDropPosition(e, currentDirection.current); + debounce(() => { + highlightDropPosition(e, currentDirection.current); + }, 100)(); renderBlocks(); } scrollObj.lastMouseMoveEvent = { From 40e2620e6e3085925643cc9205a1da3dcabb0c7d Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 7 Nov 2022 12:06:51 -0500 Subject: [PATCH 243/708] fix fill widget positioning and add rowIndex to HighlightInfo --- .../appsmith/autoLayout/AutoLayoutLayer.tsx | 2 - .../appsmith/autoLayout/FlexBoxComponent.tsx | 16 +++---- .../hooks/useAutoLayoutHighlights.ts | 8 +++- .../CanvasSagas/AutoLayoutDraggingSagas.ts | 48 ++++++------------- 4 files changed, 29 insertions(+), 45 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx index 8ea680a3c635..3a086307c657 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx @@ -97,14 +97,12 @@ function AutoLayoutLayer(props: AutoLayoutLayerProps) { {props.start} {props.center} diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx index 0d768000cf13..f4658663a389 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx @@ -43,6 +43,7 @@ interface DropPositionProps { childIndex: number; widgetId: string; isNewLayer: boolean; + rowIndex: number; } interface NewLayerProps { @@ -175,7 +176,7 @@ function FlexBoxComponent(props: FlexBoxProps) { props.alignment } layer-index-${props.layerIndex} child-index-${props.childIndex} ${ props.isVertical ? "isVertical" : "isHorizontal" - } ${props.isNewLayer ? "isNewLayer" : ""}`} + } ${props.isNewLayer ? "isNewLayer" : ""} row-index-${props.rowIndex}`} isDragging={isDragging} isVertical={props.isVertical} /> @@ -216,6 +217,7 @@ function FlexBoxComponent(props: FlexBoxProps) { isVertical={isVertical} key={getDropPositionKey(0, alignment, layerIndex, false)} layerIndex={layerIndex} + rowIndex={0} widgetId={widgetId} /> {verticalHighlights} @@ -260,6 +262,7 @@ function FlexBoxComponent(props: FlexBoxProps) { isVertical={isVertical} key={getDropPositionKey(0, alignment, layerIndex, true)} layerIndex={layerIndex} + rowIndex={0} widgetId={props.widgetId} />, ] @@ -281,6 +284,7 @@ function FlexBoxComponent(props: FlexBoxProps) { isVertical={isVertical} key={getDropPositionKey(0, alignment, layerIndex, true)} layerIndex={layerIndex} + rowIndex={count} widgetId={props.widgetId} />, ); @@ -365,7 +369,7 @@ function FlexBoxComponent(props: FlexBoxProps) { allowEmptyLayer = false, isNewLayer = false, ) { - const { children, hasFillChild } = layer; + const { children } = layer; let count = 0; let start = [], @@ -378,11 +382,7 @@ function FlexBoxComponent(props: FlexBoxProps) { for (const child of children) { count += 1; const widget = map[child.id]; - if (hasFillChild) { - start.push(widget); - startColumns += getColumns(child.id); - continue; - } + if (child.align === "end") { end.push(widget); endColumns += getColumns(child.id); @@ -427,7 +427,7 @@ function FlexBoxComponent(props: FlexBoxProps) { alignment: FlexLayerAlignment.End, isVertical: true, isNewLayer, - addInitialHighlight: !(centerColumns > 25 || startColumns > 60), + addInitialHighlight: !(centerColumns + startColumns > 60), }); return { diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index 6f26f3750d2b..8ce3e461921a 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -12,7 +12,7 @@ export interface HighlightInfo { isNewLayer: boolean; // determines if a new layer / child has been added directly to the container. index: number; // index of the child in props.children. layerIndex?: number; // index of layer in props.flexLayers. - rowIndex?: number; // index of highlight within a horizontal layer. + rowIndex: number; // index of highlight within a horizontal layer. alignment: FlexLayerAlignment; // alignment of the child in the layer. posX: number; // x position of the highlight. posY: number; // y position of the highlight. @@ -114,6 +114,9 @@ export const useAutoLayoutHighlights = ({ acc.layerIndex = parseInt(curr.split("layer-index-")[1]); else if (curr.indexOf("child-index") > -1) acc.index = parseInt(curr.split("child-index-")[1]); + else if (curr.indexOf("row-index") > -1) + acc.rowIndex = parseInt(curr.split("row-index-")[1]); + else if (curr.indexOf("new-layer") > -1) acc.isNewLayer = true; else if (curr.indexOf("isNewLayer") > -1) acc.isNewLayer = true; else if (curr.indexOf("isVertical") > -1) acc.isVertical = true; @@ -123,6 +126,7 @@ export const useAutoLayoutHighlights = ({ isNewLayer: false, index: 0, layerIndex: 0, + rowIndex: 0, alignment: FlexLayerAlignment.Start, posX: rect.x - containerDimensions.left, posY: rect.y - containerDimensions?.top, @@ -288,7 +292,7 @@ export const useAutoLayoutHighlights = ({ calculateDistance(b, pos, moveDirection) ); }); - + // console.log("#### arr", arr, base); return { highlights: [...arr.slice(1)], selectedHighlight: arr[0], diff --git a/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts b/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts index 377142d990b9..b9104ca2c117 100644 --- a/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts +++ b/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts @@ -34,7 +34,7 @@ function* addWidgetAndReorderSaga( ) { const start = performance.now(); const { direction, dropPayload, newWidget, parentId } = actionPayload.payload; - const { alignment, index, isNewLayer, layerIndex } = dropPayload; + const { alignment, index, isNewLayer, layerIndex, rowIndex } = dropPayload; try { const updatedWidgetsOnAddition: CanvasWidgetsReduxState = yield call( getUpdateDslAfterCreatingChild, @@ -55,6 +55,7 @@ function* addWidgetAndReorderSaga( alignment, direction, layerIndex, + rowIndex, }, ); let updatedWidgetsAfterResizing = updatedWidgetsOnMove; @@ -93,7 +94,7 @@ function* autoLayoutReorderSaga( parentId, } = actionPayload.payload; - const { alignment, index, isNewLayer, layerIndex } = dropPayload; + const { alignment, index, isNewLayer, layerIndex, rowIndex } = dropPayload; try { const allWidgets: CanvasWidgetsReduxState = yield select(getWidgets); @@ -109,6 +110,7 @@ function* autoLayoutReorderSaga( alignment, direction, layerIndex, + rowIndex, }, ); let updatedWidgetsAfterResizing = updatedWidgets; @@ -134,8 +136,10 @@ function* reorderAutolayoutChildren(params: { alignment: FlexLayerAlignment; direction: LayoutDirection; layerIndex?: number; + rowIndex: number; }) { const { + alignment, allWidgets, direction, index, @@ -143,8 +147,8 @@ function* reorderAutolayoutChildren(params: { layerIndex, movedWidgets, parentId, + rowIndex, } = params; - let alignment = params.alignment; const widgets = Object.assign({}, allWidgets); if (!movedWidgets) return widgets; const selectedWidgets = [...movedWidgets]; @@ -161,16 +165,6 @@ function* reorderAutolayoutChildren(params: { if (!canvas) return widgets; const flexLayers = canvas.flexLayers || []; - // TODO: temporary hack. Remove this once we have a better way to handle this. - if (isNewLayer) { - const hasFillChild = selectedWidgets.some( - (id: string) => - widgets[id] && - widgets[id].responsiveBehavior === ResponsiveBehavior.Fill, - ); - if (hasFillChild) alignment = FlexLayerAlignment.Start; - } - // Remove moved widgets from the flex layers. const filteredLayers = removeWidgetsFromCurrentLayers( widgets, @@ -201,6 +195,7 @@ function* reorderAutolayoutChildren(params: { filteredLayers, index, layerIndex, + rowIndex, ); } @@ -210,6 +205,7 @@ function* reorderAutolayoutChildren(params: { const newItems = items.filter((item) => movedWidgets.indexOf(item) === -1); // calculate valid position for drop const pos = index > newItems.length ? newItems.length : index; + updatedWidgets[parentId] = { ...updatedWidgets[parentId], children: [ @@ -308,35 +304,21 @@ function updateExistingLayer( layers: FlexLayer[], index: number, layerIndex = 0, + rowIndex: number, ): CanvasWidgetsReduxState { const widgets: CanvasWidgetsReduxState = Object.assign({}, allWidgets); const canvas = widgets[parentId]; if (!canvas || !newLayer) return widgets; - let selectedLayer: FlexLayer = layers[layerIndex]; - // Calculate the number of children in the container before the selected layer. - let childCount = 0; - layers.forEach((layer: FlexLayer, index: number) => { - if (index >= layerIndex) return; - const layerChildren = (layer.children || []).filter( - (child: LayerChild) => - newLayer.children.findIndex( - (each: LayerChild) => each.id === child.id, - ) === -1, - ); - childCount += layerChildren.length; - }); - const pos = index - childCount; - // merge the selected layer with the new layer. - selectedLayer = { - ...selectedLayer, + const selectedLayer = { + ...layers[layerIndex], children: [ - ...selectedLayer?.children?.slice(0, pos), + ...layers[layerIndex]?.children?.slice(0, rowIndex), ...newLayer?.children, - ...selectedLayer?.children?.slice(pos), + ...layers[layerIndex]?.children?.slice(rowIndex), ], - hasFillChild: newLayer.hasFillChild || selectedLayer?.hasFillChild, + hasFillChild: newLayer.hasFillChild || layers[layerIndex]?.hasFillChild, }; const updatedCanvas = { From 4f5dcc88cf59ef2896ad729cf8f5f862bcec28de Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 7 Nov 2022 14:07:36 -0500 Subject: [PATCH 244/708] increase debounce duration --- .../common/CanvasArenas/hooks/useAutoLayoutHighlights.ts | 6 +++--- .../pages/common/CanvasArenas/hooks/useCanvasDragging.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index 8ce3e461921a..58c8b1f0244b 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -304,14 +304,14 @@ export const useAutoLayoutHighlights = ({ const keys: string[] = Object.keys(newLayers); if (!positions || !positions.length) return -1; const index: number = positions.findIndex((each: number, index: number) => { - const lower: number = - expandedNewLayer !== undefined ? each : Math.max(each - 5, 0); + // const lower: number = + // expandedNewLayer !== undefined ? each : Math.max(each - 5, 0); const upper: number = expandedNewLayer !== undefined && expandedNewLayer === parseInt(keys[index]) ? each + 35 : each + 14; - return y >= lower && (y <= upper || index === positions.length - 1); + return y >= each && (y <= upper || index === positions.length - 1); }); if (index === -1) return -1; return parseInt(keys[index]); diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index f86b11b725fb..b80840b693be 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -609,7 +609,7 @@ export const useCanvasDragging = ( ) debounce(() => { highlightDropPosition(e, currentDirection.current); - }, 100)(); + }, 200)(); renderBlocks(); } scrollObj.lastMouseMoveEvent = { From 82d7e1738a6cec5ebea82d4f509ecb147b71cdc8 Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 7 Nov 2022 15:46:31 -0500 Subject: [PATCH 245/708] reduce debounce duration --- .../src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index b80840b693be..f86b11b725fb 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -609,7 +609,7 @@ export const useCanvasDragging = ( ) debounce(() => { highlightDropPosition(e, currentDirection.current); - }, 200)(); + }, 100)(); renderBlocks(); } scrollObj.lastMouseMoveEvent = { From 3cc3637f34336cd6ed320c20739d36da8231ddcc Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 7 Nov 2022 23:10:31 -0500 Subject: [PATCH 246/708] add responsive behaviour property --- app/client/src/widgets/ButtonGroupWidget/widget/index.tsx | 1 + app/client/src/widgets/CameraWidget/widget/index.tsx | 3 +++ .../widget/propertyConfig/contentConfig.ts | 4 +++- .../CodeScannerWidget/widget/propertyConfig/contentConfig.ts | 3 +++ app/client/src/widgets/DocumentViewerWidget/widget/index.tsx | 3 +++ app/client/src/widgets/IframeWidget/widget/index.tsx | 3 +++ app/client/src/widgets/ImageWidget/widget/index.tsx | 3 +++ app/client/src/widgets/MapChartWidget/widget/index.tsx | 3 +++ app/client/src/widgets/MenuButtonWidget/widget/index.tsx | 3 +++ app/client/src/widgets/ModalWidget/widget/index.tsx | 4 ++-- .../NumberSliderWidget/widget/propertyConfig/contentConfig.ts | 4 +++- app/client/src/widgets/ProgressWidget/widget/index.tsx | 3 +++ app/client/src/widgets/RadioGroupWidget/widget/index.tsx | 4 +++- .../RangeSliderWidget/widget/propertyConfig/contentConfig.ts | 4 +++- app/client/src/widgets/StatboxWidget/widget/index.tsx | 3 +++ app/client/src/widgets/SwitchGroupWidget/widget/index.tsx | 4 +++- app/client/src/widgets/SwitchWidget/widget/index.tsx | 4 +++- app/client/src/widgets/VideoWidget/widget/index.tsx | 4 +++- 18 files changed, 51 insertions(+), 9 deletions(-) diff --git a/app/client/src/widgets/ButtonGroupWidget/widget/index.tsx b/app/client/src/widgets/ButtonGroupWidget/widget/index.tsx index 23f3c0e605ab..8575886e5451 100644 --- a/app/client/src/widgets/ButtonGroupWidget/widget/index.tsx +++ b/app/client/src/widgets/ButtonGroupWidget/widget/index.tsx @@ -448,6 +448,7 @@ class ButtonGroupWidget extends BaseWidget< isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, + generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), ], }, ]; diff --git a/app/client/src/widgets/CameraWidget/widget/index.tsx b/app/client/src/widgets/CameraWidget/widget/index.tsx index 0895bbae48f2..dd3f4a1eee91 100644 --- a/app/client/src/widgets/CameraWidget/widget/index.tsx +++ b/app/client/src/widgets/CameraWidget/widget/index.tsx @@ -14,6 +14,8 @@ import { CameraModeTypes, MediaCaptureStatusTypes, } from "../constants"; +import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; +import { ResponsiveBehavior } from "components/constants"; class CameraWidget extends BaseWidget { static getPropertyPaneContentConfig() { @@ -77,6 +79,7 @@ class CameraWidget extends BaseWidget { isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, + generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), ], }, { diff --git a/app/client/src/widgets/CategorySliderWidget/widget/propertyConfig/contentConfig.ts b/app/client/src/widgets/CategorySliderWidget/widget/propertyConfig/contentConfig.ts index c304d84a0311..4ce6ef2fa2cf 100644 --- a/app/client/src/widgets/CategorySliderWidget/widget/propertyConfig/contentConfig.ts +++ b/app/client/src/widgets/CategorySliderWidget/widget/propertyConfig/contentConfig.ts @@ -1,4 +1,4 @@ -import { LabelPosition } from "components/constants"; +import { LabelPosition, ResponsiveBehavior } from "components/constants"; import { AutocompleteDataType } from "utils/autocomplete/TernServer"; import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; import { Alignment } from "@blueprintjs/core"; @@ -8,6 +8,7 @@ import { optionsCustomValidation, } from "../../validations"; import { CategorySliderWidgetProps } from ".."; +import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; export default [ { @@ -175,6 +176,7 @@ export default [ isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, + generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), ], }, { diff --git a/app/client/src/widgets/CodeScannerWidget/widget/propertyConfig/contentConfig.ts b/app/client/src/widgets/CodeScannerWidget/widget/propertyConfig/contentConfig.ts index d1df555e742e..d32f81d9d923 100644 --- a/app/client/src/widgets/CodeScannerWidget/widget/propertyConfig/contentConfig.ts +++ b/app/client/src/widgets/CodeScannerWidget/widget/propertyConfig/contentConfig.ts @@ -4,6 +4,8 @@ import { CodeScannerWidgetProps, ScannerLayout, } from "widgets/CodeScannerWidget/constants"; +import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; +import { ResponsiveBehavior } from "components/constants"; export default [ { @@ -93,6 +95,7 @@ export default [ props.scannerLayout === ScannerLayout.ALWAYS_ON, dependencies: ["scannerLayout"], }, + generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), ], }, { diff --git a/app/client/src/widgets/DocumentViewerWidget/widget/index.tsx b/app/client/src/widgets/DocumentViewerWidget/widget/index.tsx index a1a7b85d6576..122c5e359333 100644 --- a/app/client/src/widgets/DocumentViewerWidget/widget/index.tsx +++ b/app/client/src/widgets/DocumentViewerWidget/widget/index.tsx @@ -6,6 +6,8 @@ import { ValidationResponse, } from "constants/WidgetValidation"; import { AutocompleteDataType } from "utils/autocomplete/TernServer"; +import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; +import { ResponsiveBehavior } from "components/constants"; export function documentUrlValidation(value: unknown): ValidationResponse { // applied validations if value exist @@ -116,6 +118,7 @@ class DocumentViewerWidget extends BaseWidget< isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, + generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), ], }, ]; diff --git a/app/client/src/widgets/IframeWidget/widget/index.tsx b/app/client/src/widgets/IframeWidget/widget/index.tsx index 1f451e8c13e9..09e7ac9fc045 100644 --- a/app/client/src/widgets/IframeWidget/widget/index.tsx +++ b/app/client/src/widgets/IframeWidget/widget/index.tsx @@ -4,6 +4,8 @@ import { ValidationTypes } from "constants/WidgetValidation"; import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; import IframeComponent from "../component"; import { IframeWidgetProps } from "../constants"; +import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; +import { ResponsiveBehavior } from "components/constants"; class IframeWidget extends BaseWidget { static getPropertyPaneContentConfig() { @@ -64,6 +66,7 @@ class IframeWidget extends BaseWidget { isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, + generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), ], }, { diff --git a/app/client/src/widgets/ImageWidget/widget/index.tsx b/app/client/src/widgets/ImageWidget/widget/index.tsx index d2a322de8f8e..bcc0582a5edc 100644 --- a/app/client/src/widgets/ImageWidget/widget/index.tsx +++ b/app/client/src/widgets/ImageWidget/widget/index.tsx @@ -6,6 +6,8 @@ import ImageComponent from "../component"; import { ValidationTypes } from "constants/WidgetValidation"; import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; +import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; +import { ResponsiveBehavior } from "components/constants"; class ImageWidget extends BaseWidget { constructor(props: ImageWidgetProps) { @@ -150,6 +152,7 @@ class ImageWidget extends BaseWidget { isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, + generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), ], }, { diff --git a/app/client/src/widgets/MapChartWidget/widget/index.tsx b/app/client/src/widgets/MapChartWidget/widget/index.tsx index 169b5eb77414..59c3e58d18e0 100644 --- a/app/client/src/widgets/MapChartWidget/widget/index.tsx +++ b/app/client/src/widgets/MapChartWidget/widget/index.tsx @@ -22,6 +22,8 @@ import { } from "../constants"; import { MapType } from "../component"; import { AutocompleteDataType } from "utils/autocomplete/TernServer"; +import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; +import { ResponsiveBehavior } from "components/constants"; const MapChartComponent = lazy(() => retryPromise(() => @@ -202,6 +204,7 @@ class MapChartWidget extends BaseWidget { isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, + generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), ], }, { diff --git a/app/client/src/widgets/MenuButtonWidget/widget/index.tsx b/app/client/src/widgets/MenuButtonWidget/widget/index.tsx index a40230763a27..916c25a3c058 100644 --- a/app/client/src/widgets/MenuButtonWidget/widget/index.tsx +++ b/app/client/src/widgets/MenuButtonWidget/widget/index.tsx @@ -11,9 +11,11 @@ import { ButtonVariantTypes, ButtonPlacementTypes, ButtonPlacement, + ResponsiveBehavior, } from "components/constants"; import { IconName } from "@blueprintjs/icons"; import { MinimumPopupRows } from "widgets/constants"; +import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; export interface MenuButtonWidgetProps extends WidgetProps { label?: string; isDisabled?: boolean; @@ -250,6 +252,7 @@ class MenuButtonWidget extends BaseWidget { isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, + generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), ], }, ]; diff --git a/app/client/src/widgets/ModalWidget/widget/index.tsx b/app/client/src/widgets/ModalWidget/widget/index.tsx index badc982a78ee..2107428fab5e 100644 --- a/app/client/src/widgets/ModalWidget/widget/index.tsx +++ b/app/client/src/widgets/ModalWidget/widget/index.tsx @@ -17,7 +17,7 @@ import { deselectModalWidgetAction } from "actions/widgetSelectionActions"; import { ValidationTypes } from "constants/WidgetValidation"; import { CanvasWidgetsStructureReduxState } from "reducers/entityReducers/canvasWidgetsStructureReducer"; import { Alignment, Positioning, Spacing } from "components/constants"; -import { generatePositioningConfig } from "utils/layoutPropertiesUtils"; +// import { generatePositioningConfig } from "utils/layoutPropertiesUtils"; const minSize = 100; @@ -54,7 +54,7 @@ export class ModalWidget extends BaseWidget { isBindProperty: false, isTriggerProperty: false, }, - { ...generatePositioningConfig(Positioning.Fixed) }, + // { ...generatePositioningConfig(Positioning.Fixed) }, ], }, { diff --git a/app/client/src/widgets/NumberSliderWidget/widget/propertyConfig/contentConfig.ts b/app/client/src/widgets/NumberSliderWidget/widget/propertyConfig/contentConfig.ts index 34959094ff88..6f8a27f1d3b7 100644 --- a/app/client/src/widgets/NumberSliderWidget/widget/propertyConfig/contentConfig.ts +++ b/app/client/src/widgets/NumberSliderWidget/widget/propertyConfig/contentConfig.ts @@ -1,7 +1,8 @@ import { Alignment } from "@blueprintjs/core"; -import { LabelPosition } from "components/constants"; +import { LabelPosition, ResponsiveBehavior } from "components/constants"; import { ValidationTypes } from "constants/WidgetValidation"; import { AutocompleteDataType } from "utils/autocomplete/TernServer"; +import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; import { NumberSliderWidgetProps } from ".."; import { defaultValueValidation, @@ -261,6 +262,7 @@ export default [ isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, + generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), ], }, { diff --git a/app/client/src/widgets/ProgressWidget/widget/index.tsx b/app/client/src/widgets/ProgressWidget/widget/index.tsx index 1fce15a8415c..9aca8fe680d2 100644 --- a/app/client/src/widgets/ProgressWidget/widget/index.tsx +++ b/app/client/src/widgets/ProgressWidget/widget/index.tsx @@ -7,6 +7,8 @@ import ProgressComponent from "../component"; import { ProgressType, ProgressVariant } from "../constants"; import { ValidationTypes } from "constants/WidgetValidation"; import { Colors } from "constants/Colors"; +import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; +import { ResponsiveBehavior } from "components/constants"; class ProgressWidget extends BaseWidget { static getPropertyPaneContentConfig() { @@ -122,6 +124,7 @@ class ProgressWidget extends BaseWidget { hidden: (props: ProgressWidgetProps) => props.isIndeterminate, dependencies: ["isIndeterminate"], }, + generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), ], }, ]; diff --git a/app/client/src/widgets/RadioGroupWidget/widget/index.tsx b/app/client/src/widgets/RadioGroupWidget/widget/index.tsx index 631c944e2c2c..444604964ff6 100644 --- a/app/client/src/widgets/RadioGroupWidget/widget/index.tsx +++ b/app/client/src/widgets/RadioGroupWidget/widget/index.tsx @@ -13,8 +13,9 @@ import { } from "constants/WidgetValidation"; import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; import { RadioOption } from "../constants"; -import { LabelPosition } from "components/constants"; +import { LabelPosition, ResponsiveBehavior } from "components/constants"; import RadioGroupComponent from "../component"; +import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; /** * Validation rules: @@ -333,6 +334,7 @@ class RadioGroupWidget extends BaseWidget { isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, + generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), ], }, { diff --git a/app/client/src/widgets/RangeSliderWidget/widget/propertyConfig/contentConfig.ts b/app/client/src/widgets/RangeSliderWidget/widget/propertyConfig/contentConfig.ts index e46f69c6b56e..8a2ae3b80d58 100644 --- a/app/client/src/widgets/RangeSliderWidget/widget/propertyConfig/contentConfig.ts +++ b/app/client/src/widgets/RangeSliderWidget/widget/propertyConfig/contentConfig.ts @@ -1,6 +1,6 @@ import { AutocompleteDataType } from "utils/autocomplete/TernServer"; import { ValidationTypes } from "constants/WidgetValidation"; -import { LabelPosition } from "components/constants"; +import { LabelPosition, ResponsiveBehavior } from "components/constants"; import { Alignment } from "@blueprintjs/core"; import { maxValueValidation, @@ -11,6 +11,7 @@ import { endValueValidation, } from "../../validations"; import { RangeSliderWidgetProps } from ".."; +import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; export default [ { @@ -303,6 +304,7 @@ export default [ isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, + generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), ], }, { diff --git a/app/client/src/widgets/StatboxWidget/widget/index.tsx b/app/client/src/widgets/StatboxWidget/widget/index.tsx index 337ecdc261cc..50584d03f23c 100644 --- a/app/client/src/widgets/StatboxWidget/widget/index.tsx +++ b/app/client/src/widgets/StatboxWidget/widget/index.tsx @@ -2,6 +2,8 @@ import { WidgetType } from "constants/WidgetConstants"; import { ContainerWidget } from "widgets/ContainerWidget/widget"; import { ValidationTypes } from "constants/WidgetValidation"; +import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; +import { ResponsiveBehavior } from "components/constants"; class StatboxWidget extends ContainerWidget { static getPropertyPaneContentConfig() { @@ -38,6 +40,7 @@ class StatboxWidget extends ContainerWidget { isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, + generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), ], }, ]; diff --git a/app/client/src/widgets/SwitchGroupWidget/widget/index.tsx b/app/client/src/widgets/SwitchGroupWidget/widget/index.tsx index 3ca71c5092d9..c0a88b041d7b 100644 --- a/app/client/src/widgets/SwitchGroupWidget/widget/index.tsx +++ b/app/client/src/widgets/SwitchGroupWidget/widget/index.tsx @@ -9,9 +9,10 @@ import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; import SwitchGroupComponent, { OptionProps } from "../component"; -import { LabelPosition } from "components/constants"; +import { LabelPosition, ResponsiveBehavior } from "components/constants"; import { TextSize } from "constants/WidgetConstants"; import { GRID_DENSITY_MIGRATION_V1 } from "widgets/constants"; +import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; class SwitchGroupWidget extends BaseWidget< SwitchGroupWidgetProps, @@ -217,6 +218,7 @@ class SwitchGroupWidget extends BaseWidget< isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, + generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), ], }, { diff --git a/app/client/src/widgets/SwitchWidget/widget/index.tsx b/app/client/src/widgets/SwitchWidget/widget/index.tsx index a61f7e85cb28..511ca29e16ea 100644 --- a/app/client/src/widgets/SwitchWidget/widget/index.tsx +++ b/app/client/src/widgets/SwitchWidget/widget/index.tsx @@ -7,8 +7,9 @@ import { ValidationTypes } from "constants/WidgetValidation"; import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; -import { LabelPosition } from "components/constants"; +import { LabelPosition, ResponsiveBehavior } from "components/constants"; import { AlignWidgetTypes } from "widgets/constants"; +import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; class SwitchWidget extends BaseWidget { static getPropertyPaneContentConfig() { return [ @@ -105,6 +106,7 @@ class SwitchWidget extends BaseWidget { isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, + generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), ], }, { diff --git a/app/client/src/widgets/VideoWidget/widget/index.tsx b/app/client/src/widgets/VideoWidget/widget/index.tsx index b56998894f20..8c2b27b4d7bd 100644 --- a/app/client/src/widgets/VideoWidget/widget/index.tsx +++ b/app/client/src/widgets/VideoWidget/widget/index.tsx @@ -7,7 +7,8 @@ import Skeleton from "components/utils/Skeleton"; import { retryPromise } from "utils/AppsmithUtils"; import ReactPlayer from "react-player"; import { AutocompleteDataType } from "utils/autocomplete/TernServer"; -import { ButtonBorderRadius } from "components/constants"; +import { ButtonBorderRadius, ResponsiveBehavior } from "components/constants"; +import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; const VideoComponent = lazy(() => retryPromise(() => import("../component"))); @@ -82,6 +83,7 @@ class VideoWidget extends BaseWidget { isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, + generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), ], }, { From db46b8f369b1868f370e6553a7616138a37a1fe4 Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 7 Nov 2022 23:17:17 -0500 Subject: [PATCH 247/708] minor refactor --- .../hooks/useAutoLayoutHighlights.ts | 36 +++++++------------ .../widgets/ContainerWidget/widget/index.tsx | 2 -- 2 files changed, 13 insertions(+), 25 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index 58c8b1f0244b..eb36d02e96b2 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -232,25 +232,22 @@ export const useAutoLayoutHighlights = ({ e: any, moveDirection: ReflowDirection, // acceleration: number, - ): HighlightSelectionPayload | undefined => { + ): HighlightInfo | undefined => { if (!highlights) return; - const payload: HighlightSelectionPayload = getHighlightPayload( - e, - moveDirection, - ); + const highlight: HighlightInfo = getHighlightPayload(e, moveDirection); - if (!payload || !payload.selectedHighlight) return; + if (!highlight) return; - updateSelection(payload.selectedHighlight); + updateSelection(highlight); - return payload; + return highlight; }; const getHighlightPayload = ( e: any, moveDirection?: ReflowDirection, val?: XYCord, - ): HighlightSelectionPayload => { + ): HighlightInfo => { let base: HighlightInfo[] = []; if (!highlights || !highlights.length) highlights = getDropPositions(); base = highlights; @@ -293,10 +290,7 @@ export const useAutoLayoutHighlights = ({ ); }); // console.log("#### arr", arr, base); - return { - highlights: [...arr.slice(1)], - selectedHighlight: arr[0], - }; + return arr[0]; }; function isInNewLayerRange(y: number): number { @@ -304,14 +298,14 @@ export const useAutoLayoutHighlights = ({ const keys: string[] = Object.keys(newLayers); if (!positions || !positions.length) return -1; const index: number = positions.findIndex((each: number, index: number) => { - // const lower: number = - // expandedNewLayer !== undefined ? each : Math.max(each - 5, 0); + const lower: number = + expandedNewLayer !== undefined ? each : Math.max(each - 2, 0); const upper: number = expandedNewLayer !== undefined && expandedNewLayer === parseInt(keys[index]) ? each + 35 : each + 14; - return y >= each && (y <= upper || index === positions.length - 1); + return y >= lower && (y <= upper || index === positions.length - 1); }); if (index === -1) return -1; return parseInt(keys[index]); @@ -405,14 +399,10 @@ export const useAutoLayoutHighlights = ({ const getDropInfo = (val: XYCord): HighlightInfo | undefined => { if (lastActiveHighlight) return lastActiveHighlight; - const payload: HighlightSelectionPayload = getHighlightPayload( - null, - undefined, - val, - ); + const payload: HighlightInfo = getHighlightPayload(null, undefined, val); if (!payload) return; - lastActiveHighlight = payload.selectedHighlight; - return payload.selectedHighlight; + lastActiveHighlight = payload; + return payload; }; return { diff --git a/app/client/src/widgets/ContainerWidget/widget/index.tsx b/app/client/src/widgets/ContainerWidget/widget/index.tsx index dc87179f92b9..bbfdf438741d 100644 --- a/app/client/src/widgets/ContainerWidget/widget/index.tsx +++ b/app/client/src/widgets/ContainerWidget/widget/index.tsx @@ -214,8 +214,6 @@ export class ContainerWidget extends BaseWidget< }; renderAsContainerComponent(props: ContainerWidgetProps) { - // console.log(`${props.widgetName} : ${props.widgetId} =======`); - // console.log(props); return ( Date: Mon, 14 Nov 2022 11:50:36 -0500 Subject: [PATCH 248/708] temp fix for resizing --- .../components/editorComponents/ResizableComponent.tsx | 2 +- .../common/CanvasArenas/hooks/useAutoLayoutHighlights.ts | 9 --------- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/app/client/src/components/editorComponents/ResizableComponent.tsx b/app/client/src/components/editorComponents/ResizableComponent.tsx index 8f9f4ef63073..3973ad1803f3 100644 --- a/app/client/src/components/editorComponents/ResizableComponent.tsx +++ b/app/client/src/components/editorComponents/ResizableComponent.tsx @@ -183,7 +183,7 @@ export const ResizableComponent = memo(function ResizableComponent( // False, if none of the rows and cols have changed. const newRowCols: WidgetRowCols | false = computeFinalRowCols( delta, - position, + props.isFlexChild ? { x: 0, y: position.y } : position, props, ); diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index eb36d02e96b2..b59a1b696166 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -83,15 +83,6 @@ export const useAutoLayoutHighlights = ({ }; const cleanUpTempStyles = () => { - // reset display of all dragged blocks - const els = document.querySelectorAll(`.auto-layout-parent-${canvasId}`); - if (els && els.length) { - els.forEach((el) => { - (el as any).classList.remove("auto-temp-no-display"); - (el as any).style.transform = null; - }); - } - // reset state lastActiveHighlight = undefined; expandedNewLayer = undefined; From 595952740eea51803209fa88ba081188274f455c Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 14 Nov 2022 11:57:34 -0500 Subject: [PATCH 249/708] add transition duration to drop component --- .../designSystems/appsmith/autoLayout/FlexBoxComponent.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx index f4658663a389..e43138adf2f6 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx @@ -101,6 +101,7 @@ export const DropPosition = styled.div<{ display: ${({ isDragging }) => (isDragging ? "block" : "none")}; align-self: stretch; opacity: 0; + transition: 100ms ease-in-out; `; export const NewLayerStyled = styled.div<{ From b2f7e3eb495d2d9da82baee907f3c6b1069a9718 Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 14 Nov 2022 23:59:53 -0500 Subject: [PATCH 250/708] add wrapping for center and end alignments --- .../designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx index 3a086307c657..97939ca41bae 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx @@ -99,12 +99,14 @@ function AutoLayoutLayer(props: AutoLayoutLayerProps) { {props.center} {props.end} From dd152ee99578f99be0624b5921f9ddda3e78e167 Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 15 Nov 2022 12:02:32 -0500 Subject: [PATCH 251/708] add default Fill value for widgets --- app/client/src/widgets/ChartWidget/index.ts | 2 ++ app/client/src/widgets/FormWidget/index.ts | 7 ++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/app/client/src/widgets/ChartWidget/index.ts b/app/client/src/widgets/ChartWidget/index.ts index 071f524bf364..c0185c928725 100644 --- a/app/client/src/widgets/ChartWidget/index.ts +++ b/app/client/src/widgets/ChartWidget/index.ts @@ -1,3 +1,4 @@ +import { ResponsiveBehavior } from "components/constants"; import { Colors } from "constants/Colors"; import { generateReactKey } from "widgets/WidgetUtils"; import { LabelOrientation } from "./constants"; @@ -95,6 +96,7 @@ export const CONFIG = { }, }, }, + responsiveBehavior: ResponsiveBehavior.Fill, properties: { derived: Widget.getDerivedPropertiesMap(), default: Widget.getDefaultPropertiesMap(), diff --git a/app/client/src/widgets/FormWidget/index.ts b/app/client/src/widgets/FormWidget/index.ts index 70a0afa9457d..ceb96a312355 100644 --- a/app/client/src/widgets/FormWidget/index.ts +++ b/app/client/src/widgets/FormWidget/index.ts @@ -1,4 +1,8 @@ -import { ButtonVariantTypes, RecaptchaTypes } from "components/constants"; +import { + ButtonVariantTypes, + RecaptchaTypes, + ResponsiveBehavior, +} from "components/constants"; import { Colors } from "constants/Colors"; import IconSVG from "./icon.svg"; import Widget from "./widget"; @@ -89,6 +93,7 @@ export const CONFIG = { }, ], }, + responsiveBehavior: ResponsiveBehavior.Fill, }, properties: { derived: Widget.getDerivedPropertiesMap(), From 7644afeb9f20746e1eb03a172400f7dfbdf1cc2c Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 21 Nov 2022 16:08:35 -0500 Subject: [PATCH 252/708] working copy paste --- .../appsmith/autoLayout/FlexBoxComponent.tsx | 4 - app/client/src/sagas/WidgetOperationSagas.tsx | 85 ++++++++++++++++++- 2 files changed, 83 insertions(+), 6 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx index e43138adf2f6..0ff82741a533 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx @@ -101,16 +101,12 @@ export const DropPosition = styled.div<{ display: ${({ isDragging }) => (isDragging ? "block" : "none")}; align-self: stretch; opacity: 0; - transition: 100ms ease-in-out; `; export const NewLayerStyled = styled.div<{ isDragging: boolean; }>` width: 100%; - > div { - transition: 500ms ease-in-out; - } `; function FlexBoxComponent(props: FlexBoxProps) { diff --git a/app/client/src/sagas/WidgetOperationSagas.tsx b/app/client/src/sagas/WidgetOperationSagas.tsx index 215f47295f04..1e725c3ffac2 100644 --- a/app/client/src/sagas/WidgetOperationSagas.tsx +++ b/app/client/src/sagas/WidgetOperationSagas.tsx @@ -141,6 +141,15 @@ import { getSlidingCanvasName } from "constants/componentClassNameConstants"; import { builderURL } from "RouteBuilder"; import history from "utils/history"; import { updateChildrenSize } from "./AutoLayoutUtils"; +import { + FlexLayerAlignment, + Positioning, + ResponsiveBehavior, +} from "components/constants"; +import { + FlexLayer, + LayerChild, +} from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; export function* resizeSaga(resizeAction: ReduxAction) { try { @@ -1335,6 +1344,7 @@ function* pasteWidgetSaga( if (canvasId) pastingIntoWidgetId = canvasId; } + const pastingIntoWidget = widgets[pastingIntoWidgetId]; yield all( copiedWidgetGroups.map((copiedWidgets) => @@ -1512,9 +1522,79 @@ function* pasteWidgetSaga( // to include this new copied widget's id in the parent's children let parentChildren = [widget.widgetId]; const widgetChildren = widgets[pastingParentId].children; + let flexLayers: FlexLayer[] = pastingIntoWidget.flexLayers || []; + if (widgetChildren && Array.isArray(widgetChildren)) { - // Add the new child to existing children - parentChildren = parentChildren.concat(widgetChildren); + // Add the new child to existing children after it's original siblings position. + + const originalWidgetId: string = widgetList[i].widgetId; + const originalWidgetIndex: number = widgetChildren.indexOf( + originalWidgetId, + ); + parentChildren = [ + ...widgetChildren.slice(0, originalWidgetIndex + 1), + ...parentChildren, + ...widgetChildren.slice(originalWidgetIndex + 1), + ]; + + /** + * If new parent is a vertical stack, then update flex layers. + */ + if ( + pastingIntoWidget.widgetId === MAIN_CONTAINER_WIDGET_ID || + pastingIntoWidget.positioning === Positioning.Vertical || + (pastingIntoWidget.type === "CANVAS_WIDGET" && + pastingIntoWidget.parentId && + widgets[pastingIntoWidget.parentId].positioning === + Positioning.Vertical) + ) { + if (widgets[originalWidgetId].parentId !== pastingParentId) { + flexLayers = [ + ...flexLayers, + { + children: [ + { + id: widget.widgetId, + align: FlexLayerAlignment.Start, + }, + ], + hasFillChild: + widget.responsiveBehavior === ResponsiveBehavior.Fill, + }, + ]; + } else { + let rowIndex = -1, + alignment = FlexLayerAlignment.Start; + const flexLayerIndex = flexLayers.findIndex( + (layer: FlexLayer) => { + const temp = layer.children.findIndex( + (child: LayerChild) => child.id === originalWidgetId, + ); + if (temp > -1) { + rowIndex = temp; + alignment = layer.children[temp].align; + } + return temp > -1; + }, + ); + if (flexLayerIndex > -1 && rowIndex > -1) { + let selectedLayer = flexLayers[flexLayerIndex]; + selectedLayer = { + children: [ + ...selectedLayer.children.slice(0, rowIndex + 1), + { id: widget.widgetId, align: alignment }, + ...selectedLayer.children.slice(rowIndex + 1), + ], + hasFillChild: selectedLayer.hasFillChild, + }; + flexLayers = [ + ...flexLayers.slice(0, flexLayerIndex), + selectedLayer, + ...flexLayers.slice(flexLayerIndex + 1), + ]; + } + } + } } const parentBottomRow = getParentBottomRowAfterAddingWidget( widgets[pastingParentId], @@ -1527,6 +1607,7 @@ function* pasteWidgetSaga( ...widgets[pastingParentId], bottomRow: Math.max(parentBottomRow, bottomMostRow || 0), children: parentChildren, + flexLayers: flexLayers, }, }; // If the copied widget's boundaries exceed the parent's From 402733b83328ca704a36694c1069b29c6f35482e Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 21 Nov 2022 16:09:31 -0500 Subject: [PATCH 253/708] add todo --- app/client/src/sagas/WidgetOperationSagas.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/app/client/src/sagas/WidgetOperationSagas.tsx b/app/client/src/sagas/WidgetOperationSagas.tsx index 1e725c3ffac2..3fee4d542c30 100644 --- a/app/client/src/sagas/WidgetOperationSagas.tsx +++ b/app/client/src/sagas/WidgetOperationSagas.tsx @@ -1540,6 +1540,7 @@ function* pasteWidgetSaga( /** * If new parent is a vertical stack, then update flex layers. */ + // TODO: Preet - add check for main container's positioning. if ( pastingIntoWidget.widgetId === MAIN_CONTAINER_WIDGET_ID || pastingIntoWidget.positioning === Positioning.Vertical || From 6678f049a917f447ec14a098d2cdb50d59093857 Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 22 Nov 2022 08:38:47 -0500 Subject: [PATCH 254/708] merge fix --- app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx index c018b4f11cec..5b60858774ee 100644 --- a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx +++ b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx @@ -109,7 +109,7 @@ function CanvasContainer() { if (!isPreviewMode && appLayout?.type === "FLUID") { const smallestWidth = layoutConfigurations.MOBILE.minWidth; // Query the element - const ele: any = document.getElementById("main-canvas-container"); + const ele: any = document.getElementById("canvas-viewport"); const initialWidth = ele.offsetWidth; // The current position of mouse let x = 0; From 0884728b873e3b54091bdf308ab02f49729191c0 Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 22 Nov 2022 10:39:29 -0500 Subject: [PATCH 255/708] fix chart fill behaviour --- app/client/src/widgets/ChartWidget/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/client/src/widgets/ChartWidget/index.ts b/app/client/src/widgets/ChartWidget/index.ts index c0185c928725..3aba9192884e 100644 --- a/app/client/src/widgets/ChartWidget/index.ts +++ b/app/client/src/widgets/ChartWidget/index.ts @@ -20,6 +20,7 @@ export const CONFIG = { allowScroll: false, version: 1, animateLoading: true, + responsiveBehavior: ResponsiveBehavior.Fill, chartData: { [generateReactKey()]: { seriesName: "Sales", @@ -96,7 +97,6 @@ export const CONFIG = { }, }, }, - responsiveBehavior: ResponsiveBehavior.Fill, properties: { derived: Widget.getDerivedPropertiesMap(), default: Widget.getDefaultPropertiesMap(), From 91a0c1f78be86eed919c9493d5ec00ca4c518a2f Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 22 Nov 2022 11:23:11 -0500 Subject: [PATCH 256/708] merge fix --- app/client/src/pages/Editor/PropertyPane/PropertyControl.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/client/src/pages/Editor/PropertyPane/PropertyControl.tsx b/app/client/src/pages/Editor/PropertyPane/PropertyControl.tsx index ada206bb1dd4..eaaa03149ea8 100644 --- a/app/client/src/pages/Editor/PropertyPane/PropertyControl.tsx +++ b/app/client/src/pages/Editor/PropertyPane/PropertyControl.tsx @@ -58,6 +58,7 @@ import { } from "utils/editorContextUtils"; import PropertyPaneHelperText from "./PropertyPaneHelperText"; import { generateKeyAndSetFocusablePropertyPaneField } from "actions/propertyPaneActions"; +import { ReduxAction } from "ce/constants/ReduxActionConstants"; type Props = PropertyPaneControlConfig & { panel: IPanelProps; @@ -238,6 +239,7 @@ const PropertyControl = memo((props: Props) => { ); const onBatchUpdatePropertiesOfMultipleWidgets = useCallback( (updatesArray: UpdateWidgetPropertyPayload[]) => { + console.log("#### updates", updatesArray); dispatch(batchUpdateMultipleWidgetProperties(updatesArray)); }, [], From f0c6c7c3e7008cde6a1cd9e5361c1abd18b4af7f Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 22 Nov 2022 11:23:50 -0500 Subject: [PATCH 257/708] merge fix --- app/client/src/pages/Editor/PropertyPane/PropertyControl.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/app/client/src/pages/Editor/PropertyPane/PropertyControl.tsx b/app/client/src/pages/Editor/PropertyPane/PropertyControl.tsx index ada206bb1dd4..12b8847cd493 100644 --- a/app/client/src/pages/Editor/PropertyPane/PropertyControl.tsx +++ b/app/client/src/pages/Editor/PropertyPane/PropertyControl.tsx @@ -58,6 +58,7 @@ import { } from "utils/editorContextUtils"; import PropertyPaneHelperText from "./PropertyPaneHelperText"; import { generateKeyAndSetFocusablePropertyPaneField } from "actions/propertyPaneActions"; +import { ReduxAction } from "ce/constants/ReduxActionConstants"; type Props = PropertyPaneControlConfig & { panel: IPanelProps; From 5eb75101edf7ca358cbb41ac3acfa56785a6389d Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 22 Nov 2022 11:35:05 -0500 Subject: [PATCH 258/708] change from fill to hug upon resize --- .../editorComponents/ResizableComponent.tsx | 21 +++++++++++++++++-- .../Editor/PropertyPane/PropertyControl.tsx | 1 - 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/app/client/src/components/editorComponents/ResizableComponent.tsx b/app/client/src/components/editorComponents/ResizableComponent.tsx index 3973ad1803f3..5b156d8fbb1a 100644 --- a/app/client/src/components/editorComponents/ResizableComponent.tsx +++ b/app/client/src/components/editorComponents/ResizableComponent.tsx @@ -15,7 +15,7 @@ import { useShowTableFilterPane, useWidgetDragResize, } from "utils/hooks/dragResizeHooks"; -import { useSelector } from "react-redux"; +import { useDispatch, useSelector } from "react-redux"; import { AppState } from "@appsmith/reducers"; import Resizable from "resizable/resizenreflow"; import { omit, get } from "lodash"; @@ -53,6 +53,7 @@ import { } from "selectors/widgetSelectors"; import { LayoutDirection, ResponsiveBehavior } from "components/constants"; import { getIsMobile } from "selectors/mainCanvasSelectors"; +import { batchUpdateMultipleWidgetProperties } from "actions/controlActions"; export type ResizableComponentProps = WidgetProps & { paddingOffset: number; @@ -63,6 +64,7 @@ export const ResizableComponent = memo(function ResizableComponent( ) { // Fetch information from the context const { updateWidget } = useContext(EditorContext); + const dispatch = useDispatch(); const isSnipingMode = useSelector(snipingModeSelector); const isPreviewMode = useSelector(previewModeSelector); @@ -233,6 +235,20 @@ export const ResizableComponent = memo(function ResizableComponent( selectWidget && !isLastSelected && selectWidget(props.widgetId); // Make sure that this tableFilterPane should close showTableFilterPane && showTableFilterPane(); + // If resizing a fill widget, then convert it to a hug widget. + if (props.responsiveBehavior === ResponsiveBehavior.Fill) + dispatch( + batchUpdateMultipleWidgetProperties([ + { + widgetId: props.widgetId, + updates: { + modify: { + responsiveBehavior: ResponsiveBehavior.Hug, + }, + }, + }, + ]), + ); AnalyticsUtil.logEvent("WIDGET_RESIZE_START", { widgetName: props.widgetName, widgetType: props.type, @@ -241,7 +257,8 @@ export const ResizableComponent = memo(function ResizableComponent( const disabledHorizontalHandles = props.isFlexChild && props.responsiveBehavior === ResponsiveBehavior.Fill && - props.direction === LayoutDirection.Vertical + props.direction === LayoutDirection.Vertical && + false ? ["left", "right", "bottomLeft", "bottomRight", "topLeft", "topRight"] : []; const handles = useMemo(() => { diff --git a/app/client/src/pages/Editor/PropertyPane/PropertyControl.tsx b/app/client/src/pages/Editor/PropertyPane/PropertyControl.tsx index eaaa03149ea8..12b8847cd493 100644 --- a/app/client/src/pages/Editor/PropertyPane/PropertyControl.tsx +++ b/app/client/src/pages/Editor/PropertyPane/PropertyControl.tsx @@ -239,7 +239,6 @@ const PropertyControl = memo((props: Props) => { ); const onBatchUpdatePropertiesOfMultipleWidgets = useCallback( (updatesArray: UpdateWidgetPropertyPayload[]) => { - console.log("#### updates", updatesArray); dispatch(batchUpdateMultipleWidgetProperties(updatesArray)); }, [], From 5cce71729b02bb2b82a46374eedd71b681355e45 Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 22 Nov 2022 12:08:37 -0500 Subject: [PATCH 259/708] fix mobile viewport crash --- app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx index 5b60858774ee..5c3f80fd6d64 100644 --- a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx +++ b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx @@ -173,7 +173,7 @@ function CanvasContainer() { mouseDownHandler(e, false), ); } else { - const ele: any = document.getElementById("main-canvas-container"); + const ele: any = document.getElementById("canvas-viewport"); ele.style.width = "inherit"; } }, [appLayout, isPreviewMode, currentPageId]); From 95bafd0a6f2ed2e4823b2a7d07a3bded80b829d3 Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 22 Nov 2022 12:09:20 -0500 Subject: [PATCH 260/708] fix mobile viewport crash --- app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx index 5b60858774ee..5c3f80fd6d64 100644 --- a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx +++ b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx @@ -173,7 +173,7 @@ function CanvasContainer() { mouseDownHandler(e, false), ); } else { - const ele: any = document.getElementById("main-canvas-container"); + const ele: any = document.getElementById("canvas-viewport"); ele.style.width = "inherit"; } }, [appLayout, isPreviewMode, currentPageId]); From e688032ad4adb6d32cbcd55587ffe2ace00047b2 Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 22 Nov 2022 13:06:10 -0500 Subject: [PATCH 261/708] remove extra scrollbar in main container --- .../appsmith/autoLayout/FlexBoxComponent.tsx | 3 +-- app/client/src/widgets/CanvasWidget.tsx | 8 ++------ 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx index 0ff82741a533..84a946befa7c 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx @@ -75,8 +75,7 @@ export const FlexContainer = styled.div<{ overflow?.indexOf("wrap") > -1 ? overflow : "nowrap"}; width: 100%; - height: ${({ isMainContainer, stretchHeight }) => - isMainContainer || stretchHeight ? "100%" : "auto"}; + height: ${({ stretchHeight }) => (stretchHeight ? "100%" : "auto")}; overflow: hidden; overflow-y: ${({ isMainContainer, isMobile }) => diff --git a/app/client/src/widgets/CanvasWidget.tsx b/app/client/src/widgets/CanvasWidget.tsx index a3c16f29da4f..dc6fface401d 100644 --- a/app/client/src/widgets/CanvasWidget.tsx +++ b/app/client/src/widgets/CanvasWidget.tsx @@ -82,11 +82,7 @@ class CanvasWidget extends ContainerWidget { ): JSX.Element { const direction = this.getDirection(); const snapRows = getCanvasSnapRows(props.bottomRow, props.canExtend); - const stretchFlexBox = - !this.props.children || !this.props.children?.length - ? true - : this.props.alignment === Alignment.Bottom || - this.props.positioning === Positioning.Vertical; + const stretchFlexBox = !this.props.children || !this.props.children?.length; return ( {props.renderMode === RenderModes.CANVAS && ( @@ -104,7 +100,7 @@ class CanvasWidget extends ContainerWidget { widgetId={props.widgetId} widgetName={props.widgetName} /> - {/* + {/* // Removing Canvas Selection and grouping in the POC Date: Wed, 23 Nov 2022 10:41:55 -0500 Subject: [PATCH 262/708] add min width for fill widgets and logic to apply it on mobile viewport --- .../src/widgets/AudioRecorderWidget/index.ts | 2 ++ app/client/src/widgets/AudioWidget/index.tsx | 2 ++ app/client/src/widgets/BaseInputWidget/index.ts | 2 ++ app/client/src/widgets/BaseWidget.tsx | 6 ++++-- .../src/widgets/ButtonGroupWidget/index.ts | 2 ++ app/client/src/widgets/CanvasWidget.tsx | 8 ++++++-- app/client/src/widgets/ChartWidget/index.ts | 2 ++ .../src/widgets/CheckboxGroupWidget/index.ts | 2 ++ app/client/src/widgets/CheckboxWidget/index.ts | 2 ++ app/client/src/widgets/ContainerWidget/index.ts | 2 ++ .../src/widgets/CurrencyInputWidget/index.ts | 2 ++ .../src/widgets/DatePickerWidget2/index.ts | 2 ++ app/client/src/widgets/DividerWidget/index.ts | 2 ++ .../src/widgets/FilePickerWidgetV2/index.ts | 2 ++ app/client/src/widgets/FormWidget/index.ts | 2 ++ app/client/src/widgets/InputWidgetV2/index.ts | 2 ++ app/client/src/widgets/JSONFormWidget/index.ts | 2 ++ app/client/src/widgets/ListWidget/index.ts | 2 ++ app/client/src/widgets/MapWidget/index.ts | 2 ++ .../src/widgets/MultiSelectTreeWidget/index.ts | 2 ++ .../src/widgets/MultiSelectWidget/index.ts | 2 ++ .../src/widgets/MultiSelectWidgetV2/index.ts | 2 ++ .../src/widgets/PhoneInputWidget/index.ts | 2 ++ .../src/widgets/RichTextEditorWidget/index.ts | 2 ++ app/client/src/widgets/SelectWidget/index.ts | 2 ++ .../src/widgets/SingleSelectTreeWidget/index.ts | 2 ++ app/client/src/widgets/TableWidgetV2/index.ts | 2 ++ app/client/src/widgets/TabsWidget/index.ts | 2 ++ app/client/src/widgets/TextWidget/index.ts | 2 ++ app/client/src/widgets/withWidgetProps.tsx | 17 ++++++++++++++++- 30 files changed, 80 insertions(+), 5 deletions(-) diff --git a/app/client/src/widgets/AudioRecorderWidget/index.ts b/app/client/src/widgets/AudioRecorderWidget/index.ts index e478d4418af8..179281ebf0cc 100644 --- a/app/client/src/widgets/AudioRecorderWidget/index.ts +++ b/app/client/src/widgets/AudioRecorderWidget/index.ts @@ -1,6 +1,7 @@ import Widget from "./widget"; import IconSVG from "./icon.svg"; import { ResponsiveBehavior } from "components/constants"; +import { MOBILE_MAX_WIDTH } from "constants/AppConstants"; export const CONFIG = { type: Widget.getWidgetType(), @@ -18,6 +19,7 @@ export const CONFIG = { version: 1, animateLoading: true, responsiveBehavior: ResponsiveBehavior.Fill, + minWidth: MOBILE_MAX_WIDTH, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/AudioWidget/index.tsx b/app/client/src/widgets/AudioWidget/index.tsx index 50836a27eb5d..1ac2d99cf896 100644 --- a/app/client/src/widgets/AudioWidget/index.tsx +++ b/app/client/src/widgets/AudioWidget/index.tsx @@ -1,6 +1,7 @@ import Widget from "./widget"; import IconSVG from "./icon.svg"; import { ResponsiveBehavior } from "components/constants"; +import { MOBILE_MAX_WIDTH } from "constants/AppConstants"; export const CONFIG = { type: Widget.getWidgetType(), @@ -17,6 +18,7 @@ export const CONFIG = { version: 1, animateLoading: true, responsiveBehavior: ResponsiveBehavior.Fill, + minWidth: MOBILE_MAX_WIDTH, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/BaseInputWidget/index.ts b/app/client/src/widgets/BaseInputWidget/index.ts index ab7b50508778..7290ffcbd9f5 100644 --- a/app/client/src/widgets/BaseInputWidget/index.ts +++ b/app/client/src/widgets/BaseInputWidget/index.ts @@ -2,6 +2,7 @@ import Widget from "./widget"; import IconSVG from "./icon.svg"; import { LabelPosition, ResponsiveBehavior } from "components/constants"; import { Alignment } from "@blueprintjs/core"; +import { MOBILE_MAX_WIDTH } from "constants/AppConstants"; export const CONFIG = { type: Widget.getWidgetType(), @@ -28,6 +29,7 @@ export const CONFIG = { isDisabled: false, animateLoading: true, responsiveBehavior: ResponsiveBehavior.Fill, + minWidth: MOBILE_MAX_WIDTH, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index d196daa90045..9f9f6fa0d53f 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -191,6 +191,7 @@ abstract class BaseWidget< this.props.bottomRow, this.props.parentColumnSpace, this.props.parentRowSpace, + this.props.minWidth, ); }; @@ -201,6 +202,7 @@ abstract class BaseWidget< bottomRow: number, parentColumnSpace: number, parentRowSpace: number, + minWidth?: number, ): { componentWidth: number; componentHeight: number; @@ -371,8 +373,7 @@ abstract class BaseWidget< private getWidgetView(): ReactNode { let content: ReactNode; - // console.log(`${this.props.widgetName} : ${this.props.widgetId} =========`); - // console.log(this.props); + // console.log("#### widget props", this.props.widgetName, this.props); switch (this.props.renderMode) { case RenderModes.CANVAS: content = this.getWidgetComponent(); @@ -491,6 +492,7 @@ export interface WidgetPositionProps extends WidgetRowCols { isFlexChild?: boolean; direction?: LayoutDirection; responsiveBehavior?: ResponsiveBehavior; + minWidth?: number; // Required to avoid squishing of widgets on mobile viewport. } export const WIDGET_STATIC_PROPS = { diff --git a/app/client/src/widgets/ButtonGroupWidget/index.ts b/app/client/src/widgets/ButtonGroupWidget/index.ts index f0932d23423e..ca9c728b992f 100644 --- a/app/client/src/widgets/ButtonGroupWidget/index.ts +++ b/app/client/src/widgets/ButtonGroupWidget/index.ts @@ -6,6 +6,7 @@ import { klona as clone } from "klona/full"; import IconSVG from "./icon.svg"; import Widget from "./widget"; import { ResponsiveBehavior } from "components/constants"; +import { MOBILE_MAX_WIDTH } from "constants/AppConstants"; export const CONFIG = { type: Widget.getWidgetType(), @@ -24,6 +25,7 @@ export const CONFIG = { version: 1, animateLoading: true, responsiveBehavior: ResponsiveBehavior.Fill, + minWidth: MOBILE_MAX_WIDTH, groupButtons: { groupButton1: { label: "Favorite", diff --git a/app/client/src/widgets/CanvasWidget.tsx b/app/client/src/widgets/CanvasWidget.tsx index a3c16f29da4f..9724eeca7956 100644 --- a/app/client/src/widgets/CanvasWidget.tsx +++ b/app/client/src/widgets/CanvasWidget.tsx @@ -7,7 +7,10 @@ import { } from "components/constants"; import FlexBoxComponent from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; import DropTargetComponent from "components/editorComponents/DropTargetComponent"; -import { CANVAS_DEFAULT_MIN_HEIGHT_PX } from "constants/AppConstants"; +import { + CANVAS_DEFAULT_MIN_HEIGHT_PX, + MOBILE_MAX_WIDTH, +} from "constants/AppConstants"; import { GridDefaults, RenderModes } from "constants/WidgetConstants"; import { CanvasDraggingArena } from "pages/common/CanvasArenas/CanvasDraggingArena"; import React, { CSSProperties } from "react"; @@ -104,7 +107,7 @@ class CanvasWidget extends ContainerWidget { widgetId={props.widgetId} widgetName={props.widgetName} /> - {/* + {/* // Removing Canvas Selection and grouping in the POC { diff --git a/app/client/src/widgets/MapWidget/index.ts b/app/client/src/widgets/MapWidget/index.ts index 12f98ec91f22..67188cb78333 100644 --- a/app/client/src/widgets/MapWidget/index.ts +++ b/app/client/src/widgets/MapWidget/index.ts @@ -1,6 +1,7 @@ import IconSVG from "./icon.svg"; import Widget from "./widget"; import { ResponsiveBehavior } from "components/constants"; +import { MOBILE_MAX_WIDTH } from "constants/AppConstants"; export const CONFIG = { type: Widget.getWidgetType(), @@ -23,6 +24,7 @@ export const CONFIG = { version: 1, animateLoading: true, responsiveBehavior: ResponsiveBehavior.Fill, + minWidth: MOBILE_MAX_WIDTH, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/MultiSelectTreeWidget/index.ts b/app/client/src/widgets/MultiSelectTreeWidget/index.ts index a1e42531fa5e..729854206d52 100644 --- a/app/client/src/widgets/MultiSelectTreeWidget/index.ts +++ b/app/client/src/widgets/MultiSelectTreeWidget/index.ts @@ -3,6 +3,7 @@ import { LabelPosition } from "components/constants"; import IconSVG from "./icon.svg"; import Widget from "./widget"; import { ResponsiveBehavior } from "components/constants"; +import { MOBILE_MAX_WIDTH } from "constants/AppConstants"; export const CONFIG = { type: Widget.getWidgetType(), @@ -48,6 +49,7 @@ export const CONFIG = { labelWidth: 5, labelTextSize: "0.875rem", responsiveBehavior: ResponsiveBehavior.Fill, + minWidth: MOBILE_MAX_WIDTH, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/MultiSelectWidget/index.ts b/app/client/src/widgets/MultiSelectWidget/index.ts index f673d675c43f..4e38f78723ab 100644 --- a/app/client/src/widgets/MultiSelectWidget/index.ts +++ b/app/client/src/widgets/MultiSelectWidget/index.ts @@ -3,6 +3,7 @@ import { LabelPosition } from "components/constants"; import IconSVG from "./icon.svg"; import Widget from "./widget"; import { ResponsiveBehavior } from "components/constants"; +import { MOBILE_MAX_WIDTH } from "constants/AppConstants"; export const CONFIG = { type: Widget.getWidgetType(), @@ -33,6 +34,7 @@ export const CONFIG = { isDisabled: false, placeholderText: "Select option(s)", responsiveBehavior: ResponsiveBehavior.Fill, + minWidth: MOBILE_MAX_WIDTH, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/MultiSelectWidgetV2/index.ts b/app/client/src/widgets/MultiSelectWidgetV2/index.ts index a5c5a3eea482..3c4296accbf4 100644 --- a/app/client/src/widgets/MultiSelectWidgetV2/index.ts +++ b/app/client/src/widgets/MultiSelectWidgetV2/index.ts @@ -3,6 +3,7 @@ import IconSVG from "./icon.svg"; import { LabelPosition } from "components/constants"; import { Alignment } from "@blueprintjs/core"; import { ResponsiveBehavior } from "components/constants"; +import { MOBILE_MAX_WIDTH } from "constants/AppConstants"; export const CONFIG = { type: Widget.getWidgetType(), @@ -33,6 +34,7 @@ export const CONFIG = { isDisabled: false, placeholderText: "Select option(s)", responsiveBehavior: ResponsiveBehavior.Fill, + minWidth: MOBILE_MAX_WIDTH, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/PhoneInputWidget/index.ts b/app/client/src/widgets/PhoneInputWidget/index.ts index bb79a6cc9343..0af5e018bcad 100644 --- a/app/client/src/widgets/PhoneInputWidget/index.ts +++ b/app/client/src/widgets/PhoneInputWidget/index.ts @@ -1,4 +1,5 @@ import { LabelPosition, ResponsiveBehavior } from "components/constants"; +import { MOBILE_MAX_WIDTH } from "constants/AppConstants"; import { CONFIG as BaseConfig } from "widgets/BaseInputWidget"; import { getDefaultISDCode } from "./component/ISDCodeDropdown"; import IconSVG from "./icon.svg"; @@ -20,6 +21,7 @@ export const CONFIG = { allowDialCodeChange: false, allowFormatting: true, responsiveBehavior: ResponsiveBehavior.Fill, + minWidth: MOBILE_MAX_WIDTH, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/RichTextEditorWidget/index.ts b/app/client/src/widgets/RichTextEditorWidget/index.ts index 75e4c91b4594..0f73c3970b18 100644 --- a/app/client/src/widgets/RichTextEditorWidget/index.ts +++ b/app/client/src/widgets/RichTextEditorWidget/index.ts @@ -1,5 +1,6 @@ import { Alignment } from "@blueprintjs/core"; import { LabelPosition, ResponsiveBehavior } from "components/constants"; +import { MOBILE_MAX_WIDTH } from "constants/AppConstants"; import IconSVG from "./icon.svg"; import Widget from "./widget"; @@ -26,6 +27,7 @@ export const CONFIG = { labelWidth: 5, version: 1, responsiveBehavior: ResponsiveBehavior.Fill, + minWidth: MOBILE_MAX_WIDTH, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/SelectWidget/index.ts b/app/client/src/widgets/SelectWidget/index.ts index 89aa9d4b43b8..89a8923998c3 100644 --- a/app/client/src/widgets/SelectWidget/index.ts +++ b/app/client/src/widgets/SelectWidget/index.ts @@ -3,6 +3,7 @@ import IconSVG from "./icon.svg"; import { LabelPosition } from "components/constants"; import { Alignment } from "@blueprintjs/core"; import { ResponsiveBehavior } from "components/constants"; +import { MOBILE_MAX_WIDTH } from "constants/AppConstants"; export const CONFIG = { type: Widget.getWidgetType(), @@ -33,6 +34,7 @@ export const CONFIG = { animateLoading: true, labelTextSize: "0.875rem", responsiveBehavior: ResponsiveBehavior.Fill, + minWidth: MOBILE_MAX_WIDTH, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/SingleSelectTreeWidget/index.ts b/app/client/src/widgets/SingleSelectTreeWidget/index.ts index 1d3db2c3e52c..c7e9438a15b4 100644 --- a/app/client/src/widgets/SingleSelectTreeWidget/index.ts +++ b/app/client/src/widgets/SingleSelectTreeWidget/index.ts @@ -3,6 +3,7 @@ import { LabelPosition } from "components/constants"; import IconSVG from "./icon.svg"; import Widget from "./widget"; import { ResponsiveBehavior } from "components/constants"; +import { MOBILE_MAX_WIDTH } from "constants/AppConstants"; export const CONFIG = { type: Widget.getWidgetType(), @@ -47,6 +48,7 @@ export const CONFIG = { labelWidth: 5, labelTextSize: "0.875rem", responsiveBehavior: ResponsiveBehavior.Fill, + minWidth: MOBILE_MAX_WIDTH, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/TableWidgetV2/index.ts b/app/client/src/widgets/TableWidgetV2/index.ts index 9240662307d8..4a5d89177c4f 100644 --- a/app/client/src/widgets/TableWidgetV2/index.ts +++ b/app/client/src/widgets/TableWidgetV2/index.ts @@ -11,6 +11,7 @@ import IconSVG from "./icon.svg"; import Widget from "./widget"; import { escapeString } from "./widget/utilities"; import { ResponsiveBehavior } from "components/constants"; +import { MOBILE_MAX_WIDTH } from "constants/AppConstants"; export const CONFIG = { type: Widget.getWidgetType(), @@ -19,6 +20,7 @@ export const CONFIG = { needsMeta: true, defaults: { responsiveBehavior: ResponsiveBehavior.Fill, + minWidth: MOBILE_MAX_WIDTH, rows: 28, columns: 34, animateLoading: true, diff --git a/app/client/src/widgets/TabsWidget/index.ts b/app/client/src/widgets/TabsWidget/index.ts index 7f224bb6be00..05c6a5a54942 100644 --- a/app/client/src/widgets/TabsWidget/index.ts +++ b/app/client/src/widgets/TabsWidget/index.ts @@ -4,6 +4,7 @@ import { BlueprintOperationTypes } from "widgets/constants"; import IconSVG from "./icon.svg"; import Widget from "./widget"; import { Positioning, ResponsiveBehavior } from "components/constants"; +import { MOBILE_MAX_WIDTH } from "constants/AppConstants"; export const CONFIG = { type: Widget.getWidgetType(), @@ -13,6 +14,7 @@ export const CONFIG = { isCanvas: true, defaults: { responsiveBehavior: ResponsiveBehavior.Fill, + minWidth: MOBILE_MAX_WIDTH, rows: 40, columns: 24, shouldScrollContents: false, diff --git a/app/client/src/widgets/TextWidget/index.ts b/app/client/src/widgets/TextWidget/index.ts index 43dfaa1ed23d..45d25e1dfd92 100644 --- a/app/client/src/widgets/TextWidget/index.ts +++ b/app/client/src/widgets/TextWidget/index.ts @@ -1,4 +1,5 @@ import { ResponsiveBehavior } from "components/constants"; +import { MOBILE_MAX_WIDTH } from "constants/AppConstants"; import { DEFAULT_FONT_SIZE } from "constants/WidgetConstants"; import { OverflowTypes } from "./constants"; import IconSVG from "./icon.svg"; @@ -23,6 +24,7 @@ export const CONFIG = { version: 1, animateLoading: true, responsiveBehavior: ResponsiveBehavior.Fill, + minWidth: MOBILE_MAX_WIDTH, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/withWidgetProps.tsx b/app/client/src/widgets/withWidgetProps.tsx index 893590c8b5c6..8bd13a7f2d0d 100644 --- a/app/client/src/widgets/withWidgetProps.tsx +++ b/app/client/src/widgets/withWidgetProps.tsx @@ -23,6 +23,7 @@ import { createCanvasWidget, createLoadingWidget, } from "utils/widgetRenderUtils"; +import { getIsMobile } from "selectors/mainCanvasSelectors"; const WIDGETS_WITH_CHILD_WIDGETS = ["LIST_WIDGET", "FORM_WIDGET"]; @@ -45,6 +46,7 @@ function withWidgetProps(WrappedWidget: typeof BaseWidget) { const isLoading = useSelector((state: AppState) => getIsWidgetLoading(state, canvasWidget?.widgetName), ); + const isMobile = useSelector(getIsMobile); const childWidgets = useSelector((state: AppState) => { if (!WIDGETS_WITH_CHILD_WIDGETS.includes(type)) return undefined; @@ -52,7 +54,6 @@ function withWidgetProps(WrappedWidget: typeof BaseWidget) { }, equal); let widgetProps: WidgetProps = {} as WidgetProps; - if (!skipWidgetPropsHydration) { const canvasWidgetProps = (() => { if (widgetId === MAIN_CONTAINER_WIDGET_ID) { @@ -96,10 +97,24 @@ function withWidgetProps(WrappedWidget: typeof BaseWidget) { if ("isFormValid" in props) widgetProps.isFormValid = props.isFormValid; } + /** + * For Mobile Viewport: + * Adjust right column to accommodate specified minWidth. + */ + if (props.isFlexChild && isMobile && widgetProps.minWidth) { + const { minWidth, parentColumnSpace, rightColumn } = widgetProps; + if (parentColumnSpace * rightColumn < minWidth) + widgetProps.rightColumn = Math.min( + Math.floor(minWidth / parentColumnSpace), + 64, + ); + } + widgetProps.children = children; widgetProps.isLoading = isLoading; widgetProps.childWidgets = childWidgets; + widgetProps.foo = "bar"; } //merging with original props From bc66c7196be70c9a41957a8e150edce7a8d2da23 Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 23 Nov 2022 12:11:56 -0500 Subject: [PATCH 263/708] hug widget constants --- .../appsmith/autoLayout/FlexComponent.tsx | 11 +++----- app/client/src/constants/minWidthConstants.ts | 5 ++++ .../src/widgets/AudioRecorderWidget/index.ts | 4 +-- app/client/src/widgets/AudioWidget/index.tsx | 4 +-- .../src/widgets/BaseInputWidget/index.ts | 4 +-- app/client/src/widgets/BaseWidget.tsx | 2 +- .../src/widgets/ButtonGroupWidget/index.ts | 4 +-- app/client/src/widgets/ButtonWidget/index.ts | 2 ++ app/client/src/widgets/CanvasWidget.tsx | 8 +++--- app/client/src/widgets/ChartWidget/index.ts | 4 +-- .../src/widgets/CheckboxGroupWidget/index.ts | 4 +-- .../src/widgets/CheckboxWidget/index.ts | 4 +-- .../src/widgets/ContainerWidget/index.ts | 4 +-- .../src/widgets/CurrencyInputWidget/index.ts | 4 +-- .../src/widgets/DatePickerWidget2/index.ts | 4 +-- app/client/src/widgets/DividerWidget/index.ts | 4 +-- .../src/widgets/FilePickerWidgetV2/index.ts | 4 +-- app/client/src/widgets/FormWidget/index.ts | 4 +-- .../src/widgets/IconButtonWidget/index.ts | 2 ++ app/client/src/widgets/InputWidgetV2/index.ts | 4 +-- .../src/widgets/JSONFormWidget/index.ts | 4 +-- app/client/src/widgets/ListWidget/index.ts | 4 +-- app/client/src/widgets/MapWidget/index.ts | 4 +-- .../widgets/MultiSelectTreeWidget/index.ts | 4 +-- .../src/widgets/MultiSelectWidget/index.ts | 4 +-- .../src/widgets/MultiSelectWidgetV2/index.ts | 4 +-- .../src/widgets/PhoneInputWidget/index.ts | 4 +-- .../src/widgets/RichTextEditorWidget/index.ts | 4 +-- app/client/src/widgets/SelectWidget/index.ts | 4 +-- .../widgets/SingleSelectTreeWidget/index.ts | 4 +-- app/client/src/widgets/TableWidgetV2/index.ts | 4 +-- app/client/src/widgets/TabsWidget/index.ts | 4 +-- app/client/src/widgets/TextWidget/index.ts | 4 +-- app/client/src/widgets/withWidgetProps.tsx | 25 +++++++++++++++++-- 34 files changed, 94 insertions(+), 69 deletions(-) create mode 100644 app/client/src/constants/minWidthConstants.ts diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx index ac643c9e419e..a0f6bf4e05bf 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx @@ -38,7 +38,6 @@ const FlexWidget = styled.div<{ componentWidth: number; isMobile: boolean; isFillWidget: boolean; - minWidth?: string; padding: number; zIndex: number; zIndexOnHover: number; @@ -52,7 +51,6 @@ const FlexWidget = styled.div<{ width: ${({ componentWidth }) => `${Math.floor(componentWidth)}px`}; height: ${({ componentHeight, isMobile }) => isMobile ? "auto" : Math.floor(componentHeight) + "px"}; - min-width: ${({ minWidth }) => minWidth}; min-height: 30px; padding: ${({ isAffectedByDrag, padding }) => @@ -110,10 +108,10 @@ export function FlexComponent(props: AutoLayoutProps) { props.widgetId } ${widgetTypeClassname(props.widgetType)}`; - const minWidth = - props.responsiveBehavior === ResponsiveBehavior.Fill && isMobile - ? "100%" - : props.minWidth + "px"; + // const minWidth = + // props.responsiveBehavior === ResponsiveBehavior.Fill && isMobile + // ? "100%" + // : props.minWidth + "px"; const dragMargin = props.parentId === MAIN_CONTAINER_WIDGET_ID ? DEFAULT_MARGIN @@ -141,7 +139,6 @@ export function FlexComponent(props: AutoLayoutProps) { isAffectedByDrag={isAffectedByDrag} isFillWidget={isFillWidget} isMobile={isMobile} - minWidth={minWidth} onClick={stopEventPropagation} onClickCapture={onClickFn} padding={WIDGET_PADDING} diff --git a/app/client/src/constants/minWidthConstants.ts b/app/client/src/constants/minWidthConstants.ts new file mode 100644 index 000000000000..d2e61763b83d --- /dev/null +++ b/app/client/src/constants/minWidthConstants.ts @@ -0,0 +1,5 @@ +import { MOBILE_MAX_WIDTH } from "./AppConstants"; + +export const FILL_WIDGET_MIN_WIDTH: number = MOBILE_MAX_WIDTH; +export const ICON_BUTTON_MIN_WIDTH = 40; +export const BUTTON_MIN_WIDTH = 120; diff --git a/app/client/src/widgets/AudioRecorderWidget/index.ts b/app/client/src/widgets/AudioRecorderWidget/index.ts index 179281ebf0cc..6ba108e755ed 100644 --- a/app/client/src/widgets/AudioRecorderWidget/index.ts +++ b/app/client/src/widgets/AudioRecorderWidget/index.ts @@ -1,7 +1,7 @@ import Widget from "./widget"; import IconSVG from "./icon.svg"; import { ResponsiveBehavior } from "components/constants"; -import { MOBILE_MAX_WIDTH } from "constants/AppConstants"; +import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; export const CONFIG = { type: Widget.getWidgetType(), @@ -19,7 +19,7 @@ export const CONFIG = { version: 1, animateLoading: true, responsiveBehavior: ResponsiveBehavior.Fill, - minWidth: MOBILE_MAX_WIDTH, + minWidth: FILL_WIDGET_MIN_WIDTH, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/AudioWidget/index.tsx b/app/client/src/widgets/AudioWidget/index.tsx index 1ac2d99cf896..d98b878483c8 100644 --- a/app/client/src/widgets/AudioWidget/index.tsx +++ b/app/client/src/widgets/AudioWidget/index.tsx @@ -1,7 +1,7 @@ import Widget from "./widget"; import IconSVG from "./icon.svg"; import { ResponsiveBehavior } from "components/constants"; -import { MOBILE_MAX_WIDTH } from "constants/AppConstants"; +import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; export const CONFIG = { type: Widget.getWidgetType(), @@ -18,7 +18,7 @@ export const CONFIG = { version: 1, animateLoading: true, responsiveBehavior: ResponsiveBehavior.Fill, - minWidth: MOBILE_MAX_WIDTH, + minWidth: FILL_WIDGET_MIN_WIDTH, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/BaseInputWidget/index.ts b/app/client/src/widgets/BaseInputWidget/index.ts index 7290ffcbd9f5..4c9dd1ade907 100644 --- a/app/client/src/widgets/BaseInputWidget/index.ts +++ b/app/client/src/widgets/BaseInputWidget/index.ts @@ -2,7 +2,7 @@ import Widget from "./widget"; import IconSVG from "./icon.svg"; import { LabelPosition, ResponsiveBehavior } from "components/constants"; import { Alignment } from "@blueprintjs/core"; -import { MOBILE_MAX_WIDTH } from "constants/AppConstants"; +import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; export const CONFIG = { type: Widget.getWidgetType(), @@ -29,7 +29,7 @@ export const CONFIG = { isDisabled: false, animateLoading: true, responsiveBehavior: ResponsiveBehavior.Fill, - minWidth: MOBILE_MAX_WIDTH, + minWidth: FILL_WIDGET_MIN_WIDTH, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index 9f9f6fa0d53f..2be79ff76cff 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -373,7 +373,7 @@ abstract class BaseWidget< private getWidgetView(): ReactNode { let content: ReactNode; - // console.log("#### widget props", this.props.widgetName, this.props); + console.log("^^^^ widget props", this.props.widgetName, this.props); switch (this.props.renderMode) { case RenderModes.CANVAS: content = this.getWidgetComponent(); diff --git a/app/client/src/widgets/ButtonGroupWidget/index.ts b/app/client/src/widgets/ButtonGroupWidget/index.ts index ca9c728b992f..41df2794a89f 100644 --- a/app/client/src/widgets/ButtonGroupWidget/index.ts +++ b/app/client/src/widgets/ButtonGroupWidget/index.ts @@ -6,7 +6,7 @@ import { klona as clone } from "klona/full"; import IconSVG from "./icon.svg"; import Widget from "./widget"; import { ResponsiveBehavior } from "components/constants"; -import { MOBILE_MAX_WIDTH } from "constants/AppConstants"; +import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; export const CONFIG = { type: Widget.getWidgetType(), @@ -25,7 +25,7 @@ export const CONFIG = { version: 1, animateLoading: true, responsiveBehavior: ResponsiveBehavior.Fill, - minWidth: MOBILE_MAX_WIDTH, + minWidth: FILL_WIDGET_MIN_WIDTH, groupButtons: { groupButton1: { label: "Favorite", diff --git a/app/client/src/widgets/ButtonWidget/index.ts b/app/client/src/widgets/ButtonWidget/index.ts index 5536546b030a..9fc3d312728f 100644 --- a/app/client/src/widgets/ButtonWidget/index.ts +++ b/app/client/src/widgets/ButtonWidget/index.ts @@ -4,6 +4,7 @@ import { RecaptchaTypes, ResponsiveBehavior, } from "components/constants"; +import { BUTTON_MIN_WIDTH } from "constants/minWidthConstants"; import IconSVG from "./icon.svg"; import Widget from "./widget"; @@ -29,6 +30,7 @@ export const CONFIG = { recaptchaType: RecaptchaTypes.V3, version: 1, responsiveBehavior: ResponsiveBehavior.Hug, + minWidth: BUTTON_MIN_WIDTH, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/CanvasWidget.tsx b/app/client/src/widgets/CanvasWidget.tsx index 9724eeca7956..44dbf936fb30 100644 --- a/app/client/src/widgets/CanvasWidget.tsx +++ b/app/client/src/widgets/CanvasWidget.tsx @@ -7,10 +7,8 @@ import { } from "components/constants"; import FlexBoxComponent from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; import DropTargetComponent from "components/editorComponents/DropTargetComponent"; -import { - CANVAS_DEFAULT_MIN_HEIGHT_PX, - MOBILE_MAX_WIDTH, -} from "constants/AppConstants"; +import { CANVAS_DEFAULT_MIN_HEIGHT_PX } from "constants/AppConstants"; +import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; import { GridDefaults, RenderModes } from "constants/WidgetConstants"; import { CanvasDraggingArena } from "pages/common/CanvasArenas/CanvasDraggingArena"; import React, { CSSProperties } from "react"; @@ -205,7 +203,7 @@ export const CONFIG = { detachFromLayout: true, flexLayers: [], responsiveBehavior: ResponsiveBehavior.Fill, - minWidth: MOBILE_MAX_WIDTH, + minWidth: FILL_WIDGET_MIN_WIDTH, }, properties: { derived: CanvasWidget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/ChartWidget/index.ts b/app/client/src/widgets/ChartWidget/index.ts index f2dc7d78cf65..9e4049e11b26 100644 --- a/app/client/src/widgets/ChartWidget/index.ts +++ b/app/client/src/widgets/ChartWidget/index.ts @@ -1,6 +1,6 @@ import { ResponsiveBehavior } from "components/constants"; -import { MOBILE_MAX_WIDTH } from "constants/AppConstants"; import { Colors } from "constants/Colors"; +import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; import { generateReactKey } from "widgets/WidgetUtils"; import { LabelOrientation } from "./constants"; import IconSVG from "./icon.svg"; @@ -22,7 +22,7 @@ export const CONFIG = { version: 1, animateLoading: true, responsiveBehavior: ResponsiveBehavior.Fill, - minWidth: MOBILE_MAX_WIDTH, + minWidth: FILL_WIDGET_MIN_WIDTH, chartData: { [generateReactKey()]: { seriesName: "Sales", diff --git a/app/client/src/widgets/CheckboxGroupWidget/index.ts b/app/client/src/widgets/CheckboxGroupWidget/index.ts index 1b44ffe350c3..2bf046870e07 100644 --- a/app/client/src/widgets/CheckboxGroupWidget/index.ts +++ b/app/client/src/widgets/CheckboxGroupWidget/index.ts @@ -1,6 +1,6 @@ import { Alignment } from "@blueprintjs/core"; import { LabelPosition, ResponsiveBehavior } from "components/constants"; -import { MOBILE_MAX_WIDTH } from "constants/AppConstants"; +import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; import IconSVG from "./icon.svg"; import Widget from "./widget"; @@ -31,7 +31,7 @@ export const CONFIG = { widgetName: "CheckboxGroup", version: 2, responsiveBehavior: ResponsiveBehavior.Fill, - minWidth: MOBILE_MAX_WIDTH, + minWidth: FILL_WIDGET_MIN_WIDTH, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/CheckboxWidget/index.ts b/app/client/src/widgets/CheckboxWidget/index.ts index 3c8777de31b5..c98fc5dcfa11 100644 --- a/app/client/src/widgets/CheckboxWidget/index.ts +++ b/app/client/src/widgets/CheckboxWidget/index.ts @@ -2,7 +2,7 @@ import Widget from "./widget"; import IconSVG from "./icon.svg"; import { LabelPosition, ResponsiveBehavior } from "components/constants"; import { AlignWidgetTypes } from "widgets/constants"; -import { MOBILE_MAX_WIDTH } from "constants/AppConstants"; +import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; export const CONFIG = { type: Widget.getWidgetType(), @@ -23,7 +23,7 @@ export const CONFIG = { isRequired: false, animateLoading: true, responsiveBehavior: ResponsiveBehavior.Fill, - minWidth: MOBILE_MAX_WIDTH, + minWidth: FILL_WIDGET_MIN_WIDTH, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/ContainerWidget/index.ts b/app/client/src/widgets/ContainerWidget/index.ts index 8cdcaf4d2ccc..001e001178b8 100644 --- a/app/client/src/widgets/ContainerWidget/index.ts +++ b/app/client/src/widgets/ContainerWidget/index.ts @@ -3,8 +3,8 @@ import { Positioning, ResponsiveBehavior, } from "components/constants"; -import { MOBILE_MAX_WIDTH } from "constants/AppConstants"; import { Colors } from "constants/Colors"; +import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; import IconSVG from "./icon.svg"; import Widget from "./widget"; @@ -42,7 +42,7 @@ export const CONFIG = { version: 1, positioning: Positioning.Vertical, responsiveBehavior: ResponsiveBehavior.Fill, - minWidth: MOBILE_MAX_WIDTH, + minWidth: FILL_WIDGET_MIN_WIDTH, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/CurrencyInputWidget/index.ts b/app/client/src/widgets/CurrencyInputWidget/index.ts index 9afcb698d0b5..c0efe45fd0fb 100644 --- a/app/client/src/widgets/CurrencyInputWidget/index.ts +++ b/app/client/src/widgets/CurrencyInputWidget/index.ts @@ -1,5 +1,5 @@ import { LabelPosition, ResponsiveBehavior } from "components/constants"; -import { MOBILE_MAX_WIDTH } from "constants/AppConstants"; +import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; import { CONFIG as BaseConfig } from "widgets/BaseInputWidget"; import { getDefaultCurrency } from "./component/CurrencyCodeDropdown"; import IconSVG from "./icon.svg"; @@ -21,7 +21,7 @@ export const CONFIG = { defaultCurrencyCode: getDefaultCurrency().currency, decimals: 0, responsiveBehavior: ResponsiveBehavior.Fill, - minWidth: MOBILE_MAX_WIDTH, + minWidth: FILL_WIDGET_MIN_WIDTH, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/DatePickerWidget2/index.ts b/app/client/src/widgets/DatePickerWidget2/index.ts index 61c26db9c8a5..5d749c213938 100644 --- a/app/client/src/widgets/DatePickerWidget2/index.ts +++ b/app/client/src/widgets/DatePickerWidget2/index.ts @@ -1,6 +1,6 @@ import { Alignment } from "@blueprintjs/core"; import { LabelPosition, ResponsiveBehavior } from "components/constants"; -import { MOBILE_MAX_WIDTH } from "constants/AppConstants"; +import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; import moment from "moment"; import { TimePrecision } from "./constants"; import IconSVG from "./icon.svg"; @@ -35,7 +35,7 @@ export const CONFIG = { timePrecision: TimePrecision.MINUTE, animateLoading: true, responsiveBehavior: ResponsiveBehavior.Fill, - minWidth: MOBILE_MAX_WIDTH, + minWidth: FILL_WIDGET_MIN_WIDTH, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/DividerWidget/index.ts b/app/client/src/widgets/DividerWidget/index.ts index 98b08c99a304..ed487c89a62e 100644 --- a/app/client/src/widgets/DividerWidget/index.ts +++ b/app/client/src/widgets/DividerWidget/index.ts @@ -1,6 +1,6 @@ import { ResponsiveBehavior } from "components/constants"; -import { MOBILE_MAX_WIDTH } from "constants/AppConstants"; import { Colors } from "constants/Colors"; +import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; import IconSVG from "./icon.svg"; import Widget from "./widget"; @@ -23,7 +23,7 @@ export const CONFIG = { version: 1, animateLoading: true, responsiveBehavior: ResponsiveBehavior.Fill, - minWidth: MOBILE_MAX_WIDTH, + minWidth: FILL_WIDGET_MIN_WIDTH, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/FilePickerWidgetV2/index.ts b/app/client/src/widgets/FilePickerWidgetV2/index.ts index 0270d80a99df..3af79cdef839 100644 --- a/app/client/src/widgets/FilePickerWidgetV2/index.ts +++ b/app/client/src/widgets/FilePickerWidgetV2/index.ts @@ -1,5 +1,5 @@ import { ResponsiveBehavior } from "components/constants"; -import { MOBILE_MAX_WIDTH } from "constants/AppConstants"; +import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; import FileDataTypes from "./constants"; import IconSVG from "./icon.svg"; import Widget from "./widget"; @@ -28,7 +28,7 @@ export const CONFIG = { isDisabled: false, animateLoading: true, responsiveBehavior: ResponsiveBehavior.Fill, - minWidth: MOBILE_MAX_WIDTH, + minWidth: FILL_WIDGET_MIN_WIDTH, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/FormWidget/index.ts b/app/client/src/widgets/FormWidget/index.ts index 34e926a700cd..55e676e31497 100644 --- a/app/client/src/widgets/FormWidget/index.ts +++ b/app/client/src/widgets/FormWidget/index.ts @@ -3,8 +3,8 @@ import { RecaptchaTypes, ResponsiveBehavior, } from "components/constants"; -import { MOBILE_MAX_WIDTH } from "constants/AppConstants"; import { Colors } from "constants/Colors"; +import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; import IconSVG from "./icon.svg"; import Widget from "./widget"; @@ -95,7 +95,7 @@ export const CONFIG = { ], }, responsiveBehavior: ResponsiveBehavior.Fill, - minWidth: MOBILE_MAX_WIDTH, + minWidth: FILL_WIDGET_MIN_WIDTH, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/IconButtonWidget/index.ts b/app/client/src/widgets/IconButtonWidget/index.ts index cbb9395e7b05..945872d93bc8 100644 --- a/app/client/src/widgets/IconButtonWidget/index.ts +++ b/app/client/src/widgets/IconButtonWidget/index.ts @@ -1,5 +1,6 @@ import { IconNames } from "@blueprintjs/icons"; import { ButtonVariantTypes } from "components/constants"; +import { ICON_BUTTON_MIN_WIDTH } from "constants/minWidthConstants"; import IconSVG from "./icon.svg"; import Widget from "./widget"; @@ -18,6 +19,7 @@ export const CONFIG = { widgetName: "IconButton", version: 1, animateLoading: true, + minWidth: ICON_BUTTON_MIN_WIDTH, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/InputWidgetV2/index.ts b/app/client/src/widgets/InputWidgetV2/index.ts index 6fb610e395a0..18088057da08 100644 --- a/app/client/src/widgets/InputWidgetV2/index.ts +++ b/app/client/src/widgets/InputWidgetV2/index.ts @@ -1,5 +1,5 @@ import { LabelPosition, ResponsiveBehavior } from "components/constants"; -import { MOBILE_MAX_WIDTH } from "constants/AppConstants"; +import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; import { CONFIG as BaseConfig } from "widgets/BaseInputWidget"; import IconSVG from "./icon.svg"; import Widget from "./widget"; @@ -18,7 +18,7 @@ export const CONFIG = { widgetName: "Input", version: 2, responsiveBehavior: ResponsiveBehavior.Fill, - minWidth: MOBILE_MAX_WIDTH, + minWidth: FILL_WIDGET_MIN_WIDTH, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/JSONFormWidget/index.ts b/app/client/src/widgets/JSONFormWidget/index.ts index 4e8f6a978357..9ec39a40ffc8 100644 --- a/app/client/src/widgets/JSONFormWidget/index.ts +++ b/app/client/src/widgets/JSONFormWidget/index.ts @@ -4,7 +4,7 @@ import Widget, { JSONFormWidgetProps } from "./widget"; import { ButtonVariantTypes } from "components/constants"; import { BlueprintOperationTypes } from "widgets/constants"; import { ResponsiveBehavior } from "components/constants"; -import { MOBILE_MAX_WIDTH } from "constants/AppConstants"; +import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; const SUBMIT_BUTTON_DEFAULT_STYLES = { buttonVariant: ButtonVariantTypes.PRIMARY, @@ -21,7 +21,7 @@ export const CONFIG = { needsMeta: true, defaults: { responsiveBehavior: ResponsiveBehavior.Fill, - minWidth: MOBILE_MAX_WIDTH, + minWidth: FILL_WIDGET_MIN_WIDTH, animateLoading: true, backgroundColor: "#fff", columns: 25, diff --git a/app/client/src/widgets/ListWidget/index.ts b/app/client/src/widgets/ListWidget/index.ts index f2bda7bfaec0..3251abe9b79a 100644 --- a/app/client/src/widgets/ListWidget/index.ts +++ b/app/client/src/widgets/ListWidget/index.ts @@ -11,7 +11,7 @@ import { import IconSVG from "./icon.svg"; import Widget from "./widget"; import { ResponsiveBehavior } from "components/constants"; -import { MOBILE_MAX_WIDTH } from "constants/AppConstants"; +import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; export const CONFIG = { type: Widget.getWidgetType(), @@ -28,7 +28,7 @@ export const CONFIG = { gridType: "vertical", template: {}, responsiveBehavior: ResponsiveBehavior.Fill, - minWidth: MOBILE_MAX_WIDTH, + minWidth: FILL_WIDGET_MIN_WIDTH, enhancements: { child: { autocomplete: (parentProps: any) => { diff --git a/app/client/src/widgets/MapWidget/index.ts b/app/client/src/widgets/MapWidget/index.ts index 67188cb78333..d3fb965f62ad 100644 --- a/app/client/src/widgets/MapWidget/index.ts +++ b/app/client/src/widgets/MapWidget/index.ts @@ -1,7 +1,7 @@ import IconSVG from "./icon.svg"; import Widget from "./widget"; import { ResponsiveBehavior } from "components/constants"; -import { MOBILE_MAX_WIDTH } from "constants/AppConstants"; +import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; export const CONFIG = { type: Widget.getWidgetType(), @@ -24,7 +24,7 @@ export const CONFIG = { version: 1, animateLoading: true, responsiveBehavior: ResponsiveBehavior.Fill, - minWidth: MOBILE_MAX_WIDTH, + minWidth: FILL_WIDGET_MIN_WIDTH, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/MultiSelectTreeWidget/index.ts b/app/client/src/widgets/MultiSelectTreeWidget/index.ts index 729854206d52..b05a15be334c 100644 --- a/app/client/src/widgets/MultiSelectTreeWidget/index.ts +++ b/app/client/src/widgets/MultiSelectTreeWidget/index.ts @@ -3,7 +3,7 @@ import { LabelPosition } from "components/constants"; import IconSVG from "./icon.svg"; import Widget from "./widget"; import { ResponsiveBehavior } from "components/constants"; -import { MOBILE_MAX_WIDTH } from "constants/AppConstants"; +import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; export const CONFIG = { type: Widget.getWidgetType(), @@ -49,7 +49,7 @@ export const CONFIG = { labelWidth: 5, labelTextSize: "0.875rem", responsiveBehavior: ResponsiveBehavior.Fill, - minWidth: MOBILE_MAX_WIDTH, + minWidth: FILL_WIDGET_MIN_WIDTH, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/MultiSelectWidget/index.ts b/app/client/src/widgets/MultiSelectWidget/index.ts index 4e38f78723ab..b6ced58c13da 100644 --- a/app/client/src/widgets/MultiSelectWidget/index.ts +++ b/app/client/src/widgets/MultiSelectWidget/index.ts @@ -3,7 +3,7 @@ import { LabelPosition } from "components/constants"; import IconSVG from "./icon.svg"; import Widget from "./widget"; import { ResponsiveBehavior } from "components/constants"; -import { MOBILE_MAX_WIDTH } from "constants/AppConstants"; +import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; export const CONFIG = { type: Widget.getWidgetType(), @@ -34,7 +34,7 @@ export const CONFIG = { isDisabled: false, placeholderText: "Select option(s)", responsiveBehavior: ResponsiveBehavior.Fill, - minWidth: MOBILE_MAX_WIDTH, + minWidth: FILL_WIDGET_MIN_WIDTH, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/MultiSelectWidgetV2/index.ts b/app/client/src/widgets/MultiSelectWidgetV2/index.ts index 3c4296accbf4..5c879f38c9b0 100644 --- a/app/client/src/widgets/MultiSelectWidgetV2/index.ts +++ b/app/client/src/widgets/MultiSelectWidgetV2/index.ts @@ -3,7 +3,7 @@ import IconSVG from "./icon.svg"; import { LabelPosition } from "components/constants"; import { Alignment } from "@blueprintjs/core"; import { ResponsiveBehavior } from "components/constants"; -import { MOBILE_MAX_WIDTH } from "constants/AppConstants"; +import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; export const CONFIG = { type: Widget.getWidgetType(), @@ -34,7 +34,7 @@ export const CONFIG = { isDisabled: false, placeholderText: "Select option(s)", responsiveBehavior: ResponsiveBehavior.Fill, - minWidth: MOBILE_MAX_WIDTH, + minWidth: FILL_WIDGET_MIN_WIDTH, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/PhoneInputWidget/index.ts b/app/client/src/widgets/PhoneInputWidget/index.ts index 0af5e018bcad..13578e8df19d 100644 --- a/app/client/src/widgets/PhoneInputWidget/index.ts +++ b/app/client/src/widgets/PhoneInputWidget/index.ts @@ -1,5 +1,5 @@ import { LabelPosition, ResponsiveBehavior } from "components/constants"; -import { MOBILE_MAX_WIDTH } from "constants/AppConstants"; +import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; import { CONFIG as BaseConfig } from "widgets/BaseInputWidget"; import { getDefaultISDCode } from "./component/ISDCodeDropdown"; import IconSVG from "./icon.svg"; @@ -21,7 +21,7 @@ export const CONFIG = { allowDialCodeChange: false, allowFormatting: true, responsiveBehavior: ResponsiveBehavior.Fill, - minWidth: MOBILE_MAX_WIDTH, + minWidth: FILL_WIDGET_MIN_WIDTH, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/RichTextEditorWidget/index.ts b/app/client/src/widgets/RichTextEditorWidget/index.ts index 0f73c3970b18..f96b35601094 100644 --- a/app/client/src/widgets/RichTextEditorWidget/index.ts +++ b/app/client/src/widgets/RichTextEditorWidget/index.ts @@ -1,6 +1,6 @@ import { Alignment } from "@blueprintjs/core"; import { LabelPosition, ResponsiveBehavior } from "components/constants"; -import { MOBILE_MAX_WIDTH } from "constants/AppConstants"; +import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; import IconSVG from "./icon.svg"; import Widget from "./widget"; @@ -27,7 +27,7 @@ export const CONFIG = { labelWidth: 5, version: 1, responsiveBehavior: ResponsiveBehavior.Fill, - minWidth: MOBILE_MAX_WIDTH, + minWidth: FILL_WIDGET_MIN_WIDTH, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/SelectWidget/index.ts b/app/client/src/widgets/SelectWidget/index.ts index 89a8923998c3..bb3443422d5d 100644 --- a/app/client/src/widgets/SelectWidget/index.ts +++ b/app/client/src/widgets/SelectWidget/index.ts @@ -3,7 +3,7 @@ import IconSVG from "./icon.svg"; import { LabelPosition } from "components/constants"; import { Alignment } from "@blueprintjs/core"; import { ResponsiveBehavior } from "components/constants"; -import { MOBILE_MAX_WIDTH } from "constants/AppConstants"; +import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; export const CONFIG = { type: Widget.getWidgetType(), @@ -34,7 +34,7 @@ export const CONFIG = { animateLoading: true, labelTextSize: "0.875rem", responsiveBehavior: ResponsiveBehavior.Fill, - minWidth: MOBILE_MAX_WIDTH, + minWidth: FILL_WIDGET_MIN_WIDTH, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/SingleSelectTreeWidget/index.ts b/app/client/src/widgets/SingleSelectTreeWidget/index.ts index c7e9438a15b4..157697294dc0 100644 --- a/app/client/src/widgets/SingleSelectTreeWidget/index.ts +++ b/app/client/src/widgets/SingleSelectTreeWidget/index.ts @@ -3,7 +3,7 @@ import { LabelPosition } from "components/constants"; import IconSVG from "./icon.svg"; import Widget from "./widget"; import { ResponsiveBehavior } from "components/constants"; -import { MOBILE_MAX_WIDTH } from "constants/AppConstants"; +import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; export const CONFIG = { type: Widget.getWidgetType(), @@ -48,7 +48,7 @@ export const CONFIG = { labelWidth: 5, labelTextSize: "0.875rem", responsiveBehavior: ResponsiveBehavior.Fill, - minWidth: MOBILE_MAX_WIDTH, + minWidth: FILL_WIDGET_MIN_WIDTH, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/TableWidgetV2/index.ts b/app/client/src/widgets/TableWidgetV2/index.ts index 4a5d89177c4f..d7e7657214b8 100644 --- a/app/client/src/widgets/TableWidgetV2/index.ts +++ b/app/client/src/widgets/TableWidgetV2/index.ts @@ -11,7 +11,7 @@ import IconSVG from "./icon.svg"; import Widget from "./widget"; import { escapeString } from "./widget/utilities"; import { ResponsiveBehavior } from "components/constants"; -import { MOBILE_MAX_WIDTH } from "constants/AppConstants"; +import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; export const CONFIG = { type: Widget.getWidgetType(), @@ -20,7 +20,7 @@ export const CONFIG = { needsMeta: true, defaults: { responsiveBehavior: ResponsiveBehavior.Fill, - minWidth: MOBILE_MAX_WIDTH, + minWidth: FILL_WIDGET_MIN_WIDTH, rows: 28, columns: 34, animateLoading: true, diff --git a/app/client/src/widgets/TabsWidget/index.ts b/app/client/src/widgets/TabsWidget/index.ts index 05c6a5a54942..dd005f14e6bc 100644 --- a/app/client/src/widgets/TabsWidget/index.ts +++ b/app/client/src/widgets/TabsWidget/index.ts @@ -4,7 +4,7 @@ import { BlueprintOperationTypes } from "widgets/constants"; import IconSVG from "./icon.svg"; import Widget from "./widget"; import { Positioning, ResponsiveBehavior } from "components/constants"; -import { MOBILE_MAX_WIDTH } from "constants/AppConstants"; +import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; export const CONFIG = { type: Widget.getWidgetType(), @@ -14,7 +14,7 @@ export const CONFIG = { isCanvas: true, defaults: { responsiveBehavior: ResponsiveBehavior.Fill, - minWidth: MOBILE_MAX_WIDTH, + minWidth: FILL_WIDGET_MIN_WIDTH, rows: 40, columns: 24, shouldScrollContents: false, diff --git a/app/client/src/widgets/TextWidget/index.ts b/app/client/src/widgets/TextWidget/index.ts index 45d25e1dfd92..a78c6884b2f8 100644 --- a/app/client/src/widgets/TextWidget/index.ts +++ b/app/client/src/widgets/TextWidget/index.ts @@ -1,5 +1,5 @@ import { ResponsiveBehavior } from "components/constants"; -import { MOBILE_MAX_WIDTH } from "constants/AppConstants"; +import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; import { DEFAULT_FONT_SIZE } from "constants/WidgetConstants"; import { OverflowTypes } from "./constants"; import IconSVG from "./icon.svg"; @@ -24,7 +24,7 @@ export const CONFIG = { version: 1, animateLoading: true, responsiveBehavior: ResponsiveBehavior.Fill, - minWidth: MOBILE_MAX_WIDTH, + minWidth: FILL_WIDGET_MIN_WIDTH, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/withWidgetProps.tsx b/app/client/src/widgets/withWidgetProps.tsx index 8bd13a7f2d0d..8a6b5fa1a000 100644 --- a/app/client/src/widgets/withWidgetProps.tsx +++ b/app/client/src/widgets/withWidgetProps.tsx @@ -101,13 +101,29 @@ function withWidgetProps(WrappedWidget: typeof BaseWidget) { * For Mobile Viewport: * Adjust right column to accommodate specified minWidth. */ + console.log( + "#### check:", + widgetProps.widgetName, + isMobile, + props.isFlexChild, + widgetProps.minWidth, + ); if (props.isFlexChild && isMobile && widgetProps.minWidth) { const { minWidth, parentColumnSpace, rightColumn } = widgetProps; - if (parentColumnSpace * rightColumn < minWidth) + if (parentColumnSpace * rightColumn < minWidth) { widgetProps.rightColumn = Math.min( Math.floor(minWidth / parentColumnSpace), 64, ); + console.log( + "#### data", + widgetProps.widgetName, + minWidth, + parentColumnSpace * rightColumn, + Math.min(Math.floor(minWidth / parentColumnSpace), 64), + widgetProps.rightColumn, + ); + } } widgetProps.children = children; @@ -116,7 +132,12 @@ function withWidgetProps(WrappedWidget: typeof BaseWidget) { widgetProps.childWidgets = childWidgets; widgetProps.foo = "bar"; } - + console.log( + "$$$$ widget props", + widgetProps.widgetName, + widgetProps, + props, + ); //merging with original props widgetProps = { ...props, ...widgetProps, renderMode }; From ba2af82ecb389aa66741fec97647269d06ba2e94 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Thu, 24 Nov 2022 07:39:30 +0530 Subject: [PATCH 264/708] Fixing layout resizer --- app/client/src/utils/hooks/useDynamicAppLayout.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/client/src/utils/hooks/useDynamicAppLayout.tsx b/app/client/src/utils/hooks/useDynamicAppLayout.tsx index cb0dba7c6088..95d0305454b8 100644 --- a/app/client/src/utils/hooks/useDynamicAppLayout.tsx +++ b/app/client/src/utils/hooks/useDynamicAppLayout.tsx @@ -103,7 +103,7 @@ export const useDynamicAppLayout = () => { calculatedWidth -= explorerWidth; } - const ele: any = document.getElementById("main-canvas-container"); + const ele: any = document.getElementById("canvas-viewport"); if ( appMode === "EDIT" && appLayout?.type === "FLUID" && @@ -163,7 +163,7 @@ export const useDynamicAppLayout = () => { const resizeObserver = new ResizeObserver(immediateDebouncedResize); useEffect(() => { - const ele: any = document.getElementById("main-canvas-container"); + const ele: any = document.getElementById("canvas-viewport"); if (ele) { if (appLayout?.type === "FLUID") { resizeObserver.observe(ele); From 9e2b5ebb07e7240e9f8260dfbfa3056c19c851d8 Mon Sep 17 00:00:00 2001 From: Preet Date: Fri, 25 Nov 2022 11:36:14 -0500 Subject: [PATCH 265/708] fix missing drop positions --- app/client/src/actions/autoLayoutActions.ts | 2 ++ .../appsmith/autoLayout/AutoLayoutLayer.tsx | 22 ++++++++------- .../appsmith/autoLayout/FlexBoxComponent.tsx | 21 +++++++++------ .../editorComponents/ResizableComponent.tsx | 6 ++++- app/client/src/constants/minWidthConstants.ts | 2 +- app/client/src/sagas/AutoLayoutUtils.ts | 27 +++++++++++++++---- app/client/src/sagas/LayoutUpdateSagas.tsx | 5 ++-- .../src/utils/hooks/useDynamicAppLayout.tsx | 15 +++++++---- app/client/src/widgets/BaseWidget.tsx | 14 +++++++--- app/client/src/widgets/withWidgetProps.tsx | 3 +++ 10 files changed, 82 insertions(+), 35 deletions(-) diff --git a/app/client/src/actions/autoLayoutActions.ts b/app/client/src/actions/autoLayoutActions.ts index f2f0e747d37d..49b2e3f8778c 100644 --- a/app/client/src/actions/autoLayoutActions.ts +++ b/app/client/src/actions/autoLayoutActions.ts @@ -15,10 +15,12 @@ export const addWrappers = (parentId: string) => ({ export const updateLayoutForMobileBreakpoint = ( parentId: string, isMobile: boolean, + canvasWidth: number, ) => ({ type: ReduxActionTypes.RECALCULATE_COLUMNS, payload: { parentId, isMobile, + canvasWidth, }, }); diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx index 97939ca41bae..8ba30cd725fa 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx @@ -22,22 +22,24 @@ export interface AutoLayoutLayerProps { isCurrentCanvasDragging: boolean; currentChildCount: number; hideOnLoad?: boolean; + wrapStart: boolean; + wrapCenter: boolean; + wrapEnd: boolean; } const LayoutLayerContainer = styled.div<{ flexDirection: FlexDirection; hideOnLoad?: boolean; isCurrentCanvasDragging: boolean; + wrap?: boolean; }>` display: ${({ hideOnLoad }) => (hideOnLoad ? "none" : "flex")}; flex-direction: ${({ flexDirection }) => flexDirection || FlexDirection.Row}; justify-content: flex-start; align-items: flex-start; + flex-wrap: ${({ wrap }) => (wrap ? "wrap" : "nowrap")}; width: 100%; - height: auto; - min-height: ${({ isCurrentCanvasDragging }) => - isCurrentCanvasDragging ? "40px" : "auto"}; margin-top: ${DRAG_MARGIN}px; `; @@ -49,10 +51,9 @@ const SubWrapper = styled.div<{ flex: 1 1 33.3%; display: flex; flex-direction: ${({ flexDirection }) => flexDirection || "row"}; - align-items: "flex-start"; + align-items: flex-start; + align-self: stretch; flex-wrap: ${({ wrap }) => (wrap ? "wrap" : "nowrap")}; - height: ${({ isCurrentCanvasDragging }) => - isCurrentCanvasDragging ? "100%" : "auto"}; `; const StartWrapper = styled(SubWrapper)` @@ -88,25 +89,28 @@ function AutoLayoutLayer(props: AutoLayoutLayerProps) { flexDirection={flexDirection} hideOnLoad={props.hideOnLoad} isCurrentCanvasDragging={props.isCurrentCanvasDragging} + wrap={ + props.isMobile && (props.wrapStart || props.wrapCenter || props.wrapEnd) + } > {props.start} {props.center} {props.end} diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx index 84a946befa7c..76ad36e3bec1 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx @@ -1,6 +1,6 @@ import { isArray } from "lodash"; import React, { ReactNode } from "react"; -import styled from "styled-components"; +import styled, { css } from "styled-components"; import { AppState } from "ce/reducers"; import { @@ -202,7 +202,6 @@ function FlexBoxComponent(props: FlexBoxProps) { return ( @@ -289,9 +288,12 @@ function FlexBoxComponent(props: FlexBoxProps) { return res; }; - function getColumns(id: string): number { - if (!allWidgets[id]) return 0; - return allWidgets[id].rightColumn; + function getColumns(id: string, isMobile: boolean): number { + const widget = allWidgets[id]; + if (!widget) return 0; + return isMobile && widget.mobileRightColumn + ? widget.mobileRightColumn + : widget.rightColumn; } function processLayers(map: { [key: string]: any }) { @@ -381,13 +383,13 @@ function FlexBoxComponent(props: FlexBoxProps) { if (child.align === "end") { end.push(widget); - endColumns += getColumns(child.id); + endColumns += getColumns(child.id, isMobile); } else if (child.align === "center") { center.push(widget); - centerColumns += getColumns(child.id); + centerColumns += getColumns(child.id, isMobile); } else { start.push(widget); - startColumns += getColumns(child.id); + startColumns += getColumns(child.id, isMobile); } } /** @@ -441,6 +443,9 @@ function FlexBoxComponent(props: FlexBoxProps) { key={index} start={start} widgetId={props.widgetId} + wrapCenter={centerColumns > 64} + wrapEnd={endColumns > 64} + wrapStart={startColumns > 64} /> ), count, diff --git a/app/client/src/components/editorComponents/ResizableComponent.tsx b/app/client/src/components/editorComponents/ResizableComponent.tsx index 5b156d8fbb1a..3686c30f35cf 100644 --- a/app/client/src/components/editorComponents/ResizableComponent.tsx +++ b/app/client/src/components/editorComponents/ResizableComponent.tsx @@ -109,7 +109,11 @@ export const ResizableComponent = memo(function ResizableComponent( // The ResizableContainer's size prop is controlled const dimensions: UIElementSize = { width: - (props.rightColumn - props.leftColumn) * props.parentColumnSpace - + ((props.isMobile && props.mobileRightColumn + ? props.mobileRightColumn + : props.rightColumn) - + props.leftColumn) * + props.parentColumnSpace - 2 * props.paddingOffset, height: (props.bottomRow - props.topRow) * props.parentRowSpace - diff --git a/app/client/src/constants/minWidthConstants.ts b/app/client/src/constants/minWidthConstants.ts index d2e61763b83d..59fd4eb1ea8e 100644 --- a/app/client/src/constants/minWidthConstants.ts +++ b/app/client/src/constants/minWidthConstants.ts @@ -1,5 +1,5 @@ import { MOBILE_MAX_WIDTH } from "./AppConstants"; export const FILL_WIDGET_MIN_WIDTH: number = MOBILE_MAX_WIDTH; -export const ICON_BUTTON_MIN_WIDTH = 40; +export const ICON_BUTTON_MIN_WIDTH = 50; export const BUTTON_MIN_WIDTH = 120; diff --git a/app/client/src/sagas/AutoLayoutUtils.ts b/app/client/src/sagas/AutoLayoutUtils.ts index 9f9dd29b6359..f7381d8bf34e 100644 --- a/app/client/src/sagas/AutoLayoutUtils.ts +++ b/app/client/src/sagas/AutoLayoutUtils.ts @@ -247,24 +247,41 @@ export function updateSizeOfAllChildren( export function alterLayoutForMobile( allWidgets: CanvasWidgetsReduxState, parentId: string, + canvasWidth: number, ): CanvasWidgetsReduxState { let widgets = { ...allWidgets }; const parent = widgets[parentId]; const children = parent.children; - if (checkIsNotVerticalStack(parent) && parent.widgetId !== "0") + if (checkIsNotVerticalStack(parent) && parent.widgetId !== "0") { return widgets; + } if (!children || !children.length) return widgets; for (const child of children) { const widget = { ...widgets[child] }; - if (widget.responsiveBehavior === ResponsiveBehavior.Fill) { - widget.rightColumn = 64; - widget.leftColumn = 0; + // if (widget.responsiveBehavior === ResponsiveBehavior.Fill) { + // widget.rightColumn = 64; + // widget.leftColumn = 0; + // } + if (widget.minWidth && !widget.mobileRightColumn) { + const { minWidth, rightColumn } = widget; + const columnSpace = canvasWidth / 64; + if (columnSpace * rightColumn < minWidth) { + widget.leftColumn = 0; + widget.mobileRightColumn = Math.min( + Math.floor(minWidth / columnSpace), + 64, + ); + } } - widgets = alterLayoutForMobile(widgets, child); + widgets = alterLayoutForMobile( + widgets, + child, + (canvasWidth * (widget.mobileRightColumn || 1)) / 64, + ); widgets[child] = widget; } return widgets; diff --git a/app/client/src/sagas/LayoutUpdateSagas.tsx b/app/client/src/sagas/LayoutUpdateSagas.tsx index 6dc2c59c1743..8bafdebf5658 100644 --- a/app/client/src/sagas/LayoutUpdateSagas.tsx +++ b/app/client/src/sagas/LayoutUpdateSagas.tsx @@ -98,14 +98,15 @@ export function* updateLayoutForMobileCheckpoint( actionPayload: ReduxAction<{ parentId: string; isMobile: boolean; + canvasWidth: number; }>, ) { try { const start = performance.now(); - const { isMobile, parentId } = actionPayload.payload; + const { canvasWidth, isMobile, parentId } = actionPayload.payload; const allWidgets: CanvasWidgetsReduxState = yield select(getWidgets); const updatedWidgets: CanvasWidgetsReduxState = isMobile - ? alterLayoutForMobile(allWidgets, parentId) + ? alterLayoutForMobile(allWidgets, parentId, canvasWidth) : alterLayoutForDesktop(allWidgets, parentId); yield put(updateAndSaveLayout(updatedWidgets)); log.debug( diff --git a/app/client/src/utils/hooks/useDynamicAppLayout.tsx b/app/client/src/utils/hooks/useDynamicAppLayout.tsx index cb0dba7c6088..ab7ede38d325 100644 --- a/app/client/src/utils/hooks/useDynamicAppLayout.tsx +++ b/app/client/src/utils/hooks/useDynamicAppLayout.tsx @@ -7,6 +7,7 @@ import { updateCanvasLayoutAction } from "actions/editorActions"; import { DefaultLayoutType, layoutConfigurations, + MAIN_CONTAINER_WIDGET_ID, } from "constants/WidgetConstants"; import { APP_MODE } from "entities/App"; import { @@ -80,8 +81,6 @@ export const useDynamicAppLayout = () => { * - if calculated width is larger than max width, use max width * - by default use min width * - * @param screenWidth - * @param layoutMaxWidth * @returns */ const calculateCanvasWidth = () => { @@ -103,7 +102,7 @@ export const useDynamicAppLayout = () => { calculatedWidth -= explorerWidth; } - const ele: any = document.getElementById("main-canvas-container"); + const ele: any = document.getElementById("canvas-viewport"); if ( appMode === "EDIT" && appLayout?.type === "FLUID" && @@ -163,7 +162,7 @@ export const useDynamicAppLayout = () => { const resizeObserver = new ResizeObserver(immediateDebouncedResize); useEffect(() => { - const ele: any = document.getElementById("main-canvas-container"); + const ele: any = document.getElementById("canvas-viewport"); if (ele) { if (appLayout?.type === "FLUID") { resizeObserver.observe(ele); @@ -213,7 +212,13 @@ export const useDynamicAppLayout = () => { useEffect(() => { function relayoutAtBreakpoint() { - dispatch(updateLayoutForMobileBreakpoint("0", mainCanvasProps?.isMobile)); + dispatch( + updateLayoutForMobileBreakpoint( + MAIN_CONTAINER_WIDGET_ID, + mainCanvasProps?.isMobile, + calculateCanvasWidth(), + ), + ); } relayoutAtBreakpoint(); }, [mainCanvasProps?.isMobile]); diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index 2be79ff76cff..5f0a24bfb397 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -191,7 +191,8 @@ abstract class BaseWidget< this.props.bottomRow, this.props.parentColumnSpace, this.props.parentRowSpace, - this.props.minWidth, + this.props.mobileRightColumn, + this.props.isMobile, ); }; @@ -202,13 +203,16 @@ abstract class BaseWidget< bottomRow: number, parentColumnSpace: number, parentRowSpace: number, - minWidth?: number, + mobileRightColumn?: number, + isMobile?: boolean, ): { componentWidth: number; componentHeight: number; } { + const right = + isMobile && mobileRightColumn ? mobileRightColumn : rightColumn; return { - componentWidth: (rightColumn - leftColumn) * parentColumnSpace, + componentWidth: (right - leftColumn) * parentColumnSpace, componentHeight: (bottomRow - topRow) * parentRowSpace, }; } @@ -373,7 +377,7 @@ abstract class BaseWidget< private getWidgetView(): ReactNode { let content: ReactNode; - console.log("^^^^ widget props", this.props.widgetName, this.props); + switch (this.props.renderMode) { case RenderModes.CANVAS: content = this.getWidgetComponent(); @@ -477,6 +481,7 @@ export type WidgetRowCols = { topRow: number; bottomRow: number; minHeight?: number; // Required to reduce the size of CanvasWidgets. + mobileRightColumn?: number; }; export interface WidgetPositionProps extends WidgetRowCols { @@ -493,6 +498,7 @@ export interface WidgetPositionProps extends WidgetRowCols { direction?: LayoutDirection; responsiveBehavior?: ResponsiveBehavior; minWidth?: number; // Required to avoid squishing of widgets on mobile viewport. + isMobile?: boolean; } export const WIDGET_STATIC_PROPS = { diff --git a/app/client/src/widgets/withWidgetProps.tsx b/app/client/src/widgets/withWidgetProps.tsx index 8a6b5fa1a000..b8a2b6b261c0 100644 --- a/app/client/src/widgets/withWidgetProps.tsx +++ b/app/client/src/widgets/withWidgetProps.tsx @@ -66,6 +66,9 @@ function withWidgetProps(WrappedWidget: typeof BaseWidget) { })(); widgetProps = { ...canvasWidgetProps }; + + widgetProps.isMobile = !!isMobile; + /** * MODAL_WIDGET by default is to be hidden unless the isVisible property is found. * If the isVisible property is undefined and the widget is MODAL_WIDGET then isVisible From cbd2e8fc4235632045722e60eb993a821cf0f5b5 Mon Sep 17 00:00:00 2001 From: Preet Date: Fri, 25 Nov 2022 12:34:04 -0500 Subject: [PATCH 266/708] fix canvas column space issue --- .../appsmith/autoLayout/AutoLayoutLayer.tsx | 5 ++- .../appsmith/autoLayout/FlexBoxComponent.tsx | 1 + app/client/src/widgets/BaseWidget.tsx | 4 ++- app/client/src/widgets/withWidgetProps.tsx | 36 ------------------- 4 files changed, 6 insertions(+), 40 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx index 8ba30cd725fa..2c087940753c 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx @@ -25,6 +25,7 @@ export interface AutoLayoutLayerProps { wrapStart: boolean; wrapCenter: boolean; wrapEnd: boolean; + wrapLayer: boolean; } const LayoutLayerContainer = styled.div<{ @@ -89,9 +90,7 @@ function AutoLayoutLayer(props: AutoLayoutLayerProps) { flexDirection={flexDirection} hideOnLoad={props.hideOnLoad} isCurrentCanvasDragging={props.isCurrentCanvasDragging} - wrap={ - props.isMobile && (props.wrapStart || props.wrapCenter || props.wrapEnd) - } + wrap={props.isMobile && props.wrapLayer} > 64} wrapEnd={endColumns > 64} + wrapLayer={startColumns + centerColumns + endColumns > 64} wrapStart={startColumns > 64} /> ), diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index 5f0a24bfb397..9d0dc70ed204 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -210,7 +210,9 @@ abstract class BaseWidget< componentHeight: number; } { const right = - isMobile && mobileRightColumn ? mobileRightColumn : rightColumn; + isMobile && mobileRightColumn && parentColumnSpace !== 1 + ? mobileRightColumn + : rightColumn; return { componentWidth: (right - leftColumn) * parentColumnSpace, componentHeight: (bottomRow - topRow) * parentRowSpace, diff --git a/app/client/src/widgets/withWidgetProps.tsx b/app/client/src/widgets/withWidgetProps.tsx index b8a2b6b261c0..e5ee65a48fd1 100644 --- a/app/client/src/widgets/withWidgetProps.tsx +++ b/app/client/src/widgets/withWidgetProps.tsx @@ -100,47 +100,11 @@ function withWidgetProps(WrappedWidget: typeof BaseWidget) { if ("isFormValid" in props) widgetProps.isFormValid = props.isFormValid; } - /** - * For Mobile Viewport: - * Adjust right column to accommodate specified minWidth. - */ - console.log( - "#### check:", - widgetProps.widgetName, - isMobile, - props.isFlexChild, - widgetProps.minWidth, - ); - if (props.isFlexChild && isMobile && widgetProps.minWidth) { - const { minWidth, parentColumnSpace, rightColumn } = widgetProps; - if (parentColumnSpace * rightColumn < minWidth) { - widgetProps.rightColumn = Math.min( - Math.floor(minWidth / parentColumnSpace), - 64, - ); - console.log( - "#### data", - widgetProps.widgetName, - minWidth, - parentColumnSpace * rightColumn, - Math.min(Math.floor(minWidth / parentColumnSpace), 64), - widgetProps.rightColumn, - ); - } - } - widgetProps.children = children; widgetProps.isLoading = isLoading; widgetProps.childWidgets = childWidgets; - widgetProps.foo = "bar"; } - console.log( - "$$$$ widget props", - widgetProps.widgetName, - widgetProps, - props, - ); //merging with original props widgetProps = { ...props, ...widgetProps, renderMode }; From 113b632e7b9a5040c9eed57b50862be02aa933d7 Mon Sep 17 00:00:00 2001 From: Preet Date: Fri, 25 Nov 2022 12:49:11 -0500 Subject: [PATCH 267/708] merge fix --- .../designSystems/appsmith/autoLayout/FlexBoxComponent.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx index 1ec720fb7f2e..9954ebb91e31 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx @@ -1,6 +1,6 @@ import { isArray } from "lodash"; import React, { ReactNode } from "react"; -import styled, { css } from "styled-components"; +import styled from "styled-components"; import { AppState } from "ce/reducers"; import { From 0e5b98e18cb6140ef31c3fd3c9cc7e0f9ddf99c5 Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 28 Nov 2022 10:35:40 -0500 Subject: [PATCH 268/708] fix layer wrap --- .../designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx index 2c087940753c..5708f747d65c 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx @@ -49,7 +49,7 @@ const SubWrapper = styled.div<{ flexDirection: FlexDirection; wrap?: boolean; }>` - flex: 1 1 33.3%; + flex: ${({ wrap }) => `1 1 ${wrap ? "100" : "33.3"}%`}; display: flex; flex-direction: ${({ flexDirection }) => flexDirection || "row"}; align-items: flex-start; From f56d761f0a2afb73a088726203ae79a153c41fc2 Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 28 Nov 2022 11:04:14 -0500 Subject: [PATCH 269/708] update fill to hug only for horizontal resize --- .../editorComponents/ResizableComponent.tsx | 6 +-- .../src/resizable/resizenreflow/index.tsx | 44 ++++++++++++------- 2 files changed, 30 insertions(+), 20 deletions(-) diff --git a/app/client/src/components/editorComponents/ResizableComponent.tsx b/app/client/src/components/editorComponents/ResizableComponent.tsx index 3686c30f35cf..8ad3da3d9d6d 100644 --- a/app/client/src/components/editorComponents/ResizableComponent.tsx +++ b/app/client/src/components/editorComponents/ResizableComponent.tsx @@ -234,13 +234,13 @@ export const ResizableComponent = memo(function ResizableComponent( }); }; - const handleResizeStart = () => { + const handleResizeStart = (affectsWidth = false) => { setIsResizing && !isResizing && setIsResizing(true); selectWidget && !isLastSelected && selectWidget(props.widgetId); // Make sure that this tableFilterPane should close showTableFilterPane && showTableFilterPane(); - // If resizing a fill widget, then convert it to a hug widget. - if (props.responsiveBehavior === ResponsiveBehavior.Fill) + // If resizing a fill widget "horizontally", then convert it to a hug widget. + if (props.responsiveBehavior === ResponsiveBehavior.Fill && affectsWidth) dispatch( batchUpdateMultipleWidgetProperties([ { diff --git a/app/client/src/resizable/resizenreflow/index.tsx b/app/client/src/resizable/resizenreflow/index.tsx index 62b278d91902..2abcabbe0da0 100644 --- a/app/client/src/resizable/resizenreflow/index.tsx +++ b/app/client/src/resizable/resizenreflow/index.tsx @@ -143,7 +143,7 @@ type ResizableProps = { resizedPositions?: OccupiedSpace; }; originalPositions: OccupiedSpace; - onStart: () => void; + onStart: (affectsWidth?: boolean) => void; onStop: ( size: { width: number; height: number }, position: { x: number; y: number }, @@ -336,6 +336,7 @@ export function ReflowResizable(props: ResizableProps) { }); }, component: props.handles.left, + affectsWidth: true, }); } @@ -352,6 +353,7 @@ export function ReflowResizable(props: ResizableProps) { }); }, component: props.handles.top, + affectsWidth: false, }); } @@ -368,6 +370,7 @@ export function ReflowResizable(props: ResizableProps) { }); }, component: props.handles.right, + affectsWidth: true, }); } @@ -384,6 +387,7 @@ export function ReflowResizable(props: ResizableProps) { }); }, component: props.handles.bottom, + affectsWidth: false, }); } @@ -401,6 +405,7 @@ export function ReflowResizable(props: ResizableProps) { }); }, component: props.handles.topLeft, + affectsWidth: true, }); } @@ -418,6 +423,7 @@ export function ReflowResizable(props: ResizableProps) { }); }, component: props.handles.topRight, + affectsWidth: true, }); } @@ -435,6 +441,7 @@ export function ReflowResizable(props: ResizableProps) { }); }, component: props.handles.bottomRight, + affectsWidth: true, }); } @@ -452,6 +459,7 @@ export function ReflowResizable(props: ResizableProps) { }); }, component: props.handles.bottomLeft, + affectsWidth: true, }); } const onResizeStop = () => { @@ -469,22 +477,24 @@ export function ReflowResizable(props: ResizableProps) { setResizing(false); }; - const renderHandles = handles.map((handle, index) => ( - { - togglePointerEvents(false); - props.onStart(); - setResizing(true); - }} - onStop={onResizeStop} - scrollParent={resizableRef.current} - snapGrid={props.snapGrid} - /> - )); + const renderHandles = handles.map((handle, index) => { + return ( + { + togglePointerEvents(false); + props.onStart(handle?.affectsWidth); + setResizing(true); + }} + onStop={onResizeStop} + scrollParent={resizableRef.current} + snapGrid={props.snapGrid} + /> + ); + }); const widgetWidth = reflowedPosition?.width === undefined From f64e4a92fabf25a93919f14232dd8efdcc4a84aa Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 5 Dec 2022 18:37:12 -0500 Subject: [PATCH 270/708] wip: working for hug widgets --- .../appsmith/autoLayout/AutoLayoutLayer.tsx | 5 +- .../appsmith/autoLayout/FlexBoxComponent.tsx | 43 +--- .../hooks/useAutoLayoutHighlights.ts | 206 +++++++++--------- .../hooks/useBlocksToBeDraggedOnCanvas.ts | 4 + .../src/utils/WidgetPropsUtils.test.tsx | 2 + 5 files changed, 121 insertions(+), 139 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx index 5708f747d65c..ad772870bcb0 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx @@ -21,7 +21,6 @@ export interface AutoLayoutLayerProps { isMobile?: boolean; isCurrentCanvasDragging: boolean; currentChildCount: number; - hideOnLoad?: boolean; wrapStart: boolean; wrapCenter: boolean; wrapEnd: boolean; @@ -30,11 +29,10 @@ export interface AutoLayoutLayerProps { const LayoutLayerContainer = styled.div<{ flexDirection: FlexDirection; - hideOnLoad?: boolean; isCurrentCanvasDragging: boolean; wrap?: boolean; }>` - display: ${({ hideOnLoad }) => (hideOnLoad ? "none" : "flex")}; + display: flex; flex-direction: ${({ flexDirection }) => flexDirection || FlexDirection.Row}; justify-content: flex-start; align-items: flex-start; @@ -88,7 +86,6 @@ function AutoLayoutLayer(props: AutoLayoutLayerProps) { diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx index 9954ebb91e31..7ed30249c7f1 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx @@ -10,11 +10,11 @@ import { } from "components/constants"; import { APP_MODE } from "entities/App"; import { useSelector } from "react-redux"; +import { getWidgets } from "sagas/selectors"; +import { isCurrentCanvasDragging } from "selectors/autoLayoutSelectors"; import { getAppMode } from "selectors/entitiesSelector"; import { getIsMobile } from "selectors/mainCanvasSelectors"; import AutoLayoutLayer from "./AutoLayoutLayer"; -import { isCurrentCanvasDragging } from "selectors/autoLayoutSelectors"; -import { getWidgets } from "sagas/selectors"; export interface FlexBoxProps { direction?: LayoutDirection; @@ -89,12 +89,13 @@ export const DEFAULT_HIGHLIGHT_SIZE = 4; export const DropPosition = styled.div<{ isDragging: boolean; + isNewLayer: boolean; isVertical: boolean; }>` width: ${({ isVertical }) => - isVertical ? `${DEFAULT_HIGHLIGHT_SIZE}px` : "calc(100% - 4px)"}; - height: ${({ isVertical }) => - isVertical ? "auto" : `${DEFAULT_HIGHLIGHT_SIZE}px`}; + isVertical ? `${DEFAULT_HIGHLIGHT_SIZE}px` : "calc(33% - 4px)"}; + height: ${({ isNewLayer, isVertical }) => + isVertical && !isNewLayer ? "auto" : `${DEFAULT_HIGHLIGHT_SIZE}px`}; background-color: rgba(223, 158, 206, 0.6); margin: 2px; display: ${({ isDragging }) => (isDragging ? "block" : "none")}; @@ -106,6 +107,7 @@ export const NewLayerStyled = styled.div<{ isDragging: boolean; }>` width: 100%; + height: ${({ isDragging }) => (isDragging ? "6px" : "0px")}; `; function FlexBoxComponent(props: FlexBoxProps) { @@ -174,22 +176,14 @@ function FlexBoxComponent(props: FlexBoxProps) { props.isVertical ? "isVertical" : "isHorizontal" } ${props.isNewLayer ? "isNewLayer" : ""} row-index-${props.rowIndex}`} isDragging={isDragging} + isNewLayer={props.isNewLayer} isVertical={props.isVertical} /> ); } function NewLayerComponent(props: NewLayerProps): JSX.Element { - const { - alignment, - childCount, - isDragging, - isNewLayer, - isVertical, - layerIndex, - map, - widgetId, - } = props; + const { childCount, isDragging, layerIndex, map, widgetId } = props; const { element: verticalHighlights } = processIndividualLayer( { children: [], hasFillChild: false }, @@ -197,7 +191,6 @@ function FlexBoxComponent(props: FlexBoxProps) { layerIndex, map, true, - true, ); return ( @@ -205,16 +198,6 @@ function FlexBoxComponent(props: FlexBoxProps) { id={`new-layer-${widgetId}-${layerIndex}`} isDragging={isDragging} > - {verticalHighlights} ); @@ -364,7 +347,6 @@ function FlexBoxComponent(props: FlexBoxProps) { childCount: number, index: number, map: { [key: string]: any }, - allowEmptyLayer = false, isNewLayer = false, ) { const { children } = layer; @@ -403,7 +385,7 @@ function FlexBoxComponent(props: FlexBoxProps) { childCount, layerIndex: index, alignment: FlexLayerAlignment.Start, - isVertical: true, + isVertical: !isNewLayer, isNewLayer, addInitialHighlight: !( start.length === 0 && centerColumns + endColumns > 60 @@ -414,7 +396,7 @@ function FlexBoxComponent(props: FlexBoxProps) { childCount: childCount + startLength, layerIndex: index, alignment: FlexLayerAlignment.Center, - isVertical: true, + isVertical: !isNewLayer, isNewLayer, addInitialHighlight: !(startColumns > 25 || endColumns > 25), }); @@ -423,7 +405,7 @@ function FlexBoxComponent(props: FlexBoxProps) { childCount: childCount + startLength + centerLength, layerIndex: index, alignment: FlexLayerAlignment.End, - isVertical: true, + isVertical: !isNewLayer, isNewLayer, addInitialHighlight: !(centerColumns + startColumns > 60), }); @@ -436,7 +418,6 @@ function FlexBoxComponent(props: FlexBoxProps) { direction={direction} end={end} hasFillChild={layer.hasFillChild} - hideOnLoad={allowEmptyLayer} index={index} isCurrentCanvasDragging={isDragging} isMobile={isMobile} diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index b59a1b696166..3ac9eb0e20d8 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -1,6 +1,12 @@ -import { FlexLayerAlignment, LayoutDirection } from "components/constants"; -import { DEFAULT_HIGHLIGHT_SIZE } from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; +import { + FlexLayerAlignment, + LayoutDirection, + ResponsiveBehavior, +} from "components/constants"; import { ReflowDirection } from "reflow/reflowTypes"; +import { getWidgets } from "sagas/selectors"; +import { useSelector } from "store"; +import WidgetFactory from "utils/WidgetFactory"; import { WidgetDraggingBlock } from "./useBlocksToBeDraggedOnCanvas"; interface XYCord { @@ -45,10 +51,12 @@ export const useAutoLayoutHighlights = ({ isDragging, useAutoLayout, }: AutoLayoutHighlightProps) => { + const allWidgets = useSelector(getWidgets); let highlights: HighlightInfo[] = []; let newLayers: { [key: string]: number } = {}; let lastActiveHighlight: HighlightInfo | undefined; let expandedNewLayer: number | undefined; + let isFillWidget = false; const isVerticalStack = direction === LayoutDirection.Vertical; let containerDimensions: { @@ -93,6 +101,7 @@ export const useAutoLayoutHighlights = ({ const getDropPositions = () => { const els = document.querySelectorAll(`.t--drop-position-${canvasId}`); const highlights: HighlightInfo[] = []; + for (const el of els) { const rect: DOMRect = el.getBoundingClientRect(); const classList = Array.from(el.classList); @@ -107,7 +116,6 @@ export const useAutoLayoutHighlights = ({ acc.index = parseInt(curr.split("child-index-")[1]); else if (curr.indexOf("row-index") > -1) acc.rowIndex = parseInt(curr.split("row-index-")[1]); - else if (curr.indexOf("new-layer") > -1) acc.isNewLayer = true; else if (curr.indexOf("isNewLayer") > -1) acc.isNewLayer = true; else if (curr.indexOf("isVertical") > -1) acc.isVertical = true; @@ -134,17 +142,25 @@ export const useAutoLayoutHighlights = ({ return highlights; }; - const updateHighlight = (index: number): HighlightInfo => { - const highlight = highlights[index]; - if (!highlight || !highlight.el) return highlight; - const rect: DOMRect = highlight.el.getBoundingClientRect(); - - highlight.posX = rect.x - containerDimensions.left; - highlight.posY = rect.y - containerDimensions.top; - highlight.width = rect.width; - highlight.height = rect.height; - highlights[index] = highlight; - return highlight; + const checkForFillWidget = () => { + if (!blocksToDraw?.length) return; + for (const block of blocksToDraw) { + const widget = allWidgets[block.widgetId]; + if (widget) { + if (widget.responsiveBehavior === ResponsiveBehavior.Fill) { + isFillWidget = true; + break; + } + continue; + } + const config = WidgetFactory.widgetConfigMap.get(block.widgetId); + if (config) { + if (config.responsiveBehavior === ResponsiveBehavior.Fill) { + isFillWidget = true; + break; + } + } + } }; const calculateHighlights = (): HighlightInfo[] => { @@ -157,7 +173,7 @@ export const useAutoLayoutHighlights = ({ * That implies the container is null. */ if (!updateContainerDimensions()) return []; - + checkForFillWidget(); highlights = getDropPositions(); } // console.log("#### highlights", highlights); @@ -168,54 +184,57 @@ export const useAutoLayoutHighlights = ({ * END AUTO LAYOUT OFFSET CALCULATION */ - const toggleNewLayerAlignments = ( - el: Element | undefined, - reveal: boolean, - ): void => { - if (!el) return; - const horizontalElement = el as HTMLElement; - const verticalElement = el?.nextSibling as HTMLElement; - if (verticalElement) { - if (reveal) { - horizontalElement.style.display = "none"; - verticalElement.style.display = "flex"; - verticalElement.style.height = "40px"; - verticalElement.style.border = "1px dotted rgba(223, 158, 206, 0.6)"; - } else { - (horizontalElement as HTMLElement).style.display = "block"; - verticalElement.style.display = "none"; - } - } + const updateHighlight = (index: number): HighlightInfo => { + const highlight = highlights[index]; + if (!highlight || !highlight.el) return highlight; + const rect: DOMRect = highlight.el.getBoundingClientRect(); + + highlight.posX = rect.x - containerDimensions.left; + highlight.posY = rect.y - containerDimensions.top; + highlight.width = isFillWidget + ? highlight.alignment === FlexLayerAlignment.Start + ? containerDimensions.width + : 0 + : blocksToDraw[0].width; + highlight.height = rect.height; + (highlight.el as HTMLElement).style.width = `${highlight.width}px`; + highlights[index] = highlight; + + return highlight; + }; + + const updateHighlights = (moveDirection?: ReflowDirection) => { + if (!highlights?.length || !moveDirection) return; + const isVerticalDrag = [ + ReflowDirection.TOP, + ReflowDirection.BOTTOM, + ].includes(moveDirection); + highlights.map((highlight: HighlightInfo, index: number) => { + let updatedHighlight: HighlightInfo = highlight; + if ((highlight.isNewLayer && isVerticalDrag) || !highlight.height) + updatedHighlight = updateHighlight(index); + return updatedHighlight; + }); }; const toggleHighlightVisibility = ( arr: HighlightInfo[], - selected: HighlightInfo[], + selected: HighlightInfo, ): void => { arr.forEach((each: HighlightInfo) => { const el = each.el as HTMLElement; - if (!isVerticalStack) el.style.opacity = "1"; - else el.style.opacity = selected.includes(each) ? "1" : "0"; + // if (!isVerticalStack) el.style.opacity = "1"; + el.style.opacity = selected === each ? "1" : "0"; }); }; const updateSelection = (highlight: HighlightInfo): void => { if (lastActiveHighlight) { const lastEl = lastActiveHighlight?.el as HTMLElement; - if (lastEl) { - lastActiveHighlight.isVertical - ? (lastEl.style.width = `${DEFAULT_HIGHLIGHT_SIZE}px`) - : (lastEl.style.height = `${DEFAULT_HIGHLIGHT_SIZE}px`); - lastEl.style.backgroundColor = "rgba(223, 158, 206, 0.6)"; - } + if (lastEl) lastEl.style.backgroundColor = "rgba(223, 158, 206, 0.6)"; } const el = highlight.el as HTMLElement; - if (el) { - highlight.isVertical - ? (el.style.width = `${DEFAULT_HIGHLIGHT_SIZE * 1.5}px`) - : (el.style.height = `${DEFAULT_HIGHLIGHT_SIZE * 1.5}px`); - el.style.backgroundColor = "rgba(196, 139, 181, 1)"; - } + if (el) el.style.backgroundColor = "rgba(196, 139, 181, 1)"; lastActiveHighlight = highlight; }; @@ -249,59 +268,30 @@ export const useAutoLayoutHighlights = ({ }; let filteredHighlights: HighlightInfo[] = []; - const newLayerIndex = isInNewLayerRange(pos.y); - - if (newLayerIndex > -1) { - filteredHighlights = [ - ...base.slice( - newLayerIndex + 1, - Math.min(newLayerIndex + 4, base.length), - ), - ]; - if (filteredHighlights[0].height === 0) { - filteredHighlights[0] = updateHighlight(newLayerIndex + 1); - filteredHighlights[1] = updateHighlight(newLayerIndex + 2); - filteredHighlights[2] = updateHighlight(newLayerIndex + 3); - } - expandedNewLayer !== undefined && - toggleNewLayerAlignments(highlights[expandedNewLayer]?.el, false); - toggleNewLayerAlignments(highlights[newLayerIndex].el, true); - expandedNewLayer = newLayerIndex; - } else { - filteredHighlights = getViableDropPositions(base, pos, moveDirection); - expandedNewLayer !== undefined && - toggleNewLayerAlignments(highlights[expandedNewLayer]?.el, false); - expandedNewLayer = undefined; - } - toggleHighlightVisibility(base, filteredHighlights); + updateHighlights(moveDirection); + filteredHighlights = getViableDropPositions(base, pos, moveDirection); + const arr = filteredHighlights.sort((a, b) => { return ( - calculateDistance(a, pos, moveDirection) - - calculateDistance(b, pos, moveDirection) + calculateDistance( + a, + pos, + moveDirection, + a.isNewLayer && isVerticalStack, + ) - + calculateDistance( + b, + pos, + moveDirection, + a.isNewLayer && isVerticalStack, + ) ); }); + toggleHighlightVisibility(base, arr[0]); // console.log("#### arr", arr, base); return arr[0]; }; - function isInNewLayerRange(y: number): number { - const positions: number[] = Object.values(newLayers); - const keys: string[] = Object.keys(newLayers); - if (!positions || !positions.length) return -1; - const index: number = positions.findIndex((each: number, index: number) => { - const lower: number = - expandedNewLayer !== undefined ? each : Math.max(each - 2, 0); - const upper: number = - expandedNewLayer !== undefined && - expandedNewLayer === parseInt(keys[index]) - ? each + 35 - : each + 14; - return y >= lower && (y <= upper || index === positions.length - 1); - }); - if (index === -1) return -1; - return parseInt(keys[index]); - } - function getViableDropPositions( arr: HighlightInfo[], pos: XYCord, @@ -326,7 +316,12 @@ export const useAutoLayoutHighlights = ({ let filteredHighlights: HighlightInfo[] = arr.filter( (highlight: HighlightInfo) => { // Return only horizontal highlights for vertical drag. - if (isVerticalDrag) return !highlight.isVertical; + if (isVerticalDrag) + return ( + !highlight.isVertical && + pos.x >= highlight.posX && + pos.x <= highlight.posX + highlight.width + ); // Return only vertical highlights for horizontal drag, if they lie in the same x plane. return ( highlight.isVertical && @@ -337,15 +332,12 @@ export const useAutoLayoutHighlights = ({ ); // console.log("#### pos", arr, filteredHighlights, isVerticalDrag); // For horizontal drag, if no vertical highlight exists in the same x plane, - // return the last horizontal highlight. - if (!isVerticalDrag && !filteredHighlights.length) { - const horizontalHighlights = arr.filter( - (highlight: HighlightInfo) => !highlight.isVertical, - ); - filteredHighlights = [ - horizontalHighlights[horizontalHighlights.length - 1], - ]; - } + // return the horizontal highlights for the last layer. + if (!isVerticalDrag && !filteredHighlights.length) + filteredHighlights = arr + .slice(arr.length - 3) + .filter((highlight: HighlightInfo) => !highlight.isVertical); + return filteredHighlights; } @@ -367,6 +359,7 @@ export const useAutoLayoutHighlights = ({ a: HighlightInfo, b: XYCord, moveDirection?: ReflowDirection, + usePerpendicularDistance?: boolean, ): number => { /** * Calculate perpendicular distance of a point from a line. @@ -376,7 +369,12 @@ export const useAutoLayoutHighlights = ({ moveDirection && [ReflowDirection.TOP, ReflowDirection.BOTTOM].includes(moveDirection); - let distX: number = a.isVertical && isVerticalDrag ? 0 : a.posX - b.x; + let distX: number = + a.isVertical && isVerticalDrag + ? usePerpendicularDistance + ? (a.posX + a.width) / 2 - b.x + : 0 + : a.posX - b.x; let distY: number = !a.isVertical && !isVerticalDrag ? 0 : a.posY - b.y; if (moveDirection === ReflowDirection.LEFT && distX > 20) distX += 2000; diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts b/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts index 12e92acb0589..d9f1238524fa 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts @@ -48,6 +48,7 @@ export type WidgetDraggingBlock = { widgetId: string; isNotColliding: boolean; detachFromLayout?: boolean; + type: string; }; const containerJumpMetrics = new ContainerJumpMetrics<{ @@ -232,6 +233,7 @@ export const useBlocksToBeDraggedOnCanvas = ({ widgetId: newWidget.widgetId, detachFromLayout: newWidget.detachFromLayout, isNotColliding: true, + type: newWidget.type, }, ], draggingSpaces: [ @@ -259,6 +261,7 @@ export const useBlocksToBeDraggedOnCanvas = ({ rowHeight: each.bottom - each.top, widgetId: each.id, isNotColliding: true, + type: allWidgets[each.id].type, })), }; } @@ -354,6 +357,7 @@ export const useBlocksToBeDraggedOnCanvas = ({ widgetId: widget.widgetId, isNotColliding: true, detachFromLayout: widget.detachFromLayout, + type: widget.type, }; }, ); diff --git a/app/client/src/utils/WidgetPropsUtils.test.tsx b/app/client/src/utils/WidgetPropsUtils.test.tsx index 3d6f706fce7a..d3e7bd45acfb 100644 --- a/app/client/src/utils/WidgetPropsUtils.test.tsx +++ b/app/client/src/utils/WidgetPropsUtils.test.tsx @@ -32,6 +32,7 @@ describe("WidgetProps tests", () => { isNotColliding: true, columnWidth: 10, rowHeight: 10, + type: "", }, { left: 310, @@ -42,6 +43,7 @@ describe("WidgetProps tests", () => { isNotColliding: true, columnWidth: 10, rowHeight: 10, + type: "", }, ]; const draggingSpaces = [ From a4692a0ff357d021258ead31ee376dff65254fe2 Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 5 Dec 2022 18:49:22 -0500 Subject: [PATCH 271/708] full width for fill widgets --- .../hooks/useAutoLayoutHighlights.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index 3ac9eb0e20d8..cd51de80dc2d 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -55,7 +55,6 @@ export const useAutoLayoutHighlights = ({ let highlights: HighlightInfo[] = []; let newLayers: { [key: string]: number } = {}; let lastActiveHighlight: HighlightInfo | undefined; - let expandedNewLayer: number | undefined; let isFillWidget = false; const isVerticalStack = direction === LayoutDirection.Vertical; @@ -93,7 +92,6 @@ export const useAutoLayoutHighlights = ({ const cleanUpTempStyles = () => { // reset state lastActiveHighlight = undefined; - expandedNewLayer = undefined; highlights = []; newLayers = {}; }; @@ -142,25 +140,27 @@ export const useAutoLayoutHighlights = ({ return highlights; }; - const checkForFillWidget = () => { - if (!blocksToDraw?.length) return; + const checkForFillWidget = (): boolean => { + let flag = false; + if (!blocksToDraw?.length) return flag; for (const block of blocksToDraw) { const widget = allWidgets[block.widgetId]; if (widget) { if (widget.responsiveBehavior === ResponsiveBehavior.Fill) { - isFillWidget = true; + flag = true; break; } continue; } - const config = WidgetFactory.widgetConfigMap.get(block.widgetId); + const config = WidgetFactory.widgetConfigMap.get(block.type); if (config) { if (config.responsiveBehavior === ResponsiveBehavior.Fill) { - isFillWidget = true; + flag = true; break; } } } + return flag; }; const calculateHighlights = (): HighlightInfo[] => { @@ -173,7 +173,7 @@ export const useAutoLayoutHighlights = ({ * That implies the container is null. */ if (!updateContainerDimensions()) return []; - checkForFillWidget(); + isFillWidget = checkForFillWidget(); highlights = getDropPositions(); } // console.log("#### highlights", highlights); From ff8cd4c15cff6539670e0349edbeeb842000ab08 Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 5 Dec 2022 19:55:32 -0500 Subject: [PATCH 272/708] fix drag width --- .../appsmith/autoLayout/FlexComponent.tsx | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx index a0f6bf4e05bf..ad60f09c1ecd 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx @@ -62,7 +62,7 @@ const FlexWidget = styled.div<{ z-index: ${({ zIndexOnHover }) => zIndexOnHover} !important; } margin: ${({ dragMargin, isAffectedByDrag }) => - isAffectedByDrag ? `${DRAG_MARGIN}px ${dragMargin / 2}px` : "0px"}; + isAffectedByDrag ? `2px ${dragMargin / 2}px` : "0px"}; `; const DEFAULT_MARGIN = 20; @@ -108,10 +108,6 @@ export function FlexComponent(props: AutoLayoutProps) { props.widgetId } ${widgetTypeClassname(props.widgetType)}`; - // const minWidth = - // props.responsiveBehavior === ResponsiveBehavior.Fill && isMobile - // ? "100%" - // : props.minWidth + "px"; const dragMargin = props.parentId === MAIN_CONTAINER_WIDGET_ID ? DEFAULT_MARGIN @@ -120,13 +116,20 @@ export function FlexComponent(props: AutoLayoutProps) { isCurrentCanvasDragging || (isDragging && props.parentId === MAIN_CONTAINER_WIDGET_ID); // TODO: Simplify this logic. + /** + * resize logic: + * if isAffectedByDrag + * newWidth = width - drag margin - + * (decrease in parent width) / # of siblings - + * width of the DropPosition introduced due to the widget. + */ const resizedWidth: number = isAffectedByDrag ? props.componentWidth - dragMargin - (props.parentId !== MAIN_CONTAINER_WIDGET_ID && siblingCount > 0 ? DEFAULT_MARGIN / siblingCount : 0) - - ((siblingCount || 0) + 1) * 6 + (props.parentId !== MAIN_CONTAINER_WIDGET_ID ? 8 : 0) : props.componentWidth; return ( From 16d5fe8d3424d0e690caa9cfa54c243ab5d9929c Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 5 Dec 2022 20:20:38 -0500 Subject: [PATCH 273/708] remove horizontal stack option --- app/client/src/utils/layoutPropertiesUtils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/client/src/utils/layoutPropertiesUtils.ts b/app/client/src/utils/layoutPropertiesUtils.ts index 467b7bb4b988..63e70324b982 100644 --- a/app/client/src/utils/layoutPropertiesUtils.ts +++ b/app/client/src/utils/layoutPropertiesUtils.ts @@ -161,7 +161,7 @@ export const generateSpacingConfig = (value: Spacing = Spacing.None): any => { }; export const generatePositioningConfig = ( - value: Positioning = Positioning.Fixed, + value: Positioning = Positioning.Vertical, ): any => { return { helpText: "Position styles to be applied to the children", @@ -171,7 +171,7 @@ export const generatePositioningConfig = ( defaultValue: value, options: [ // { label: "Fixed", value: Positioning.Fixed }, - { label: "Horizontal stack", value: Positioning.Horizontal }, + // { label: "Horizontal stack", value: Positioning.Horizontal }, { label: "Vertical stack", value: Positioning.Vertical }, ], isJSConvertible: false, From 361d7f395441be1f01769372ce952335fb495382 Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 5 Dec 2022 21:40:37 -0500 Subject: [PATCH 274/708] minor update --- .../appsmith/autoLayout/FlexComponent.tsx | 4 +++- .../hooks/useAutoLayoutHighlights.ts | 20 +++++++++++-------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx index ad60f09c1ecd..9ec7832a019a 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx @@ -129,7 +129,9 @@ export function FlexComponent(props: AutoLayoutProps) { (props.parentId !== MAIN_CONTAINER_WIDGET_ID && siblingCount > 0 ? DEFAULT_MARGIN / siblingCount : 0) - - (props.parentId !== MAIN_CONTAINER_WIDGET_ID ? 8 : 0) + (props.parentId !== MAIN_CONTAINER_WIDGET_ID || isCurrentCanvasDragging + ? 8 + : 4) : props.componentWidth; return ( diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index cd51de80dc2d..9938fb640776 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -205,13 +205,9 @@ export const useAutoLayoutHighlights = ({ const updateHighlights = (moveDirection?: ReflowDirection) => { if (!highlights?.length || !moveDirection) return; - const isVerticalDrag = [ - ReflowDirection.TOP, - ReflowDirection.BOTTOM, - ].includes(moveDirection); highlights.map((highlight: HighlightInfo, index: number) => { let updatedHighlight: HighlightInfo = highlight; - if ((highlight.isNewLayer && isVerticalDrag) || !highlight.height) + if (highlight.isNewLayer || !highlight.height) updatedHighlight = updateHighlight(index); return updatedHighlight; }); @@ -244,6 +240,8 @@ export const useAutoLayoutHighlights = ({ // acceleration: number, ): HighlightInfo | undefined => { if (!highlights) return; + updateHighlights(moveDirection); + const highlight: HighlightInfo = getHighlightPayload(e, moveDirection); if (!highlight) return; @@ -268,7 +266,6 @@ export const useAutoLayoutHighlights = ({ }; let filteredHighlights: HighlightInfo[] = []; - updateHighlights(moveDirection); filteredHighlights = getViableDropPositions(base, pos, moveDirection); const arr = filteredHighlights.sort((a, b) => { @@ -288,7 +285,7 @@ export const useAutoLayoutHighlights = ({ ); }); toggleHighlightVisibility(base, arr[0]); - // console.log("#### arr", arr, base); + // console.log("#### arr", arr, base, moveDirection); return arr[0]; }; @@ -319,6 +316,9 @@ export const useAutoLayoutHighlights = ({ if (isVerticalDrag) return ( !highlight.isVertical && + highlight.width > 0 && + pos.x > 0 && + pos.y > 0 && pos.x >= highlight.posX && pos.x <= highlight.posX + highlight.width ); @@ -336,7 +336,11 @@ export const useAutoLayoutHighlights = ({ if (!isVerticalDrag && !filteredHighlights.length) filteredHighlights = arr .slice(arr.length - 3) - .filter((highlight: HighlightInfo) => !highlight.isVertical); + .filter((highlight: HighlightInfo) => + !highlight.isVertical && isFillWidget + ? highlight.alignment === FlexLayerAlignment.Start + : true, + ); return filteredHighlights; } From 82d116168db5d209e1b69819099aeff548aba5da Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 6 Dec 2022 11:34:33 -0500 Subject: [PATCH 275/708] disable width resizing for fill widgets --- .../src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts | 2 +- app/client/src/resizable/resizenreflow/index.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index f86b11b725fb..57cdf53c6bad 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -600,7 +600,7 @@ export const useCanvasDragging = ( canScroll.current = false; renderNewRows(delta); } else if (!isUpdatingRows) { - currentDirection.current = getMouseMoveDirection(e, 1); + currentDirection.current = getMouseMoveDirection(e); triggerReflow(e, firstMove); if ( useAutoLayout && diff --git a/app/client/src/resizable/resizenreflow/index.tsx b/app/client/src/resizable/resizenreflow/index.tsx index 2abcabbe0da0..9251a7db4ca7 100644 --- a/app/client/src/resizable/resizenreflow/index.tsx +++ b/app/client/src/resizable/resizenreflow/index.tsx @@ -481,7 +481,7 @@ export function ReflowResizable(props: ResizableProps) { return ( { From 06c65b580e287cdf51d5351f7a5ee950cb7ca9b1 Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 6 Dec 2022 11:39:55 -0500 Subject: [PATCH 276/708] fix disable of resize handle --- app/client/src/resizable/resizenreflow/index.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/client/src/resizable/resizenreflow/index.tsx b/app/client/src/resizable/resizenreflow/index.tsx index 9251a7db4ca7..3f9b9a5bdc35 100644 --- a/app/client/src/resizable/resizenreflow/index.tsx +++ b/app/client/src/resizable/resizenreflow/index.tsx @@ -481,7 +481,13 @@ export function ReflowResizable(props: ResizableProps) { return ( { From 5129f8b2425bb7528110f88dee8fc379a5a93ed3 Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 6 Dec 2022 12:29:11 -0500 Subject: [PATCH 277/708] add vertical alignment property --- app/client/src/components/constants.ts | 6 +++++ .../appsmith/autoLayout/FlexComponent.tsx | 10 ++++++++- app/client/src/utils/layoutPropertiesUtils.ts | 22 +++++++++++++++++++ .../AudioRecorderWidget/widget/index.tsx | 11 ++++++++-- .../src/widgets/AudioWidget/widget/index.tsx | 11 ++++++++-- .../widgets/BaseInputWidget/widget/index.tsx | 12 ++++++++-- app/client/src/widgets/BaseWidget.tsx | 10 ++++++++- .../ButtonGroupWidget/widget/index.tsx | 7 +++++- .../src/widgets/ButtonWidget/widget/index.tsx | 7 +++++- .../src/widgets/CameraWidget/widget/index.tsx | 11 ++++++++-- .../widget/propertyConfig/contentConfig.ts | 12 ++++++++-- .../ChartWidget/widget/propertyConfig.ts | 11 ++++++++-- .../widget/propertyConfig/contentConfig.ts | 11 ++++++++-- .../widgets/ContainerWidget/widget/index.tsx | 8 ++++++- .../DatePickerWidget2/widget/index.tsx | 12 ++++++++-- .../widgets/DividerWidget/widget/index.tsx | 11 ++++++++-- .../DocumentViewerWidget/widget/index.tsx | 11 ++++++++-- .../JSONFormWidget/widget/propertyConfig.ts | 7 +++++- .../ListWidget/widget/propertyConfig.ts | 11 ++++++++-- .../widgets/MapChartWidget/widget/index.tsx | 11 ++++++++-- .../src/widgets/MapWidget/widget/index.tsx | 11 ++++++++-- .../widgets/MenuButtonWidget/widget/index.tsx | 7 +++++- .../MultiSelectWidgetV2/widget/index.tsx | 12 ++++++++-- .../widget/propertyConfig/contentConfig.ts | 12 ++++++++-- .../widgets/ProgressWidget/widget/index.tsx | 11 ++++++++-- .../widgets/RadioGroupWidget/widget/index.tsx | 12 ++++++++-- .../widget/propertyConfig/contentConfig.ts | 12 ++++++++-- .../RichTextEditorWidget/widget/index.tsx | 12 ++++++++-- .../src/widgets/SelectWidget/widget/index.tsx | 12 ++++++++-- .../SingleSelectTreeWidget/widget/index.tsx | 12 ++++++++-- .../widgets/StatboxWidget/widget/index.tsx | 11 ++++++++-- .../SwitchGroupWidget/widget/index.tsx | 12 ++++++++-- .../src/widgets/SwitchWidget/widget/index.tsx | 12 ++++++++-- .../widget/propertyConfig/contentConfig.ts | 11 ++++++++-- .../src/widgets/TabsWidget/widget/index.tsx | 3 +++ .../src/widgets/TextWidget/widget/index.tsx | 11 ++++++++-- .../src/widgets/VideoWidget/widget/index.tsx | 12 ++++++++-- 37 files changed, 336 insertions(+), 61 deletions(-) diff --git a/app/client/src/components/constants.ts b/app/client/src/components/constants.ts index 341080a4ec77..b5c6ccf7069b 100644 --- a/app/client/src/components/constants.ts +++ b/app/client/src/components/constants.ts @@ -176,3 +176,9 @@ export enum FlexLayerAlignment { Center = "center", End = "end", } + +export enum FlexVerticalAlignment { + Top = "start", + Center = "center", + Bottom = "end", +} diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx index 9ec7832a019a..dac5153a4f16 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx @@ -1,7 +1,11 @@ import React, { ReactNode, useCallback } from "react"; import styled from "styled-components"; -import { LayoutDirection, ResponsiveBehavior } from "components/constants"; +import { + LayoutDirection, + ResponsiveBehavior, + FlexVerticalAlignment, +} from "components/constants"; import { MAIN_CONTAINER_WIDGET_ID, WidgetType, @@ -31,6 +35,7 @@ export type AutoLayoutProps = { widgetId: string; widgetType: WidgetType; parentColumnSpace: number; + flexVerticalAlignment: FlexVerticalAlignment; }; const FlexWidget = styled.div<{ @@ -44,6 +49,7 @@ const FlexWidget = styled.div<{ dragMargin: number; isAffectedByDrag: boolean; parentId?: string; + flexVerticalAlignment: FlexVerticalAlignment; }>` position: relative; z-index: ${({ zIndex }) => zIndex}; @@ -57,6 +63,7 @@ const FlexWidget = styled.div<{ isAffectedByDrag ? 0 : padding + "px"}; flex-grow: ${({ isFillWidget }) => (isFillWidget ? "1" : "0")}; + align-self: ${({ flexVerticalAlignment }) => flexVerticalAlignment}; &:hover { z-index: ${({ zIndexOnHover }) => zIndexOnHover} !important; @@ -140,6 +147,7 @@ export function FlexComponent(props: AutoLayoutProps) { componentHeight={props.componentHeight} componentWidth={resizedWidth} dragMargin={dragMargin} + flexVerticalAlignment={props.flexVerticalAlignment} id={props.widgetId} isAffectedByDrag={isAffectedByDrag} isFillWidget={isFillWidget} diff --git a/app/client/src/utils/layoutPropertiesUtils.ts b/app/client/src/utils/layoutPropertiesUtils.ts index 63e70324b982..a76160f3a211 100644 --- a/app/client/src/utils/layoutPropertiesUtils.ts +++ b/app/client/src/utils/layoutPropertiesUtils.ts @@ -8,6 +8,7 @@ import { Positioning, ResponsiveBehavior, Spacing, + FlexVerticalAlignment, } from "components/constants"; import { ValidationTypes } from "constants/WidgetValidation"; @@ -199,6 +200,27 @@ export const generatePositioningConfig = ( }; }; +export const generateVerticalAlignmentConfig = ( + value: FlexVerticalAlignment = FlexVerticalAlignment.Top, +): any => { + return { + helpText: "Vertical alignment with respect to the siblings in the same row", + propertyName: "flexVerticalAlignment", + label: "Vertical Alignment", + controlType: "DROP_DOWN", + defaultValue: value, + options: [ + { label: "Top", value: FlexVerticalAlignment.Top }, + { label: "Center", value: FlexVerticalAlignment.Center }, + { label: "Bottom", value: FlexVerticalAlignment.Bottom }, + ], + isJSConvertible: false, + isBindProperty: true, + isTriggerProperty: true, + validation: { type: ValidationTypes.TEXT }, + }; +}; + export function getLayoutConfig(alignment: Alignment, spacing: Spacing): any[] { return [generateAlignmentConfig(alignment), generateSpacingConfig(spacing)]; } diff --git a/app/client/src/widgets/AudioRecorderWidget/widget/index.tsx b/app/client/src/widgets/AudioRecorderWidget/widget/index.tsx index d86e340a666d..5e69baa5c1a3 100644 --- a/app/client/src/widgets/AudioRecorderWidget/widget/index.tsx +++ b/app/client/src/widgets/AudioRecorderWidget/widget/index.tsx @@ -1,11 +1,17 @@ import React from "react"; -import { ResponsiveBehavior } from "components/constants"; +import { + ResponsiveBehavior, + FlexVerticalAlignment, +} from "components/constants"; import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; import { WidgetType } from "constants/WidgetConstants"; import { ValidationTypes } from "constants/WidgetValidation"; import { createBlobUrl } from "utils/AppsmithUtils"; -import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; +import { + generateResponsiveBehaviorConfig, + generateVerticalAlignmentConfig, +} from "utils/layoutPropertiesUtils"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; import { FileDataTypes } from "widgets/constants"; @@ -69,6 +75,7 @@ class AudioRecorderWidget extends BaseWidget< validation: { type: ValidationTypes.BOOLEAN }, }, { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, + { ...generateVerticalAlignmentConfig(FlexVerticalAlignment.Top) }, ], }, { diff --git a/app/client/src/widgets/AudioWidget/widget/index.tsx b/app/client/src/widgets/AudioWidget/widget/index.tsx index b62dc728bf58..81455684a140 100644 --- a/app/client/src/widgets/AudioWidget/widget/index.tsx +++ b/app/client/src/widgets/AudioWidget/widget/index.tsx @@ -7,8 +7,14 @@ import Skeleton from "components/utils/Skeleton"; import { retryPromise } from "utils/AppsmithUtils"; import ReactPlayer from "react-player"; import { AutocompleteDataType } from "utils/autocomplete/TernServer"; -import { ResponsiveBehavior } from "components/constants"; -import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; +import { + ResponsiveBehavior, + FlexVerticalAlignment, +} from "components/constants"; +import { + generateResponsiveBehaviorConfig, + generateVerticalAlignmentConfig, +} from "utils/layoutPropertiesUtils"; const AudioComponent = lazy(() => retryPromise(() => import("../component"))); @@ -84,6 +90,7 @@ class AudioWidget extends BaseWidget { validation: { type: ValidationTypes.BOOLEAN }, }, { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, + { ...generateVerticalAlignmentConfig(FlexVerticalAlignment.Top) }, ], }, { diff --git a/app/client/src/widgets/BaseInputWidget/widget/index.tsx b/app/client/src/widgets/BaseInputWidget/widget/index.tsx index cd39f278f4b9..7b0071246286 100644 --- a/app/client/src/widgets/BaseInputWidget/widget/index.tsx +++ b/app/client/src/widgets/BaseInputWidget/widget/index.tsx @@ -11,8 +11,15 @@ import { ValidationTypes } from "constants/WidgetValidation"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; import BaseInputComponent from "../component"; import { InputTypes } from "../constants"; -import { LabelPosition, ResponsiveBehavior } from "components/constants"; -import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; +import { + LabelPosition, + ResponsiveBehavior, + FlexVerticalAlignment, +} from "components/constants"; +import { + generateResponsiveBehaviorConfig, + generateVerticalAlignmentConfig, +} from "utils/layoutPropertiesUtils"; class BaseInputWidget< T extends BaseInputWidgetProps, @@ -231,6 +238,7 @@ class BaseInputWidget< }, }, generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill), + generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, { diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index 9d0dc70ed204..c38d92b19926 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -4,7 +4,11 @@ * Widgets are also responsible for dispatching actions and updating the state tree */ import { BatchPropertyUpdatePayload } from "actions/controlActions"; -import { LayoutDirection, ResponsiveBehavior } from "components/constants"; +import { + LayoutDirection, + ResponsiveBehavior, + FlexVerticalAlignment, +} from "components/constants"; import FlexComponent from "components/designSystems/appsmith/autoLayout/FlexComponent"; import PositionedContainer from "components/designSystems/appsmith/PositionedContainer"; import DraggableComponent from "components/editorComponents/DraggableComponent"; @@ -343,6 +347,9 @@ abstract class BaseWidget< componentHeight={componentHeight} componentWidth={componentWidth} direction={this.props.direction} + flexVerticalAlignment={ + this.props.flexVerticalAlignment || FlexVerticalAlignment.Top + } focused={this.props.focused} parentColumnSpace={this.props.parentColumnSpace} parentId={this.props.parentId} @@ -501,6 +508,7 @@ export interface WidgetPositionProps extends WidgetRowCols { responsiveBehavior?: ResponsiveBehavior; minWidth?: number; // Required to avoid squishing of widgets on mobile viewport. isMobile?: boolean; + flexVerticalAlignment?: FlexVerticalAlignment; } export const WIDGET_STATIC_PROPS = { diff --git a/app/client/src/widgets/ButtonGroupWidget/widget/index.tsx b/app/client/src/widgets/ButtonGroupWidget/widget/index.tsx index 8575886e5451..ae205a608296 100644 --- a/app/client/src/widgets/ButtonGroupWidget/widget/index.tsx +++ b/app/client/src/widgets/ButtonGroupWidget/widget/index.tsx @@ -11,11 +11,15 @@ import { ButtonPlacement, ButtonVariantTypes, ResponsiveBehavior, + FlexVerticalAlignment, } from "components/constants"; import ButtonGroupComponent from "../component"; import { MinimumPopupRows } from "widgets/constants"; import { getStylesheetValue } from "./helpers"; -import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; +import { + generateResponsiveBehaviorConfig, + generateVerticalAlignmentConfig, +} from "utils/layoutPropertiesUtils"; class ButtonGroupWidget extends BaseWidget< ButtonGroupWidgetProps, @@ -449,6 +453,7 @@ class ButtonGroupWidget extends BaseWidget< validation: { type: ValidationTypes.BOOLEAN }, }, generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), + generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, ]; diff --git a/app/client/src/widgets/ButtonWidget/widget/index.tsx b/app/client/src/widgets/ButtonWidget/widget/index.tsx index 5ed2676c5acc..575f98909f1b 100644 --- a/app/client/src/widgets/ButtonWidget/widget/index.tsx +++ b/app/client/src/widgets/ButtonWidget/widget/index.tsx @@ -18,8 +18,12 @@ import { ButtonPlacementTypes, ButtonPlacement, ResponsiveBehavior, + FlexVerticalAlignment, } from "components/constants"; -import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; +import { + generateResponsiveBehaviorConfig, + generateVerticalAlignmentConfig, +} from "utils/layoutPropertiesUtils"; class ButtonWidget extends BaseWidget { onButtonClickBound: (event: React.MouseEvent) => void; @@ -104,6 +108,7 @@ class ButtonWidget extends BaseWidget { validation: { type: ValidationTypes.BOOLEAN }, }, { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug) }, + generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, { diff --git a/app/client/src/widgets/CameraWidget/widget/index.tsx b/app/client/src/widgets/CameraWidget/widget/index.tsx index dd3f4a1eee91..e3a9bd850227 100644 --- a/app/client/src/widgets/CameraWidget/widget/index.tsx +++ b/app/client/src/widgets/CameraWidget/widget/index.tsx @@ -14,8 +14,14 @@ import { CameraModeTypes, MediaCaptureStatusTypes, } from "../constants"; -import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; -import { ResponsiveBehavior } from "components/constants"; +import { + generateResponsiveBehaviorConfig, + generateVerticalAlignmentConfig, +} from "utils/layoutPropertiesUtils"; +import { + ResponsiveBehavior, + FlexVerticalAlignment, +} from "components/constants"; class CameraWidget extends BaseWidget { static getPropertyPaneContentConfig() { @@ -80,6 +86,7 @@ class CameraWidget extends BaseWidget { validation: { type: ValidationTypes.BOOLEAN }, }, generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), + generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, { diff --git a/app/client/src/widgets/CategorySliderWidget/widget/propertyConfig/contentConfig.ts b/app/client/src/widgets/CategorySliderWidget/widget/propertyConfig/contentConfig.ts index 4ce6ef2fa2cf..f0f40613d991 100644 --- a/app/client/src/widgets/CategorySliderWidget/widget/propertyConfig/contentConfig.ts +++ b/app/client/src/widgets/CategorySliderWidget/widget/propertyConfig/contentConfig.ts @@ -1,4 +1,8 @@ -import { LabelPosition, ResponsiveBehavior } from "components/constants"; +import { + LabelPosition, + ResponsiveBehavior, + FlexVerticalAlignment, +} from "components/constants"; import { AutocompleteDataType } from "utils/autocomplete/TernServer"; import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; import { Alignment } from "@blueprintjs/core"; @@ -8,7 +12,10 @@ import { optionsCustomValidation, } from "../../validations"; import { CategorySliderWidgetProps } from ".."; -import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; +import { + generateResponsiveBehaviorConfig, + generateVerticalAlignmentConfig, +} from "utils/layoutPropertiesUtils"; export default [ { @@ -177,6 +184,7 @@ export default [ validation: { type: ValidationTypes.BOOLEAN }, }, generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), + generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, { diff --git a/app/client/src/widgets/ChartWidget/widget/propertyConfig.ts b/app/client/src/widgets/ChartWidget/widget/propertyConfig.ts index eca8d41ad5fd..abef10f5961b 100644 --- a/app/client/src/widgets/ChartWidget/widget/propertyConfig.ts +++ b/app/client/src/widgets/ChartWidget/widget/propertyConfig.ts @@ -3,8 +3,14 @@ import { ValidationTypes } from "constants/WidgetValidation"; import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; import { CUSTOM_CHART_TYPES, LabelOrientation } from "../constants"; import { isLabelOrientationApplicableFor } from "../component"; -import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; -import { ResponsiveBehavior } from "components/constants"; +import { + generateResponsiveBehaviorConfig, + generateVerticalAlignmentConfig, +} from "utils/layoutPropertiesUtils"; +import { + ResponsiveBehavior, + FlexVerticalAlignment, +} from "components/constants"; export const contentConfig = [ { @@ -244,6 +250,7 @@ export const contentConfig = [ dependencies: ["chartType"], }, generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill), + generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, { diff --git a/app/client/src/widgets/CodeScannerWidget/widget/propertyConfig/contentConfig.ts b/app/client/src/widgets/CodeScannerWidget/widget/propertyConfig/contentConfig.ts index d32f81d9d923..510ef1d2b0f3 100644 --- a/app/client/src/widgets/CodeScannerWidget/widget/propertyConfig/contentConfig.ts +++ b/app/client/src/widgets/CodeScannerWidget/widget/propertyConfig/contentConfig.ts @@ -4,8 +4,14 @@ import { CodeScannerWidgetProps, ScannerLayout, } from "widgets/CodeScannerWidget/constants"; -import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; -import { ResponsiveBehavior } from "components/constants"; +import { + generateResponsiveBehaviorConfig, + generateVerticalAlignmentConfig, +} from "utils/layoutPropertiesUtils"; +import { + ResponsiveBehavior, + FlexVerticalAlignment, +} from "components/constants"; export default [ { @@ -96,6 +102,7 @@ export default [ dependencies: ["scannerLayout"], }, generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), + generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, { diff --git a/app/client/src/widgets/ContainerWidget/widget/index.tsx b/app/client/src/widgets/ContainerWidget/widget/index.tsx index bbfdf438741d..9efe4460ce9f 100644 --- a/app/client/src/widgets/ContainerWidget/widget/index.tsx +++ b/app/client/src/widgets/ContainerWidget/widget/index.tsx @@ -16,10 +16,15 @@ import { ValidationTypes } from "constants/WidgetValidation"; import { compact, map, sortBy } from "lodash"; import WidgetsMultiSelectBox from "pages/Editor/WidgetsMultiSelectBox"; -import { Positioning, ResponsiveBehavior } from "components/constants"; +import { + Positioning, + ResponsiveBehavior, + FlexVerticalAlignment, +} from "components/constants"; import { generatePositioningConfig, generateResponsiveBehaviorConfig, + generateVerticalAlignmentConfig, } from "utils/layoutPropertiesUtils"; export class ContainerWidget extends BaseWidget< @@ -67,6 +72,7 @@ export class ContainerWidget extends BaseWidget< }, generatePositioningConfig(Positioning.Vertical), { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, + generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, ]; diff --git a/app/client/src/widgets/DatePickerWidget2/widget/index.tsx b/app/client/src/widgets/DatePickerWidget2/widget/index.tsx index f25e1a62f2f3..1a209d1d0901 100644 --- a/app/client/src/widgets/DatePickerWidget2/widget/index.tsx +++ b/app/client/src/widgets/DatePickerWidget2/widget/index.tsx @@ -10,11 +10,18 @@ import { AutocompleteDataType } from "utils/autocomplete/TernServer"; import derivedProperties from "./parseDerivedProperties"; import { DatePickerType, TimePrecision } from "../constants"; -import { LabelPosition, ResponsiveBehavior } from "components/constants"; +import { + LabelPosition, + ResponsiveBehavior, + FlexVerticalAlignment, +} from "components/constants"; import { Alignment } from "@blueprintjs/core"; import { GRID_DENSITY_MIGRATION_V1 } from "widgets/constants"; import { DateFormatOptions } from "./constants"; -import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; +import { + generateResponsiveBehaviorConfig, + generateVerticalAlignmentConfig, +} from "utils/layoutPropertiesUtils"; function allowedRange(value: any) { const allowedValues = [0, 1, 2, 3, 4, 5, 6]; @@ -281,6 +288,7 @@ class DatePickerWidget extends BaseWidget { validation: { type: ValidationTypes.BOOLEAN }, }, { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, + generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, { diff --git a/app/client/src/widgets/DividerWidget/widget/index.tsx b/app/client/src/widgets/DividerWidget/widget/index.tsx index 9765cae96a34..5715815ee0d2 100644 --- a/app/client/src/widgets/DividerWidget/widget/index.tsx +++ b/app/client/src/widgets/DividerWidget/widget/index.tsx @@ -4,8 +4,14 @@ import { WidgetType } from "constants/WidgetConstants"; import DividerComponent from "../component"; import { ValidationTypes } from "constants/WidgetValidation"; -import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; -import { ResponsiveBehavior } from "components/constants"; +import { + generateResponsiveBehaviorConfig, + generateVerticalAlignmentConfig, +} from "utils/layoutPropertiesUtils"; +import { + ResponsiveBehavior, + FlexVerticalAlignment, +} from "components/constants"; class DividerWidget extends BaseWidget { static getPropertyPaneContentConfig() { @@ -35,6 +41,7 @@ class DividerWidget extends BaseWidget { validation: { type: ValidationTypes.BOOLEAN }, }, { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, + generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, ]; diff --git a/app/client/src/widgets/DocumentViewerWidget/widget/index.tsx b/app/client/src/widgets/DocumentViewerWidget/widget/index.tsx index 122c5e359333..fc6cc9367b59 100644 --- a/app/client/src/widgets/DocumentViewerWidget/widget/index.tsx +++ b/app/client/src/widgets/DocumentViewerWidget/widget/index.tsx @@ -6,8 +6,14 @@ import { ValidationResponse, } from "constants/WidgetValidation"; import { AutocompleteDataType } from "utils/autocomplete/TernServer"; -import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; -import { ResponsiveBehavior } from "components/constants"; +import { + generateResponsiveBehaviorConfig, + generateVerticalAlignmentConfig, +} from "utils/layoutPropertiesUtils"; +import { + ResponsiveBehavior, + FlexVerticalAlignment, +} from "components/constants"; export function documentUrlValidation(value: unknown): ValidationResponse { // applied validations if value exist @@ -119,6 +125,7 @@ class DocumentViewerWidget extends BaseWidget< validation: { type: ValidationTypes.BOOLEAN }, }, generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), + generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, ]; diff --git a/app/client/src/widgets/JSONFormWidget/widget/propertyConfig.ts b/app/client/src/widgets/JSONFormWidget/widget/propertyConfig.ts index 40a02559db9a..1aea30f11f4b 100644 --- a/app/client/src/widgets/JSONFormWidget/widget/propertyConfig.ts +++ b/app/client/src/widgets/JSONFormWidget/widget/propertyConfig.ts @@ -10,12 +10,16 @@ import { ButtonVariantTypes, ButtonPlacementTypes, ResponsiveBehavior, + FlexVerticalAlignment, } from "components/constants"; import { ButtonWidgetProps } from "widgets/ButtonWidget/widget"; import { OnButtonClickProps } from "components/propertyControls/ButtonControl"; import { ComputedSchemaStatus, computeSchema } from "./helper"; import { EVALUATION_PATH } from "utils/DynamicBindingUtils"; -import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; +import { + generateResponsiveBehaviorConfig, + generateVerticalAlignmentConfig, +} from "utils/layoutPropertiesUtils"; const MAX_NESTING_LEVEL = 5; @@ -268,6 +272,7 @@ export const contentConfig = [ validation: { type: ValidationTypes.TEXT }, }, generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill), + generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, { diff --git a/app/client/src/widgets/ListWidget/widget/propertyConfig.ts b/app/client/src/widgets/ListWidget/widget/propertyConfig.ts index ab2d632f9b73..29d148e3fe97 100644 --- a/app/client/src/widgets/ListWidget/widget/propertyConfig.ts +++ b/app/client/src/widgets/ListWidget/widget/propertyConfig.ts @@ -6,8 +6,14 @@ import { ValidationTypes } from "constants/WidgetValidation"; import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; import { EVAL_VALUE_PATH } from "utils/DynamicBindingUtils"; import { AutocompleteDataType } from "utils/autocomplete/TernServer"; -import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; -import { ResponsiveBehavior } from "components/constants"; +import { + generateResponsiveBehaviorConfig, + generateVerticalAlignmentConfig, +} from "utils/layoutPropertiesUtils"; +import { + ResponsiveBehavior, + FlexVerticalAlignment, +} from "components/constants"; export const PropertyPaneContentConfig = [ { @@ -92,6 +98,7 @@ export const PropertyPaneContentConfig = [ validation: { type: ValidationTypes.BOOLEAN }, }, { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, + generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, { diff --git a/app/client/src/widgets/MapChartWidget/widget/index.tsx b/app/client/src/widgets/MapChartWidget/widget/index.tsx index 59c3e58d18e0..f6975c131f21 100644 --- a/app/client/src/widgets/MapChartWidget/widget/index.tsx +++ b/app/client/src/widgets/MapChartWidget/widget/index.tsx @@ -22,8 +22,14 @@ import { } from "../constants"; import { MapType } from "../component"; import { AutocompleteDataType } from "utils/autocomplete/TernServer"; -import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; -import { ResponsiveBehavior } from "components/constants"; +import { + generateResponsiveBehaviorConfig, + generateVerticalAlignmentConfig, +} from "utils/layoutPropertiesUtils"; +import { + ResponsiveBehavior, + FlexVerticalAlignment, +} from "components/constants"; const MapChartComponent = lazy(() => retryPromise(() => @@ -205,6 +211,7 @@ class MapChartWidget extends BaseWidget { validation: { type: ValidationTypes.BOOLEAN }, }, generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), + generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, { diff --git a/app/client/src/widgets/MapWidget/widget/index.tsx b/app/client/src/widgets/MapWidget/widget/index.tsx index 667c4f80c80e..3ad1512d6a50 100644 --- a/app/client/src/widgets/MapWidget/widget/index.tsx +++ b/app/client/src/widgets/MapWidget/widget/index.tsx @@ -12,8 +12,14 @@ import { getBorderCSSShorthand } from "constants/DefaultTheme"; import { MarkerProps } from "../constants"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; -import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; -import { ResponsiveBehavior } from "components/constants"; +import { + generateResponsiveBehaviorConfig, + generateVerticalAlignmentConfig, +} from "utils/layoutPropertiesUtils"; +import { + ResponsiveBehavior, + FlexVerticalAlignment, +} from "components/constants"; const { google } = getAppsmithConfigs(); @@ -203,6 +209,7 @@ class MapWidget extends BaseWidget { isTriggerProperty: false, }, generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill), + generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, { diff --git a/app/client/src/widgets/MenuButtonWidget/widget/index.tsx b/app/client/src/widgets/MenuButtonWidget/widget/index.tsx index 916c25a3c058..cfd57cfc5c18 100644 --- a/app/client/src/widgets/MenuButtonWidget/widget/index.tsx +++ b/app/client/src/widgets/MenuButtonWidget/widget/index.tsx @@ -12,10 +12,14 @@ import { ButtonPlacementTypes, ButtonPlacement, ResponsiveBehavior, + FlexVerticalAlignment, } from "components/constants"; import { IconName } from "@blueprintjs/icons"; import { MinimumPopupRows } from "widgets/constants"; -import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; +import { + generateResponsiveBehaviorConfig, + generateVerticalAlignmentConfig, +} from "utils/layoutPropertiesUtils"; export interface MenuButtonWidgetProps extends WidgetProps { label?: string; isDisabled?: boolean; @@ -253,6 +257,7 @@ class MenuButtonWidget extends BaseWidget { validation: { type: ValidationTypes.BOOLEAN }, }, generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), + generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, ]; diff --git a/app/client/src/widgets/MultiSelectWidgetV2/widget/index.tsx b/app/client/src/widgets/MultiSelectWidgetV2/widget/index.tsx index e7927efb565e..742723df2942 100644 --- a/app/client/src/widgets/MultiSelectWidgetV2/widget/index.tsx +++ b/app/client/src/widgets/MultiSelectWidgetV2/widget/index.tsx @@ -14,10 +14,17 @@ import MultiSelectComponent from "../component"; import { DraftValueType, LabelInValueType } from "rc-select/lib/Select"; import { Layers } from "constants/Layers"; import { MinimumPopupRows, GRID_DENSITY_MIGRATION_V1 } from "widgets/constants"; -import { LabelPosition, ResponsiveBehavior } from "components/constants"; +import { + LabelPosition, + ResponsiveBehavior, + FlexVerticalAlignment, +} from "components/constants"; import { Alignment } from "@blueprintjs/core"; import { AutocompleteDataType } from "utils/autocomplete/TernServer"; -import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; +import { + generateResponsiveBehaviorConfig, + generateVerticalAlignmentConfig, +} from "utils/layoutPropertiesUtils"; export function defaultOptionValueValidation( value: unknown, @@ -433,6 +440,7 @@ class MultiSelectWidget extends BaseWidget< validation: { type: ValidationTypes.BOOLEAN }, }, { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, + generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, { diff --git a/app/client/src/widgets/NumberSliderWidget/widget/propertyConfig/contentConfig.ts b/app/client/src/widgets/NumberSliderWidget/widget/propertyConfig/contentConfig.ts index 6f8a27f1d3b7..5701ee5bb9d5 100644 --- a/app/client/src/widgets/NumberSliderWidget/widget/propertyConfig/contentConfig.ts +++ b/app/client/src/widgets/NumberSliderWidget/widget/propertyConfig/contentConfig.ts @@ -1,8 +1,15 @@ import { Alignment } from "@blueprintjs/core"; -import { LabelPosition, ResponsiveBehavior } from "components/constants"; +import { + LabelPosition, + ResponsiveBehavior, + FlexVerticalAlignment, +} from "components/constants"; import { ValidationTypes } from "constants/WidgetValidation"; import { AutocompleteDataType } from "utils/autocomplete/TernServer"; -import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; +import { + generateResponsiveBehaviorConfig, + generateVerticalAlignmentConfig, +} from "utils/layoutPropertiesUtils"; import { NumberSliderWidgetProps } from ".."; import { defaultValueValidation, @@ -263,6 +270,7 @@ export default [ validation: { type: ValidationTypes.BOOLEAN }, }, generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), + generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, { diff --git a/app/client/src/widgets/ProgressWidget/widget/index.tsx b/app/client/src/widgets/ProgressWidget/widget/index.tsx index 9aca8fe680d2..04c561f422a7 100644 --- a/app/client/src/widgets/ProgressWidget/widget/index.tsx +++ b/app/client/src/widgets/ProgressWidget/widget/index.tsx @@ -7,8 +7,14 @@ import ProgressComponent from "../component"; import { ProgressType, ProgressVariant } from "../constants"; import { ValidationTypes } from "constants/WidgetValidation"; import { Colors } from "constants/Colors"; -import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; -import { ResponsiveBehavior } from "components/constants"; +import { + generateResponsiveBehaviorConfig, + generateVerticalAlignmentConfig, +} from "utils/layoutPropertiesUtils"; +import { + ResponsiveBehavior, + FlexVerticalAlignment, +} from "components/constants"; class ProgressWidget extends BaseWidget { static getPropertyPaneContentConfig() { @@ -125,6 +131,7 @@ class ProgressWidget extends BaseWidget { dependencies: ["isIndeterminate"], }, generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), + generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, ]; diff --git a/app/client/src/widgets/RadioGroupWidget/widget/index.tsx b/app/client/src/widgets/RadioGroupWidget/widget/index.tsx index 444604964ff6..ae3471322095 100644 --- a/app/client/src/widgets/RadioGroupWidget/widget/index.tsx +++ b/app/client/src/widgets/RadioGroupWidget/widget/index.tsx @@ -13,9 +13,16 @@ import { } from "constants/WidgetValidation"; import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; import { RadioOption } from "../constants"; -import { LabelPosition, ResponsiveBehavior } from "components/constants"; +import { + LabelPosition, + ResponsiveBehavior, + FlexVerticalAlignment, +} from "components/constants"; import RadioGroupComponent from "../component"; -import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; +import { + generateResponsiveBehaviorConfig, + generateVerticalAlignmentConfig, +} from "utils/layoutPropertiesUtils"; /** * Validation rules: @@ -335,6 +342,7 @@ class RadioGroupWidget extends BaseWidget { validation: { type: ValidationTypes.BOOLEAN }, }, generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), + generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, { diff --git a/app/client/src/widgets/RangeSliderWidget/widget/propertyConfig/contentConfig.ts b/app/client/src/widgets/RangeSliderWidget/widget/propertyConfig/contentConfig.ts index 8a2ae3b80d58..e42b43b133be 100644 --- a/app/client/src/widgets/RangeSliderWidget/widget/propertyConfig/contentConfig.ts +++ b/app/client/src/widgets/RangeSliderWidget/widget/propertyConfig/contentConfig.ts @@ -1,6 +1,10 @@ import { AutocompleteDataType } from "utils/autocomplete/TernServer"; import { ValidationTypes } from "constants/WidgetValidation"; -import { LabelPosition, ResponsiveBehavior } from "components/constants"; +import { + LabelPosition, + ResponsiveBehavior, + FlexVerticalAlignment, +} from "components/constants"; import { Alignment } from "@blueprintjs/core"; import { maxValueValidation, @@ -11,7 +15,10 @@ import { endValueValidation, } from "../../validations"; import { RangeSliderWidgetProps } from ".."; -import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; +import { + generateResponsiveBehaviorConfig, + generateVerticalAlignmentConfig, +} from "utils/layoutPropertiesUtils"; export default [ { @@ -305,6 +312,7 @@ export default [ validation: { type: ValidationTypes.BOOLEAN }, }, generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), + generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, { diff --git a/app/client/src/widgets/RichTextEditorWidget/widget/index.tsx b/app/client/src/widgets/RichTextEditorWidget/widget/index.tsx index bb868662a301..a7e67d7b5114 100644 --- a/app/client/src/widgets/RichTextEditorWidget/widget/index.tsx +++ b/app/client/src/widgets/RichTextEditorWidget/widget/index.tsx @@ -6,11 +6,18 @@ import { ValidationTypes } from "constants/WidgetValidation"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; import Skeleton from "components/utils/Skeleton"; import { retryPromise } from "utils/AppsmithUtils"; -import { LabelPosition, ResponsiveBehavior } from "components/constants"; +import { + LabelPosition, + ResponsiveBehavior, + FlexVerticalAlignment, +} from "components/constants"; import { Alignment } from "@blueprintjs/core"; import { GRID_DENSITY_MIGRATION_V1 } from "widgets/constants"; import showdown from "showdown"; -import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; +import { + generateResponsiveBehaviorConfig, + generateVerticalAlignmentConfig, +} from "utils/layoutPropertiesUtils"; export enum RTEFormats { MARKDOWN = "markdown", @@ -197,6 +204,7 @@ class RichTextEditorWidget extends BaseWidget< validation: { type: ValidationTypes.BOOLEAN }, }, { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, + generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, { diff --git a/app/client/src/widgets/SelectWidget/widget/index.tsx b/app/client/src/widgets/SelectWidget/widget/index.tsx index f1b7c3af673a..596efcaedcd4 100644 --- a/app/client/src/widgets/SelectWidget/widget/index.tsx +++ b/app/client/src/widgets/SelectWidget/widget/index.tsx @@ -10,7 +10,11 @@ import { } from "constants/WidgetValidation"; import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; import { MinimumPopupRows, GRID_DENSITY_MIGRATION_V1 } from "widgets/constants"; -import { LabelPosition, ResponsiveBehavior } from "components/constants"; +import { + LabelPosition, + ResponsiveBehavior, + FlexVerticalAlignment, +} from "components/constants"; import { Alignment } from "@blueprintjs/core"; import { AutocompleteDataType } from "utils/autocomplete/TernServer"; import { @@ -23,7 +27,10 @@ import { } from "lodash"; import equal from "fast-deep-equal/es6"; import derivedProperties from "./parseDerivedProperties"; -import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; +import { + generateResponsiveBehaviorConfig, + generateVerticalAlignmentConfig, +} from "utils/layoutPropertiesUtils"; export function defaultOptionValueValidation( value: unknown, @@ -358,6 +365,7 @@ class SelectWidget extends BaseWidget { validation: { type: ValidationTypes.BOOLEAN }, }, { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, + generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, { diff --git a/app/client/src/widgets/SingleSelectTreeWidget/widget/index.tsx b/app/client/src/widgets/SingleSelectTreeWidget/widget/index.tsx index a3323e9da7d7..8a82afbf5231 100644 --- a/app/client/src/widgets/SingleSelectTreeWidget/widget/index.tsx +++ b/app/client/src/widgets/SingleSelectTreeWidget/widget/index.tsx @@ -13,9 +13,16 @@ import { Layers } from "constants/Layers"; import { AutocompleteDataType } from "utils/autocomplete/TernServer"; import { GRID_DENSITY_MIGRATION_V1, MinimumPopupRows } from "widgets/constants"; import SingleSelectTreeComponent from "../component"; -import { LabelPosition, ResponsiveBehavior } from "components/constants"; +import { + LabelPosition, + ResponsiveBehavior, + FlexVerticalAlignment, +} from "components/constants"; import { Alignment } from "@blueprintjs/core"; -import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; +import { + generateResponsiveBehaviorConfig, + generateVerticalAlignmentConfig, +} from "utils/layoutPropertiesUtils"; import derivedProperties from "./parseDerivedProperties"; function defaultOptionValueValidation(value: unknown): ValidationResponse { @@ -282,6 +289,7 @@ class SingleSelectTreeWidget extends BaseWidget< validation: { type: ValidationTypes.BOOLEAN }, }, { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, + generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, { diff --git a/app/client/src/widgets/StatboxWidget/widget/index.tsx b/app/client/src/widgets/StatboxWidget/widget/index.tsx index 50584d03f23c..be7152281d8e 100644 --- a/app/client/src/widgets/StatboxWidget/widget/index.tsx +++ b/app/client/src/widgets/StatboxWidget/widget/index.tsx @@ -2,8 +2,14 @@ import { WidgetType } from "constants/WidgetConstants"; import { ContainerWidget } from "widgets/ContainerWidget/widget"; import { ValidationTypes } from "constants/WidgetValidation"; -import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; -import { ResponsiveBehavior } from "components/constants"; +import { + generateResponsiveBehaviorConfig, + generateVerticalAlignmentConfig, +} from "utils/layoutPropertiesUtils"; +import { + ResponsiveBehavior, + FlexVerticalAlignment, +} from "components/constants"; class StatboxWidget extends ContainerWidget { static getPropertyPaneContentConfig() { @@ -41,6 +47,7 @@ class StatboxWidget extends ContainerWidget { validation: { type: ValidationTypes.BOOLEAN }, }, generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), + generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, ]; diff --git a/app/client/src/widgets/SwitchGroupWidget/widget/index.tsx b/app/client/src/widgets/SwitchGroupWidget/widget/index.tsx index c0a88b041d7b..af52239b3142 100644 --- a/app/client/src/widgets/SwitchGroupWidget/widget/index.tsx +++ b/app/client/src/widgets/SwitchGroupWidget/widget/index.tsx @@ -9,10 +9,17 @@ import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; import SwitchGroupComponent, { OptionProps } from "../component"; -import { LabelPosition, ResponsiveBehavior } from "components/constants"; +import { + LabelPosition, + ResponsiveBehavior, + FlexVerticalAlignment, +} from "components/constants"; import { TextSize } from "constants/WidgetConstants"; import { GRID_DENSITY_MIGRATION_V1 } from "widgets/constants"; -import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; +import { + generateResponsiveBehaviorConfig, + generateVerticalAlignmentConfig, +} from "utils/layoutPropertiesUtils"; class SwitchGroupWidget extends BaseWidget< SwitchGroupWidgetProps, @@ -219,6 +226,7 @@ class SwitchGroupWidget extends BaseWidget< validation: { type: ValidationTypes.BOOLEAN }, }, generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), + generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, { diff --git a/app/client/src/widgets/SwitchWidget/widget/index.tsx b/app/client/src/widgets/SwitchWidget/widget/index.tsx index 511ca29e16ea..821a5e612a63 100644 --- a/app/client/src/widgets/SwitchWidget/widget/index.tsx +++ b/app/client/src/widgets/SwitchWidget/widget/index.tsx @@ -7,9 +7,16 @@ import { ValidationTypes } from "constants/WidgetValidation"; import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; -import { LabelPosition, ResponsiveBehavior } from "components/constants"; +import { + LabelPosition, + ResponsiveBehavior, + FlexVerticalAlignment, +} from "components/constants"; import { AlignWidgetTypes } from "widgets/constants"; -import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; +import { + generateResponsiveBehaviorConfig, + generateVerticalAlignmentConfig, +} from "utils/layoutPropertiesUtils"; class SwitchWidget extends BaseWidget { static getPropertyPaneContentConfig() { return [ @@ -107,6 +114,7 @@ class SwitchWidget extends BaseWidget { validation: { type: ValidationTypes.BOOLEAN }, }, generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), + generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, { diff --git a/app/client/src/widgets/TableWidgetV2/widget/propertyConfig/contentConfig.ts b/app/client/src/widgets/TableWidgetV2/widget/propertyConfig/contentConfig.ts index 5a0af7b2fa6b..94f3848285e5 100644 --- a/app/client/src/widgets/TableWidgetV2/widget/propertyConfig/contentConfig.ts +++ b/app/client/src/widgets/TableWidgetV2/widget/propertyConfig/contentConfig.ts @@ -19,8 +19,14 @@ import { import panelConfig from "./PanelConfig"; import { composePropertyUpdateHook } from "widgets/WidgetUtils"; import { PropertyPaneConfig } from "constants/PropertyControlConstants"; -import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; -import { ResponsiveBehavior } from "components/constants"; +import { + generateResponsiveBehaviorConfig, + generateVerticalAlignmentConfig, +} from "utils/layoutPropertiesUtils"; +import { + ResponsiveBehavior, + FlexVerticalAlignment, +} from "components/constants"; export default [ { @@ -480,6 +486,7 @@ export default [ dependencies: ["isVisibleDownload"], }, { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, + generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, ] as PropertyPaneConfig[]; diff --git a/app/client/src/widgets/TabsWidget/widget/index.tsx b/app/client/src/widgets/TabsWidget/widget/index.tsx index 128965de2c8f..79164aca86b4 100644 --- a/app/client/src/widgets/TabsWidget/widget/index.tsx +++ b/app/client/src/widgets/TabsWidget/widget/index.tsx @@ -17,10 +17,12 @@ import { LayoutDirection, Positioning, ResponsiveBehavior, + FlexVerticalAlignment, } from "components/constants"; import { generatePositioningConfig, generateResponsiveBehaviorConfig, + generateVerticalAlignmentConfig, } from "utils/layoutPropertiesUtils"; export function selectedTabValidation( @@ -183,6 +185,7 @@ class TabsWidget extends BaseWidget< isTriggerProperty: false, }, { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, + generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, { diff --git a/app/client/src/widgets/TextWidget/widget/index.tsx b/app/client/src/widgets/TextWidget/widget/index.tsx index 33406ceb99b3..3ca28174221b 100644 --- a/app/client/src/widgets/TextWidget/widget/index.tsx +++ b/app/client/src/widgets/TextWidget/widget/index.tsx @@ -14,8 +14,14 @@ import { AutocompleteDataType } from "utils/autocomplete/TernServer"; import { OverflowTypes } from "../constants"; import WidgetStyleContainer from "components/designSystems/appsmith/WidgetStyleContainer"; import { pick } from "lodash"; -import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; -import { ResponsiveBehavior } from "components/constants"; +import { + generateResponsiveBehaviorConfig, + generateVerticalAlignmentConfig, +} from "utils/layoutPropertiesUtils"; +import { + ResponsiveBehavior, + FlexVerticalAlignment, +} from "components/constants"; const MAX_HTML_PARSING_LENGTH = 1000; class TextWidget extends BaseWidget { @@ -93,6 +99,7 @@ class TextWidget extends BaseWidget { validation: { type: ValidationTypes.BOOLEAN }, }, { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, + generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, ]; diff --git a/app/client/src/widgets/VideoWidget/widget/index.tsx b/app/client/src/widgets/VideoWidget/widget/index.tsx index 8c2b27b4d7bd..e19850424982 100644 --- a/app/client/src/widgets/VideoWidget/widget/index.tsx +++ b/app/client/src/widgets/VideoWidget/widget/index.tsx @@ -7,8 +7,15 @@ import Skeleton from "components/utils/Skeleton"; import { retryPromise } from "utils/AppsmithUtils"; import ReactPlayer from "react-player"; import { AutocompleteDataType } from "utils/autocomplete/TernServer"; -import { ButtonBorderRadius, ResponsiveBehavior } from "components/constants"; -import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; +import { + ButtonBorderRadius, + ResponsiveBehavior, + FlexVerticalAlignment, +} from "components/constants"; +import { + generateResponsiveBehaviorConfig, + generateVerticalAlignmentConfig, +} from "utils/layoutPropertiesUtils"; const VideoComponent = lazy(() => retryPromise(() => import("../component"))); @@ -84,6 +91,7 @@ class VideoWidget extends BaseWidget { validation: { type: ValidationTypes.BOOLEAN }, }, generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), + generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, { From 8214a900a59315a7d866881624335139fd6f6422 Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 6 Dec 2022 16:22:08 -0500 Subject: [PATCH 278/708] fix update existing layer --- .../CanvasSagas/AutoLayoutDraggingSagas.ts | 67 ++++++++++++------- 1 file changed, 44 insertions(+), 23 deletions(-) diff --git a/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts b/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts index b9104ca2c117..ee99a3c4598a 100644 --- a/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts +++ b/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts @@ -302,36 +302,57 @@ function updateExistingLayer( allWidgets: CanvasWidgetsReduxState, parentId: string, layers: FlexLayer[], - index: number, + index = 0, layerIndex = 0, rowIndex: number, ): CanvasWidgetsReduxState { - const widgets: CanvasWidgetsReduxState = Object.assign({}, allWidgets); - const canvas = widgets[parentId]; - if (!canvas || !newLayer) return widgets; + try { + const widgets: CanvasWidgetsReduxState = Object.assign({}, allWidgets); + const canvas = widgets[parentId]; + if (!canvas || !newLayer) return widgets; - // merge the selected layer with the new layer. - const selectedLayer = { - ...layers[layerIndex], - children: [ - ...layers[layerIndex]?.children?.slice(0, rowIndex), + const map: { [key: string]: LayerChild[] } = { + [FlexLayerAlignment.Start]: [], + [FlexLayerAlignment.Center]: [], + [FlexLayerAlignment.End]: [], + }; + + for (const child of layers[layerIndex]?.children) { + map[child.align] = [...map[child.align], child]; + } + const alignment = newLayer.children[0].align; + map[alignment] = [ + ...map[alignment].slice(0, rowIndex), ...newLayer?.children, - ...layers[layerIndex]?.children?.slice(rowIndex), - ], - hasFillChild: newLayer.hasFillChild || layers[layerIndex]?.hasFillChild, - }; + ...map[alignment].slice(rowIndex), + ]; - const updatedCanvas = { - ...canvas, - flexLayers: [ - ...layers.slice(0, layerIndex), - selectedLayer, - ...layers.slice(layerIndex + 1), - ], - }; + // merge the selected layer with the new layer. + const selectedLayer = { + ...layers[layerIndex], + children: [ + ...map[FlexLayerAlignment.Start], + ...map[FlexLayerAlignment.Center], + ...map[FlexLayerAlignment.End], + ], + hasFillChild: newLayer.hasFillChild || layers[layerIndex]?.hasFillChild, + }; - const updatedWidgets = { ...widgets, [parentId]: updatedCanvas }; - return updatedWidgets; + const updatedCanvas = { + ...canvas, + flexLayers: [ + ...layers.slice(0, layerIndex), + selectedLayer, + ...layers.slice(layerIndex + 1), + ], + }; + + const updatedWidgets = { ...widgets, [parentId]: updatedCanvas }; + return updatedWidgets; + } catch (e) { + console.error("#### update existing layer error", e); + return allWidgets; + } } /** From 0bd09f3b415a93ba7841ffefabc8d6b883c1c8c2 Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 7 Dec 2022 11:10:30 -0500 Subject: [PATCH 279/708] temp fix for deploy mode --- app/client/src/utils/WidgetFactory.tsx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/app/client/src/utils/WidgetFactory.tsx b/app/client/src/utils/WidgetFactory.tsx index 93088a4af77e..babbd7f7cf2a 100644 --- a/app/client/src/utils/WidgetFactory.tsx +++ b/app/client/src/utils/WidgetFactory.tsx @@ -3,7 +3,10 @@ import React from "react"; import { PropertyPaneConfig } from "constants/PropertyControlConstants"; import { WidgetConfigProps } from "reducers/entityReducers/widgetConfigReducer"; -import { RenderMode } from "constants/WidgetConstants"; +import { + MAIN_CONTAINER_WIDGET_ID, + RenderMode, +} from "constants/WidgetConstants"; import * as log from "loglevel"; import { WidgetFeatures } from "./WidgetFeatures"; import { @@ -12,6 +15,7 @@ import { enhancePropertyPaneConfig, } from "./WidgetFactoryHelpers"; import { CanvasWidgetStructure } from "widgets/constants"; +import { Positioning } from "components/constants"; type WidgetDerivedPropertyType = any; export type DerivedPropertiesMap = Record; @@ -155,6 +159,10 @@ class WidgetFactory { ...widgetData, renderMode, }; + if (widgetData.widgetId === MAIN_CONTAINER_WIDGET_ID) { + widgetProps.useAutoLayout = true; + widgetProps.positioning = Positioning.Vertical; + } const widgetBuilder = this.widgetMap.get(widgetData.type); if (widgetBuilder) { const widget = widgetBuilder.buildWidget(widgetProps); From 5bd5ab882e8a53a87656e2909675e20c50c2f3b9 Mon Sep 17 00:00:00 2001 From: Preet Date: Fri, 9 Dec 2022 11:01:58 -0800 Subject: [PATCH 280/708] reduce ui flicker --- .../appsmith/autoLayout/FlexBoxComponent.tsx | 17 +++++++++++----- .../appsmith/autoLayout/FlexComponent.tsx | 20 +++++++------------ .../editorComponents/ResizableComponent.tsx | 16 ++------------- 3 files changed, 21 insertions(+), 32 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx index 7ed30249c7f1..6b400f1be2fd 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx @@ -15,6 +15,7 @@ import { isCurrentCanvasDragging } from "selectors/autoLayoutSelectors"; import { getAppMode } from "selectors/entitiesSelector"; import { getIsMobile } from "selectors/mainCanvasSelectors"; import AutoLayoutLayer from "./AutoLayoutLayer"; +import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; export interface FlexBoxProps { direction?: LayoutDirection; @@ -97,7 +98,7 @@ export const DropPosition = styled.div<{ height: ${({ isNewLayer, isVertical }) => isVertical && !isNewLayer ? "auto" : `${DEFAULT_HIGHLIGHT_SIZE}px`}; background-color: rgba(223, 158, 206, 0.6); - margin: 2px; + margin: ${({ isVertical }) => (isVertical ? "2px 4px" : "2px")}; display: ${({ isDragging }) => (isDragging ? "block" : "none")}; align-self: stretch; opacity: 0; @@ -119,10 +120,16 @@ function FlexBoxComponent(props: FlexBoxProps) { const appMode = useSelector(getAppMode); const leaveSpaceForWidgetName = appMode === APP_MODE.EDIT; // TODO: Add support for multiple dragged widgets - const draggedWidget = useSelector( - (state: AppState) => - state.ui.widgetDragResize?.dragDetails?.draggingGroupCenter?.widgetId, + const { dragDetails } = useSelector( + (state: AppState) => state.ui.widgetDragResize, ); + const draggedOn: string | undefined = dragDetails + ? dragDetails?.draggedOn + : undefined; + + const draggedWidget = dragDetails + ? dragDetails?.draggingGroupCenter?.widgetId + : ""; const isDragging = useSelector(isCurrentCanvasDragging(props.widgetId)); @@ -175,7 +182,7 @@ function FlexBoxComponent(props: FlexBoxProps) { } layer-index-${props.layerIndex} child-index-${props.childIndex} ${ props.isVertical ? "isVertical" : "isHorizontal" } ${props.isNewLayer ? "isNewLayer" : ""} row-index-${props.rowIndex}`} - isDragging={isDragging} + isDragging={draggedOn !== undefined} isNewLayer={props.isNewLayer} isVertical={props.isVertical} /> diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx index dac5153a4f16..d882b7c3ca00 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx @@ -68,11 +68,10 @@ const FlexWidget = styled.div<{ &:hover { z-index: ${({ zIndexOnHover }) => zIndexOnHover} !important; } - margin: ${({ dragMargin, isAffectedByDrag }) => - isAffectedByDrag ? `2px ${dragMargin / 2}px` : "0px"}; + margin-top: ${({ isAffectedByDrag }) => (isAffectedByDrag ? "4px" : "0px")}; `; -const DEFAULT_MARGIN = 20; +const DEFAULT_MARGIN = 16; export function FlexComponent(props: AutoLayoutProps) { const isMobile = useSelector(getIsMobile); @@ -86,8 +85,7 @@ export function FlexComponent(props: AutoLayoutProps) { (state: AppState) => state.ui.widgetDragResize, ); const isDragging: boolean = dragDetails?.draggedOn !== undefined; - const isCurrentCanvasDragging: boolean = - dragDetails?.draggedOn === props.parentId; + const siblingCount = useSelector( getSiblingCount(props.widgetId, props.parentId || MAIN_CONTAINER_WIDGET_ID), ); @@ -119,9 +117,8 @@ export function FlexComponent(props: AutoLayoutProps) { props.parentId === MAIN_CONTAINER_WIDGET_ID ? DEFAULT_MARGIN : Math.max(props.parentColumnSpace, DRAG_MARGIN); - const isAffectedByDrag: boolean = - isCurrentCanvasDragging || - (isDragging && props.parentId === MAIN_CONTAINER_WIDGET_ID); + + const isAffectedByDrag: boolean = isDragging; // TODO: Simplify this logic. /** * resize logic: @@ -134,11 +131,8 @@ export function FlexComponent(props: AutoLayoutProps) { ? props.componentWidth - dragMargin - (props.parentId !== MAIN_CONTAINER_WIDGET_ID && siblingCount > 0 - ? DEFAULT_MARGIN / siblingCount - : 0) - - (props.parentId !== MAIN_CONTAINER_WIDGET_ID || isCurrentCanvasDragging - ? 8 - : 4) + ? (DEFAULT_MARGIN * siblingCount + 2) / siblingCount + : 0) : props.componentWidth; return ( diff --git a/app/client/src/components/editorComponents/ResizableComponent.tsx b/app/client/src/components/editorComponents/ResizableComponent.tsx index 8ad3da3d9d6d..9a2e2929ff88 100644 --- a/app/client/src/components/editorComponents/ResizableComponent.tsx +++ b/app/client/src/components/editorComponents/ResizableComponent.tsx @@ -38,10 +38,7 @@ import { } from "selectors/editorSelectors"; import { useWidgetSelection } from "utils/hooks/useWidgetSelection"; import { focusWidget } from "actions/widgetActions"; -import { - GridDefaults, - MAIN_CONTAINER_WIDGET_ID, -} from "constants/WidgetConstants"; +import { GridDefaults } from "constants/WidgetConstants"; import { DropTargetContext } from "./DropTargetComponent"; import { XYCord } from "pages/common/CanvasArenas/hooks/useCanvasDragging"; import { getParentToOpenSelector } from "selectors/widgetSelectors"; @@ -97,13 +94,6 @@ export const ResizableComponent = memo(function ResizableComponent( ); const isWidgetFocused = isFocused || isLastSelected || isSelected; - const { dragDetails } = useSelector( - (state: AppState) => state.ui.widgetDragResize, - ); - - const isCurrentCanvasDragging = - dragDetails && dragDetails?.draggedOn === props.parentId; - const isMobile = useSelector(getIsMobile); // Calculate the dimensions of the widget, // The ResizableContainer's size prop is controlled @@ -311,9 +301,7 @@ export const ResizableComponent = memo(function ResizableComponent( } }; - const isAffectedByDrag = - isCurrentCanvasDragging || - (isDragging && props.parentId === MAIN_CONTAINER_WIDGET_ID); + const isAffectedByDrag: boolean = isDragging; return ( Date: Fri, 9 Dec 2022 11:14:47 -0800 Subject: [PATCH 281/708] remove grid during drag --- .../designSystems/appsmith/autoLayout/FlexBoxComponent.tsx | 1 - .../src/components/editorComponents/DropTargetComponent.tsx | 4 +++- app/client/src/widgets/CanvasWidget.tsx | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx index 6b400f1be2fd..574592c9dccd 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx @@ -15,7 +15,6 @@ import { isCurrentCanvasDragging } from "selectors/autoLayoutSelectors"; import { getAppMode } from "selectors/entitiesSelector"; import { getIsMobile } from "selectors/mainCanvasSelectors"; import AutoLayoutLayer from "./AutoLayoutLayer"; -import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; export interface FlexBoxProps { direction?: LayoutDirection; diff --git a/app/client/src/components/editorComponents/DropTargetComponent.tsx b/app/client/src/components/editorComponents/DropTargetComponent.tsx index b4885b9f2da1..e8de828a3a09 100644 --- a/app/client/src/components/editorComponents/DropTargetComponent.tsx +++ b/app/client/src/components/editorComponents/DropTargetComponent.tsx @@ -36,6 +36,7 @@ type DropTargetComponentProps = WidgetProps & { minHeight: number; noPad?: boolean; isWrapper?: boolean; + useAutoLayout?: boolean; }; const StyledDropTarget = styled.div` @@ -105,7 +106,8 @@ export function DropTargetComponent(props: DropTargetComponentProps) { const { deselectAll, focusWidget } = useWidgetSelection(); const updateCanvasSnapRows = useCanvasSnapRowsUpdateHook(); const showDragLayer = - (isDragging && draggedOn === props.widgetId) || isResizing; + ((isDragging && draggedOn === props.widgetId) || isResizing) && + !props.useAutoLayout; useEffect(() => { const snapRows = getCanvasSnapRows(props.bottomRow, props.canExtend); diff --git a/app/client/src/widgets/CanvasWidget.tsx b/app/client/src/widgets/CanvasWidget.tsx index 4decf697c001..f537caf641d6 100644 --- a/app/client/src/widgets/CanvasWidget.tsx +++ b/app/client/src/widgets/CanvasWidget.tsx @@ -1,5 +1,4 @@ import { - Alignment, LayoutDirection, Overflow, Positioning, @@ -53,6 +52,7 @@ class CanvasWidget extends ContainerWidget { {...canvasProps} {...this.getSnapSpaces()} minHeight={this.props.minHeight || CANVAS_DEFAULT_MIN_HEIGHT_PX} + useAutoLayout={this.props.useAutoLayout} > {this.renderAsContainerComponent(canvasProps)} From 36cafa44937a2e6e056c8d8a56f0b8309be91f22 Mon Sep 17 00:00:00 2001 From: Preet Date: Fri, 9 Dec 2022 11:26:32 -0800 Subject: [PATCH 282/708] update mobile size for fill widget --- app/client/src/sagas/AutoLayoutUtils.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/app/client/src/sagas/AutoLayoutUtils.ts b/app/client/src/sagas/AutoLayoutUtils.ts index f7381d8bf34e..ecd6445cee65 100644 --- a/app/client/src/sagas/AutoLayoutUtils.ts +++ b/app/client/src/sagas/AutoLayoutUtils.ts @@ -261,11 +261,10 @@ export function alterLayoutForMobile( for (const child of children) { const widget = { ...widgets[child] }; - // if (widget.responsiveBehavior === ResponsiveBehavior.Fill) { - // widget.rightColumn = 64; - // widget.leftColumn = 0; - // } - if (widget.minWidth && !widget.mobileRightColumn) { + if (widget.responsiveBehavior === ResponsiveBehavior.Fill) { + widget.rightColumn = 64; + widget.leftColumn = 0; + } else if (widget.minWidth && !widget.mobileRightColumn) { const { minWidth, rightColumn } = widget; const columnSpace = canvasWidth / 64; if (columnSpace * rightColumn < minWidth) { From 12060f106cdf3044a5f6bf5e9c7de1b40a744260 Mon Sep 17 00:00:00 2001 From: Preet Date: Fri, 9 Dec 2022 12:28:51 -0800 Subject: [PATCH 283/708] add responsive layout section --- .../AudioRecorderWidget/widget/index.tsx | 14 ++++---- .../src/widgets/AudioWidget/widget/index.tsx | 14 ++++---- .../widgets/BaseInputWidget/widget/index.tsx | 15 ++++---- .../ButtonGroupWidget/widget/index.tsx | 9 +++-- .../src/widgets/ButtonWidget/widget/index.tsx | 10 ++++-- .../src/widgets/CameraWidget/widget/index.tsx | 14 ++++---- .../widget/propertyConfig/contentConfig.ts | 12 +------ .../widget/propertyConfig/styleConfig.ts | 12 +++++++ .../ChartWidget/widget/propertyConfig.ts | 14 ++++---- .../CheckboxGroupWidget/widget/index.tsx | 13 +++++-- .../widgets/CheckboxWidget/widget/index.tsx | 13 +++++-- .../widget/propertyConfig/contentConfig.ts | 12 +------ .../widget/propertyConfig/styleConfig.ts | 13 ++++++- .../widgets/ContainerWidget/widget/index.tsx | 17 ++++----- .../DatePickerWidget2/widget/index.tsx | 15 ++++---- .../widgets/DividerWidget/widget/index.tsx | 14 ++++---- .../DocumentViewerWidget/widget/index.tsx | 12 ++++--- .../FilePickerWidgetV2/widget/index.tsx | 13 +++++-- .../widgets/IconButtonWidget/widget/index.tsx | 13 +++++-- .../src/widgets/IframeWidget/widget/index.tsx | 13 +++++-- .../src/widgets/ImageWidget/widget/index.tsx | 13 +++++-- .../JSONFormWidget/widget/propertyConfig.ts | 28 ++++++++------- .../ListWidget/widget/propertyConfig.ts | 16 +++++---- .../widgets/MapChartWidget/widget/index.tsx | 14 ++++---- .../src/widgets/MapWidget/widget/index.tsx | 14 ++++---- .../widgets/MenuButtonWidget/widget/index.tsx | 10 ++++-- .../MultiSelectTreeWidget/widget/index.tsx | 13 +++++-- .../MultiSelectWidgetV2/widget/index.tsx | 15 ++++---- .../widget/propertyConfig/contentConfig.ts | 12 +------ .../widget/propertyConfig/styleConfig.ts | 12 +++++++ .../widgets/ProgressWidget/widget/index.tsx | 22 ++++++------ .../QRGeneratorWidget/widget/index.tsx | 12 +++++++ .../widgets/RadioGroupWidget/widget/index.tsx | 31 ++++++++-------- .../widget/propertyConfig/contentConfig.ts | 24 ++++--------- .../widget/propertyConfig/styleConfig.ts | 12 +++++++ .../src/widgets/RateWidget/widget/index.tsx | 12 +++++++ .../RichTextEditorWidget/widget/index.tsx | 31 ++++++++-------- .../src/widgets/SelectWidget/widget/index.tsx | 35 ++++++++++--------- .../SingleSelectTreeWidget/widget/index.tsx | 31 ++++++++-------- .../widgets/StatboxWidget/widget/index.tsx | 14 ++++---- .../SwitchGroupWidget/widget/index.tsx | 9 +++-- .../src/widgets/SwitchWidget/widget/index.tsx | 9 +++-- .../widget/propertyConfig/contentConfig.ts | 24 ++++--------- .../widget/propertyConfig/styleConfig.ts | 12 +++++++ .../src/widgets/TabsWidget/widget/index.tsx | 9 +++-- .../src/widgets/TextWidget/widget/index.tsx | 26 +++++++------- .../src/widgets/VideoWidget/widget/index.tsx | 25 ++++++------- 47 files changed, 454 insertions(+), 293 deletions(-) diff --git a/app/client/src/widgets/AudioRecorderWidget/widget/index.tsx b/app/client/src/widgets/AudioRecorderWidget/widget/index.tsx index 5e69baa5c1a3..b44cb826b4a4 100644 --- a/app/client/src/widgets/AudioRecorderWidget/widget/index.tsx +++ b/app/client/src/widgets/AudioRecorderWidget/widget/index.tsx @@ -1,9 +1,6 @@ import React from "react"; -import { - ResponsiveBehavior, - FlexVerticalAlignment, -} from "components/constants"; +import { ResponsiveBehavior } from "components/constants"; import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; import { WidgetType } from "constants/WidgetConstants"; import { ValidationTypes } from "constants/WidgetValidation"; @@ -74,8 +71,6 @@ class AudioRecorderWidget extends BaseWidget< isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, - { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, - { ...generateVerticalAlignmentConfig(FlexVerticalAlignment.Top) }, ], }, { @@ -105,6 +100,13 @@ class AudioRecorderWidget extends BaseWidget< } static getPropertyPaneStyleConfig() { return [ + { + sectionName: "Responsive Layout", + children: [ + generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill), + generateVerticalAlignmentConfig(), + ], + }, { sectionName: "Styles", children: [ diff --git a/app/client/src/widgets/AudioWidget/widget/index.tsx b/app/client/src/widgets/AudioWidget/widget/index.tsx index 81455684a140..38b9fea4613e 100644 --- a/app/client/src/widgets/AudioWidget/widget/index.tsx +++ b/app/client/src/widgets/AudioWidget/widget/index.tsx @@ -7,10 +7,7 @@ import Skeleton from "components/utils/Skeleton"; import { retryPromise } from "utils/AppsmithUtils"; import ReactPlayer from "react-player"; import { AutocompleteDataType } from "utils/autocomplete/TernServer"; -import { - ResponsiveBehavior, - FlexVerticalAlignment, -} from "components/constants"; +import { ResponsiveBehavior } from "components/constants"; import { generateResponsiveBehaviorConfig, generateVerticalAlignmentConfig, @@ -89,8 +86,6 @@ class AudioWidget extends BaseWidget { isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, - { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, - { ...generateVerticalAlignmentConfig(FlexVerticalAlignment.Top) }, ], }, { @@ -125,6 +120,13 @@ class AudioWidget extends BaseWidget { }, ], }, + { + sectionName: "Responsive Layout", + children: [ + generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill), + generateVerticalAlignmentConfig(), + ], + }, ]; } diff --git a/app/client/src/widgets/BaseInputWidget/widget/index.tsx b/app/client/src/widgets/BaseInputWidget/widget/index.tsx index 7b0071246286..5a7e082ddd08 100644 --- a/app/client/src/widgets/BaseInputWidget/widget/index.tsx +++ b/app/client/src/widgets/BaseInputWidget/widget/index.tsx @@ -11,11 +11,7 @@ import { ValidationTypes } from "constants/WidgetValidation"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; import BaseInputComponent from "../component"; import { InputTypes } from "../constants"; -import { - LabelPosition, - ResponsiveBehavior, - FlexVerticalAlignment, -} from "components/constants"; +import { LabelPosition, ResponsiveBehavior } from "components/constants"; import { generateResponsiveBehaviorConfig, generateVerticalAlignmentConfig, @@ -237,8 +233,6 @@ class BaseInputWidget< return props.type !== "PHONE_INPUT_WIDGET"; }, }, - generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill), - generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, { @@ -280,6 +274,13 @@ class BaseInputWidget< static getPropertyPaneStyleConfig() { return [ + { + sectionName: "Responsive Layout", + children: [ + generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill), + generateVerticalAlignmentConfig(), + ], + }, { sectionName: "Label Styles", children: [ diff --git a/app/client/src/widgets/ButtonGroupWidget/widget/index.tsx b/app/client/src/widgets/ButtonGroupWidget/widget/index.tsx index ae205a608296..4dbb6f1a6b50 100644 --- a/app/client/src/widgets/ButtonGroupWidget/widget/index.tsx +++ b/app/client/src/widgets/ButtonGroupWidget/widget/index.tsx @@ -452,8 +452,6 @@ class ButtonGroupWidget extends BaseWidget< isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, - generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), - generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, ]; @@ -521,6 +519,13 @@ class ButtonGroupWidget extends BaseWidget< }, ], }, + { + sectionName: "Responsive Layout", + children: [ + generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill), + generateVerticalAlignmentConfig(), + ], + }, { sectionName: "Border and Shadow", children: [ diff --git a/app/client/src/widgets/ButtonWidget/widget/index.tsx b/app/client/src/widgets/ButtonWidget/widget/index.tsx index 575f98909f1b..7d05f2b16ef5 100644 --- a/app/client/src/widgets/ButtonWidget/widget/index.tsx +++ b/app/client/src/widgets/ButtonWidget/widget/index.tsx @@ -18,7 +18,6 @@ import { ButtonPlacementTypes, ButtonPlacement, ResponsiveBehavior, - FlexVerticalAlignment, } from "components/constants"; import { generateResponsiveBehaviorConfig, @@ -107,8 +106,6 @@ class ButtonWidget extends BaseWidget { isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, - { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug) }, - generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, { @@ -224,6 +221,13 @@ class ButtonWidget extends BaseWidget { }, ], }, + { + sectionName: "Responsive Layout", + children: [ + generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), + generateVerticalAlignmentConfig(), + ], + }, { sectionName: "Icon", children: [ diff --git a/app/client/src/widgets/CameraWidget/widget/index.tsx b/app/client/src/widgets/CameraWidget/widget/index.tsx index e3a9bd850227..72efc26f4b2b 100644 --- a/app/client/src/widgets/CameraWidget/widget/index.tsx +++ b/app/client/src/widgets/CameraWidget/widget/index.tsx @@ -18,10 +18,7 @@ import { generateResponsiveBehaviorConfig, generateVerticalAlignmentConfig, } from "utils/layoutPropertiesUtils"; -import { - ResponsiveBehavior, - FlexVerticalAlignment, -} from "components/constants"; +import { ResponsiveBehavior } from "components/constants"; class CameraWidget extends BaseWidget { static getPropertyPaneContentConfig() { @@ -85,8 +82,6 @@ class CameraWidget extends BaseWidget { isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, - generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), - generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, { @@ -156,6 +151,13 @@ class CameraWidget extends BaseWidget { static getPropertyPaneStyleConfig() { return [ + { + sectionName: "Responsive Layout", + children: [ + generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), + generateVerticalAlignmentConfig(), + ], + }, { sectionName: "Border and Shadow", children: [ diff --git a/app/client/src/widgets/CategorySliderWidget/widget/propertyConfig/contentConfig.ts b/app/client/src/widgets/CategorySliderWidget/widget/propertyConfig/contentConfig.ts index f0f40613d991..c304d84a0311 100644 --- a/app/client/src/widgets/CategorySliderWidget/widget/propertyConfig/contentConfig.ts +++ b/app/client/src/widgets/CategorySliderWidget/widget/propertyConfig/contentConfig.ts @@ -1,8 +1,4 @@ -import { - LabelPosition, - ResponsiveBehavior, - FlexVerticalAlignment, -} from "components/constants"; +import { LabelPosition } from "components/constants"; import { AutocompleteDataType } from "utils/autocomplete/TernServer"; import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; import { Alignment } from "@blueprintjs/core"; @@ -12,10 +8,6 @@ import { optionsCustomValidation, } from "../../validations"; import { CategorySliderWidgetProps } from ".."; -import { - generateResponsiveBehaviorConfig, - generateVerticalAlignmentConfig, -} from "utils/layoutPropertiesUtils"; export default [ { @@ -183,8 +175,6 @@ export default [ isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, - generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), - generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, { diff --git a/app/client/src/widgets/CategorySliderWidget/widget/propertyConfig/styleConfig.ts b/app/client/src/widgets/CategorySliderWidget/widget/propertyConfig/styleConfig.ts index a03f244241a6..5533c99cef1e 100644 --- a/app/client/src/widgets/CategorySliderWidget/widget/propertyConfig/styleConfig.ts +++ b/app/client/src/widgets/CategorySliderWidget/widget/propertyConfig/styleConfig.ts @@ -1,4 +1,9 @@ +import { ResponsiveBehavior } from "components/constants"; import { ValidationTypes } from "constants/WidgetValidation"; +import { + generateResponsiveBehaviorConfig, + generateVerticalAlignmentConfig, +} from "utils/layoutPropertiesUtils"; export default [ { @@ -34,6 +39,13 @@ export default [ }, ], }, + { + sectionName: "Responsive Layout", + children: [ + generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), + generateVerticalAlignmentConfig(), + ], + }, { sectionName: "Label Styles", children: [ diff --git a/app/client/src/widgets/ChartWidget/widget/propertyConfig.ts b/app/client/src/widgets/ChartWidget/widget/propertyConfig.ts index abef10f5961b..186a85c41d70 100644 --- a/app/client/src/widgets/ChartWidget/widget/propertyConfig.ts +++ b/app/client/src/widgets/ChartWidget/widget/propertyConfig.ts @@ -7,10 +7,7 @@ import { generateResponsiveBehaviorConfig, generateVerticalAlignmentConfig, } from "utils/layoutPropertiesUtils"; -import { - ResponsiveBehavior, - FlexVerticalAlignment, -} from "components/constants"; +import { ResponsiveBehavior } from "components/constants"; export const contentConfig = [ { @@ -249,8 +246,6 @@ export const contentConfig = [ x.chartType === "CUSTOM_FUSION_CHART" || x.chartType === "PIE_CHART", dependencies: ["chartType"], }, - generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill), - generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, { @@ -337,6 +332,13 @@ export const contentConfig = [ ]; export const styleConfig = [ + { + sectionName: "Responsive Layout", + children: [ + generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill), + generateVerticalAlignmentConfig(), + ], + }, { sectionName: "Border and Shadow", children: [ diff --git a/app/client/src/widgets/CheckboxGroupWidget/widget/index.tsx b/app/client/src/widgets/CheckboxGroupWidget/widget/index.tsx index 1a1fc9bf5ed7..121dd8750423 100644 --- a/app/client/src/widgets/CheckboxGroupWidget/widget/index.tsx +++ b/app/client/src/widgets/CheckboxGroupWidget/widget/index.tsx @@ -22,7 +22,10 @@ import { GRID_DENSITY_MIGRATION_V1 } from "widgets/constants"; import CheckboxGroupComponent from "../component"; import { OptionProps, SelectAllState, SelectAllStates } from "../constants"; -import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; +import { + generateResponsiveBehaviorConfig, + generateVerticalAlignmentConfig, +} from "utils/layoutPropertiesUtils"; export function defaultSelectedValuesValidation( value: unknown, @@ -276,7 +279,6 @@ class CheckboxGroupWidget extends BaseWidget< isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, - generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill), ], }, { @@ -298,6 +300,13 @@ class CheckboxGroupWidget extends BaseWidget< static getPropertyPaneStyleConfig() { return [ + { + sectionName: "Responsive Layout", + children: [ + generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill), + generateVerticalAlignmentConfig(), + ], + }, { sectionName: "Label Styles", children: [ diff --git a/app/client/src/widgets/CheckboxWidget/widget/index.tsx b/app/client/src/widgets/CheckboxWidget/widget/index.tsx index 8e871d14181e..0930d20602b1 100644 --- a/app/client/src/widgets/CheckboxWidget/widget/index.tsx +++ b/app/client/src/widgets/CheckboxWidget/widget/index.tsx @@ -7,7 +7,10 @@ import { ValidationTypes } from "constants/WidgetValidation"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; import { LabelPosition, ResponsiveBehavior } from "components/constants"; import { AlignWidgetTypes } from "widgets/constants"; -import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; +import { + generateResponsiveBehaviorConfig, + generateVerticalAlignmentConfig, +} from "utils/layoutPropertiesUtils"; class CheckboxWidget extends BaseWidget { static getPropertyPaneContentConfig() { @@ -119,7 +122,6 @@ class CheckboxWidget extends BaseWidget { isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, - { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, ], }, { @@ -141,6 +143,13 @@ class CheckboxWidget extends BaseWidget { static getPropertyPaneStyleConfig() { return [ + { + sectionName: "Responsive Layout", + children: [ + generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill), + generateVerticalAlignmentConfig(), + ], + }, { sectionName: "Label Styles", children: [ diff --git a/app/client/src/widgets/CodeScannerWidget/widget/propertyConfig/contentConfig.ts b/app/client/src/widgets/CodeScannerWidget/widget/propertyConfig/contentConfig.ts index 510ef1d2b0f3..04b889ffd5f1 100644 --- a/app/client/src/widgets/CodeScannerWidget/widget/propertyConfig/contentConfig.ts +++ b/app/client/src/widgets/CodeScannerWidget/widget/propertyConfig/contentConfig.ts @@ -1,17 +1,9 @@ -import { ValidationTypes } from "constants/WidgetValidation"; import { PropertyPaneConfig } from "constants/PropertyControlConstants"; +import { ValidationTypes } from "constants/WidgetValidation"; import { CodeScannerWidgetProps, ScannerLayout, } from "widgets/CodeScannerWidget/constants"; -import { - generateResponsiveBehaviorConfig, - generateVerticalAlignmentConfig, -} from "utils/layoutPropertiesUtils"; -import { - ResponsiveBehavior, - FlexVerticalAlignment, -} from "components/constants"; export default [ { @@ -101,8 +93,6 @@ export default [ props.scannerLayout === ScannerLayout.ALWAYS_ON, dependencies: ["scannerLayout"], }, - generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), - generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, { diff --git a/app/client/src/widgets/CodeScannerWidget/widget/propertyConfig/styleConfig.ts b/app/client/src/widgets/CodeScannerWidget/widget/propertyConfig/styleConfig.ts index 6bab5d77b1d1..46c1dd98fb2e 100644 --- a/app/client/src/widgets/CodeScannerWidget/widget/propertyConfig/styleConfig.ts +++ b/app/client/src/widgets/CodeScannerWidget/widget/propertyConfig/styleConfig.ts @@ -1,12 +1,23 @@ import { ValidationTypes } from "constants/WidgetValidation"; -import { ButtonPlacementTypes } from "components/constants"; +import { ButtonPlacementTypes, ResponsiveBehavior } from "components/constants"; import { updateStyles } from "../propertyUtils"; import { CodeScannerWidgetProps, ScannerLayout, } from "widgets/CodeScannerWidget/constants"; +import { + generateResponsiveBehaviorConfig, + generateVerticalAlignmentConfig, +} from "utils/layoutPropertiesUtils"; export default [ + { + sectionName: "Responsive Layout", + children: [ + generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), + generateVerticalAlignmentConfig(), + ], + }, { sectionName: "Icon", children: [ diff --git a/app/client/src/widgets/ContainerWidget/widget/index.tsx b/app/client/src/widgets/ContainerWidget/widget/index.tsx index 9efe4460ce9f..daab27743786 100644 --- a/app/client/src/widgets/ContainerWidget/widget/index.tsx +++ b/app/client/src/widgets/ContainerWidget/widget/index.tsx @@ -16,11 +16,7 @@ import { ValidationTypes } from "constants/WidgetValidation"; import { compact, map, sortBy } from "lodash"; import WidgetsMultiSelectBox from "pages/Editor/WidgetsMultiSelectBox"; -import { - Positioning, - ResponsiveBehavior, - FlexVerticalAlignment, -} from "components/constants"; +import { Positioning, ResponsiveBehavior } from "components/constants"; import { generatePositioningConfig, generateResponsiveBehaviorConfig, @@ -70,9 +66,6 @@ export class ContainerWidget extends BaseWidget< isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, - generatePositioningConfig(Positioning.Vertical), - { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, - generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, ]; @@ -80,6 +73,14 @@ export class ContainerWidget extends BaseWidget< static getPropertyPaneStyleConfig() { return [ + { + sectionName: "Responsive Layout", + children: [ + generatePositioningConfig(Positioning.Vertical), + generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill), + generateVerticalAlignmentConfig(), + ], + }, { sectionName: "Color", children: [ diff --git a/app/client/src/widgets/DatePickerWidget2/widget/index.tsx b/app/client/src/widgets/DatePickerWidget2/widget/index.tsx index 1a209d1d0901..dbf63323f0b9 100644 --- a/app/client/src/widgets/DatePickerWidget2/widget/index.tsx +++ b/app/client/src/widgets/DatePickerWidget2/widget/index.tsx @@ -10,11 +10,7 @@ import { AutocompleteDataType } from "utils/autocomplete/TernServer"; import derivedProperties from "./parseDerivedProperties"; import { DatePickerType, TimePrecision } from "../constants"; -import { - LabelPosition, - ResponsiveBehavior, - FlexVerticalAlignment, -} from "components/constants"; +import { LabelPosition, ResponsiveBehavior } from "components/constants"; import { Alignment } from "@blueprintjs/core"; import { GRID_DENSITY_MIGRATION_V1 } from "widgets/constants"; import { DateFormatOptions } from "./constants"; @@ -287,8 +283,6 @@ class DatePickerWidget extends BaseWidget { isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, - { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, - generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, { @@ -311,6 +305,13 @@ class DatePickerWidget extends BaseWidget { static getPropertyPaneStyleConfig() { return [ + { + sectionName: "Responsive Layout", + children: [ + generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill), + generateVerticalAlignmentConfig(), + ], + }, { sectionName: "Label Styles", children: [ diff --git a/app/client/src/widgets/DividerWidget/widget/index.tsx b/app/client/src/widgets/DividerWidget/widget/index.tsx index 5715815ee0d2..46c6c2cbf668 100644 --- a/app/client/src/widgets/DividerWidget/widget/index.tsx +++ b/app/client/src/widgets/DividerWidget/widget/index.tsx @@ -8,10 +8,7 @@ import { generateResponsiveBehaviorConfig, generateVerticalAlignmentConfig, } from "utils/layoutPropertiesUtils"; -import { - ResponsiveBehavior, - FlexVerticalAlignment, -} from "components/constants"; +import { ResponsiveBehavior } from "components/constants"; class DividerWidget extends BaseWidget { static getPropertyPaneContentConfig() { @@ -40,8 +37,6 @@ class DividerWidget extends BaseWidget { isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, - { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, - generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, ]; @@ -75,6 +70,13 @@ class DividerWidget extends BaseWidget { }, ], }, + { + sectionName: "Responsive Layout", + children: [ + generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill), + generateVerticalAlignmentConfig(), + ], + }, { sectionName: "Stroke", children: [ diff --git a/app/client/src/widgets/DocumentViewerWidget/widget/index.tsx b/app/client/src/widgets/DocumentViewerWidget/widget/index.tsx index fc6cc9367b59..972c80120f1b 100644 --- a/app/client/src/widgets/DocumentViewerWidget/widget/index.tsx +++ b/app/client/src/widgets/DocumentViewerWidget/widget/index.tsx @@ -10,10 +10,7 @@ import { generateResponsiveBehaviorConfig, generateVerticalAlignmentConfig, } from "utils/layoutPropertiesUtils"; -import { - ResponsiveBehavior, - FlexVerticalAlignment, -} from "components/constants"; +import { ResponsiveBehavior } from "components/constants"; export function documentUrlValidation(value: unknown): ValidationResponse { // applied validations if value exist @@ -124,8 +121,13 @@ class DocumentViewerWidget extends BaseWidget< isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, + ], + }, + { + sectionName: "Responsive Layout", + children: [ generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), - generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), + generateVerticalAlignmentConfig(), ], }, ]; diff --git a/app/client/src/widgets/FilePickerWidgetV2/widget/index.tsx b/app/client/src/widgets/FilePickerWidgetV2/widget/index.tsx index 98ed3a17ebc2..8c70f5a89b53 100644 --- a/app/client/src/widgets/FilePickerWidgetV2/widget/index.tsx +++ b/app/client/src/widgets/FilePickerWidgetV2/widget/index.tsx @@ -25,7 +25,10 @@ import Papa from "papaparse"; import { klona } from "klona"; import { UppyFile } from "@uppy/utils"; import { ResponsiveBehavior } from "components/constants"; -import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; +import { + generateResponsiveBehaviorConfig, + generateVerticalAlignmentConfig, +} from "utils/layoutPropertiesUtils"; const CSV_ARRAY_LABEL = "Array (CSVs only)"; const CSV_FILE_TYPE_REGEX = /.+(\/csv)$/; @@ -423,7 +426,6 @@ class FilePickerWidget extends BaseWidget< isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, - { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, ], }, { @@ -446,6 +448,13 @@ class FilePickerWidget extends BaseWidget< static getPropertyPaneStyleConfig() { return [ + { + sectionName: "Responsive Layout", + children: [ + generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill), + generateVerticalAlignmentConfig(), + ], + }, { sectionName: "Color", children: [ diff --git a/app/client/src/widgets/IconButtonWidget/widget/index.tsx b/app/client/src/widgets/IconButtonWidget/widget/index.tsx index 59f8aac972a4..ad070c7f5183 100644 --- a/app/client/src/widgets/IconButtonWidget/widget/index.tsx +++ b/app/client/src/widgets/IconButtonWidget/widget/index.tsx @@ -13,7 +13,10 @@ import { ButtonVariantTypes, ResponsiveBehavior, } from "components/constants"; -import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; +import { + generateResponsiveBehaviorConfig, + generateVerticalAlignmentConfig, +} from "utils/layoutPropertiesUtils"; const ICON_NAMES = Object.keys(IconNames).map( (name: string) => IconNames[name as keyof typeof IconNames], @@ -108,7 +111,6 @@ class IconButtonWidget extends BaseWidget { isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, - { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug) }, ], }, ]; @@ -156,6 +158,13 @@ class IconButtonWidget extends BaseWidget { }, ], }, + { + sectionName: "Responsive Layout", + children: [ + generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), + generateVerticalAlignmentConfig(), + ], + }, { sectionName: "Color", children: [ diff --git a/app/client/src/widgets/IframeWidget/widget/index.tsx b/app/client/src/widgets/IframeWidget/widget/index.tsx index 09e7ac9fc045..c4ca69f4a72b 100644 --- a/app/client/src/widgets/IframeWidget/widget/index.tsx +++ b/app/client/src/widgets/IframeWidget/widget/index.tsx @@ -4,7 +4,10 @@ import { ValidationTypes } from "constants/WidgetValidation"; import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; import IframeComponent from "../component"; import { IframeWidgetProps } from "../constants"; -import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; +import { + generateResponsiveBehaviorConfig, + generateVerticalAlignmentConfig, +} from "utils/layoutPropertiesUtils"; import { ResponsiveBehavior } from "components/constants"; class IframeWidget extends BaseWidget { @@ -66,7 +69,6 @@ class IframeWidget extends BaseWidget { isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, - generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), ], }, { @@ -106,6 +108,13 @@ class IframeWidget extends BaseWidget { static getPropertyPaneStyleConfig() { return [ + { + sectionName: "Responsive Layout", + children: [ + generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), + generateVerticalAlignmentConfig(), + ], + }, { sectionName: "Color", children: [ diff --git a/app/client/src/widgets/ImageWidget/widget/index.tsx b/app/client/src/widgets/ImageWidget/widget/index.tsx index bcc0582a5edc..f2018652e540 100644 --- a/app/client/src/widgets/ImageWidget/widget/index.tsx +++ b/app/client/src/widgets/ImageWidget/widget/index.tsx @@ -6,7 +6,10 @@ import ImageComponent from "../component"; import { ValidationTypes } from "constants/WidgetValidation"; import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; -import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; +import { + generateResponsiveBehaviorConfig, + generateVerticalAlignmentConfig, +} from "utils/layoutPropertiesUtils"; import { ResponsiveBehavior } from "components/constants"; class ImageWidget extends BaseWidget { @@ -152,7 +155,6 @@ class ImageWidget extends BaseWidget { isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, - generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), ], }, { @@ -174,6 +176,13 @@ class ImageWidget extends BaseWidget { static getPropertyPaneStyleConfig() { return [ + { + sectionName: "Responsive Layout", + children: [ + generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), + generateVerticalAlignmentConfig(), + ], + }, { sectionName: "Border and Shadow", children: [ diff --git a/app/client/src/widgets/JSONFormWidget/widget/propertyConfig.ts b/app/client/src/widgets/JSONFormWidget/widget/propertyConfig.ts index 1aea30f11f4b..0fc6f9ab8a15 100644 --- a/app/client/src/widgets/JSONFormWidget/widget/propertyConfig.ts +++ b/app/client/src/widgets/JSONFormWidget/widget/propertyConfig.ts @@ -1,25 +1,24 @@ import { Alignment } from "@blueprintjs/core"; -import generatePanelPropertyConfig from "./propertyConfig/generatePanelPropertyConfig"; -import { AutocompleteDataType } from "utils/autocomplete/TernServer"; -import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; -import { JSONFormWidgetProps } from "."; -import { ROOT_SCHEMA_KEY } from "../constants"; -import { ValidationTypes } from "constants/WidgetValidation"; import { - ButtonVariantTypes, ButtonPlacementTypes, + ButtonVariantTypes, ResponsiveBehavior, - FlexVerticalAlignment, } from "components/constants"; -import { ButtonWidgetProps } from "widgets/ButtonWidget/widget"; import { OnButtonClickProps } from "components/propertyControls/ButtonControl"; -import { ComputedSchemaStatus, computeSchema } from "./helper"; +import { ValidationTypes } from "constants/WidgetValidation"; +import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; +import { AutocompleteDataType } from "utils/autocomplete/TernServer"; import { EVALUATION_PATH } from "utils/DynamicBindingUtils"; import { generateResponsiveBehaviorConfig, generateVerticalAlignmentConfig, } from "utils/layoutPropertiesUtils"; +import { ButtonWidgetProps } from "widgets/ButtonWidget/widget"; +import { JSONFormWidgetProps } from "."; +import { ROOT_SCHEMA_KEY } from "../constants"; +import { ComputedSchemaStatus, computeSchema } from "./helper"; +import generatePanelPropertyConfig from "./propertyConfig/generatePanelPropertyConfig"; const MAX_NESTING_LEVEL = 5; @@ -271,8 +270,6 @@ export const contentConfig = [ isTriggerProperty: false, validation: { type: ValidationTypes.TEXT }, }, - generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill), - generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, { @@ -463,6 +460,13 @@ const generateButtonStyleControlsV2For = (prefix: string) => [ ]; export const styleConfig = [ + { + sectionName: "Responsive Layout", + children: [ + generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill), + generateVerticalAlignmentConfig(), + ], + }, { sectionName: "Color", children: [ diff --git a/app/client/src/widgets/ListWidget/widget/propertyConfig.ts b/app/client/src/widgets/ListWidget/widget/propertyConfig.ts index 29d148e3fe97..d64dcb51cdb0 100644 --- a/app/client/src/widgets/ListWidget/widget/propertyConfig.ts +++ b/app/client/src/widgets/ListWidget/widget/propertyConfig.ts @@ -4,16 +4,13 @@ import { ListWidgetProps } from "../constants"; import { ValidationTypes } from "constants/WidgetValidation"; import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; -import { EVAL_VALUE_PATH } from "utils/DynamicBindingUtils"; import { AutocompleteDataType } from "utils/autocomplete/TernServer"; +import { EVAL_VALUE_PATH } from "utils/DynamicBindingUtils"; import { generateResponsiveBehaviorConfig, generateVerticalAlignmentConfig, } from "utils/layoutPropertiesUtils"; -import { - ResponsiveBehavior, - FlexVerticalAlignment, -} from "components/constants"; +import { ResponsiveBehavior } from "components/constants"; export const PropertyPaneContentConfig = [ { @@ -97,8 +94,6 @@ export const PropertyPaneContentConfig = [ isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, - { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, - generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, { @@ -153,6 +148,13 @@ export const PropertyPaneStyleConfig = [ }, ], }, + { + sectionName: "Responsive Layout", + children: [ + generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill), + generateVerticalAlignmentConfig(), + ], + }, { sectionName: "Color", children: [ diff --git a/app/client/src/widgets/MapChartWidget/widget/index.tsx b/app/client/src/widgets/MapChartWidget/widget/index.tsx index f6975c131f21..545e4142867b 100644 --- a/app/client/src/widgets/MapChartWidget/widget/index.tsx +++ b/app/client/src/widgets/MapChartWidget/widget/index.tsx @@ -26,10 +26,7 @@ import { generateResponsiveBehaviorConfig, generateVerticalAlignmentConfig, } from "utils/layoutPropertiesUtils"; -import { - ResponsiveBehavior, - FlexVerticalAlignment, -} from "components/constants"; +import { ResponsiveBehavior } from "components/constants"; const MapChartComponent = lazy(() => retryPromise(() => @@ -210,8 +207,6 @@ class MapChartWidget extends BaseWidget { isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, - generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), - generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, { @@ -301,6 +296,13 @@ class MapChartWidget extends BaseWidget { }, ], }, + { + sectionName: "Responsive Layout", + children: [ + generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), + generateVerticalAlignmentConfig(), + ], + }, { sectionName: "Border and Shadow", children: [ diff --git a/app/client/src/widgets/MapWidget/widget/index.tsx b/app/client/src/widgets/MapWidget/widget/index.tsx index 3ad1512d6a50..8af0a16cc32b 100644 --- a/app/client/src/widgets/MapWidget/widget/index.tsx +++ b/app/client/src/widgets/MapWidget/widget/index.tsx @@ -16,10 +16,7 @@ import { generateResponsiveBehaviorConfig, generateVerticalAlignmentConfig, } from "utils/layoutPropertiesUtils"; -import { - ResponsiveBehavior, - FlexVerticalAlignment, -} from "components/constants"; +import { ResponsiveBehavior } from "components/constants"; const { google } = getAppsmithConfigs(); @@ -208,8 +205,6 @@ class MapWidget extends BaseWidget { isBindProperty: false, isTriggerProperty: false, }, - generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill), - generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, { @@ -258,6 +253,13 @@ class MapWidget extends BaseWidget { static getPropertyPaneStyleConfig() { return [ + { + sectionName: "Responsive Layout", + children: [ + generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill), + generateVerticalAlignmentConfig(), + ], + }, { sectionName: "Border and Shadow", children: [ diff --git a/app/client/src/widgets/MenuButtonWidget/widget/index.tsx b/app/client/src/widgets/MenuButtonWidget/widget/index.tsx index cfd57cfc5c18..cb655bee5d1d 100644 --- a/app/client/src/widgets/MenuButtonWidget/widget/index.tsx +++ b/app/client/src/widgets/MenuButtonWidget/widget/index.tsx @@ -12,7 +12,6 @@ import { ButtonPlacementTypes, ButtonPlacement, ResponsiveBehavior, - FlexVerticalAlignment, } from "components/constants"; import { IconName } from "@blueprintjs/icons"; import { MinimumPopupRows } from "widgets/constants"; @@ -256,8 +255,6 @@ class MenuButtonWidget extends BaseWidget { isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, - generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), - generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, ]; @@ -305,6 +302,13 @@ class MenuButtonWidget extends BaseWidget { }, ], }, + { + sectionName: "Responsive Layout", + children: [ + generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), + generateVerticalAlignmentConfig(), + ], + }, { sectionName: "Icon", children: [ diff --git a/app/client/src/widgets/MultiSelectTreeWidget/widget/index.tsx b/app/client/src/widgets/MultiSelectTreeWidget/widget/index.tsx index 02980b7806ac..73bd7a334605 100644 --- a/app/client/src/widgets/MultiSelectTreeWidget/widget/index.tsx +++ b/app/client/src/widgets/MultiSelectTreeWidget/widget/index.tsx @@ -17,7 +17,10 @@ import MultiTreeSelectComponent from "../component"; import { LabelPosition, ResponsiveBehavior } from "components/constants"; import { Alignment } from "@blueprintjs/core"; import derivedProperties from "./parseDerivedProperties"; -import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; +import { + generateResponsiveBehaviorConfig, + generateVerticalAlignmentConfig, +} from "utils/layoutPropertiesUtils"; function defaultOptionValueValidation(value: unknown): ValidationResponse { let values: string[] = []; @@ -318,7 +321,6 @@ class MultiSelectTreeWidget extends BaseWidget< isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, - { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, ], }, { @@ -340,6 +342,13 @@ class MultiSelectTreeWidget extends BaseWidget< static getPropertyPaneStyleConfig() { return [ + { + sectionName: "Responsive Layout", + children: [ + generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill), + generateVerticalAlignmentConfig(), + ], + }, { sectionName: "Label Styles", children: [ diff --git a/app/client/src/widgets/MultiSelectWidgetV2/widget/index.tsx b/app/client/src/widgets/MultiSelectWidgetV2/widget/index.tsx index 742723df2942..9edbf6c91b16 100644 --- a/app/client/src/widgets/MultiSelectWidgetV2/widget/index.tsx +++ b/app/client/src/widgets/MultiSelectWidgetV2/widget/index.tsx @@ -14,11 +14,7 @@ import MultiSelectComponent from "../component"; import { DraftValueType, LabelInValueType } from "rc-select/lib/Select"; import { Layers } from "constants/Layers"; import { MinimumPopupRows, GRID_DENSITY_MIGRATION_V1 } from "widgets/constants"; -import { - LabelPosition, - ResponsiveBehavior, - FlexVerticalAlignment, -} from "components/constants"; +import { LabelPosition, ResponsiveBehavior } from "components/constants"; import { Alignment } from "@blueprintjs/core"; import { AutocompleteDataType } from "utils/autocomplete/TernServer"; import { @@ -439,8 +435,6 @@ class MultiSelectWidget extends BaseWidget< isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, - { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, - generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, { @@ -462,6 +456,13 @@ class MultiSelectWidget extends BaseWidget< static getPropertyPaneStyleConfig() { return [ + { + sectionName: "Responsive Layout", + children: [ + generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill), + generateVerticalAlignmentConfig(), + ], + }, { sectionName: "Label Styles", children: [ diff --git a/app/client/src/widgets/NumberSliderWidget/widget/propertyConfig/contentConfig.ts b/app/client/src/widgets/NumberSliderWidget/widget/propertyConfig/contentConfig.ts index 5701ee5bb9d5..34959094ff88 100644 --- a/app/client/src/widgets/NumberSliderWidget/widget/propertyConfig/contentConfig.ts +++ b/app/client/src/widgets/NumberSliderWidget/widget/propertyConfig/contentConfig.ts @@ -1,15 +1,7 @@ import { Alignment } from "@blueprintjs/core"; -import { - LabelPosition, - ResponsiveBehavior, - FlexVerticalAlignment, -} from "components/constants"; +import { LabelPosition } from "components/constants"; import { ValidationTypes } from "constants/WidgetValidation"; import { AutocompleteDataType } from "utils/autocomplete/TernServer"; -import { - generateResponsiveBehaviorConfig, - generateVerticalAlignmentConfig, -} from "utils/layoutPropertiesUtils"; import { NumberSliderWidgetProps } from ".."; import { defaultValueValidation, @@ -269,8 +261,6 @@ export default [ isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, - generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), - generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, { diff --git a/app/client/src/widgets/NumberSliderWidget/widget/propertyConfig/styleConfig.ts b/app/client/src/widgets/NumberSliderWidget/widget/propertyConfig/styleConfig.ts index a03f244241a6..5533c99cef1e 100644 --- a/app/client/src/widgets/NumberSliderWidget/widget/propertyConfig/styleConfig.ts +++ b/app/client/src/widgets/NumberSliderWidget/widget/propertyConfig/styleConfig.ts @@ -1,4 +1,9 @@ +import { ResponsiveBehavior } from "components/constants"; import { ValidationTypes } from "constants/WidgetValidation"; +import { + generateResponsiveBehaviorConfig, + generateVerticalAlignmentConfig, +} from "utils/layoutPropertiesUtils"; export default [ { @@ -34,6 +39,13 @@ export default [ }, ], }, + { + sectionName: "Responsive Layout", + children: [ + generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), + generateVerticalAlignmentConfig(), + ], + }, { sectionName: "Label Styles", children: [ diff --git a/app/client/src/widgets/ProgressWidget/widget/index.tsx b/app/client/src/widgets/ProgressWidget/widget/index.tsx index 04c561f422a7..00b600362a9b 100644 --- a/app/client/src/widgets/ProgressWidget/widget/index.tsx +++ b/app/client/src/widgets/ProgressWidget/widget/index.tsx @@ -1,20 +1,17 @@ import React from "react"; -import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; +import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; -import ProgressComponent from "../component"; -import { ProgressType, ProgressVariant } from "../constants"; -import { ValidationTypes } from "constants/WidgetValidation"; +import { ResponsiveBehavior } from "components/constants"; import { Colors } from "constants/Colors"; +import { ValidationTypes } from "constants/WidgetValidation"; import { generateResponsiveBehaviorConfig, generateVerticalAlignmentConfig, } from "utils/layoutPropertiesUtils"; -import { - ResponsiveBehavior, - FlexVerticalAlignment, -} from "components/constants"; +import ProgressComponent from "../component"; +import { ProgressType, ProgressVariant } from "../constants"; class ProgressWidget extends BaseWidget { static getPropertyPaneContentConfig() { @@ -130,8 +127,6 @@ class ProgressWidget extends BaseWidget { hidden: (props: ProgressWidgetProps) => props.isIndeterminate, dependencies: ["isIndeterminate"], }, - generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), - generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, ]; @@ -139,6 +134,13 @@ class ProgressWidget extends BaseWidget { static getPropertyPaneStyleConfig() { return [ + { + sectionName: "Responsive Layout", + children: [ + generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), + generateVerticalAlignmentConfig(), + ], + }, { sectionName: "Color", children: [ diff --git a/app/client/src/widgets/QRGeneratorWidget/widget/index.tsx b/app/client/src/widgets/QRGeneratorWidget/widget/index.tsx index 0baf2b4d9281..283be12c7fff 100644 --- a/app/client/src/widgets/QRGeneratorWidget/widget/index.tsx +++ b/app/client/src/widgets/QRGeneratorWidget/widget/index.tsx @@ -14,6 +14,11 @@ import { AutocompleteDataType } from "utils/autocomplete/TernServer"; import { OverflowTypes } from "../constants"; import WidgetStyleContainer from "components/designSystems/appsmith/WidgetStyleContainer"; import { pick } from "lodash"; +import { ResponsiveBehavior } from "components/constants"; +import { + generateResponsiveBehaviorConfig, + generateVerticalAlignmentConfig, +} from "utils/layoutPropertiesUtils"; const MAX_HTML_PARSING_LENGTH = 1000; class TextWidget extends BaseWidget { @@ -391,6 +396,13 @@ class TextWidget extends BaseWidget { static getPropertyPaneStyleConfig() { return [ + { + sectionName: "Responsive Layout", + children: [ + generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), + generateVerticalAlignmentConfig(), + ], + }, { sectionName: "General", children: [ diff --git a/app/client/src/widgets/RadioGroupWidget/widget/index.tsx b/app/client/src/widgets/RadioGroupWidget/widget/index.tsx index ae3471322095..4385538a3ca0 100644 --- a/app/client/src/widgets/RadioGroupWidget/widget/index.tsx +++ b/app/client/src/widgets/RadioGroupWidget/widget/index.tsx @@ -1,28 +1,24 @@ -import React from "react"; import { Alignment } from "@blueprintjs/core"; -import { isArray, compact, isNumber } from "lodash"; +import { compact, isArray, isNumber } from "lodash"; +import React from "react"; -import BaseWidget, { WidgetProps, WidgetState } from "../../BaseWidget"; -import { TextSize, WidgetType } from "constants/WidgetConstants"; -import { GRID_DENSITY_MIGRATION_V1 } from "widgets/constants"; -import { AutocompleteDataType } from "utils/autocomplete/TernServer"; +import { LabelPosition, ResponsiveBehavior } from "components/constants"; import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; +import { TextSize, WidgetType } from "constants/WidgetConstants"; import { ValidationResponse, ValidationTypes, } from "constants/WidgetValidation"; import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; -import { RadioOption } from "../constants"; -import { - LabelPosition, - ResponsiveBehavior, - FlexVerticalAlignment, -} from "components/constants"; -import RadioGroupComponent from "../component"; +import { AutocompleteDataType } from "utils/autocomplete/TernServer"; import { generateResponsiveBehaviorConfig, generateVerticalAlignmentConfig, } from "utils/layoutPropertiesUtils"; +import { GRID_DENSITY_MIGRATION_V1 } from "widgets/constants"; +import BaseWidget, { WidgetProps, WidgetState } from "../../BaseWidget"; +import RadioGroupComponent from "../component"; +import { RadioOption } from "../constants"; /** * Validation rules: @@ -341,8 +337,6 @@ class RadioGroupWidget extends BaseWidget { isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, - generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), - generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, { @@ -365,6 +359,13 @@ class RadioGroupWidget extends BaseWidget { static getPropertyPaneStyleConfig() { return [ + { + sectionName: "Responsive Layout", + children: [ + generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), + generateVerticalAlignmentConfig(), + ], + }, { sectionName: "Label Styles", children: [ diff --git a/app/client/src/widgets/RangeSliderWidget/widget/propertyConfig/contentConfig.ts b/app/client/src/widgets/RangeSliderWidget/widget/propertyConfig/contentConfig.ts index e42b43b133be..40579d432ebc 100644 --- a/app/client/src/widgets/RangeSliderWidget/widget/propertyConfig/contentConfig.ts +++ b/app/client/src/widgets/RangeSliderWidget/widget/propertyConfig/contentConfig.ts @@ -1,24 +1,16 @@ -import { AutocompleteDataType } from "utils/autocomplete/TernServer"; -import { ValidationTypes } from "constants/WidgetValidation"; -import { - LabelPosition, - ResponsiveBehavior, - FlexVerticalAlignment, -} from "components/constants"; import { Alignment } from "@blueprintjs/core"; +import { LabelPosition } from "components/constants"; +import { ValidationTypes } from "constants/WidgetValidation"; +import { AutocompleteDataType } from "utils/autocomplete/TernServer"; +import { RangeSliderWidgetProps } from ".."; import { + endValueValidation, maxValueValidation, - minValueValidation, minRangeValidation, - stepSizeValidation, + minValueValidation, startValueValidation, - endValueValidation, + stepSizeValidation, } from "../../validations"; -import { RangeSliderWidgetProps } from ".."; -import { - generateResponsiveBehaviorConfig, - generateVerticalAlignmentConfig, -} from "utils/layoutPropertiesUtils"; export default [ { @@ -311,8 +303,6 @@ export default [ isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, - generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), - generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, { diff --git a/app/client/src/widgets/RangeSliderWidget/widget/propertyConfig/styleConfig.ts b/app/client/src/widgets/RangeSliderWidget/widget/propertyConfig/styleConfig.ts index 8687e6263bb3..bfbc45124caf 100644 --- a/app/client/src/widgets/RangeSliderWidget/widget/propertyConfig/styleConfig.ts +++ b/app/client/src/widgets/RangeSliderWidget/widget/propertyConfig/styleConfig.ts @@ -1,4 +1,9 @@ +import { ResponsiveBehavior } from "components/constants"; import { ValidationTypes } from "constants/WidgetValidation"; +import { + generateResponsiveBehaviorConfig, + generateVerticalAlignmentConfig, +} from "utils/layoutPropertiesUtils"; export default [ { @@ -34,6 +39,13 @@ export default [ }, ], }, + { + sectionName: "Responsive Layout", + children: [ + generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), + generateVerticalAlignmentConfig(), + ], + }, { sectionName: "Label Styles", children: [ diff --git a/app/client/src/widgets/RateWidget/widget/index.tsx b/app/client/src/widgets/RateWidget/widget/index.tsx index 098dd67e05b3..797679ecf708 100644 --- a/app/client/src/widgets/RateWidget/widget/index.tsx +++ b/app/client/src/widgets/RateWidget/widget/index.tsx @@ -8,6 +8,11 @@ import { ValidationTypes } from "constants/WidgetValidation"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; import { AutocompleteDataType } from "utils/autocomplete/TernServer"; +import { + generateResponsiveBehaviorConfig, + generateVerticalAlignmentConfig, +} from "utils/layoutPropertiesUtils"; +import { ResponsiveBehavior } from "components/constants"; function validateDefaultRate(value: unknown, props: any, _: any) { try { @@ -224,6 +229,13 @@ class RateWidget extends BaseWidget { }, ], }, + { + sectionName: "Responsive Layout", + children: [ + generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), + generateVerticalAlignmentConfig(), + ], + }, { sectionName: "Color", children: [ diff --git a/app/client/src/widgets/RichTextEditorWidget/widget/index.tsx b/app/client/src/widgets/RichTextEditorWidget/widget/index.tsx index a7e67d7b5114..2d594bb487ae 100644 --- a/app/client/src/widgets/RichTextEditorWidget/widget/index.tsx +++ b/app/client/src/widgets/RichTextEditorWidget/widget/index.tsx @@ -1,23 +1,19 @@ -import React, { lazy, Suspense } from "react"; -import BaseWidget, { WidgetProps, WidgetState } from "../../BaseWidget"; -import { TextSize, WidgetType } from "constants/WidgetConstants"; +import { Alignment } from "@blueprintjs/core"; +import { LabelPosition, ResponsiveBehavior } from "components/constants"; +import Skeleton from "components/utils/Skeleton"; import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; +import { TextSize, WidgetType } from "constants/WidgetConstants"; import { ValidationTypes } from "constants/WidgetValidation"; -import { DerivedPropertiesMap } from "utils/WidgetFactory"; -import Skeleton from "components/utils/Skeleton"; -import { retryPromise } from "utils/AppsmithUtils"; -import { - LabelPosition, - ResponsiveBehavior, - FlexVerticalAlignment, -} from "components/constants"; -import { Alignment } from "@blueprintjs/core"; -import { GRID_DENSITY_MIGRATION_V1 } from "widgets/constants"; +import React, { lazy, Suspense } from "react"; import showdown from "showdown"; +import { retryPromise } from "utils/AppsmithUtils"; import { generateResponsiveBehaviorConfig, generateVerticalAlignmentConfig, } from "utils/layoutPropertiesUtils"; +import { DerivedPropertiesMap } from "utils/WidgetFactory"; +import { GRID_DENSITY_MIGRATION_V1 } from "widgets/constants"; +import BaseWidget, { WidgetProps, WidgetState } from "../../BaseWidget"; export enum RTEFormats { MARKDOWN = "markdown", @@ -203,8 +199,6 @@ class RichTextEditorWidget extends BaseWidget< isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, - { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, - generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, { @@ -226,6 +220,13 @@ class RichTextEditorWidget extends BaseWidget< static getPropertyPaneStyleConfig() { return [ + { + sectionName: "Responsive Layout", + children: [ + generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill), + generateVerticalAlignmentConfig(), + ], + }, { sectionName: "Label Styles", children: [ diff --git a/app/client/src/widgets/SelectWidget/widget/index.tsx b/app/client/src/widgets/SelectWidget/widget/index.tsx index 596efcaedcd4..8290eb346976 100644 --- a/app/client/src/widgets/SelectWidget/widget/index.tsx +++ b/app/client/src/widgets/SelectWidget/widget/index.tsx @@ -1,22 +1,13 @@ -import React from "react"; -import BaseWidget, { WidgetProps, WidgetState } from "../../BaseWidget"; -import { WidgetType } from "constants/WidgetConstants"; +import { Alignment } from "@blueprintjs/core"; +import { LabelPosition, ResponsiveBehavior } from "components/constants"; import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; -import SelectComponent from "../component"; -import { DropdownOption } from "../constants"; +import { WidgetType } from "constants/WidgetConstants"; import { ValidationResponse, ValidationTypes, } from "constants/WidgetValidation"; import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; -import { MinimumPopupRows, GRID_DENSITY_MIGRATION_V1 } from "widgets/constants"; -import { - LabelPosition, - ResponsiveBehavior, - FlexVerticalAlignment, -} from "components/constants"; -import { Alignment } from "@blueprintjs/core"; -import { AutocompleteDataType } from "utils/autocomplete/TernServer"; +import equal from "fast-deep-equal/es6"; import { findIndex, isArray, @@ -25,12 +16,17 @@ import { isString, LoDashStatic, } from "lodash"; -import equal from "fast-deep-equal/es6"; -import derivedProperties from "./parseDerivedProperties"; +import React from "react"; +import { AutocompleteDataType } from "utils/autocomplete/TernServer"; import { generateResponsiveBehaviorConfig, generateVerticalAlignmentConfig, } from "utils/layoutPropertiesUtils"; +import { GRID_DENSITY_MIGRATION_V1, MinimumPopupRows } from "widgets/constants"; +import BaseWidget, { WidgetProps, WidgetState } from "../../BaseWidget"; +import SelectComponent from "../component"; +import { DropdownOption } from "../constants"; +import derivedProperties from "./parseDerivedProperties"; export function defaultOptionValueValidation( value: unknown, @@ -364,8 +360,6 @@ class SelectWidget extends BaseWidget { isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, - { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, - generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, { @@ -387,6 +381,13 @@ class SelectWidget extends BaseWidget { static getPropertyPaneStyleConfig() { return [ + { + sectionName: "Responsive Layout", + children: [ + generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill), + generateVerticalAlignmentConfig(), + ], + }, { sectionName: "Label Styles", children: [ diff --git a/app/client/src/widgets/SingleSelectTreeWidget/widget/index.tsx b/app/client/src/widgets/SingleSelectTreeWidget/widget/index.tsx index 8a82afbf5231..21a8c705fd32 100644 --- a/app/client/src/widgets/SingleSelectTreeWidget/widget/index.tsx +++ b/app/client/src/widgets/SingleSelectTreeWidget/widget/index.tsx @@ -1,28 +1,24 @@ -import React, { ReactNode } from "react"; -import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; -import { TextSize, WidgetType } from "constants/WidgetConstants"; +import { Alignment } from "@blueprintjs/core"; +import { LabelPosition, ResponsiveBehavior } from "components/constants"; import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; -import { isArray } from "lodash"; +import { Layers } from "constants/Layers"; +import { TextSize, WidgetType } from "constants/WidgetConstants"; import { ValidationResponse, ValidationTypes, } from "constants/WidgetValidation"; import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; +import { isArray } from "lodash"; import { DefaultValueType } from "rc-tree-select/lib/interface"; -import { Layers } from "constants/Layers"; +import React, { ReactNode } from "react"; import { AutocompleteDataType } from "utils/autocomplete/TernServer"; -import { GRID_DENSITY_MIGRATION_V1, MinimumPopupRows } from "widgets/constants"; -import SingleSelectTreeComponent from "../component"; -import { - LabelPosition, - ResponsiveBehavior, - FlexVerticalAlignment, -} from "components/constants"; -import { Alignment } from "@blueprintjs/core"; import { generateResponsiveBehaviorConfig, generateVerticalAlignmentConfig, } from "utils/layoutPropertiesUtils"; +import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; +import { GRID_DENSITY_MIGRATION_V1, MinimumPopupRows } from "widgets/constants"; +import SingleSelectTreeComponent from "../component"; import derivedProperties from "./parseDerivedProperties"; function defaultOptionValueValidation(value: unknown): ValidationResponse { @@ -288,8 +284,6 @@ class SingleSelectTreeWidget extends BaseWidget< isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, - { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, - generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, { @@ -311,6 +305,13 @@ class SingleSelectTreeWidget extends BaseWidget< static getPropertyPaneStyleConfig() { return [ + { + sectionName: "Responsive Layout", + children: [ + generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill), + generateVerticalAlignmentConfig(), + ], + }, { sectionName: "Label Styles", children: [ diff --git a/app/client/src/widgets/StatboxWidget/widget/index.tsx b/app/client/src/widgets/StatboxWidget/widget/index.tsx index be7152281d8e..30a8916ce883 100644 --- a/app/client/src/widgets/StatboxWidget/widget/index.tsx +++ b/app/client/src/widgets/StatboxWidget/widget/index.tsx @@ -1,15 +1,12 @@ import { WidgetType } from "constants/WidgetConstants"; import { ContainerWidget } from "widgets/ContainerWidget/widget"; +import { ResponsiveBehavior } from "components/constants"; import { ValidationTypes } from "constants/WidgetValidation"; import { generateResponsiveBehaviorConfig, generateVerticalAlignmentConfig, } from "utils/layoutPropertiesUtils"; -import { - ResponsiveBehavior, - FlexVerticalAlignment, -} from "components/constants"; class StatboxWidget extends ContainerWidget { static getPropertyPaneContentConfig() { @@ -46,8 +43,6 @@ class StatboxWidget extends ContainerWidget { isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, - generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), - generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, ]; @@ -55,6 +50,13 @@ class StatboxWidget extends ContainerWidget { static getPropertyPaneStyleConfig() { return [ + { + sectionName: "Responsive Layout", + children: [ + generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), + generateVerticalAlignmentConfig(), + ], + }, { sectionName: "Color", children: [ diff --git a/app/client/src/widgets/SwitchGroupWidget/widget/index.tsx b/app/client/src/widgets/SwitchGroupWidget/widget/index.tsx index af52239b3142..0e3280e8d486 100644 --- a/app/client/src/widgets/SwitchGroupWidget/widget/index.tsx +++ b/app/client/src/widgets/SwitchGroupWidget/widget/index.tsx @@ -225,8 +225,6 @@ class SwitchGroupWidget extends BaseWidget< isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, - generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), - generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, { @@ -249,6 +247,13 @@ class SwitchGroupWidget extends BaseWidget< static getPropertyPaneStyleConfig() { return [ + { + sectionName: "Responsive Layout", + children: [ + generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), + generateVerticalAlignmentConfig(), + ], + }, { sectionName: "Label Styles", children: [ diff --git a/app/client/src/widgets/SwitchWidget/widget/index.tsx b/app/client/src/widgets/SwitchWidget/widget/index.tsx index 821a5e612a63..bfa7adc81800 100644 --- a/app/client/src/widgets/SwitchWidget/widget/index.tsx +++ b/app/client/src/widgets/SwitchWidget/widget/index.tsx @@ -113,8 +113,6 @@ class SwitchWidget extends BaseWidget { isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, - generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), - generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, { @@ -136,6 +134,13 @@ class SwitchWidget extends BaseWidget { static getPropertyPaneStyleConfig() { return [ + { + sectionName: "Responsive Layout", + children: [ + generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), + generateVerticalAlignmentConfig(), + ], + }, { sectionName: "Label Styles", children: [ diff --git a/app/client/src/widgets/TableWidgetV2/widget/propertyConfig/contentConfig.ts b/app/client/src/widgets/TableWidgetV2/widget/propertyConfig/contentConfig.ts index 94f3848285e5..a435b939cd89 100644 --- a/app/client/src/widgets/TableWidgetV2/widget/propertyConfig/contentConfig.ts +++ b/app/client/src/widgets/TableWidgetV2/widget/propertyConfig/contentConfig.ts @@ -1,3 +1,8 @@ +import { + createMessage, + TABLE_WIDGET_TOTAL_RECORD_TOOLTIP, +} from "@appsmith/constants/messages"; +import { PropertyPaneConfig } from "constants/PropertyControlConstants"; import { ValidationTypes } from "constants/WidgetValidation"; import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; import { AutocompleteDataType } from "utils/autocomplete/TernServer"; @@ -5,28 +10,15 @@ import { InlineEditingSaveOptions, TableWidgetProps, } from "widgets/TableWidgetV2/constants"; +import { composePropertyUpdateHook } from "widgets/WidgetUtils"; import { totalRecordsCountValidation, uniqueColumnNameValidation, updateColumnOrderHook, - updateInlineEditingSaveOptionHook, updateInlineEditingOptionDropdownVisibilityHook, + updateInlineEditingSaveOptionHook, } from "../propertyUtils"; -import { - createMessage, - TABLE_WIDGET_TOTAL_RECORD_TOOLTIP, -} from "@appsmith/constants/messages"; import panelConfig from "./PanelConfig"; -import { composePropertyUpdateHook } from "widgets/WidgetUtils"; -import { PropertyPaneConfig } from "constants/PropertyControlConstants"; -import { - generateResponsiveBehaviorConfig, - generateVerticalAlignmentConfig, -} from "utils/layoutPropertiesUtils"; -import { - ResponsiveBehavior, - FlexVerticalAlignment, -} from "components/constants"; export default [ { @@ -485,8 +477,6 @@ export default [ hidden: (props: TableWidgetProps) => !props.isVisibleDownload, dependencies: ["isVisibleDownload"], }, - { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, - generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, ] as PropertyPaneConfig[]; diff --git a/app/client/src/widgets/TableWidgetV2/widget/propertyConfig/styleConfig.ts b/app/client/src/widgets/TableWidgetV2/widget/propertyConfig/styleConfig.ts index 70617211bed9..562e9122f270 100644 --- a/app/client/src/widgets/TableWidgetV2/widget/propertyConfig/styleConfig.ts +++ b/app/client/src/widgets/TableWidgetV2/widget/propertyConfig/styleConfig.ts @@ -1,4 +1,9 @@ +import { ResponsiveBehavior } from "components/constants"; import { ValidationTypes } from "constants/WidgetValidation"; +import { + generateResponsiveBehaviorConfig, + generateVerticalAlignmentConfig, +} from "utils/layoutPropertiesUtils"; import { updateColumnStyles } from "../propertyUtils"; export default [ @@ -31,6 +36,13 @@ export default [ }, ], }, + { + sectionName: "Responsive Layout", + children: [ + generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill), + generateVerticalAlignmentConfig(), + ], + }, { sectionName: "Text Formatting", children: [ diff --git a/app/client/src/widgets/TabsWidget/widget/index.tsx b/app/client/src/widgets/TabsWidget/widget/index.tsx index 79164aca86b4..015cf98aa7bd 100644 --- a/app/client/src/widgets/TabsWidget/widget/index.tsx +++ b/app/client/src/widgets/TabsWidget/widget/index.tsx @@ -184,8 +184,6 @@ class TabsWidget extends BaseWidget< isBindProperty: false, isTriggerProperty: false, }, - { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, - generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, { @@ -207,6 +205,13 @@ class TabsWidget extends BaseWidget< static getPropertyPaneStyleConfig() { return [ + { + sectionName: "Responsive Layout", + children: [ + generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill), + generateVerticalAlignmentConfig(), + ], + }, { sectionName: "Colors, Borders and Shadows", children: [ diff --git a/app/client/src/widgets/TextWidget/widget/index.tsx b/app/client/src/widgets/TextWidget/widget/index.tsx index 3ca28174221b..c4bc9b8dc936 100644 --- a/app/client/src/widgets/TextWidget/widget/index.tsx +++ b/app/client/src/widgets/TextWidget/widget/index.tsx @@ -6,22 +6,19 @@ import { countOccurrences } from "workers/Evaluation/helpers"; import { ValidationTypes } from "constants/WidgetValidation"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; -import { Color } from "constants/Colors"; -import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; -import TextComponent, { TextAlign } from "../component"; -import { ContainerStyle } from "widgets/ContainerWidget/component"; -import { AutocompleteDataType } from "utils/autocomplete/TernServer"; -import { OverflowTypes } from "../constants"; +import { ResponsiveBehavior } from "components/constants"; import WidgetStyleContainer from "components/designSystems/appsmith/WidgetStyleContainer"; +import { Color } from "constants/Colors"; import { pick } from "lodash"; +import { AutocompleteDataType } from "utils/autocomplete/TernServer"; import { generateResponsiveBehaviorConfig, generateVerticalAlignmentConfig, } from "utils/layoutPropertiesUtils"; -import { - ResponsiveBehavior, - FlexVerticalAlignment, -} from "components/constants"; +import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; +import { ContainerStyle } from "widgets/ContainerWidget/component"; +import TextComponent, { TextAlign } from "../component"; +import { OverflowTypes } from "../constants"; const MAX_HTML_PARSING_LENGTH = 1000; class TextWidget extends BaseWidget { @@ -98,8 +95,6 @@ class TextWidget extends BaseWidget { isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, - { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, - generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, ]; @@ -212,6 +207,13 @@ class TextWidget extends BaseWidget { }, ], }, + { + sectionName: "Responsive Layout", + children: [ + generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill), + generateVerticalAlignmentConfig(), + ], + }, { sectionName: "Color", children: [ diff --git a/app/client/src/widgets/VideoWidget/widget/index.tsx b/app/client/src/widgets/VideoWidget/widget/index.tsx index e19850424982..5bdc3c55f7c9 100644 --- a/app/client/src/widgets/VideoWidget/widget/index.tsx +++ b/app/client/src/widgets/VideoWidget/widget/index.tsx @@ -1,21 +1,17 @@ -import React, { Suspense, lazy } from "react"; -import BaseWidget, { WidgetProps, WidgetState } from "../../BaseWidget"; -import { WidgetType } from "constants/WidgetConstants"; +import { ButtonBorderRadius, ResponsiveBehavior } from "components/constants"; +import Skeleton from "components/utils/Skeleton"; import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; +import { WidgetType } from "constants/WidgetConstants"; import { ValidationTypes } from "constants/WidgetValidation"; -import Skeleton from "components/utils/Skeleton"; -import { retryPromise } from "utils/AppsmithUtils"; +import React, { lazy, Suspense } from "react"; import ReactPlayer from "react-player"; +import { retryPromise } from "utils/AppsmithUtils"; import { AutocompleteDataType } from "utils/autocomplete/TernServer"; -import { - ButtonBorderRadius, - ResponsiveBehavior, - FlexVerticalAlignment, -} from "components/constants"; import { generateResponsiveBehaviorConfig, generateVerticalAlignmentConfig, } from "utils/layoutPropertiesUtils"; +import BaseWidget, { WidgetProps, WidgetState } from "../../BaseWidget"; const VideoComponent = lazy(() => retryPromise(() => import("../component"))); @@ -90,8 +86,6 @@ class VideoWidget extends BaseWidget { isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, - generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), - generateVerticalAlignmentConfig(FlexVerticalAlignment.Top), ], }, { @@ -131,6 +125,13 @@ class VideoWidget extends BaseWidget { static getPropertyPaneStyleConfig() { return [ + { + sectionName: "Responsive Layout", + children: [ + generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), + generateVerticalAlignmentConfig(), + ], + }, { sectionName: "Color", children: [ From 7e989c2105c718df02f8c6341ffa2278b0abce73 Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 12 Dec 2022 22:59:10 -0800 Subject: [PATCH 284/708] fix list and stat box widget --- app/client/src/utils/layoutPropertiesUtils.ts | 2 +- app/client/src/widgets/ListWidget/index.ts | 4 +++- app/client/src/widgets/ListWidget/widget/index.tsx | 5 +++++ app/client/src/widgets/ListWidget/widget/propertyConfig.ts | 4 +++- app/client/src/widgets/StatboxWidget/index.ts | 3 ++- app/client/src/widgets/StatboxWidget/widget/index.tsx | 4 +++- 6 files changed, 17 insertions(+), 5 deletions(-) diff --git a/app/client/src/utils/layoutPropertiesUtils.ts b/app/client/src/utils/layoutPropertiesUtils.ts index a76160f3a211..52d432e7a1ea 100644 --- a/app/client/src/utils/layoutPropertiesUtils.ts +++ b/app/client/src/utils/layoutPropertiesUtils.ts @@ -171,7 +171,7 @@ export const generatePositioningConfig = ( controlType: "DROP_DOWN", defaultValue: value, options: [ - // { label: "Fixed", value: Positioning.Fixed }, + { label: "Fixed", value: Positioning.Fixed }, // { label: "Horizontal stack", value: Positioning.Horizontal }, { label: "Vertical stack", value: Positioning.Vertical }, ], diff --git a/app/client/src/widgets/ListWidget/index.ts b/app/client/src/widgets/ListWidget/index.ts index 3251abe9b79a..b368e9286a5b 100644 --- a/app/client/src/widgets/ListWidget/index.ts +++ b/app/client/src/widgets/ListWidget/index.ts @@ -10,7 +10,7 @@ import { } from "widgets/constants"; import IconSVG from "./icon.svg"; import Widget from "./widget"; -import { ResponsiveBehavior } from "components/constants"; +import { Positioning, ResponsiveBehavior } from "components/constants"; import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; export const CONFIG = { @@ -29,6 +29,7 @@ export const CONFIG = { template: {}, responsiveBehavior: ResponsiveBehavior.Fill, minWidth: FILL_WIDGET_MIN_WIDTH, + positioning: Positioning.Fixed, enhancements: { child: { autocomplete: (parentProps: any) => { @@ -128,6 +129,7 @@ export const CONFIG = { disablePropertyPane: true, openParentPropertyPane: true, children: [], + positioning: Positioning.Fixed, blueprint: { view: [ { diff --git a/app/client/src/widgets/ListWidget/widget/index.tsx b/app/client/src/widgets/ListWidget/widget/index.tsx index 8479be9ddf0d..d71c9912e91c 100644 --- a/app/client/src/widgets/ListWidget/widget/index.tsx +++ b/app/client/src/widgets/ListWidget/widget/index.tsx @@ -42,6 +42,7 @@ import { entityDefinitions } from "utils/autocomplete/EntityDefinitions"; import { PrivateWidgets } from "entities/DataTree/dataTreeFactory"; import equal from "fast-deep-equal/es6"; import { klona } from "klona/lite"; +import { Positioning } from "components/constants"; const LIST_WIDGET_PAGINATION_HEIGHT = 36; @@ -361,6 +362,10 @@ class ListWidget extends BaseWidget, WidgetState> { childWidgetData.bottomRow = shouldPaginate ? componentHeight - LIST_WIDGET_PAGINATION_HEIGHT : componentHeight; + const positioning: Positioning = + this.props.positioning || childWidgetData.positioning; + childWidgetData.positioning = positioning; + childWidgetData.useAutoLayout = positioning === Positioning.Vertical; return WidgetFactory.createWidget(childWidgetData, this.props.renderMode); }; diff --git a/app/client/src/widgets/ListWidget/widget/propertyConfig.ts b/app/client/src/widgets/ListWidget/widget/propertyConfig.ts index d64dcb51cdb0..f5ec5ed21947 100644 --- a/app/client/src/widgets/ListWidget/widget/propertyConfig.ts +++ b/app/client/src/widgets/ListWidget/widget/propertyConfig.ts @@ -7,10 +7,11 @@ import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; import { AutocompleteDataType } from "utils/autocomplete/TernServer"; import { EVAL_VALUE_PATH } from "utils/DynamicBindingUtils"; import { + generatePositioningConfig, generateResponsiveBehaviorConfig, generateVerticalAlignmentConfig, } from "utils/layoutPropertiesUtils"; -import { ResponsiveBehavior } from "components/constants"; +import { Positioning, ResponsiveBehavior } from "components/constants"; export const PropertyPaneContentConfig = [ { @@ -151,6 +152,7 @@ export const PropertyPaneStyleConfig = [ { sectionName: "Responsive Layout", children: [ + generatePositioningConfig(Positioning.Fixed), generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill), generateVerticalAlignmentConfig(), ], diff --git a/app/client/src/widgets/StatboxWidget/index.ts b/app/client/src/widgets/StatboxWidget/index.ts index 52dde8e21717..a1925ee1c1c1 100644 --- a/app/client/src/widgets/StatboxWidget/index.ts +++ b/app/client/src/widgets/StatboxWidget/index.ts @@ -1,4 +1,4 @@ -import { ButtonVariantTypes } from "components/constants"; +import { ButtonVariantTypes, Positioning } from "components/constants"; import { Colors } from "constants/Colors"; import { THEMEING_TEXT_SIZES } from "constants/ThemeConstants"; import IconSVG from "./icon.svg"; @@ -19,6 +19,7 @@ export const CONFIG = { borderWidth: "1", borderColor: Colors.GREY_5, children: [], + positioning: Positioning.Fixed, blueprint: { view: [ { diff --git a/app/client/src/widgets/StatboxWidget/widget/index.tsx b/app/client/src/widgets/StatboxWidget/widget/index.tsx index 30a8916ce883..52a6f94b50e1 100644 --- a/app/client/src/widgets/StatboxWidget/widget/index.tsx +++ b/app/client/src/widgets/StatboxWidget/widget/index.tsx @@ -1,9 +1,10 @@ import { WidgetType } from "constants/WidgetConstants"; import { ContainerWidget } from "widgets/ContainerWidget/widget"; -import { ResponsiveBehavior } from "components/constants"; +import { Positioning, ResponsiveBehavior } from "components/constants"; import { ValidationTypes } from "constants/WidgetValidation"; import { + generatePositioningConfig, generateResponsiveBehaviorConfig, generateVerticalAlignmentConfig, } from "utils/layoutPropertiesUtils"; @@ -53,6 +54,7 @@ class StatboxWidget extends ContainerWidget { { sectionName: "Responsive Layout", children: [ + generatePositioningConfig(Positioning.Fixed), generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), generateVerticalAlignmentConfig(), ], From d4130c6eed6de67f1e5b05fba0645f32fb50a69b Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 14 Dec 2022 13:35:07 -0500 Subject: [PATCH 285/708] fix fill widget mobile width issue --- .../designSystems/appsmith/autoLayout/FlexComponent.tsx | 2 +- app/client/src/sagas/AutoLayoutUtils.ts | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx index d882b7c3ca00..d6542dc2ddca 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx @@ -37,7 +37,7 @@ export type AutoLayoutProps = { parentColumnSpace: number; flexVerticalAlignment: FlexVerticalAlignment; }; - +// TODO: create a memoized style object for the div instead. const FlexWidget = styled.div<{ componentHeight: number; componentWidth: number; diff --git a/app/client/src/sagas/AutoLayoutUtils.ts b/app/client/src/sagas/AutoLayoutUtils.ts index ecd6445cee65..7dca477f6bb4 100644 --- a/app/client/src/sagas/AutoLayoutUtils.ts +++ b/app/client/src/sagas/AutoLayoutUtils.ts @@ -262,9 +262,12 @@ export function alterLayoutForMobile( const widget = { ...widgets[child] }; if (widget.responsiveBehavior === ResponsiveBehavior.Fill) { - widget.rightColumn = 64; + widget.mobileRightColumn = 64; widget.leftColumn = 0; - } else if (widget.minWidth && !widget.mobileRightColumn) { + } else if ( + widget.responsiveBehavior === ResponsiveBehavior.Hug && + widget.minWidth + ) { const { minWidth, rightColumn } = widget; const columnSpace = canvasWidth / 64; if (columnSpace * rightColumn < minWidth) { From a74a8c369f469658dbd926debeace0118e0cb878 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Fri, 16 Dec 2022 09:44:49 +0530 Subject: [PATCH 286/708] Mobile/v1/dynamic height (#18990) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Updated Label Config * Update top contributors * docs: Updating contribution guidelines (#18190) The contribution guidelines for documentation are outdated due to the recent migration. Here are the fixes/updates - - Removed the following files from `contribution/docs/` - - [CONTRIBUTING.md](https://github.com/appsmithorg/appsmith/blob/release/contributions/docs/CONTRIBUTING.md) - [DB Integrations.md](https://github.com/appsmithorg/appsmith/blob/release/contributions/docs/DB%20Integrations.md) - [UploadingAssets.md](https://github.com/appsmithorg/appsmith/blob/release/contributions/docs/UploadingAssets.md) - [Widgets.md](https://github.com/appsmithorg/appsmith/blob/release/contributions/docs/Widgets.md) - [adding_guides.md](https://github.com/appsmithorg/appsmith/blob/release/contributions/docs/adding_guides.md) - Removed [Documentation Improvement](https://github.com/appsmithorg/appsmith/blob/release/.github/ISSUE_TEMPLATE/--documentation-improvement.yaml) template as we have created [new templates](https://github.com/appsmithorg/appsmith-docs/issues/new/choose) in the [appsmith-docs](https://github.com/appsmithorg/appsmith-docs) repository. - Updated the relevant links in the [Documentation Improvement](https://github.com/appsmithorg/appsmith/blob/release/CONTRIBUTING.md#-improve-the-documentation) section ( template links and contribution guidelines for docs) of [CONTRIBUTING.md](https://github.com/appsmithorg/appsmith/blob/release/CONTRIBUTING.md) file. Co-authored-by: Pranay105 * ci: fix upload success step in test-build-docker-image.yml (#18379) * feat: Automatic height updates for widgets based on contents (Auto Height) (#18341) * added multi select back * (WIP): Complete the dynamc height update logic * (WIP): Dynamic height logic * (WIP): Container computation logic, Next steps: Prevent reflow when resize is disabled. Fix logic of widgets randomly changing positions (Debug) * Fix logic in container computations * Integrate for PoC * fixed the no initial load dynamic height updates * Stop vertical resize and reflow when dynamic height is enabled for a widget * added another container in text widget * enabled dynamic height for container widgets * removed dynamic height feature from list widget * Fixed Button and Input components height increase * added an experiment to overflow the content if maxHEight is less * removed the ref of Textwidget by mistake, added it back * fixed text widget height overflow problem with a little hack * added long labels with text * fixed the table scroll issue * overflow fixed for json form widget * added extra 8px height for Switch, Rating and Checkbox Height * (WIP): Resolve issues * (WIP): Fix widget padding issue * added overflow container for Radio and Switch group widgets * (WIP): Have modals work with dynamic height * added the overlay and the handles * added dragging behavior to the dots * fixed the overlapping with the selection tool * (WIP): Fix issues reported * now we can update the property pane values back from overlay handles * now we can update the property pane values back from overlay handles * (WIP): Fix table widget * Fix package.json * Remove unit tests temporarily * Fix unit test * (WIP): Fix modal resize. Fix cursors. Fix border issue on non-resizable widgets * fetch component heights using the requestAnimationFrame callback * behavioural changes * (WIP): Fix issues on the platform * Update main container size appropriately * more behavioural changes * overlay now only be visible when hovering over the dots * grid showing and widget reselecting * added onfocus and onblur events to property pane listeners * added onfocus and onblur events to property pane listeners * added a range slider for min and max * added demarcations for slider values * (WIP): Fix platform workflows for dynamic height * Fix issues with widgets * Fix removed import * - Add missing cypress files * set the limits * limit increase on change * Fix z-index of min max limit indicators. Fix unused-vars warnings * Fix Table Widget and Text Widget issues * Fix: all the bugs in the bug master list for DH (#16268) * changed the zindex for the signifiers * showing signifiers only when the widget is selected * made changes suggested by Momcilo * activate the dots when the fields are active * created a new centered dot handle * removed overlays on focus and made the border more like deisgn * handles on top of other widgets * hide the overlay when multiple widgets are selected * added a white border * added a white border * bug #15509 resolved * changed the minDynamicHeightLimit to 2 instead of 4 to fix the Bug #15527 * removed the height auto fix from BaseInputComponent to fix the Bug #15388 * removed the condition to not ccalculate dynamic height when the row difference is less than 2 to fix the bug 15353 * made fixes for the bug #16307 * made fixes for the bug #16308 * made fixes for bug 16310 * made fixes for the bug #16402 * removed some log statements * made fixes for the bug #16407 * fixed label problem found in the issue #16543 * made fixes for the issue #16547 * made fixes for the bug #16492 * redeploy * (WIP): Fix to make this branch functional * imported LabelWithTooltip back from design system * signifier is now centered * filled the signifier with primary color * overlay hidden while dragging * made the signifier dashed border also draggable * Fix issue #16590 (#16798) * set the limits to 4 rows * replaced the static 40 value * added signifiers for modal widget * added signifiers for modal widget * tried solving the scroll issue for widgets when there are limits * solved the height problem using ResizeObserver * (WIP): Fix maxDynamicHeight issue with container widgets: * made the changes as per the review * fixed the issue for input widget when label gets out of border * hide text widget overflow options if auto height is enabled * (WIP): In view mode, invisible widgets now donot take space (#16920) * (WIP): In view mode, invisible widgets now donot take space * (WIP): Enable the feature where invisible widgets in view mode don't take space to all widgets irrespective of the dynamic height feature * Remove Replay conditional * removed the scroll container for container type widgets * removed the scroll container for container type widgets * updated the hook to set overflow none for text widget * fixed the should dynamic height logic to respect the min height limit * Modal widget adheres to dynamic height (#16995) * Modal widget adheres to dynamic height * WIP: POC: fix dynamic height issues (#16996) Fix height less than 4 issue. Fix JSONForm adherence to min and max height * POC: Dynamic height undo redo issue (#17085) * Revert debouce timeout * (WIP): Fix issue with undo-redo in dynamic height * fix: Dynamic height issue fixes (#17153) * Dynamic height issue fixes == - Fix issue where nested widgets did not ensure parent dynamic height updates - Fix issue where Modal widget updates came in subsequent renders - Fix issue where JSONForm collapses - Fix performance issue for independent updates * Use functions to get min and max dynamic height * Fix issue where variable might have been undefined * added the dynamic container into the deploy mode as well * added overflow-x hidden when overflow-y is active in the dynamic height container * fix: Dynamic height Issue fixes (#17204) Fix preview mode invisible widgets. Fix Tabs widget dynamic height. * removed a console.log statement * removed the slider control file * imported the LabelWithTooltip from the repo rather than ds * word-break CSS rules added for Switch and Checkbox widget when Dynamic Height is enabled * abstracted the check for dynamic height with limits enabled as isDynamicHeightWithLimitsEnabledForWidget * abstracted the static value of 10 in dynamic height overlay to GridDefaults * abstracted min and max dynamic height limits to getters * fix: replaced all the refs for simpler widgets (#17353) * replaced all the refs for simpler widgets * removed the updateDynamicHeight from componentDidUpdate in BaseWidget * added back lifecycle methods back to BaseWidget * removed the contentRef from SwitchGroup and Table * updating the height from the auto height with limits as well * some hacks to make the limits work * working solution * used setTimeout to send an update to updateDynamicHeight from overlay update * removed a log * added requestanimationframe in settimeout Co-authored-by: Ankur Singhal Co-authored-by: Ankur Singhal * Fix issues caused during merge * Remove unneeded derived property * removed more unnecessary code which should have been removed after removing the ref dependency * fixed the maxDynamicHeight issue * Fix issue where property configs were not being sent * fix: Auto Height Feature - add selectors for tests (#17687) Add selectors for auto height cypress tests * fix: removed height auto default theme (#17415) removed height auto css rule from the default theme Co-authored-by: Ankur Singhal * fix: Auto Height Feature - Resolve issues and restructure code (#17686) * Fix issues in dynamic height. Restructure code and reduce abstraction leaks * Fix typescript issues * Update based on review comments. Comment migrations, as a cyclic import is causing the jest tests to fail. * Remove unused imports * Decrease code nesting * added the base styles for the overlay like position and z-index in its styled component css * used the isDynamicHeightEnabled prop to set the height of SwitchGroup and RadioGroup widgets from 32px to 100% in case of inline mode * fix: Auto Height - Resolve issues (#17737) * Fix Tabs Widget showTabs toggle based auto height. Revert removal of BaseWidget code. Remove box-intersect and use a bruteforce algorithm. Add base logic for having containers collapse due to hidden child widgets * Hide scroll contents and overflow property pane controls when dynamic height is enabled * Removed the class property expectedHeight from BaseWidget as it is not useful in the overlay logic after some changes * fixed the left alignment issue of label in the rich text editor by adding some styles applied only when the dynamic height is enabled * fixed the input field stretching issue in case of Dynamic height by adding some CSS styles when isDynamicHeight is true * Fix failing modal widget cypress tests * Fix issue with scrollContents and Tabs Widget defaulTab * added a little bit padding of 4px to the right of scroll container of dynamic height with limit * Add test locators for resize handles * removed the dynamic height logic from the table widget * fix: Auto-Height invisible widgets (#17849) * Fix issue where invisible widgets were still taking space * Make sure to collapse only if dynamic height is enabled * Fix issues with reflow (not the invisible widgets) * Fix container min height issues * Fix reflow with original bottom and top values. Testing needed * Fix invisible widgets * fix: enabled dynamic height for stat box widget (#17971) enabled dynamic height for stat box widget Co-authored-by: Ankur Singhal * fix: added a min height to rich text editor so that it does not collapse (#17970) added a min height to rich text editor so that it does not collapse Co-authored-by: Ankur Singhal * Fix issue with resizing auto height widget * Add helper text to educate users regarding the scroll disconnect in WYSIWYG * fix: Auto Height Fixes (#18111) AUTO HEIGHT FIXES - Fix JSONForm height discrepancy - Fix issue where widgets moved below the other - Fix droptarget height after parent container resize * fix: sliced up the DynamicHeightOverlay component a little bit (#18100) * sliced up the DynamicHeightOverlay component a little bit * more refactoring * more refactoring * used release event emitter and refactored more Co-authored-by: Ankur Singhal * fix: rich text editor center alignment issue (#18142) * removed the center alignment from rich text editor * dummy commit Co-authored-by: Ankur Singhal * fix: old DSL container collapse (#18160) * Fix issue where old containers from old DSLs used to collapse when auto height was enabled * Fix issue where old containers don't allow new widgets to be added when auto height is enabled, this is because the shouldScrollContents is undefined * fix: input widgets issue (#18172) fixed the auto height not working issue Co-authored-by: Ankur Singhal * fix: preview deploy mode (#18174) fixed the preview and deploy mode Co-authored-by: Ankur Singhal * fix: auto height limits label intersection with handle dot (#18186) fixed the position of the limits label to the right so that it will not intersect with the handle dot Co-authored-by: Ankur Singhal * fix: auto height limits rich text editor min height (#18187) decrease the min height of the RTE so that it does not have the boundary issue with the max limit when auto height with limits is enabled Co-authored-by: Ankur Singhal * fix: grammatical error in the help text (#18188) changed react to reacts in the helpText of the dynamic height property in the proeprty pane Co-authored-by: Ankur Singhal * fix: auto height tabs double scroll (#18210) solved the issue by disabling the scroll for the child canvas widget in the tabs widget Co-authored-by: Ankur Singhal * fix: auto height limits resizing (#18213) * fixed the auto height limits resizing issue * made the auto height overlay independent of isResizing and used its own property to show the grid * some more refactoring Co-authored-by: Ankur Singhal * dummy commit * fix: old apps container issue (#18255) filtered out the widgets which are detached from layout Co-authored-by: Ankur Singhal * fix: fixing auto height in childless containers. (#18263) fixing auto height in childless containers. * task: Dynamic height reflow fixes in Branch (#18244) dynamic height reflow fixes * fix: compact label issue and min and max limits numeric input (#18282) fixed compact label issue and turned min and max limits to numeric input Co-authored-by: Ankur Singhal * fix: LabelWithTooltip help icon fix * fix: NaN and min limit for min and max (#18284) * fixed compact label issue and turned min and max limits to numeric input * fixed NaN and set min to be 4 Co-authored-by: Ankur Singhal * fix: validation issues for min max (#18286) * fixed compact label issue and turned min and max limits to numeric input * fixed NaN and set min to be 4 * validations start working min max Co-authored-by: Ankur Singhal * added a full stop to container scroll helper text * validations start working min max * dummy commit * feat: stop resizing auto height widgets vertically because of Drag n Drop Reflow (#18267) * reflow fixes * stop resizing auto height widgets vertically because of Drag n Drop Reflow * feat: Analytics for Dynamic height (#18279) * Fix canvas min height issue and invisible widgets issue and remove logs and fix issue where widgets overlapped when coming back from preview mode to edit mode * Fix issue with containers not respecting auto height and decreasing height * Fix issue with modal widget not hugging contents, and container widgets never become visible after going invisible * Fix issue where existing containers don't have correct min height for child canvas * fix: canvasLevelsReducers test (#18301) fixed the canvasLevelsReducers test Co-authored-by: Ankur Singhal * fix: removed auto height min max config from widget features (#18316) removed auto height min max config from widget features Co-authored-by: Ankur Singhal * fix: Fixing Modal Height updates (#18317) Fixing Modal Height updates * fix: text widget background auto height (#18319) added background color of Text widget back to the auto height container Co-authored-by: Ankur Singhal * test: cypress tests for auto height (#17676) * Added tests for dynamic height * updated tests for another usecase * moved locators into commonfile * updated common method * added tests for some more widgets * Added tests for jsonForm / Form widget * Updated the test * updated test for multiple text widgets * updated test with few more usecases * updated the dsl * updated tests for text change * updated tests based on new changes * updated cypress test fixes * fix: auto height container merge poc wrt release (#18334) updated the poc wrt PR already merged in the release regarding the auto height container Co-authored-by: Ankur Singhal * fix: renamed auto height overlay components and added some tests (#18333) * renamed auto height overlay components and added some tests * replaced the 10 value with GridDefaults * avoiding event to reach drop target Co-authored-by: Ankur Singhal * updated tests * Merge all code into one branch * Fix failing AutoHeightcontainer test * fix: Fix reflow computations which were causing widget overlap (#18300) * Fix reflow computations which were causing widget overlap * Fix issues with parent container height and overlapping widgets * Remove console logs * Revert comment * Fix issues related to reflow of containers * feat: Making getEffectedBoxes a Recursive function in autoHeight Reflow (#18336) Making getEffectedBoxes a Recursive function in autoHeight Reflow * Return null for invisible widgets from withWidgetProps * Remove duplicate import Co-authored-by: rahulramesha <71900764+rahulramesha@users.noreply.github.com> * Remove missed console log * fix: Label position gets deselected on selecting already selected option (#18298) * fix: Label position gets deselected on selecting the already selected value * Added migration for Currency & Phone input widgets * simplify migration function using a utility * combine conditions * Increments LATEST_PAGE_VERSION * Update DynamicHeight_Visibility_spec.js updated a check wrt auto height * Handling Modals for canvas size calculations * fix: migrate label position test failing issue (#18365) fixed migrate label postition test failing issue Co-authored-by: Ankur Singhal * removed the two unwanted imports from DSLMigrations to fix client build * fix: Auto height zero and limits issue (#18366) fixed the auto height zero and limits issue Co-authored-by: Ankur Singhal * fix: Auto height regression issues (#18367) * Fix auto height regression issues #18367 * feat: auto height migrations (#18368) Add auto height migrations * Increase file caching size * Use manual array for list of auto height enabled widgets * Fix cypress test dsl versions * Revert changes to shouldUpdateHeightDynamically * Update test results based on code changes * Marginally increase the workbox file size cache * review comment incorporated for test spec * Update container auto height property on drop * added small wait for validation Co-authored-by: Ankur Singhal Co-authored-by: rahulramesha Co-authored-by: Abhinav Jha Co-authored-by: Ankur Singhal Co-authored-by: Ankur Singhal Co-authored-by: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Co-authored-by: rahulramesha <71900764+rahulramesha@users.noreply.github.com> Co-authored-by: Albin Co-authored-by: Aswath K Co-authored-by: NandanAnantharamu <67676905+NandanAnantharamu@users.noreply.github.com> Co-authored-by: Apple * test: Script updates for flaky tests (#18349) * flaky fix * remove only * NumberSlier & BasicLint fix * jsform hidden field spec fix * Theme_MultiSelectWidget_spec.js fix * Bug 16702 spec fix * Add certmanager and classname fields in ingress (#18266) * chore: Add data points for debugging event log (#18404) * Update top contributors * chore: bump minimatch from 3.0.4 to 3.1.2 in /app/util/plugin-generation (#18412) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore: add back feature flag for context switching (#18417) ## Description Add back feature flag for context switching * chore: Add logs from segment error retry handler (#18432) * fix: auto height regression fixes (#18410) * removed block and added flex consistent in the alignment case of left and right * fixed radio group which broke because of display block * default enabled auto height for every widget * added reivew changes * reverted the switch group style changes and added the check for Text widget background in the AutoHeightContainer * changed the scroll helper text for containers * reverted the default enable for all the widgets * test: fixed DH flaky test (#18415) fixed DH flaky test * fix: auto height container computations (#18408) * Fix auto height container computations * fix: Invisible Widgets Overlap while switching back and forth between edit and preview modes in rare cases (#18398) sort Space based on original position if dynamic positions are equal while generating tree * fix: group widgets label alignment. (#18360) * fix: group widgets label alignment. * fix: switch and radio fix transform styles for auto height. * feat: disable auto height limits for modal (#18386) * added multi select back * (WIP): Complete the dynamc height update logic * (WIP): Dynamic height logic * (WIP): Container computation logic, Next steps: Prevent reflow when resize is disabled. Fix logic of widgets randomly changing positions (Debug) * Fix logic in container computations * Integrate for PoC * fixed the no initial load dynamic height updates * Stop vertical resize and reflow when dynamic height is enabled for a widget * added another container in text widget * enabled dynamic height for container widgets * removed dynamic height feature from list widget * Fixed Button and Input components height increase * added an experiment to overflow the content if maxHEight is less * removed the ref of Textwidget by mistake, added it back * fixed text widget height overflow problem with a little hack * added long labels with text * fixed the table scroll issue * overflow fixed for json form widget * added extra 8px height for Switch, Rating and Checkbox Height * (WIP): Resolve issues * (WIP): Fix widget padding issue * added overflow container for Radio and Switch group widgets * (WIP): Have modals work with dynamic height * added the overlay and the handles * added dragging behavior to the dots * fixed the overlapping with the selection tool * (WIP): Fix issues reported * now we can update the property pane values back from overlay handles * now we can update the property pane values back from overlay handles * (WIP): Fix table widget * Fix package.json * Remove unit tests temporarily * Fix unit test * (WIP): Fix modal resize. Fix cursors. Fix border issue on non-resizable widgets * fetch component heights using the requestAnimationFrame callback * behavioural changes * (WIP): Fix issues on the platform * Update main container size appropriately * more behavioural changes * overlay now only be visible when hovering over the dots * grid showing and widget reselecting * added onfocus and onblur events to property pane listeners * added onfocus and onblur events to property pane listeners * added a range slider for min and max * added demarcations for slider values * (WIP): Fix platform workflows for dynamic height * Fix issues with widgets * Fix removed import * - Add missing cypress files * set the limits * limit increase on change * Fix z-index of min max limit indicators. Fix unused-vars warnings * Fix Table Widget and Text Widget issues * Fix: all the bugs in the bug master list for DH (#16268) * changed the zindex for the signifiers * showing signifiers only when the widget is selected * made changes suggested by Momcilo * activate the dots when the fields are active * created a new centered dot handle * removed overlays on focus and made the border more like deisgn * handles on top of other widgets * hide the overlay when multiple widgets are selected * added a white border * added a white border * bug #15509 resolved * changed the minDynamicHeightLimit to 2 instead of 4 to fix the Bug #15527 * removed the height auto fix from BaseInputComponent to fix the Bug #15388 * removed the condition to not ccalculate dynamic height when the row difference is less than 2 to fix the bug 15353 * made fixes for the bug #16307 * made fixes for the bug #16308 * made fixes for bug 16310 * made fixes for the bug #16402 * removed some log statements * made fixes for the bug #16407 * fixed label problem found in the issue #16543 * made fixes for the issue #16547 * made fixes for the bug #16492 * redeploy * (WIP): Fix to make this branch functional * imported LabelWithTooltip back from design system * signifier is now centered * filled the signifier with primary color * overlay hidden while dragging * made the signifier dashed border also draggable * Fix issue #16590 (#16798) * set the limits to 4 rows * replaced the static 40 value * added signifiers for modal widget * added signifiers for modal widget * tried solving the scroll issue for widgets when there are limits * solved the height problem using ResizeObserver * (WIP): Fix maxDynamicHeight issue with container widgets: * made the changes as per the review * fixed the issue for input widget when label gets out of border * hide text widget overflow options if auto height is enabled * (WIP): In view mode, invisible widgets now donot take space (#16920) * (WIP): In view mode, invisible widgets now donot take space * (WIP): Enable the feature where invisible widgets in view mode don't take space to all widgets irrespective of the dynamic height feature * Remove Replay conditional * removed the scroll container for container type widgets * removed the scroll container for container type widgets * updated the hook to set overflow none for text widget * fixed the should dynamic height logic to respect the min height limit * Modal widget adheres to dynamic height (#16995) * Modal widget adheres to dynamic height * WIP: POC: fix dynamic height issues (#16996) Fix height less than 4 issue. Fix JSONForm adherence to min and max height * POC: Dynamic height undo redo issue (#17085) * Revert debouce timeout * (WIP): Fix issue with undo-redo in dynamic height * fix: Dynamic height issue fixes (#17153) * Dynamic height issue fixes == - Fix issue where nested widgets did not ensure parent dynamic height updates - Fix issue where Modal widget updates came in subsequent renders - Fix issue where JSONForm collapses - Fix performance issue for independent updates * Use functions to get min and max dynamic height * Fix issue where variable might have been undefined * added the dynamic container into the deploy mode as well * added overflow-x hidden when overflow-y is active in the dynamic height container * fix: Dynamic height Issue fixes (#17204) Fix preview mode invisible widgets. Fix Tabs widget dynamic height. * removed a console.log statement * removed the slider control file * imported the LabelWithTooltip from the repo rather than ds * word-break CSS rules added for Switch and Checkbox widget when Dynamic Height is enabled * abstracted the check for dynamic height with limits enabled as isDynamicHeightWithLimitsEnabledForWidget * abstracted the static value of 10 in dynamic height overlay to GridDefaults * abstracted min and max dynamic height limits to getters * fix: replaced all the refs for simpler widgets (#17353) * replaced all the refs for simpler widgets * removed the updateDynamicHeight from componentDidUpdate in BaseWidget * added back lifecycle methods back to BaseWidget * removed the contentRef from SwitchGroup and Table * updating the height from the auto height with limits as well * some hacks to make the limits work * working solution * used setTimeout to send an update to updateDynamicHeight from overlay update * removed a log * added requestanimationframe in settimeout Co-authored-by: Ankur Singhal Co-authored-by: Ankur Singhal * Fix issues caused during merge * Remove unneeded derived property * removed more unnecessary code which should have been removed after removing the ref dependency * fixed the maxDynamicHeight issue * Fix issue where property configs were not being sent * fix: Auto Height Feature - add selectors for tests (#17687) Add selectors for auto height cypress tests * fix: removed height auto default theme (#17415) removed height auto css rule from the default theme Co-authored-by: Ankur Singhal * fix: Auto Height Feature - Resolve issues and restructure code (#17686) * Fix issues in dynamic height. Restructure code and reduce abstraction leaks * Fix typescript issues * Update based on review comments. Comment migrations, as a cyclic import is causing the jest tests to fail. * Remove unused imports * Decrease code nesting * added the base styles for the overlay like position and z-index in its styled component css * used the isDynamicHeightEnabled prop to set the height of SwitchGroup and RadioGroup widgets from 32px to 100% in case of inline mode * fix: Auto Height - Resolve issues (#17737) * Fix Tabs Widget showTabs toggle based auto height. Revert removal of BaseWidget code. Remove box-intersect and use a bruteforce algorithm. Add base logic for having containers collapse due to hidden child widgets * Hide scroll contents and overflow property pane controls when dynamic height is enabled * Removed the class property expectedHeight from BaseWidget as it is not useful in the overlay logic after some changes * fixed the left alignment issue of label in the rich text editor by adding some styles applied only when the dynamic height is enabled * fixed the input field stretching issue in case of Dynamic height by adding some CSS styles when isDynamicHeight is true * Fix failing modal widget cypress tests * Fix issue with scrollContents and Tabs Widget defaulTab * added a little bit padding of 4px to the right of scroll container of dynamic height with limit * Add test locators for resize handles * removed the dynamic height logic from the table widget * fix: Auto-Height invisible widgets (#17849) * Fix issue where invisible widgets were still taking space * Make sure to collapse only if dynamic height is enabled * Fix issues with reflow (not the invisible widgets) * Fix container min height issues * Fix reflow with original bottom and top values. Testing needed * Fix invisible widgets * fix: enabled dynamic height for stat box widget (#17971) enabled dynamic height for stat box widget Co-authored-by: Ankur Singhal * fix: added a min height to rich text editor so that it does not collapse (#17970) added a min height to rich text editor so that it does not collapse Co-authored-by: Ankur Singhal * Fix issue with resizing auto height widget * Add helper text to educate users regarding the scroll disconnect in WYSIWYG * fix: Auto Height Fixes (#18111) AUTO HEIGHT FIXES - Fix JSONForm height discrepancy - Fix issue where widgets moved below the other - Fix droptarget height after parent container resize * fix: sliced up the DynamicHeightOverlay component a little bit (#18100) * sliced up the DynamicHeightOverlay component a little bit * more refactoring * more refactoring * used release event emitter and refactored more Co-authored-by: Ankur Singhal * fix: rich text editor center alignment issue (#18142) * removed the center alignment from rich text editor * dummy commit Co-authored-by: Ankur Singhal * fix: old DSL container collapse (#18160) * Fix issue where old containers from old DSLs used to collapse when auto height was enabled * Fix issue where old containers don't allow new widgets to be added when auto height is enabled, this is because the shouldScrollContents is undefined * fix: input widgets issue (#18172) fixed the auto height not working issue Co-authored-by: Ankur Singhal * fix: preview deploy mode (#18174) fixed the preview and deploy mode Co-authored-by: Ankur Singhal * fix: auto height limits label intersection with handle dot (#18186) fixed the position of the limits label to the right so that it will not intersect with the handle dot Co-authored-by: Ankur Singhal * fix: auto height limits rich text editor min height (#18187) decrease the min height of the RTE so that it does not have the boundary issue with the max limit when auto height with limits is enabled Co-authored-by: Ankur Singhal * fix: grammatical error in the help text (#18188) changed react to reacts in the helpText of the dynamic height property in the proeprty pane Co-authored-by: Ankur Singhal * fix: auto height tabs double scroll (#18210) solved the issue by disabling the scroll for the child canvas widget in the tabs widget Co-authored-by: Ankur Singhal * fix: auto height limits resizing (#18213) * fixed the auto height limits resizing issue * made the auto height overlay independent of isResizing and used its own property to show the grid * some more refactoring Co-authored-by: Ankur Singhal * dummy commit * fix: old apps container issue (#18255) filtered out the widgets which are detached from layout Co-authored-by: Ankur Singhal * fix: fixing auto height in childless containers. (#18263) fixing auto height in childless containers. * task: Dynamic height reflow fixes in Branch (#18244) dynamic height reflow fixes * fix: compact label issue and min and max limits numeric input (#18282) fixed compact label issue and turned min and max limits to numeric input Co-authored-by: Ankur Singhal * fix: LabelWithTooltip help icon fix * fix: NaN and min limit for min and max (#18284) * fixed compact label issue and turned min and max limits to numeric input * fixed NaN and set min to be 4 Co-authored-by: Ankur Singhal * fix: validation issues for min max (#18286) * fixed compact label issue and turned min and max limits to numeric input * fixed NaN and set min to be 4 * validations start working min max Co-authored-by: Ankur Singhal * added a full stop to container scroll helper text * validations start working min max * dummy commit * feat: stop resizing auto height widgets vertically because of Drag n Drop Reflow (#18267) * reflow fixes * stop resizing auto height widgets vertically because of Drag n Drop Reflow * feat: Analytics for Dynamic height (#18279) * Fix canvas min height issue and invisible widgets issue and remove logs and fix issue where widgets overlapped when coming back from preview mode to edit mode * Fix issue with containers not respecting auto height and decreasing height * Fix issue with modal widget not hugging contents, and container widgets never become visible after going invisible * Fix issue where existing containers don't have correct min height for child canvas * fix: canvasLevelsReducers test (#18301) fixed the canvasLevelsReducers test Co-authored-by: Ankur Singhal * fix: removed auto height min max config from widget features (#18316) removed auto height min max config from widget features Co-authored-by: Ankur Singhal * fix: Fixing Modal Height updates (#18317) Fixing Modal Height updates * fix: text widget background auto height (#18319) added background color of Text widget back to the auto height container Co-authored-by: Ankur Singhal * test: cypress tests for auto height (#17676) * Added tests for dynamic height * updated tests for another usecase * moved locators into commonfile * updated common method * added tests for some more widgets * Added tests for jsonForm / Form widget * Updated the test * updated test for multiple text widgets * updated test with few more usecases * updated the dsl * updated tests for text change * updated tests based on new changes * updated cypress test fixes * fix: auto height container merge poc wrt release (#18334) updated the poc wrt PR already merged in the release regarding the auto height container Co-authored-by: Ankur Singhal * fix: renamed auto height overlay components and added some tests (#18333) * renamed auto height overlay components and added some tests * replaced the 10 value with GridDefaults * avoiding event to reach drop target Co-authored-by: Ankur Singhal * updated tests * Merge all code into one branch * Fix failing AutoHeightcontainer test * fix: Fix reflow computations which were causing widget overlap (#18300) * Fix reflow computations which were causing widget overlap * Fix issues with parent container height and overlapping widgets * Remove console logs * Revert comment * Fix issues related to reflow of containers * feat: Making getEffectedBoxes a Recursive function in autoHeight Reflow (#18336) Making getEffectedBoxes a Recursive function in autoHeight Reflow * Return null for invisible widgets from withWidgetProps * Remove duplicate import Co-authored-by: rahulramesha <71900764+rahulramesha@users.noreply.github.com> * Remove missed console log * fix: Label position gets deselected on selecting already selected option (#18298) * fix: Label position gets deselected on selecting the already selected value * Added migration for Currency & Phone input widgets * simplify migration function using a utility * combine conditions * Increments LATEST_PAGE_VERSION * Update DynamicHeight_Visibility_spec.js updated a check wrt auto height * Handling Modals for canvas size calculations * fix: migrate label position test failing issue (#18365) fixed migrate label postition test failing issue Co-authored-by: Ankur Singhal * removed the two unwanted imports from DSLMigrations to fix client build * fix: Auto height zero and limits issue (#18366) fixed the auto height zero and limits issue Co-authored-by: Ankur Singhal * fix: Auto height regression issues (#18367) * Fix auto height regression issues #18367 * feat: auto height migrations (#18368) Add auto height migrations * Increase file caching size * Use manual array for list of auto height enabled widgets * Fix cypress test dsl versions * Revert changes to shouldUpdateHeightDynamically * Update test results based on code changes * Marginally increase the workbox file size cache * review comment incorporated for test spec * Update container auto height property on drop * Disable auto height with limits for modal Co-authored-by: Ankur Singhal Co-authored-by: rahulramesha Co-authored-by: Abhinav Jha Co-authored-by: Ankur Singhal Co-authored-by: Ankur Singhal Co-authored-by: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Co-authored-by: rahulramesha <71900764+rahulramesha@users.noreply.github.com> Co-authored-by: Albin Co-authored-by: Aswath K Co-authored-by: NandanAnantharamu <67676905+NandanAnantharamu@users.noreply.github.com> Co-authored-by: Apple * chore: Moved height property to General section (#18402) * fix: Height property is moved to General section * Logs an Error if section is not General * fix: Cypress * enabled auto height for all the widgets except json form * removes accidentally committed files Co-authored-by: Ankur Singhal * disabled default auto height for input widgets and select widgets and datepicker widget * feat: Disable auto height for widgets in List Widget (#18381) * Remove auto height for container Inside List widget * remove unneccessary changes * disable feature at generalConfig instead of firstConfig * add todo comment * skipped the get min limit tests * fixed AutoHeightLimitHandleDot tests * Updated cypress tests for regression changes * update visual tests for widget layout Co-authored-by: Ankur Singhal Co-authored-by: NandanAnantharamu <67676905+NandanAnantharamu@users.noreply.github.com> Co-authored-by: Abhinav Jha Co-authored-by: rahulramesha <71900764+rahulramesha@users.noreply.github.com> Co-authored-by: Arsalan Yaldram Co-authored-by: rahulramesha Co-authored-by: Abhinav Jha Co-authored-by: Ankur Singhal Co-authored-by: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Co-authored-by: Albin Co-authored-by: Aswath K Co-authored-by: Apple Co-authored-by: Parthvi Goswami * fix: list widget remove autoLayoutContainer for list widget template children (#18441) * added an experiment to overflow the content if maxHEight is less * removed the ref of Textwidget by mistake, added it back * fixed text widget height overflow problem with a little hack * added long labels with text * fixed the table scroll issue * overflow fixed for json form widget * added extra 8px height for Switch, Rating and Checkbox Height * (WIP): Resolve issues * (WIP): Fix widget padding issue * added overflow container for Radio and Switch group widgets * (WIP): Have modals work with dynamic height * added the overlay and the handles * added dragging behavior to the dots * fixed the overlapping with the selection tool * (WIP): Fix issues reported * now we can update the property pane values back from overlay handles * now we can update the property pane values back from overlay handles * (WIP): Fix table widget * Fix package.json * Remove unit tests temporarily * Fix unit test * (WIP): Fix modal resize. Fix cursors. Fix border issue on non-resizable widgets * fetch component heights using the requestAnimationFrame callback * behavioural changes * (WIP): Fix issues on the platform * Update main container size appropriately * more behavioural changes * overlay now only be visible when hovering over the dots * grid showing and widget reselecting * added onfocus and onblur events to property pane listeners * added onfocus and onblur events to property pane listeners * added a range slider for min and max * added demarcations for slider values * (WIP): Fix platform workflows for dynamic height * Fix issues with widgets * Fix removed import * - Add missing cypress files * set the limits * limit increase on change * Fix z-index of min max limit indicators. Fix unused-vars warnings * Fix Table Widget and Text Widget issues * Fix: all the bugs in the bug master list for DH (#16268) * changed the zindex for the signifiers * showing signifiers only when the widget is selected * made changes suggested by Momcilo * activate the dots when the fields are active * created a new centered dot handle * removed overlays on focus and made the border more like deisgn * handles on top of other widgets * hide the overlay when multiple widgets are selected * added a white border * added a white border * bug #15509 resolved * changed the minDynamicHeightLimit to 2 instead of 4 to fix the Bug #15527 * removed the height auto fix from BaseInputComponent to fix the Bug #15388 * removed the condition to not ccalculate dynamic height when the row difference is less than 2 to fix the bug 15353 * made fixes for the bug #16307 * made fixes for the bug #16308 * made fixes for bug 16310 * made fixes for the bug #16402 * removed some log statements * made fixes for the bug #16407 * fixed label problem found in the issue #16543 * made fixes for the issue #16547 * made fixes for the bug #16492 * redeploy * (WIP): Fix to make this branch functional * imported LabelWithTooltip back from design system * signifier is now centered * filled the signifier with primary color * overlay hidden while dragging * made the signifier dashed border also draggable * Fix issue #16590 (#16798) * set the limits to 4 rows * replaced the static 40 value * added signifiers for modal widget * added signifiers for modal widget * tried solving the scroll issue for widgets when there are limits * solved the height problem using ResizeObserver * (WIP): Fix maxDynamicHeight issue with container widgets: * made the changes as per the review * fixed the issue for input widget when label gets out of border * hide text widget overflow options if auto height is enabled * (WIP): In view mode, invisible widgets now donot take space (#16920) * (WIP): In view mode, invisible widgets now donot take space * (WIP): Enable the feature where invisible widgets in view mode don't take space to all widgets irrespective of the dynamic height feature * Remove Replay conditional * removed the scroll container for container type widgets * removed the scroll container for container type widgets * updated the hook to set overflow none for text widget * fixed the should dynamic height logic to respect the min height limit * Modal widget adheres to dynamic height (#16995) * Modal widget adheres to dynamic height * WIP: POC: fix dynamic height issues (#16996) Fix height less than 4 issue. Fix JSONForm adherence to min and max height * POC: Dynamic height undo redo issue (#17085) * Revert debouce timeout * (WIP): Fix issue with undo-redo in dynamic height * fix: Dynamic height issue fixes (#17153) * Dynamic height issue fixes == - Fix issue where nested widgets did not ensure parent dynamic height updates - Fix issue where Modal widget updates came in subsequent renders - Fix issue where JSONForm collapses - Fix performance issue for independent updates * Use functions to get min and max dynamic height * Fix issue where variable might have been undefined * added the dynamic container into the deploy mode as well * added overflow-x hidden when overflow-y is active in the dynamic height container * fix: Dynamic height Issue fixes (#17204) Fix preview mode invisible widgets. Fix Tabs widget dynamic height. * removed a console.log statement * removed the slider control file * imported the LabelWithTooltip from the repo rather than ds * word-break CSS rules added for Switch and Checkbox widget when Dynamic Height is enabled * abstracted the check for dynamic height with limits enabled as isDynamicHeightWithLimitsEnabledForWidget * abstracted the static value of 10 in dynamic height overlay to GridDefaults * abstracted min and max dynamic height limits to getters * fix: replaced all the refs for simpler widgets (#17353) * replaced all the refs for simpler widgets * removed the updateDynamicHeight from componentDidUpdate in BaseWidget * added back lifecycle methods back to BaseWidget * removed the contentRef from SwitchGroup and Table * updating the height from the auto height with limits as well * some hacks to make the limits work * working solution * used setTimeout to send an update to updateDynamicHeight from overlay update * removed a log * added requestanimationframe in settimeout Co-authored-by: Ankur Singhal Co-authored-by: Ankur Singhal * Fix issues caused during merge * Remove unneeded derived property * removed more unnecessary code which should have been removed after removing the ref dependency * fixed the maxDynamicHeight issue * Fix issue where property configs were not being sent * fix: Auto Height Feature - add selectors for tests (#17687) Add selectors for auto height cypress tests * fix: removed height auto default theme (#17415) removed height auto css rule from the default theme Co-authored-by: Ankur Singhal * fix: Auto Height Feature - Resolve issues and restructure code (#17686) * Fix issues in dynamic height. Restructure code and reduce abstraction leaks * Fix typescript issues * Update based on review comments. Comment migrations, as a cyclic import is causing the jest tests to fail. * Remove unused imports * Decrease code nesting * added the base styles for the overlay like position and z-index in its styled component css * used the isDynamicHeightEnabled prop to set the height of SwitchGroup and RadioGroup widgets from 32px to 100% in case of inline mode * fix: Auto Height - Resolve issues (#17737) * Fix Tabs Widget showTabs toggle based auto height. Revert removal of BaseWidget code. Remove box-intersect and use a bruteforce algorithm. Add base logic for having containers collapse due to hidden child widgets * Hide scroll contents and overflow property pane controls when dynamic height is enabled * Removed the class property expectedHeight from BaseWidget as it is not useful in the overlay logic after some changes * fixed the left alignment issue of label in the rich text editor by adding some styles applied only when the dynamic height is enabled * fixed the input field stretching issue in case of Dynamic height by adding some CSS styles when isDynamicHeight is true * Fix failing modal widget cypress tests * Fix issue with scrollContents and Tabs Widget defaulTab * added a little bit padding of 4px to the right of scroll container of dynamic height with limit * Add test locators for resize handles * removed the dynamic height logic from the table widget * fix: Auto-Height invisible widgets (#17849) * Fix issue where invisible widgets were still taking space * Make sure to collapse only if dynamic height is enabled * Fix issues with reflow (not the invisible widgets) * Fix container min height issues * Fix reflow with original bottom and top values. Testing needed * Fix invisible widgets * fix: enabled dynamic height for stat box widget (#17971) enabled dynamic height for stat box widget Co-authored-by: Ankur Singhal * fix: added a min height to rich text editor so that it does not collapse (#17970) added a min height to rich text editor so that it does not collapse Co-authored-by: Ankur Singhal * Fix issue with resizing auto height widget * Add helper text to educate users regarding the scroll disconnect in WYSIWYG * fix: Auto Height Fixes (#18111) AUTO HEIGHT FIXES - Fix JSONForm height discrepancy - Fix issue where widgets moved below the other - Fix droptarget height after parent container resize * fix: sliced up the DynamicHeightOverlay component a little bit (#18100) * sliced up the DynamicHeightOverlay component a little bit * more refactoring * more refactoring * used release event emitter and refactored more Co-authored-by: Ankur Singhal * fix: rich text editor center alignment issue (#18142) * removed the center alignment from rich text editor * dummy commit Co-authored-by: Ankur Singhal * fix: old DSL container collapse (#18160) * Fix issue where old containers from old DSLs used to collapse when auto height was enabled * Fix issue where old containers don't allow new widgets to be added when auto height is enabled, this is because the shouldScrollContents is undefined * fix: input widgets issue (#18172) fixed the auto height not working issue Co-authored-by: Ankur Singhal * fix: preview deploy mode (#18174) fixed the preview and deploy mode Co-authored-by: Ankur Singhal * fix: auto height limits label intersection with handle dot (#18186) fixed the position of the limits label to the right so that it will not intersect with the handle dot Co-authored-by: Ankur Singhal * fix: auto height limits rich text editor min height (#18187) decrease the min height of the RTE so that it does not have the boundary issue with the max limit when auto height with limits is enabled Co-authored-by: Ankur Singhal * fix: grammatical error in the help text (#18188) changed react to reacts in the helpText of the dynamic height property in the proeprty pane Co-authored-by: Ankur Singhal * fix: auto height tabs double scroll (#18210) solved the issue by disabling the scroll for the child canvas widget in the tabs widget Co-authored-by: Ankur Singhal * fix: auto height limits resizing (#18213) * fixed the auto height limits resizing issue * made the auto height overlay independent of isResizing and used its own property to show the grid * some more refactoring Co-authored-by: Ankur Singhal * dummy commit * fix: old apps container issue (#18255) filtered out the widgets which are detached from layout Co-authored-by: Ankur Singhal * fix: fixing auto height in childless containers. (#18263) fixing auto height in childless containers. * task: Dynamic height reflow fixes in Branch (#18244) dynamic height reflow fixes * fix: compact label issue and min and max limits numeric input (#18282) fixed compact label issue and turned min and max limits to numeric input Co-authored-by: Ankur Singhal * fix: LabelWithTooltip help icon fix * fix: NaN and min limit for min and max (#18284) * fixed compact label issue and turned min and max limits to numeric input * fixed NaN and set min to be 4 Co-authored-by: Ankur Singhal * fix: validation issues for min max (#18286) * fixed compact label issue and turned min and max limits to numeric input * fixed NaN and set min to be 4 * validations start working min max Co-authored-by: Ankur Singhal * added a full stop to container scroll helper text * validations start working min max * dummy commit * feat: stop resizing auto height widgets vertically because of Drag n Drop Reflow (#18267) * reflow fixes * stop resizing auto height widgets vertically because of Drag n Drop Reflow * feat: Analytics for Dynamic height (#18279) * Fix canvas min height issue and invisible widgets issue and remove logs and fix issue where widgets overlapped when coming back from preview mode to edit mode * Fix issue with containers not respecting auto height and decreasing height * Fix issue with modal widget not hugging contents, and container widgets never become visible after going invisible * Fix issue where existing containers don't have correct min height for child canvas * fix: canvasLevelsReducers test (#18301) fixed the canvasLevelsReducers test Co-authored-by: Ankur Singhal * fix: removed auto height min max config from widget features (#18316) removed auto height min max config from widget features Co-authored-by: Ankur Singhal * fix: Fixing Modal Height updates (#18317) Fixing Modal Height updates * fix: text widget background auto height (#18319) added background color of Text widget back to the auto height container Co-authored-by: Ankur Singhal * test: cypress tests for auto height (#17676) * Added tests for dynamic height * updated tests for another usecase * moved locators into commonfile * updated common method * added tests for some more widgets * Added tests for jsonForm / Form widget * Updated the test * updated test for multiple text widgets * updated test with few more usecases * updated the dsl * updated tests for text change * updated tests based on new changes * updated cypress test fixes * fix: auto height container merge poc wrt release (#18334) updated the poc wrt PR already merged in the release regarding the auto height container Co-authored-by: Ankur Singhal * fix: renamed auto height overlay components and added some tests (#18333) * renamed auto height overlay components and added some tests * replaced the 10 value with GridDefaults * avoiding event to reach drop target Co-authored-by: Ankur Singhal * updated tests * Merge all code into one branch * Fix failing AutoHeightcontainer test * fix: Fix reflow computations which were causing widget overlap (#18300) * Fix reflow computations which were causing widget overlap * Fix issues with parent container height and overlapping widgets * Remove console logs * Revert comment * Fix issues related to reflow of containers * feat: Making getEffectedBoxes a Recursive function in autoHeight Reflow (#18336) Making getEffectedBoxes a Recursive function in autoHeight Reflow * Return null for invisible widgets from withWidgetProps * Remove duplicate import Co-authored-by: rahulramesha <71900764+rahulramesha@users.noreply.github.com> * Remove missed console log * fix: Label position gets deselected on selecting already selected option (#18298) * fix: Label position gets deselected on selecting the already selected value * Added migration for Currency & Phone input widgets * simplify migration function using a utility * combine conditions * Increments LATEST_PAGE_VERSION * Update DynamicHeight_Visibility_spec.js updated a check wrt auto height * Handling Modals for canvas size calculations * fix: migrate label position test failing issue (#18365) fixed migrate label postition test failing issue Co-authored-by: Ankur Singhal * removed the two unwanted imports from DSLMigrations to fix client build * fix: Auto height zero and limits issue (#18366) fixed the auto height zero and limits issue Co-authored-by: Ankur Singhal * fix: Auto height regression issues (#18367) * Fix auto height regression issues #18367 * feat: auto height migrations (#18368) Add auto height migrations * Increase file caching size * Use manual array for list of auto height enabled widgets * Fix cypress test dsl versions * Revert changes to shouldUpdateHeightDynamically * Update test results based on code changes * Marginally increase the workbox file size cache * review comment incorporated for test spec * Update container auto height property on drop * Remove auto height for container Inside List widget * remove unneccessary changes * removed block and added flex consistent in the alignment case of left and right * fixed radio group which broke because of display block * default enabled auto height for every widget * added reivew changes * reverted the switch group style changes and added the check for Text widget background in the AutoHeightContainer * changed the scroll helper text for containers * reverted the default enable for all the widgets * test: fixed DH flaky test (#18415) fixed DH flaky test * fix: auto height container computations (#18408) * Fix auto height container computations * fix: Invisible Widgets Overlap while switching back and forth between edit and preview modes in rare cases (#18398) sort Space based on original position if dynamic positions are equal while generating tree * fix: group widgets label alignment. (#18360) * fix: group widgets label alignment. * fix: switch and radio fix transform styles for auto height. * feat: disable auto height limits for modal (#18386) * added multi select back * (WIP): Complete the dynamc height update logic * (WIP): Dynamic height logic * (WIP): Container computation logic, Next steps: Prevent reflow when resize is disabled. Fix logic of widgets randomly changing positions (Debug) * Fix logic in container computations * Integrate for PoC * fixed the no initial load dynamic height updates * Stop vertical resize and reflow when dynamic height is enabled for a widget * added another container in text widget * enabled dynamic height for container widgets * removed dynamic height feature from list widget * Fixed Button and Input components height increase * added an experiment to overflow the content if maxHEight is less * removed the ref of Textwidget by mistake, added it back * fixed text widget height overflow problem with a little hack * added long labels with text * fixed the table scroll issue * overflow fixed for json form widget * added extra 8px height for Switch, Rating and Checkbox Height * (WIP): Resolve issues * (WIP): Fix widget padding issue * added overflow container for Radio and Switch group widgets * (WIP): Have modals work with dynamic height * added the overlay and the handles * added dragging behavior to the dots * fixed the overlapping with the selection tool * (WIP): Fix issues reported * now we can update the property pane values back from overlay handles * now we can update the property pane values back from overlay handles * (WIP): Fix table widget * Fix package.json * Remove unit tests temporarily * Fix unit test * (WIP): Fix modal resize. Fix cursors. Fix border issue on non-resizable widgets * fetch component heights using the requestAnimationFrame callback * behavioural changes * (WIP): Fix issues on the platform * Update main container size appropriately * more behavioural changes * overlay now only be visible when hovering over the dots * grid showing and widget reselecting * added onfocus and onblur events to property pane listeners * added onfocus and onblur events to property pane listeners * added a range slider for min and max * added demarcations for slider values * (WIP): Fix platform workflows for dynamic height * Fix issues with widgets * Fix removed import * - Add missing cypress files * set the limits * limit increase on change * Fix z-index of min max limit indicators. Fix unused-vars warnings * Fix Table Widget and Text Widget issues * Fix: all the bugs in the bug master list for DH (#16268) * changed the zindex for the signifiers * showing signifiers only when the widget is selected * made changes suggested by Momcilo * activate the dots when the fields are active * created a new centered dot handle * removed overlays on focus and made the border more like deisgn * handles on top of other widgets * hide the overlay when multiple widgets are selected * added a white border * added a white border * bug #15509 resolved * changed the minDynamicHeightLimit to 2 instead of 4 to fix the Bug #15527 * removed the height auto fix from BaseInputComponent to fix the Bug #15388 * removed the condition to not ccalculate dynamic height when the row difference is less than 2 to fix the bug 15353 * made fixes for the bug #16307 * made fixes for the bug #16308 * made fixes for bug 16310 * made fixes for the bug #16402 * removed some log statements * made fixes for the bug #16407 * fixed label problem found in the issue #16543 * made fixes for the issue #16547 * made fixes for the bug #16492 * redeploy * (WIP): Fix to make this branch functional * imported LabelWithTooltip back from design system * signifier is now centered * filled the signifier with primary color * overlay hidden while dragging * made the signifier dashed border also draggable * Fix issue #16590 (#16798) * set the limits to 4 rows * replaced the static 40 value * added signifiers for modal widget * added signifiers for modal widget * tried solving the scroll issue for widgets when there are limits * solved the height problem using ResizeObserver * (WIP): Fix maxDynamicHeight issue with container widgets: * made the changes as per the review * fixed the issue for input widget when label gets out of border * hide text widget overflow options if auto height is enabled * (WIP): In view mode, invisible widgets now donot take space (#16920) * (WIP): In view mode, invisible widgets now donot take space * (WIP): Enable the feature where invisible widgets in view mode don't take space to all widgets irrespective of the dynamic height feature * Remove Replay conditional * removed the scroll container for container type widgets * removed the scroll container for container type widgets * updated the hook to set overflow none for text widget * fixed the should dynamic height logic to respect the min height limit * Modal widget adheres to dynamic height (#16995) * Modal widget adheres to dynamic height * WIP: POC: fix dynamic height issues (#16996) Fix height less than 4 issue. Fix JSONForm adherence to min and max height * POC: Dynamic height undo redo issue (#17085) * Revert debouce timeout * (WIP): Fix issue with undo-redo in dynamic height * fix: Dynamic height issue fixes (#17153) * Dynamic height issue fixes == - Fix issue where nested widgets did not ensure parent dynamic height updates - Fix issue where Modal widget updates came in subsequent renders - Fix issue where JSONForm collapses - Fix performance issue for independent updates * Use functions to get min and max dynamic height * Fix issue where variable might have been undefined * added the dynamic container into the deploy mode as well * added overflow-x hidden when overflow-y is active in the dynamic height container * fix: Dynamic height Issue fixes (#17204) Fix preview mode invisible widgets. Fix Tabs widget dynamic height. * removed a console.log statement * removed the slider control file * imported the LabelWithTooltip from the repo rather than ds * word-break CSS rules added for Switch and Checkbox widget when Dynamic Height is enabled * abstracted the check for dynamic height with limits enabled as isDynamicHeightWithLimitsEnabledForWidget * abstracted the static value of 10 in dynamic height overlay to GridDefaults * abstracted min and max dynamic height limits to getters * fix: replaced all the refs for simpler widgets (#17353) * replaced all the refs for simpler widgets * removed the updateDynamicHeight from componentDidUpdate in BaseWidget * added back lifecycle methods back to BaseWidget * removed the contentRef from SwitchGroup and Table * updating the height from the auto height with limits as well * some hacks to make the limits work * working solution * used setTimeout to send an update to updateDynamicHeight from overlay update * removed a log * added requestanimationframe in settimeout Co-authored-by: Ankur Singhal Co-authored-by: Ankur Singhal * Fix issues caused during merge * Remove unneeded derived property * removed more unnecessary code which should have been removed after removing the ref dependency * fixed the maxDynamicHeight issue * Fix issue where property configs were not being sent * fix: Auto Height Feature - add selectors for tests (#17687) Add selectors for auto height cypress tests * fix: removed height auto default theme (#17415) removed height auto css rule from the default theme Co-authored-by: Ankur Singhal * fix: Auto Height Feature - Resolve issues and restructure code (#17686) * Fix issues in dynamic height. Restructure code and reduce abstraction leaks * Fix typescript issues * Update based on review comments. Comment migrations, as a cyclic import is causing the jest tests to fail. * Remove unused imports * Decrease code nesting * added the base styles for the overlay like position and z-index in its styled component css * used the isDynamicHeightEnabled prop to set the height of SwitchGroup and RadioGroup widgets from 32px to 100% in case of inline mode * fix: Auto Height - Resolve issues (#17737) * Fix Tabs Widget showTabs toggle based auto height. Revert removal of BaseWidget code. Remove box-intersect and use a bruteforce algorithm. Add base logic for having containers collapse due to hidden child widgets * Hide scroll contents and overflow property pane controls when dynamic height is enabled * Removed the class property expectedHeight from BaseWidget as it is not useful in the overlay logic after some changes * fixed the left alignment issue of label in the rich text editor by adding some styles applied only when the dynamic height is enabled * fixed the input field stretching issue in case of Dynamic height by adding some CSS styles when isDynamicHeight is true * Fix failing modal widget cypress tests * Fix issue with scrollContents and Tabs Widget defaulTab * added a little bit padding of 4px to the right of scroll container of dynamic height with limit * Add test locators for resize handles * removed the dynamic height logic from the table widget * fix: Auto-Height invisible widgets (#17849) * Fix issue where invisible widgets were still taking space * Make sure to collapse only if dynamic height is enabled * Fix issues with reflow (not the invisible widgets) * Fix container min height issues * Fix reflow with original bottom and top values. Testing needed * Fix invisible widgets * fix: enabled dynamic height for stat box widget (#17971) enabled dynamic height for stat box widget Co-authored-by: Ankur Singhal * fix: added a min height to rich text editor so that it does not collapse (#17970) added a min height to rich text editor so that it does not collapse Co-authored-by: Ankur Singhal * Fix issue with resizing auto height widget * Add helper text to educate users regarding the scroll disconnect in WYSIWYG * fix: Auto Height Fixes (#18111) AUTO HEIGHT FIXES - Fix JSONForm height discrepancy - Fix issue where widgets moved below the other - Fix droptarget height after parent container resize * fix: sliced up the DynamicHeightOverlay component a little bit (#18100) * sliced up the DynamicHeightOverlay component a little bit * more refactoring * more refactoring * used release event emitter and refactored more Co-authored-by: Ankur Singhal * fix: rich text editor center alignment issue (#18142) * removed the center alignment from rich text editor * dummy commit Co-authored-by: Ankur Singhal * fix: old DSL container collapse (#18160) * Fix issue where old containers from old DSLs used to collapse when auto height was enabled * Fix issue where old containers don't allow new widgets to be added when auto height is enabled, this is because the shouldScrollContents is undefined * fix: input widgets issue (#18172) fixed the auto height not working issue Co-authored-by: Ankur Singhal * fix: preview deploy mode (#18174) fixed the preview and deploy mode Co-authored-by: Ankur Singhal * fix: auto height limits label intersection with handle dot (#18186) fixed the position of the limits label to the right so that it will not intersect with the handle dot Co-authored-by: Ankur Singhal * fix: auto height limits rich text editor min height (#18187) decrease the min height of the RTE so that it does not have the boundary issue with the max limit when auto height with limits is enabled Co-authored-by: Ankur Singhal * fix: grammatical error in the help text (#18188) changed react to reacts in the helpText of the dynamic height property in the proeprty pane Co-authored-by: Ankur Singhal * fix: auto height tabs double scroll (#18210) solved the issue by disabling the scroll for the child canvas widget in the tabs widget Co-authored-by: Ankur Singhal * fix: auto height limits resizing (#18213) * fixed the auto height limits resizing issue * made the auto height overlay independent of isResizing and used its own property to show the grid * some more refactoring Co-authored-by: Ankur Singhal * dummy commit * fix: old apps container issue (#18255) filtered out the widgets which are detached from layout Co-authored-by: Ankur Singhal * fix: fixing auto height in childless containers. (#18263) fixing auto height in childless containers. * task: Dynamic height reflow fixes in Branch (#18244) dynamic height reflow fixes * fix: compact label issue and min and max limits numeric input (#18282) fixed compact label issue and turned min and max limits to numeric input Co-authored-by: Ankur Singhal * fix: LabelWithTooltip help icon fix * fix: NaN and min limit for min and max (#18284) * fixed compact label issue and turned min and max limits to numeric input * fixed NaN and set min to be 4 Co-authored-by: Ankur Singhal * fix: validation issues for min max (#18286) * fixed compact label issue and turned min and max limits to numeric input * fixed NaN and set min to be 4 * validations start working min max Co-authored-by: Ankur Singhal * added a full stop to container scroll helper text * validations start working min max * dummy commit * feat: stop resizing auto height widgets vertically because of Drag n Drop Reflow (#18267) * reflow fixes * stop resizing auto height widgets vertically because of Drag n Drop Reflow * feat: Analytics for Dynamic height (#18279) * Fix canvas min height issue and invisible widgets issue and remove logs and fix issue where widgets overlapped when coming back from preview mode to edit mode * Fix issue with containers not respecting auto height and decreasing height * Fix issue with modal widget not hugging contents, and container widgets never become visible after going invisible * Fix issue where existing containers don't have correct min height for child canvas * fix: canvasLevelsReducers test (#18301) fixed the canvasLevelsReducers test Co-authored-by: Ankur Singhal * fix: removed auto height min max config from widget features (#18316) removed auto height min max config from widget features Co-authored-by: Ankur Singhal * fix: Fixing Modal Height updates (#18317) Fixing Modal Height updates * fix: text widget background auto height (#18319) added background color of Text widget back to the auto height container Co-authored-by: Ankur Singhal * test: cypress tests for auto height (#17676) * Added tests for dynamic height * updated tests for another usecase * moved locators into commonfile * updated common method * added tests for some more widgets * Added tests for jsonForm / Form widget * Updated the test * updated test for multiple text widgets * updated test with few more usecases * updated the dsl * updated tests for text change * updated tests based on new changes * updated cypress test fixes * fix: auto height container merge poc wrt release (#18334) updated the poc wrt PR already merged in the release regarding the auto height container Co-authored-by: Ankur Singhal * fix: renamed auto height overlay components and added some tests (#18333) * renamed auto height overlay components and added some tests * replaced the 10 value with GridDefaults * avoiding event to reach drop target Co-authored-by: Ankur Singhal * updated tests * Merge all code into one branch * Fix failing AutoHeightcontainer test * fix: Fix reflow computations which were causing widget overlap (#18300) * Fix reflow computations which were causing widget overlap * Fix issues with parent container height and overlapping widgets * Remove console logs * Revert comment * Fix issues related to reflow of containers * feat: Making getEffectedBoxes a Recursive function in autoHeight Reflow (#18336) Making getEffectedBoxes a Recursive function in autoHeight Reflow * Return null for invisible widgets from withWidgetProps * Remove duplicate import Co-authored-by: rahulramesha <71900764+rahulramesha@users.noreply.github.com> * Remove missed console log * fix: Label position gets deselected on selecting already selected option (#18298) * fix: Label position gets deselected on selecting the already selected value * Added migration for Currency & Phone input widgets * simplify migration function using a utility * combine conditions * Increments LATEST_PAGE_VERSION * Update DynamicHeight_Visibility_spec.js updated a check wrt auto height * Handling Modals for canvas size calculations * fix: migrate label position test failing issue (#18365) fixed migrate label postition test failing issue Co-authored-by: Ankur Singhal * removed the two unwanted imports from DSLMigrations to fix client build * fix: Auto height zero and limits issue (#18366) fixed the auto height zero and limits issue Co-authored-by: Ankur Singhal * fix: Auto height regression issues (#18367) * Fix auto height regression issues #18367 * feat: auto height migrations (#18368) Add auto height migrations * Increase file caching size * Use manual array for list of auto height enabled widgets * Fix cypress test dsl versions * Revert changes to shouldUpdateHeightDynamically * Update test results based on code changes * Marginally increase the workbox file size cache * review comment incorporated for test spec * Update container auto height property on drop * Disable auto height with limits for modal Co-authored-by: Ankur Singhal Co-authored-by: rahulramesha Co-authored-by: Abhinav Jha Co-authored-by: Ankur Singhal Co-authored-by: Ankur Singhal Co-authored-by: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Co-authored-by: rahulramesha <71900764+rahulramesha@users.noreply.github.com> Co-authored-by: Albin Co-authored-by: Aswath K Co-authored-by: NandanAnantharamu <67676905+NandanAnantharamu@users.noreply.github.com> Co-authored-by: Apple * chore: Moved height property to General section (#18402) * fix: Height property is moved to General section * Logs an Error if section is not General * fix: Cypress * enabled auto height for all the widgets except json form * removes accidentally committed files Co-authored-by: Ankur Singhal * disabled default auto height for input widgets and select widgets and datepicker widget * disable feature at generalConfig instead of firstConfig * add todo comment * feat: Disable auto height for widgets in List Widget (#18381) * Remove auto height for container Inside List widget * remove unneccessary changes * disable feature at generalConfig instead of firstConfig * add todo comment * skipped the get min limit tests * fixed AutoHeightLimitHandleDot tests * list widget remove autoLayoutContainer for list widget template children Co-authored-by: Ankur Singhal Co-authored-by: Abhinav Jha Co-authored-by: Abhinav Jha Co-authored-by: Ankur Singhal Co-authored-by: Ankur Singhal Co-authored-by: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Co-authored-by: Albin Co-authored-by: Aswath K Co-authored-by: NandanAnantharamu <67676905+NandanAnantharamu@users.noreply.github.com> Co-authored-by: Apple Co-authored-by: Arsalan Yaldram * Update top contributors * Add binding parameters' count to analytics data on execute action event (#18418) * fix: Using an html tag within a Text widget to access a web page, without the https:// appended causes application to crash (#18321) * fix url issue in text widget * add the fix for modal text too * add cypress test * remove target blank; * feat: embed settings (#16629) * chore: Context switching - Api right pane tabs (#17473) * feat: updatedRow property should point to the last edited row in table widget (#18225) * fix: changed the updatedRow logic * fix: removed only modifier from inline spec * fix: addressed review comments * chore: removed the downloads.html file * fix: address review comments * ci: Setup ted for perf tests (#18439) * Setup ted for perf tests * Update perf-test.yml Co-authored-by: Satish Gandham * fix: Refresh datasource structure on save (#18290) * fix: Refresh datasource structure on save * added test case * update for test failing on release * chore: bump postgresql from 42.4.1 to 42.4.3 in /app/server/appsmith-plugins/postgresPlugin (#18407) chore: bump postgresql in /app/server/appsmith-plugins/postgresPlugin Bumps [postgresql](https://github.com/pgjdbc/pgjdbc) from 42.4.1 to 42.4.3. - [Release notes](https://github.com/pgjdbc/pgjdbc/releases) - [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md) - [Commits](https://github.com/pgjdbc/pgjdbc/compare/REL42.4.1...REL42.4.3) --- updated-dependencies: - dependency-name: org.postgresql:postgresql dependency-type: direct:production ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix: error message for input widget updated for when the default value is more than the max chars allowed (#17885) * bug fixed and test case written * removed the test case * changes to fix ui-test set status in the PR * fix: table scroll issue fixed (#18264) table scroll issue fixed Co-authored-by: “sneha122” <“sneha@appsmith.com”> * feat: add missing fusion chart (#18129) HLinearGauge changed to hlineargauge * fix: App card is created even when app import fails and no action can be performed on this app card (#18034) * Add loigc to delete the app created when the corrupted file is uploaded * Update error message to fix test failures * feat: add tableheader property to autocomplete in table (#18137) * tableheader logic initiated * autocomplete logic done * logic and test cases written * test cases updated * feedback incorporated * incorporated feedback * test cases reduced * test case altered to write number inplace of object * changes made in the cypress test * fix: api url z-index to show it above response panel (#18200) * chore: Switched to sequential checks instead of a parallel one for RTS check (#18440) fix: Switched to sequential checks instead of a parallel one * chore: Added size limit check for the message before sending the data to segment (#18453) * fix: Allowing multi form to json switching and eliminating json to form sw… (#18192) * Allowing multi form to json switching and eliminating json to form switching unless form data is cleared * Fix failing jest test case Co-authored-by: Aishwarya UR * Update top contributors * chore: bump minimatch from 3.0.4 to 3.1.2 in /deploy/docker/utils (#18344) Bumps [minimatch](https://github.com/isaacs/minimatch) from 3.0.4 to 3.1.2. - [Release notes](https://github.com/isaacs/minimatch/releases) - [Commits](https://github.com/isaacs/minimatch/compare/v3.0.4...v3.1.2) --- updated-dependencies: - dependency-name: minimatch dependency-type: indirect ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore: Reduce size of payload to data sent to Segment (#18467) * chore: Fix analytics NPE for checkout remote branch flow (#18466) * fix: added label tooltips, updated validation messages. * chore: Upgrading RTS dependencies (#18463) * chore: Moved tern server to worker (#18464) * Fixes autocomplete on first page load. * chore: Add role details in replica set error message (#18472) * fix: Switch to preview mode performance issue (#18457) * In progress fixes for auto height perf * Revert collapse logic * Revert changes * Remove console logs, and fix tests * Fix scenario where container widgets don't collapse * Bring back hidden widgets * fix: Overlapping of widgets while reflowing other widgets (#18460) * fix: api url z-index to show it above response panel (#18200) * chore: Switched to sequential checks instead of a parallel one for RTS check (#18440) fix: Switched to sequential checks instead of a parallel one * chore: Added size limit check for the message before sending the data to segment (#18453) * fix: Allowing multi form to json switching and eliminating json to form sw… (#18192) * Allowing multi form to json switching and eliminating json to form switching unless form data is cleared * Fix failing jest test case Co-authored-by: Aishwarya UR * add missed width and height limits instead of incrementing depth by 1 Co-authored-by: Aman Agarwal Co-authored-by: Nidhi Co-authored-by: Anagh Hegde Co-authored-by: Ayangade Adeoluwa <37867493+Irongade@users.noreply.github.com> Co-authored-by: Aishwarya UR * fix: auto height with limits deselect widget (#18455) * fix: api url z-index to show it above response panel (#18200) * chore: Switched to sequential checks instead of a parallel one for RTS check (#18440) fix: Switched to sequential checks instead of a parallel one * chore: Added size limit check for the message before sending the data to segment (#18453) * fix: Allowing multi form to json switching and eliminating json to form sw… (#18192) * Allowing multi form to json switching and eliminating json to form switching unless form data is cleared * Fix failing jest test case Co-authored-by: Aishwarya UR * refactor overlay and handles state into ui reducer and added a check for is limits changing in the widgets editor as well * added helpful comments at relevant places Co-authored-by: Aman Agarwal Co-authored-by: Nidhi Co-authored-by: Anagh Hegde Co-authored-by: Ayangade Adeoluwa <37867493+Irongade@users.noreply.github.com> Co-authored-by: Aishwarya UR Co-authored-by: Ankur Singhal * changes the label on limit handles collision to height * fix: issues related to tabs widget in auto height (#18468) * Fix issues related to tabs widget in auto height * Fix issue where preview mode canvas did not scroll * Fix scroll issues with fixed containers * Fix issue where tabs widget computed the canvas height incorrectly * Re-compute in case of tabs widget * fix: widgets increase spacing (#18462) * Change how the spacing works when widgets push or pull widgets below * Fix type issues in test file * Fix tests to reflect changes * Add comment to describe why we're generating distanceToNearestAbove Co-authored-by: rahulramesha <71900764+rahulramesha@users.noreply.github.com> Co-authored-by: Aman Agarwal Co-authored-by: Nidhi Co-authored-by: Anagh Hegde Co-authored-by: Ayangade Adeoluwa <37867493+Irongade@users.noreply.github.com> Co-authored-by: Aishwarya UR Co-authored-by: ankurrsinghal Co-authored-by: Ankur Singhal * fix: Theme Stylesheet refactor (#18258) * fix * move stylesheet to widget level * fix types * fix types * fix types * fix types ( thanks to ashit ) * fix type issue * add config for list widget * fix jest tests * fix: gsheet save and authorize redirect issue fixed (#18389) * gsheet save and authorize redirect issue fixed * cypress test code review issue fixed Co-authored-by: “sneha122” <“sneha@appsmith.com”> * fix: Known issues with Context Switching (#18470) * feat: Add type inference for redux's useSelector hook (#18257) Add type inference for redux's useSelector hook - Used module augmentation of TS to override the default root state interface - Replaced custom hook with redux's useSelector - It brings consistency as at a lot of places in the codebase we're using only the redux's useSelector * fix: rich text editor is buggy (#18332) bug fix and cypress tests * chore: update dependencies in the client package (#18254) * Update craco to latest version 7.0.0 and remove the alpha pre-release version. * chore: bump loader-utils from 1.4.0 to 1.4.2 in /app/client #18183 * Remove the following unused dependencies - caniuse-lite emoji-mart flow-bin instantsearch.js json-fn lodash-move react-base-table react-infinite-scroller react-mentions react-transition-group Fixes #18260 Co-authored-by: Aishwarya UR * feat: segregate entity permissions (#18384) * Update top contributors * chore: Minor code refactor to support changes around permissions and test cases in EE (#18563) * chore: fix flaky embed settings test (#18491) * chore: increase timeout * chore: wait for restart notice to not exist with a timeout * fix: Form message component link issue (#18231) * fix: Form message component link issue * removed the inverted flag for testing * fix: Made changes to pass action link element to design system component - formMessage * * Ds package stable version updated * chore: Fixed a failing assertion in page clone test (#18526) * fix: Added view mode param to returned application object (#18522) Co-authored-by: Aishwarya UR * fix: text font size for text widget inside modal & statbox. (#18236) fix: match font sizes for the text widgets inside modal, statbox to property pane values. Co-authored-by: Aishwarya UR * Updated Label Config * feat: Datasource autosave improvements (#17649) * Datasource autosave improvements WIP * authenticated API and ds name updates * popup updates, api to datasource updates * issue fixes for new ds in new workspace * formatter issue fixed * console warning issue fixed * refresh edge case handled for temp ds * DS creation cypress update * datasource improvements issue fixes * CreateDS flow change cypress update * reconnect issue fixed * added space * Create Ds related script updates * SaveDs changes updated * DatasourceForm_spec.js fix * GoogleSheetsQuery_spec.js - still spec will fail * GoogleSheetsStub_spec.ts fix * MongoDatasource_spec.js fix * ElasticSearchDatasource_spec.js fix * AuthenticatedApiDatasource_spec.js * RestApiDatasource_spec.js - will stil fail * RedshiftDataSourceStub_spec.js fix * issue fixes for datasource autosave * save as datasource issue fixed * SKipped - Bug 18035 * MySQL spec fix * PostgresDatasource_spec.js fix * MySQLDataSourceStub_spec.js fix * MsSQLDataSourceStub_spec.js fix * Bug16702_Spec.ts fix * SwitchDatasource_spec.js fix * ArangoDataSourceStub_spec.js fix * code review changes, save and authorise issue fixed * cypress test issue and cypress tests fixed * client build failure issue fixed * test failure fixes * ReconnectDatasource_spec.js fix * Entity_Explorer_CopyQuery_RenameDatasource_spec.js fix * GitImport_spec.js fix * Index add * undo redo test issue fixed * fixed cypress tests of rest api ds * globalsearch_spec.js fixed * code review changes * code review comments addressed * ds schema cypress issue fixed * cypress test updates * fix updateDatasource path * rest api cypress test fixed * cypress code review changes * Removing few random .only's * replay editor fix * indexed * adding .skip Co-authored-by: “sneha122” <“sneha@appsmith.com”> Co-authored-by: Aishwarya UR * fix: Appsmith version update dialog showing up when no version is set (#18530) Confirm an version number exists before checking for its match from server * fix: flicker issue when both the min max values collide (#18547) * fix: auto height limits container select (#18546) * fixed the issue of parent container select while changing the auto height with limits by adding one more check in the clickToSelectWidget hook * added a unit test to test shouldWidgetIgnoreClicksSelector based on whether we are changing the auto height with limits * fixed the failing tests Co-authored-by: Ankur Singhal * fix: geolocation api callbacks are not called (#18235) * fix: geolocation api callbacks are not called The success and error callbacks are not being called. The code was absent. fixes #11147 * Add comment * Fix error callback not being called when location is turned off * Fixes #9852 incorrect error handling on watchPosition * Fix unit test * fix unit tests * ci: fix-fat-failedspec (#18571) * chore: Adding additional optional props to editable text component (#18542) adding additional optional props to editable text component * test: fix merge_spec.js cypress test (#18587) * feat: Enable fetch API (#17832) * Update top contributors * feat: Dynamic Menu Items - Menu Button Widget (#17652) * faet: Add menu items source for menu widget * feat: Add configuration panel for dynamic menu items * feat: Pass down items from sourceData to menu items widget * feat: Take menu items config from property pane for dynamic menu items * fix: Change all onMenuItemClick to onClick for dynamic menu items * feat: Create MenuComputeValue property control to add support for {{currentItem}} binding in menu widget * feat: Add JS toggles for style properties for menu widget * feat: onClick now supports currentItem for menu button widget * feat: Add currentItem autocomplete, move property pane config to separate files for menu button widget * feat: WIP - Add Dynamic Menu Items for Table Widget * Revert "feat: WIP - Add Dynamic Menu Items for Table Widget" This reverts commit 271f96211c8612bc6f073a1aab7467993b9d7e36. * fix: remove current item label by default for dynamic menu items in menu button * feat: Add source data max length 10 validation for dynamic menu items in menu button * feat: Add migrations for Dynamic Menu Items for Menu Button Widget * feat: Add cypress test for dynamic menu items for menu button * test: Update DSLMigration test with menu button widget tests * fix: Update MenuButtonWidget migration * fix: DSL migrations for menu button dynmaic items * fix: Style validations for menu widget * feat: Add more descriptive help text for configure menu items in menu button widget * feat: Change menu items source property type from dropdown to icon tabs * fix: Cy test for menu button widget to select menu items source from button tabs instead of dropdown * feat: Make ConfigureMenuItemsControl a Generic/reusable OpenNextPanelWithButtonControl * refactor: Change MenuComputeValue to MenuButtonDynamicItemsControl * refactor: Merge TABLE_PROPERTY and MENU_PROPERTY into one ARRAY_AND_ANY_PROPERTY * fix: Don't polute Menu Button DSL with properties for dynamic menu items until the source is static * style: Change color of curly braces hint in currentItem autocomplete to make it more readable * fix: remove unused import * refactor: Move child config panels to a different file, style: Change help text and placeholder for a few properties for Dynamic menu items - menu button * refactor: Change event autocomplete function name, use fast equal * refactor: Change source data validation function name and use camelCase throughout * refactor: Validation function for source data * refactor: Create different type for menuItems and configureMenuItems and reuse them property config * feat: refactor: move get items to widget instead of component * pref: Visible items to be calculated when menu button is clicked * refactor: replace !("menuItemsSource" in child) with in migration * refactor: Change controlType name from OPEN_NEXT_PANEL_WITH_BUTTON to OPEN_CONFIG_PANEL, use generic names inside OpenNextPanelWithButtonControl.tsx * refactor: Minor cleanup at MenuButtonDynamicItemsControl.tsx * refactor: Minor cleanup at MenuButtonDynamicItemsControl.tsx * fix: Change constant used in migration to a static value * test: Add tests for validations and helper for menu button * test: Add more Cypress tests for dynamic-menu-items * fix: Minor refactor at onclick handler and MenuButtonDynamicItemsControl * refactor: Rename ARRAY_AND_ANY_PROPERTY to ARRAY_TYPE_OR_TYPE * feat: Move initial source data and keys generation inside an update hook * refactor: Rename ARRAY_TYPE_OR_TYPE to ARRAY_OF_TYPE_OR_TYPE * refactor: Minor code refactor in MenuButtonWidget/widget/index.tsx * refactor: Change OpenNextPanelWithButtonControl with OpenConfigPanelControl * feat: Use traverseDSLAndMigrate for dynamic menu items migration * style: Minor code hygiene changes here and there for dynamic menu items * style: Minor code hygiene changes here and there for dynamic menu items * style: remove any type for visible items inside dynamic menu items * refactor: Change type MenuItems to MenuItem * feat: Add support for dynamic menu items (menu button) inside list widget * fix: updateMenuItemsSource hook not working when changing from DYNAMIC to STATIC menu items source * fix: Avoid empty icon name from rendering inside button and menu item * style: Fix a couple of code callouts * fix: Update import from TernServer to CodemirrorTernService * style: fix minor code callouts here and there * fix: Add check for configureMenuItems.config * fix: Add wait time after addOption click for DynamicHeight_Auto_Height_spec.js * fix: Increase the wait time for DynamicHeight_Auto_Height_spec.js to 200ms Co-authored-by: Aishwarya UR * fix: miscellaneous issues in table widgets (#18127) - Retain filter in table widget when table data changes but schema remain same - Cursor jumps to start while editing a cell in Table Widget - Save/Discard option should not be in the filter dropdown list. Co-authored-by: Aishwarya UR * feat: Handle permission driven views for auto-saving pages and action… (#16950) Co-authored-by: Ankita Kinger Co-authored-by: Sangeeth Sivan <74818788+berzerkeer@users.noreply.github.com> Co-authored-by: Sangeeth Sivan Co-authored-by: akash-codemonk <67054171+akash-codemonk@users.noreply.github.com> Co-authored-by: Aishwarya UR * ci:update env variable (#18557) Co-authored-by: Shrikant Sharat Kandula * Revert "ci:update env variable" (#18607) Revert "ci:update env variable (#18557)" This reverts commit e0fe306503b473c8a80dc755cf2e2cfd3faadfca. * chore: add events related to roles and groups (#18611) * fix: multiselect crashing (#18577) * fix: page list item icons aren't permission driven on first load (#18586) * fix: page list ctas permission driven fix * fix: page list and page context menu , datasource action pane create permission driven * fix: check manage permission on page aswell for cloning * fix: duplicate & fork app permission driven * fix: onclick triggers on disabled icons * Update top contributors * feat: [ Context Switching ] Maintain datasource edit mode (#17521) * fix: Reduces cloning by not adding platform functions (#18433) reduces cloning * feat: New settings pane (#16361) * chore: Update NavigateTo tests to verify params (#18581) * fix: If the user has delete permission on the workspace, allow the user to delete the workspace and also delete the default roles (without permission) (#18615) * fix: Add user permission in the response for update datasource api (#18629) * fix: update permission driven ctas to handle auto save update changes (#18609) * chore: comment out flaky assertion (#18640) * chore: perf data tree shrink (#18361) trimming dataTree object size by removing configs to make evaluation faster. * Update top contributors * fix: datasource switching issue for Google sheets (#18397) * Fix datasource switching issue for Goole sheets * Fix failing vercel deployments * fix: center align select widgets and RTE widget labels. (#18378) * fix: center align select and RTE labels. * feat: added label tooltips to select widgets. * fix: added center label alignment to datepicker. * Test : DH regression test automation (#18436) * updated tab test * updated tests * Added some more scenarios * updated valdations for presence of Height property under general * Added tests for list * Updated tests * feat: Adding an upgrade page for Access control (#18554) * updated content for access control upgrade page * added feature flag and moved content to message constants * adding feature flag for RBAC * updated some styles * added a missing comma * removed width on trigger details container * updated some styles * fix: datasource title not editable and ctas disabled on create (#18645) fix: datasource title not editable on create flow, delete save ctas disabled on rest/graphql form * Update top contributors * chore: dependentbot vulnerabilities. (#18528) * fix: dependentbot vulnerabilities. * chore: remove emoji-mart. * chore: updated react-syntax-highlighter * chore: update ts-loader add loader-utils resolution. Co-authored-by: Aishwarya UR * fix: Added support for country code flags on windows devices (#18203) * fix: add pollyfill and use font family. * fix: use latest version design system. Co-authored-by: Aishwarya UR * fix: Updating spec for member roles (#18657) updated spec for member roles * feat: [Context Switching] Maintain datasource collapse state (#17616) * chore: Remove debugging logs for analytics (#18662) * chore: Support using custom port(s) for listening in the start-https.sh script (#18451) Added `--https-port` and `--http-port`, so you can set a custom port instead of the default 80 and 443. This is useful if you want to test Appsmith when using a non-standard port, fox example, how `appsmith.URL.host` behaves, or how OAuth redirects behave etc. We also add a shortcut to start this with release endpoint. Just using `release` is now the same thing as using `https://release.app.appsmith.com`. This is useful to people like me who are _very_ good with typos. * feat: Adding Embedding Apps Functionality in the API (#18566) * chore: fix ingress fieldname in appsmith (#18684) * fix: Auto height breaks for fixed modal's children (#18691) Fix issue where auto height was throwing an error when a fixed modal widget's children were changing height automatically * fix: datasource autosave git issue fixed (#18690) * datasource autosave git issue fixed * Added appropriate comments Co-authored-by: “sneha122” <“sneha@appsmith.com”> * chore: Improved metadata on logs when reading DSL fails (#18692) * chore: Improved metadata on logs when reading DSL fails * Fixed test * Test fix * Review comment * Update top contributors * Fix NPE on reset password, and message formats (#18108) Signed-off-by: Shrikant Sharat Kandula * ci: Update perf infra dashboard link inside github workflows yml (#18702) * test: Add dummy users in Cypress test suite (#18675) * chore: Fix dropdown font family (#18693) ## Description - Fix dropdown font family. Fixes #18677 ## Type of change > Please delete options that are not relevant. - Bug fix (non-breaking change which fixes an issue) ## How Has This Been Tested? - Manual ### Test Plan > Add Testsmith test cases links that relate to this PR ### Issues raised during DP testing > Link issues raised during DP testing for better visiblity and tracking (copy link from comments dropped on this PR) ## Checklist: ### Dev activity - [ ] My code follows the style guidelines of this project - [ ] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [ ] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] PR is being merged under a feature flag ### QA activity: - [ ] Test plan has been approved by relevant developers - [ ] Test plan has been peer reviewed by QA - [ ] Cypress test cases have been added and approved by either SDET or manual QA - [ ] Organized project review call with relevant stakeholders after Round 1/2 of QA - [ ] Added Test Plan Approved label after reveiwing all Cypress test * fix: Remove redundant share CTA from org menu (#18718) * chore: Fetch pages in editor with READ permissions (#18722) * test: Added test for bug fixed (#18135) * Added test for bug fixed * Commented a test * fix: add js for icon property in button group (#18524) made icon js convertible * Update top contributors * fix: table widget header links should have pointer cursor when hovered (#18262) * fix: show filter & show download properties should not control add new row link visibility in table widget (#18348) * chore: bump decode-uri-component from 0.2.0 to 0.2.2 in /app/util/plugin-generation (#18658) Bumps [decode-uri-component](https://github.com/SamVerschueren/decode-uri-component) from 0.2.0 to 0.2.2.
Release notes

Sourced from decode-uri-component's releases.

v0.2.2

  • Prevent overwriting previously decoded tokens 980e0bf

https://github.com/SamVerschueren/decode-uri-component/compare/v0.2.1...v0.2.2

v0.2.1

  • Switch to GitHub workflows 76abc93
  • Fix issue where decode throws - fixes #6 746ca5d
  • Update license (#1) 486d7e2
  • Tidelift tasks a650457
  • Meta tweaks 66e1c28

https://github.com/SamVerschueren/decode-uri-component/compare/v0.2.0...v0.2.1

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=decode-uri-component&package-manager=npm_and_yarn&previous-version=0.2.0&new-version=0.2.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) - `@dependabot use these labels` will set the current labels as the default for future PRs for this repo and language - `@dependabot use these reviewers` will set the current reviewers as the default for future PRs for this repo and language - `@dependabot use these assignees` will set the current assignees as the default for future PRs for this repo and language - `@dependabot use this milestone` will set the current milestone as the default for future PRs for this repo and language You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/appsmithorg/appsmith/network/alerts).
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore: bump decode-uri-component from 0.2.0 to 0.2.2 in /app/client (#18659) * chore: bump decode-uri-component in /app/util/plugin-generation Bumps [decode-uri-component](https://github.com/SamVerschueren/decode-uri-component) from 0.2.0 to 0.2.2. - [Release notes](https://github.com/SamVerschueren/decode-uri-component/releases) - [Commits](https://github.com/SamVerschueren/decode-uri-component/compare/v0.2.0...v0.2.2) --- updated-dependencies: - dependency-name: decode-uri-component dependency-type: indirect ... Signed-off-by: dependabot[bot] * chore: bump decode-uri-component from 0.2.0 to 0.2.2 in /app/client Bumps [decode-uri-component](https://github.com/SamVerschueren/decode-uri-component) from 0.2.0 to 0.2.2. - [Release notes](https://github.com/SamVerschueren/decode-uri-component/releases) - [Commits](https://github.com/SamVerschueren/decode-uri-component/compare/v0.2.0...v0.2.2) --- updated-dependencies: - dependency-name: decode-uri-component dependency-type: indirect ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Arsalan * fix: workspace context menu is not permission driven (#18642) * fix: workspace context menu is now permission driven * fix: remove action operation on global search if there are no file operations * fix: hide more options when there are no options and refactor * refactor: revert settings title changes in this PR * fix: use manage permission for delete on CE * fix: move delete workspace permission to EE * fix: change in permissions * Remove redundant share CTA from org menu * fix: change of permissions for members and import/create new app * Remove redundant invite users dialog * Remove unused variables Co-authored-by: hetunandu * Update label-checker to add new bypass Add skip-testPlan label bypass in the test plan approved label checker * chore: bump qs from 6.5.2 to 6.5.3 in /app/client (#18733) Bumps [qs](https://github.com/ljharb/qs) from 6.5.2 to 6.5.3. - [Release notes](https://github.com/ljharb/qs/releases) - [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md) - [Commits](https://github.com/ljharb/qs/compare/v6.5.2...v6.5.3) --- updated-dependencies: - dependency-name: qs dependency-type: indirect ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: hetunandu * chore: bump express from 4.17.1 to 4.18.2 in /app/client (#18734) Bumps [express](https://github.com/expressjs/express) from 4.17.1 to 4.18.2. - [Release notes](https://github.com/expressjs/express/releases) - [Changelog](https://github.com/expressjs/express/blob/master/History.md) - [Commits](https://github.com/expressjs/express/compare/4.17.1...4.18.2) --- updated-dependencies: - dependency-name: express dependency-type: indirect ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: hetunandu * ci: Improve the test file check workflow (#18741) * fix: Aggregate calls to add and remove Appsmith errors (#18285) * Aggregate calls to add and remove Appsmith errors * fix switch type * Fix entity deletion errors * Code clean up * fix cypress tests * Return early for empty errors in Appsmith debugger * Remove redundant identifier * Retain order of debugger logs * Use push instead of unshift for perf reasons * redundant commit to retrigger CI * feat: update property name automatically when custom column name is changed (#18048) * feat: update button states (#18335) * fix: Form message component link issue * removed the inverted flag for testing * use updated design system * category.tertiary -> category.secondary * fix: Made changes to pass action link element to design system component - formMessage * chore: update design-system version to access new tertiary button styles * chore: update design-system version to use blacks instead of grays * chore: bump design-system version to use buttons with explicit background color * Remove rules that mess with border, background, text color on StyledPropertyPaneButton * chore: update design-system version to use updated button categories across all ads components Co-authored-by: Albin * chore: rename_triggerUtils (#18405) Refactored Renamed TriggerUtils.java to RestAPIActivateUtils.java and its usage * fix: resetting audio and video widget (#18126) * bug for audio and video widget fixed * cypress test for audio widget * audio and video updated cypress tests * cypress test cases updated * writing logic for video widget * working on video widget * intermediate logic * inbetween logic * updateWidgetMetaProperty removed from onEnded event * this.props.playing logic in onPlay & onPause events * test code * test code * Add comment * audio widget updated; comments for audio and video widget * cypress changes * removed cypress credentials * comments updated * change in comments and cypress test cases * feedback incorporated * comments added to audio and video spec files * video spec updated * video spec updated Co-authored-by: balajisoundar * Update top contributors * fix: fork template tracking (#18727) fix: fork_APPLICATIONTEMPLATE event tracking added to merge template api (#18416) * fix: fetching the plugin form config for queries if datasource is not viewable (#18749) fetching the plugin form config for queries if datasource is not viewable * refactor: Updating upgrade flows to start using upgrade hook for better code reusability (#18735) * updated upgrade flow for authentication page to start using upgrade hook * using the upgrade hook on upgrade pages as well * adding RBAC feature flag * feat: [Context Switching] Complex widgets and other Phase 2 items (#17046) This PR contains changes to retain context on the following items, Leaving and then returning to a page should maintain what api/query was open Entity explorer should stay as you left it when you left the page (collapse levels) Widget/explorer tab Width - Should be the same across all pages of an app Property Pane width Complex widgets, multi tier property panes Co-authored-by: hetunandu Co-authored-by: Akash N Co-authored-by: Hetu Nandu Co-authored-by: Aishwarya UR * feat: Command click navigation via code (#18326) * fix: Corrected action execution analytics event (#18783) * fix: gs quick wizard table header index (#18242) * fix: gs quick wizard table header index * fix: disable generate button if error * Omni spex fix Co-authored-by: Aishwarya UR * chore: Updating the text from enterprise to business to maintain consistency (#18787) * updating the text from enterprise to business to maintain consistency * minor change * Omnibar fix Co-authored-by: Aishwarya UR * Update top contributors * test: Automated tests for Bug18376 + few flaky fixes (#18769) * ApiOnload spec fix * Bug 18376 script added * Bug 18376 script fix * ValidateURL() added * OnLoadTests/APIOnLoad_Spec.ts fix * Moved Bug 16248 script * JSOnload trial fix * JsonLoad fix -disbaling not working hnce commented * Skipping GitImport (merge issue-open bug 18776) * Git imPort trial fix * PassingParams spec update * APIPage.ts wrong update corrected * Removed added comments * ApiPage.ts mistake corrected! * chore: update smartlook client (#18784) update smartlook client * feat: Add Series Colour property for default charts (#18229) * Adds functionality for Series Color property * fix an issue where chart was breaking when color property is not provided * Changes Series Color field to Color Picker type input Default color set as primary theme color * Under development - Some bug fixes to the series colour feature * Fix issue with second series color not being applied correctly New property placeholderText for ColorPickerComponentV2 Disable series color feature for Pie Chart Code clean up * chore: Add multipart payload support for admin settings (#18434) * fix: datasource discard popup issue fixed (#18761) * datasource discard popup issue fixed * cypress code review changes Co-authored-by: “sneha122” <“sneha@appsmith.com”> * fix: hide workspace menu options trigger when there are no permissions (#18742) * fix: variable mismatch fix * fix: updated to use the right boolean flag * fix: workspace menu order * fix: gsheet multiline broken formatting fixed (#18552) * gsheet multiline broken formatting fixed * junit test added Co-authored-by: “sneha122” <“sneha@appsmith.com”> * fix: Filtering out empty value env variables from /admin/env API response in backend (#18430) * Filtering out empty value env variables from /admin/env API response in backend * adding UT for getAllNonEmpty; * ci: Ship source maps + fix perf test failures on EE (#18700) * - Add missing env variables to fix perf tests failures in EE * - Delete source maps on CI only for EE * - Add the additional steps that might be required for running perf tests on EE * Update if condition * Update if condition * Switch to bash from using sh in build.sh * Check if the server started later * Update the check to see if the server has started Co-authored-by: Satish Gandham * feat: Branding (#18717) * add branding for ce * add ce changes * update colorpicker ux * remove unsued flag * Add new email templates Signed-off-by: Shrikant Sharat Kandula * add local appsmith logo * code review feedback fixes + qa fixes * remove forward slash in url of favicon * fix message * Fix tests Signed-off-by: Shrikant Sharat Kandula * update messages * Fix tests (again) Signed-off-by: Shrikant Sharat Kandula * update messages * fix cypress tests * skipping app layout test cases * fix cypress tests * remove it.only * try moving test * use stable DS version * remove __diff Signed-off-by: Shrikant Sharat Kandula Co-authored-by: Shrikant Sharat Kandula Co-authored-by: Aishwarya UR * fix: query command value from being reset to an empty state (#18559) * Fixes query command value from being reset to an empty state * Fix merge issues * test: fix flaky tests (#18603) * Update GitImport_spec.js * fix flaky tests * skip git import tests Co-authored-by: Parthvi Goswami * Update top contributors * Fix logos in email templates (#18835) Signed-off-by: Shrikant Sharat Kandula * chore: splitting the role name to avoid showing workspace name next to it (#18830) * splitting the role name to avoid showing workspace name next to it * fixing cypress tests failures * chore: add some more audit logs analytics (#18738) * Fix: Context switching UX issues (#18828) * fix Property panel jitters on focus * open widget entity on widget selection * Remove retaining Datasources url on page change * fix param string while restoring URLs * add cypress test * Update top contributors * chore: Synchronize Page,Action and ActionCollection object from original corresponding object (#18846) Synchronize Page,Action and ActionCollection object from original corresponding object * fix: Left panel not scrollable on Admin settings page when the list is too long (#18845) * feat: Property pane search (#18164) * fix: modal deselect issue auto height (#18750) modal deselect issue fixed when dynamicHeight property is changed * Update top contributors * Updated Label Config * chore: add analytics for page creation ctas (#18676) * refactor: JSONForm eliminate the use of canvasWidgets and remove childStylesheet from dynamicBindingPathList (#18715) * refactor: JSONForm eliminate the use of canvasWidgets and remove childStylesheet from dynamicBindingPathList * review changes * fixed field reset on deploy * reverted prevSourceData changes * fix: update settings pane validations (#18739) * init fix * button style update * decode url in preview * bring back custom slug validation * custom slug validation on type * remove imports * handle special characters in url preview * change label * code clean up * remove pageId from action name selector * messages clean up * tests for validations * cypress cases update * Renamed helper methods * add index for test cases * AppSettings specs script ts updates * Added Bud id in title #18698 * GIt Import spec fix * GItImport spec fix * Git import unskip * Skipping Git import Co-authored-by: Aishwarya UR * fix: Refactor entities based on AST parsing logic (#18517) * feat: Refactor entities based on AST parsing logic * Deleted jmh file * rts updates for the edge cases * adding jest test cases * update review comments * Fixed issue with references outside of bindings and some other stuff * Added tests for DSLUtils * bug fix 18699 * Test fixes * Test fixes * Review comments * Changed type to boxed Co-authored-by: ChandanBalajiBP Co-authored-by: ChandanBalajiBP <104058110+ChandanBalajiBP@users.noreply.github.com> * feat: [APPSMTH-22] Execute action on widget focus and blur (#18128) Co-authored-by: balajisoundar Co-authored-by: Aishwarya UR * chore: update import to fix compilation issue (#18875) * chore: Fix potential race condition in sending email (#18882) Signed-off-by: Shrikant Sharat Kandula Signed-off-by: Shrikant Sharat Kandula * fix: Updating the logic for getting tenant permissions to show new workspace button (#18874) * chore: Fix more potential race condition in sending emails (#18884) Signed-off-by: Shrikant Sharat Kandula * ci: Add replicate set to the mongodb url for transaction PR (#18873) * ci: revert Add replicate set to the mongodb url for transaction PR (#18890) Revert "ci: Add replicate set to the mongodb url for transaction PR (#18873)" This reverts commit bc8022caf218ef20b2cac2ad0e5a2213a2d3d8dc. * fix: Parse js actions for view mode changes (#18357) * added new function inside parse js actions for view moe * fixing test cases * added evaluateSync with isTriggered false * Add types Add switch case for diff.event Add function get back app mode * Change foreach loops to for of * Dont return jsUpdates from view mode save resolved since no operation is happening Change viewModeSaveResolvedFunctionsAndJSUpdates -> viewModeSaveResolvedFunctions to reflect what it's functionality is * Refactor JSObject code * Refactor code for JSobject * remove any * export parsed object's type from ast * create function for deleteResolvedFunctionsAndCurrentJSCollectionState * Code review changes * Fix tests * Change returns in for of loops to continue Remove try for evaluate sync Js updates return * Fix bug * Fix bug Co-authored-by: Rimil Dey Co-authored-by: Aishwarya UR * docs: Added a title to GlobalFunctions.md (#18617) * chore: rename actions for REST and GraphQL in datasource pane (#18114) * Update top contributors * ci: Add three more runners for the fat container tests (#18915) * fix: Execute api pagination fix (#17886) Query parameters encoding parameter added for paginated prev and next url. * fix: publishing an app removes page permissions (#18865) * fix: page permissions for deployed app * fix: use the selector with select * fix: datasource active tab options menu is not permission driven (#18863) * fix: ds active tab menu * fix: condition * ci: Fix perf test failure due to a wrong step added earlier (#18918) Fix perf test failure due to a wrong step added earlier Co-authored-by: Satish Gandham * test: Migration to FatContainer (new .yml faile) (#18917) ## Description - This PR adds a new fat-migration.yml file which will run all cases in fat container instead of dev.app.appsmith - This PR will not affect any existing Push/Workflow dispatches as its a separate action file - Moving to fat container will not be put into action until RBAC is live - so as to not trigger any new challenge at last minute ## Type of change - New .yml file ## How Has This Been Tested? - Cypress CI runs ## Checklist: ### QA activity: - [X] Test plan has been approved by relevant developers - [X] Test plan has been peer reviewed by QA - [X] Cypress test cases have been added and approved by either SDET or manual QA - [X] Organized project review call with relevant stakeholders after Round 1/2 of QA - [X] Added Test Plan Approved label after reveiwing all Cypress test * fix: Modal opening or closing type error (#18913) fix for sometimes widget not available * fix: treat empty valued env variables as false | FE (#18428) * converting empty env values to boolean before storing in store * triming and checking empty values * Empty commit for test Co-authored-by: Anubhav * feat: add tooltip property to select widgets (#18480) * fix: Login password length upper limit changed from 48 to 256 characters (#18176) * fix - login password length upper limit removed * removed unused variables * password max limit of 256 characters Co-authored-by: Anubhav * fix: Error handling for undefined datasource in API Authentication (#18881) * ci: Fix perf tests failure (#18926) * Fix perf test failure due to a wrong step added earlier * Remove all the steps added to make perf tests work on EE Co-authored-by: Satish Gandham * fix: removed old key_val_input, updated keyval_array, show ds info (#18626) fix: removed old key_val_input, updated keyval_array, show ds info * Update top contributors * chore: use read permission to fetch pages in edit mode (#18944) * fix: show more modal of a truncated text widget is flickering (#18888) class for updated z-index added * chore: decomposing action execution flow methods into smaller methods (#18762) * chore: use updated table widget in onboarding (#18705) * chore: Updating UI for the left panel on Admin settings page by adding separators (#18895) * chore: Adding variable to track admin settings visibility in user profile (#18956) * ci: Make perf tests a blocking check (#18955) Make perf tests a blocking check Co-authored-by: Satish Gandham * fix: Updating page permissions in view mode to solve error message for updating the page (#18946) * chore: bump tinymce from 5.10.2 to 5.10.7 in /app/client (#18792) * fix: update delete workspace permission and condition case (#18930) handles delete option edgecase: when user has the delete permission but workspace has applications, user should not be able to delete the workspace. This commit hides the option. * Update top contributors * fix: modal widget close on new widget drop (#18953) * chore: Navigation analytics (#18781) * fix: replace Appsmith's mock server endpoint with MockWebServer (#18943) * chore: Splitted import export service code for V2 (#18927) * chore: Adding adminSettingsVisible variable in User type (#18961) adding adminSettingsVisible variable in User type * chore: updated supertest package. (#18909) * chore: updated supertest package. * chore: dependencies list upgraded. * feat: reset meta widgets (#18809) * Initial commit * reset meta widgets * Annotate code * Rehydrate meta values for meta widgets when dirty * Undo rehydration logic * fix: App deleted when the user creates branch in few scenarios (#18964) ## Description > Continuing from the PR - https://github.com/appsmithorg/appsmith/pull/18034. The app getting deleted bug was introduced when creating branch was introduced in the linked PR. Current PR adds a check to ensure that none of the git flow is affected. Fixes - https://app.frontapp.com/inboxes/teams/folders/15585868/unassigned/66043703436 ## Type of change - Bug fix (non-breaking change which fixes an issue) ## How Has This Been Tested? - Manual ## Checklist: ### Dev activity - [ ] My code follows the style guidelines of this project - [ ] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [ ] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] PR is being merged under a feature flag ### QA activity: - [ ] Test plan has been approved by relevant developers - [ ] Test plan has been peer reviewed by QA - [ ] Cypress test cases have been added and approved by either SDET or manual QA - [ ] Organized project review call with relevant stakeholders after Round 1/2 of QA - [ ] Added Test Plan Approved label after reveiwing all Cypress test * chore: method signature changed for getCachedDatasourceForActtionExecution (#18950) ## Description > To accommodate for datasource context adaption for environments, Some datasourceService methods needs to be aware of `environment name` in order to evaluate the `datasource configuration` for the given environment > TL; DR : This Pr changes method signature for fetching datasource by actionId Fixes #18760 Ref #18762 ## Type of change > Chore ## How Has This Been Tested? - Manual - Jest ## Checklist: ### Dev activity - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [ ] PR is being merged under a feature flag * chore: Update Logo/Favicon url + remove EE cypress code for Branding + Update screenshots tests (#18872) * add new screenshot of apppagelayout + update logo url + remove ee code in spec * fix error message for logo * revert profile snap * remove brand colors in ce * fix auth page layout issue * revert some snap images * fix visual tests * fix cypress tests * fix cypress tests * docs: added "How Appsmith Works" image to README (#18967) * Add "How Appsmith Works" image * Add "How it works" image to README.md * docs: make "How Appsmith Works" image full width * chore: Updating the version of the design system dependency (#18965) updating the version of design system dependency * chore: Property pane search input focus (#18858) * moving changes to content config * Property pane changes for responsive behaviour config. Signed-off-by: dependabot[bot] Signed-off-by: Shrikant Sharat Kandula Co-authored-by: Nikhil Nandagopal Co-authored-by: Appsmith Bot <74705725+appsmith-bot@users.noreply.github.com> Co-authored-by: Pranay <48308728+Pranay105@users.noreply.github.com> Co-authored-by: Pranay105 Co-authored-by: yatinappsmith <84702014+yatinappsmith@users.noreply.github.com> Co-authored-by: Abhinav Jha Co-authored-by: Ankur Singhal Co-authored-by: rahulramesha Co-authored-by: Abhinav Jha Co-authored-by: Ankur Singhal Co-authored-by: Ankur Singhal Co-authored-by: rahulramesha <71900764+rahulramesha@users.noreply.github.com> Co-authored-by: Albin Co-authored-by: Aswath K Co-authored-by: NandanAnantharamu <67676905+NandanAnantharamu@users.noreply.github.com> Co-authored-by: Apple Co-authored-by: Aishwarya-U-R <91450662+Aishwarya-U-R@users.noreply.github.com> Co-authored-by: Goutham Pratapa Co-authored-by: Anagh Hegde Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: akash-codemonk <67054171+akash-codemonk@users.noreply.github.com> Co-authored-by: Arsalan Yaldram Co-authored-by: Parthvi Goswami Co-authored-by: subratadeypappu Co-authored-by: Pawan Kumar Co-authored-by: Keyur Paralkar Co-authored-by: Satish Gandham Co-authored-by: Satish Gandham Co-authored-by: ChandanBalajiBP <104058110+ChandanBalajiBP@users.noreply.github.com> Co-authored-by: Rishabh Kashyap Co-authored-by: sneha122 Co-authored-by: “sneha122” <“sneha@appsmith.com”> Co-authored-by: Aman Agarwal Co-authored-by: Nidhi Co-authored-by: Ayangade Adeoluwa <37867493+Irongade@users.noreply.github.com> Co-authored-by: Aishwarya UR Co-authored-by: Arpit Mohan Co-authored-by: arunvjn <32433245+arunvjn@users.noreply.github.com> Co-authored-by: Shrikant Sharat Kandula Co-authored-by: Ravi Kumar Prasad Co-authored-by: Nilesh Sarupriya Co-authored-by: Trisha Anand Co-authored-by: albinAppsmith <87797149+albinAppsmith@users.noreply.github.com> Co-authored-by: Hetu Nandu Co-authored-by: Ankita Kinger Co-authored-by: Parthvi <80334441+Parthvi12@users.noreply.github.com> Co-authored-by: Dhruvik Neharia Co-authored-by: balajisoundar Co-authored-by: Sangeeth Sivan <74818788+berzerkeer@users.noreply.github.com> Co-authored-by: Sangeeth Sivan Co-authored-by: Tolulope Adetula <31691737+Tooluloope@users.noreply.github.com> Co-authored-by: Apeksha Bhosale <7846888+ApekshaBhosale@users.noreply.github.com> Co-authored-by: Anand Srinivasan <66776129+eco-monk@users.noreply.github.com> Co-authored-by: Rishabh Rathod Co-authored-by: Nilansh Bansal Co-authored-by: Rhitottam Co-authored-by: hetunandu Co-authored-by: Favour Ohanekwu Co-authored-by: Souma Ghosh <103924539+souma-ghosh@users.noreply.github.com> Co-authored-by: Tanvi Bhakta Co-authored-by: appsmithguru <116803038+appsmithguru@users.noreply.github.com> Co-authored-by: Akash N Co-authored-by: Vishnu Gp Co-authored-by: Nirmal Sarswat <25587962+vivonk@users.noreply.github.com> Co-authored-by: f0c1s Co-authored-by: sidhantgoel Co-authored-by: ashit-rath Co-authored-by: ChandanBalajiBP Co-authored-by: GitStart-Appsmith <114667275+gitstart-appsmith@users.noreply.github.com> Co-authored-by: Rimil Dey Co-authored-by: Rooney30 <103701369+Rooney30@users.noreply.github.com> Co-authored-by: Bhuvaneswari Pathsamatla Co-authored-by: Vaibhav Tanwar <40293928+vaibh1297@users.noreply.github.com> Co-authored-by: Ankit Srivastava <67647761+ankitsrivas14@users.noreply.github.com> Co-authored-by: Manish Kumar <107841575+sondermanish@users.noreply.github.com> Co-authored-by: Sumit Kumar Co-authored-by: Tejas Ahluwalia --- .../--documentation-improvement.yaml | 37 - .github/config.json | 2 +- .github/workflows/fat-migration.yml | 782 ++++ .../workflows/integration-tests-command.yml | 72 +- .github/workflows/label-checker.yml | 2 +- .github/workflows/perf-test.yml | 49 +- .github/workflows/perf-tests-command.yml | 2 +- .github/workflows/pr-labeler.yml | 19 +- .github/workflows/pr-test-file-check.yml | 33 + .github/workflows/test-build-docker-image.yml | 38 +- CONTRIBUTING.md | 2 +- README.md | 52 +- app/client/build.sh | 10 +- app/client/craco.build.config.js | 2 +- app/client/cypress.env.json | 6 +- .../Bugs/CheckboxGroupInListWidgetDsl.json | 857 ++++ .../cypress/fixtures/ContextSwitching.json | 2 +- .../DynamicHeightDefaultHeightdsl.json | 157 + .../alignmentWithDynamicHeightDsl.json | 426 ++ .../fixtures/datePickerV2Updated_dsl.json | 80 + .../dynamicHeightCanvasResizeDsl.json | 302 ++ .../dynamicHeightContainerCheckboxdsl.json | 170 + .../dynamicHeightContainerScrolldsl.json | 165 + .../fixtures/dynamicHeightContainerdsl.json | 109 + .../fixtures/dynamicHeightFormSwitchdsl.json | 481 +++ .../fixtures/dynamicHeightListDsl.json | 634 +++ .../fixtures/dynamicHeightStatboxdsl.json | 281 ++ .../dynamicHeigthContainerFixedDsl.json | 262 ++ .../cypress/fixtures/dynamicTabWidgetdsl.json | 448 +++ .../cypress/fixtures/editorContextdsl.json | 466 ++- app/client/cypress/fixtures/example.json | 23 + app/client/cypress/fixtures/exportedApp.json | 429 +- app/client/cypress/fixtures/formResetDsl.json | 880 +++-- .../cypress/fixtures/invisibleWidgetdsl.json | 287 ++ .../fixtures/jsonFormDynamicHeightDsl.json | 776 ++++ .../cypress/fixtures/menuButtonDsl.json | 149 +- .../fixtures/multipleContainerdsl.json | 344 ++ .../selectMultiSelectTreeSelectWidgetDsl.json | 236 ++ app/client/cypress/fixtures/testdata.json | 1 + .../fixtures/textWidgetDynamicdsl.json | 77 + .../cypress/fixtures/widgetPopupDsl.json | 1 + .../Application/EchoApiCMS_spec.js | 3 +- .../ImportExportForkApplication_spec.js | 2 +- .../Application/MongoDBShoppingCart_spec.js | 2 +- .../Application/PgAdmin_spec.js | 2 +- .../Application/ReconnectDatasource_spec.js | 5 +- .../ActionExecution/ClearStore_spec.ts | 48 +- .../ActionExecution/NavigateTo_spec.ts | 12 +- .../Bind_JSObject_Postgress_Table_spec.js | 2 +- ..._TableV2_Widget_API_Derived_Column_spec.js | 1 + ...nd_Table_Widget_API_Derived_Column_spec.js | 3 +- .../Binding/Widget_loading_spec.js | 2 +- .../Binding/autocomplete_spec.js | 3 + .../ClientSideTests/BugTests/Bug16248_spec.ts | 19 - .../ClientSideTests/BugTests/Bug16702_Spec.ts | 10 +- .../ClientSideTests/BugTests/Bug18664_spec.ts | 24 + .../ClientSideTests/BugTests/Bug18876_Spec.ts | 25 + .../ClientSideTests/BugTests/Bug9334_Spec.ts | 7 +- .../BugTests/DatasourceSchema_spec.ts | 24 + .../ClientSideTests/BugTests/GitBugs.ts | 66 + .../ClientSideTests/BugTests/Moment_Spec.ts | 24 +- .../DynamicHeight_Auto_Height_Limit_spec.js | 49 + .../DynamicHeight_Auto_Height_spec.js | 79 + .../DynamicHeight_CanvasHeight_resize_spec.js | 78 + .../DynamicHeight_Container_Scroll_spec.js | 16 + ...Height_Container_collapse_undo_redoSpec.js | 32 + ...ynamicHeight_Form_With_SwitchGroup_spec.js | 95 + .../DynamicHeight_JsonForm_spec.js | 62 + .../DynamicHeight/DynamicHeight_List_spec.js | 38 + .../DynamicHeight_Multiple_Container_spec.js | 71 + .../DynamicHeight/DynamicHeight_Tab_spec.js | 120 + .../DynamicHeight_Text_Widget_spec.js | 42 + ...micHeight_Text_With_Different_Size_spec.js | 138 + .../DynamicHeight_Visibility_spec.js | 52 + .../Entity_Explorer_API_Pane_spec.js | 16 +- ...xplorer_CopyQuery_RenameDatasource_spec.js | 37 +- ...tity_Explorer_Datasource_Structure_spec.js | 9 +- .../Entity_Explorer_DragAndDropWidget_spec.js | 1 - ...y_Paste_Delete_Undo_Keyboard_Event_spec.js | 2 +- .../ExplorerTests/Pages_spec.js | 8 +- .../ExplorerTests/Scrolling_Spec.ts | 3 +- .../FormNativeToRawTests/Mongo_spec.ts | 2 +- .../GitDiscardChange/DiscardChanges_spec.js | 9 +- .../Git/GitImport/GitImport_spec.js | 47 +- .../{Deploy_spec.js => Deploy_spec.ts} | 22 +- .../Git/GitSync/GitSyncedApps_spec.js | 12 +- .../ClientSideTests/Git/GitSync/Merge_spec.js | 1 + ...=> PreconnectionAppNameDeployMenu_spec.ts} | 22 +- .../Git/GitWithTheming/GitWithTheming_spec.js | 9 + .../IDE/Canvas_Context_Bug_Fixes.js | 38 + .../IDE/Canvas_Context_Property_Pane_spec.js | 195 +- .../Canvas_Context_Selected_Widgets_spec.js | 48 + .../IDE/Command_Click_Navigation_spec.js | 109 + .../IDE/MaintainContext&Focus_spec.js | 60 + .../ClientSideTests/Linting/BasicLint_spec.ts | 3 +- .../Onboarding/GuidedTour_spec.js | 9 +- .../OtherUIFeatures/ExportApplication_spec.js | 3 - .../OtherUIFeatures/GlobalSearch_spec.js | 25 +- .../OtherUIFeatures/Omnibar_spec.js | 11 +- .../OtherUIFeatures/PreviewMode_spec.js | 1 + .../OtherUIFeatures/Replay_Editor_spec.js | 12 +- .../OtherUIFeatures/Resize_spec.js | 2 +- .../PropertyPane/PropertyPane_Search_spec.ts | 187 + .../SettingsPane/GeneralSettings_spec.ts | 43 + .../SettingsPane/PageSettings_spec.ts | 101 + .../Fork_Template_Existing_app_spec.js | 5 +- .../Templates/Fork_Template_To_App_spec.js | 1 + .../ThemingTests/Basic_spec.js | 32 +- .../ThemingTests/ThemeReset_spec.js | 6 + .../ThemingTests/Theme_Default_spec.js | 16 +- .../ThemingTests/Theme_FormWidget_spec.js | 16 +- .../Theme_MultiSelectWidget_spec.js | 19 +- .../VisualTests/AppPageLayout_spec.js | 3 +- .../VisualTests/WidgetsLayout_spec.js | 9 +- .../Widgets/Audio/audio_spec.js | 30 + .../Button/ButtonLintErrorValidation_spec.js | 8 +- .../Button/Button_onClickAction_spec.js | 5 - .../Datepicker/DatePickerV2Updated_spec.js | 27 + .../Widgets/Datepicker/DatePickerV2_spec.js | 22 + .../Datepicker/DatePicker_With_Switch_spec.js | 4 +- .../Dropdown/Dropdown_onOptionChange_spec.js | 12 +- .../FilePickerV2_Widget_Reskinning_spec.js | 16 + .../Widgets/Filepicker/FilePickerV2_spec.js | 9 +- .../Widgets/Form/FormReset_spec.js | 41 +- .../Widgets/Input/Input_MaxChar_spec.js | 4 +- .../Input/Input_OnFocus_OnBlur_spec.js | 63 + .../JSONForm/JSONForm_ArrayField_spec.js | 12 +- .../JSONForm/JSONForm_CustomField_spec.js | 1 + .../JSONForm/JSONForm_FieldProperties_spec.js | 4 +- .../JSONForm/JSONForm_FormBindings_spec.js | 22 +- .../JSONForm/JSONForm_HiddenFields_spec.js | 32 +- ...JSONForm_NestedField_Select_Multiselect.js | 12 +- .../JSONForm/JSONForm_UnicodeKeys_spec.js | 6 +- .../List/ListWidgetLintErrorValidation.js | 8 +- .../ClientSideTests/Widgets/Modal_spec.js | 2 + .../Widgets/Multiselect/MultiSelect4_spec.js | 12 +- .../Multiselect/Multi_Select_Tree_spec.js | 11 + .../Widgets/Others/Divider_spec.js | 2 +- .../Widgets/Others/MenuButton_spec.js | 115 +- .../Others/StatBox_DragAndDrop_spec.js | 28 + .../Widgets/Others/Statbox_spec.js | 11 +- .../Widgets/Others/Video_spec.js | 31 + .../Widgets/RTE/RichTextEditor_spec.js | 38 +- ...eSelect_MultiSelect_OnFocus_OnBlur_spec.js | 97 + .../Widgets/Select/Select_spec.js | 9 + .../Widgets/Select/Single_Select_Tree_spec.js | 9 + .../Widgets/Sliders/NumberSlider_spec.ts | 12 +- .../Widgets/Sliders/RangeSlider_spec.ts | 16 +- .../Tab/Tab_OnEvent_Navigation_spec.js | 37 + .../Table_Button_Icon_validation_spec.js | 6 +- ...ble_Derived_Column_Data_validation_spec.js | 1 + .../TableV1/Table_GeneralProperty_spec.js | 2 +- .../Table_PropertyPane_IconName_spec.js | 6 +- .../TableV1/Table_PropertyPane_spec.js | 16 +- .../TableV1/Table_Widget_Add_button_spec.js | 17 +- .../Widgets/TableV1/Table_spec.js | 2 +- .../Widgets/TableV2/Add_new_row_spec.js | 50 +- .../TableV2/Custom_column_alias_spec.js | 1 + .../Widgets/TableV2/Image_resize_spec.js | 1 + .../Widgets/TableV2/Inline_editing_spec.js | 101 + ...eV2_Derived_Column_Data_validation_spec.js | 2 + .../TableV2/TableV2_PropertyPane_spec.js | 37 + .../TableV2/TableV2_Widget_Add_button_spec.js | 3 + .../TableV2/TableV2_Widget_Copy_Paste_spec.js | 2 +- .../Widgets/TableV2/TableV2_spec.js | 130 +- .../TextWidget_LintErrorValidation_spec.js | 8 +- .../Widgets/Text/Text_new_feature_spec.js | 9 + .../Workspace/DeleteWorkspace_spec.js | 3 +- .../Workspace/LeaveWorkspaceTest_spec.js | 10 +- .../Workspace/LoginFromUIApp_spec.js | 4 - .../Workspace/MemberRoles_Spec.ts | 24 +- .../Workspace/ShareAppTests_spec.js | 5 +- .../ApiTests/API_Styles_spec.js | 6 +- .../Datasources/ArangoDataSourceStub_spec.js | 13 +- .../AuthenticatedApiDatasource_spec.js | 40 +- .../Datasources/DatasourceForm_spec.js | 22 +- .../ElasticSearchDatasource_spec.js | 10 +- .../Datasources/GoogleSheetsStub_spec.ts | 4 +- .../Datasources/MongoDatasource_spec.js | 4 +- .../Datasources/MsSQLDataSourceStub_spec.js | 29 +- .../Datasources/MySQLDataSourceStub_spec.js | 30 +- .../ServerSideTests/Datasources/MySQL_spec.js | 21 +- .../Datasources/PostgresDatasource_spec.js | 26 +- .../RedshiftDataSourceStub_spec.js | 18 +- .../Datasources/RestApiDatasource_spec.js | 9 +- .../RestApiOAuth2Validation_spec.js | 12 +- .../ServerSideTests/Datatypes/MySQL_Spec.ts | 26 +- .../Datatypes/MySQL_false_Spec.ts | 1 + .../GenerateCRUD/Mongo_Spec.ts | 10 +- .../GenerateCRUD/MySQL2_Spec.ts | 1 + .../GenerateCRUD/Postgres1_Spec.ts | 6 +- .../ServerSideTests/GenerateCRUD/S3_Spec.js | 4 +- .../JsFunctionExecution/Fetch_Spec.ts | 91 + .../OnLoadTests/APIOnLoad_Spec.ts | 2 + .../OnLoadTests/JSOnLoad1_Spec.ts | 127 +- .../JSOnLoad_cyclic_dependency_errors_spec.js | 9 +- .../OnLoadTests/OnLoadActions_Spec.ts | 2 + .../Params/ExecutionParams_spec.js | 2 +- .../Params/PassingParams_Spec.ts | 23 +- .../Postgres_DataTypes/Array_Spec.ts | 8 +- .../Postgres_DataTypes/Binary_Spec.ts | 8 +- .../Postgres_DataTypes/BooleanEnum_Spec.ts | 10 +- .../Postgres_DataTypes/Character_Spec.ts | 6 +- .../Postgres_DataTypes/DateTime_Spec.ts | 7 +- .../Postgres_DataTypes/Json_Spec.ts | 10 +- .../Postgres_DataTypes/Numeric_Spec.ts | 6 +- .../Postgres_DataTypes/UUID_Spec.ts | 19 +- .../QueryPane/AddWidgetTableAndBind_spec.js | 2 +- .../QueryPane/AddWidget_spec.js | 2 +- .../QueryPane/ConfirmRunAction_spec.js | 2 +- .../ServerSideTests/QueryPane/DSDocs_Spec.ts | 12 + .../QueryPane/EmptyDataSource_spec.js | 2 +- .../QueryPane/GoogleSheetsQuery_spec.js | 53 +- .../ServerSideTests/QueryPane/Mongo_Spec.js | 1 + .../ServerSideTests/QueryPane/Mongo_Spec.ts | 18 +- .../QueryPane/Postgres_Spec.js | 2 +- .../QueryPane/SwitchDatasource_spec.js | 18 - .../ClientSideTests/Branding/Branding_spec.js | 101 + .../EmbedSettings/EmbedSettings_spec.js | 145 + app/client/cypress/locators/AdminsSettings.js | 1 + app/client/cypress/locators/FormWidgets.json | 4 + app/client/cypress/locators/Widgets.json | 5 + .../cypress/locators/commonlocators.json | 23 +- .../cypress/locators/gitSyncLocators.js | 2 + .../CommentedScriptFiles/MsSQL_Spec.js | 4 +- .../cypress/manual_TestSuite/Modal_Spec.js | 15 +- .../AppPageLayout_spec.js/apppage.snap.png | Bin 75309 -> 59581 bytes .../emptyAppBuilder.snap.png | Bin 86421 -> 59581 bytes .../AppPageLayout_spec.js/loginpage.snap.png | Bin 25163 -> 35931 bytes .../inlineDisabled.snap.png | Bin 2086 -> 4689 bytes .../inlineEnabled.snap.png | Bin 4282 -> 4308 bytes .../cypress/support/AdminSettingsCommands.js | 10 + .../cypress/support/Objects/CommonLocators.ts | 11 +- .../cypress/support/Objects/ObjectsCore.ts | 13 + .../cypress/support/Objects/Registry.ts | 44 +- .../cypress/support/Pages/AggregateHelper.ts | 80 +- app/client/cypress/support/Pages/ApiPage.ts | 34 +- .../support/Pages/AppSettings/AppSettings.ts | 117 + .../Pages/AppSettings/GeneralSettings.ts | 58 + .../support/Pages/AppSettings/PageSettings.ts | 145 + .../Pages/AppSettings/ThemeSettings.ts | 41 + .../support/Pages/AppSettings/Utils.ts | 28 + .../cypress/support/Pages/DataSources.ts | 166 +- .../cypress/support/Pages/EntityExplorer.ts | 16 +- app/client/cypress/support/Pages/GitSync.ts | 138 +- app/client/cypress/support/Pages/HomePage.ts | 40 +- .../cypress/support/Pages/PropertyPane.ts | 77 +- .../cypress/support/WorkspaceCommands.js | 5 + app/client/cypress/support/commands.js | 15 +- .../cypress/support/dataSourceCommands.js | 41 +- app/client/cypress/support/gitSync.js | 2 + app/client/cypress/support/index.js | 10 + app/client/cypress/support/widgetCommands.js | 195 +- .../generators/widget/templates/index.js.hbs | 3 + app/client/package.json | 28 +- app/client/public/index.html | 4 +- app/client/public/manifest.json | 2 +- .../public/static/img/appsmith-logo.svg | 19 + .../{ => static/img}/favicon-orange.ico | Bin app/client/src/AppRouter.tsx | 59 +- app/client/src/LandingScreen.tsx | 2 +- app/client/src/RouteBuilder.ts | 7 +- app/client/src/RouteParamsMiddleware.ts | 3 +- app/client/src/actions/apiPaneActions.ts | 7 + .../src/actions/appSettingsPaneActions.ts | 16 + app/client/src/actions/applicationActions.ts | 8 + app/client/src/actions/autoHeightActions.ts | 4 +- app/client/src/actions/controlActions.tsx | 2 + app/client/src/actions/datasourceActions.ts | 98 +- app/client/src/actions/debuggerActions.ts | 22 +- app/client/src/actions/editorActions.ts | 7 +- .../src/actions/editorContextActions.ts | 138 +- app/client/src/actions/focusHistoryActions.ts | 24 +- app/client/src/actions/globalSearchActions.ts | 5 - app/client/src/actions/metaActions.ts | 4 +- app/client/src/actions/pageActions.tsx | 51 +- app/client/src/actions/propertyPaneActions.ts | 44 +- app/client/src/actions/recentEntityActions.ts | 5 - app/client/src/actions/widgetActions.tsx | 10 + .../src/actions/widgetSelectionActions.ts | 17 +- app/client/src/api/ApplicationApi.tsx | 32 +- app/client/src/api/PageApi.tsx | 18 +- .../prevent-accidental-damage.png | Bin 0 -> 37071 bytes .../restrict-public-exposure.png | Bin 0 -> 44906 bytes .../secure-apps-least-privilege.png | Bin 0 -> 58967 bytes app/client/src/ce/actions/settingsAction.ts | 7 +- app/client/src/ce/constants/ApiConstants.tsx | 2 + .../src/ce/constants/ReduxActionConstants.tsx | 57 +- app/client/src/ce/constants/messages.test.ts | 2 +- app/client/src/ce/constants/messages.ts | 133 +- .../src/ce/pages/AdminSettings/LeftPane.tsx | 60 +- .../src/ce/pages/AdminSettings/Main.tsx | 16 +- .../config/authentication/AuthPage.tsx | 44 +- .../config/branding/UpgradeBanner.tsx | 51 + .../AdminSettings/config/branding/index.tsx | 15 + .../config/branding/useBrandingForm.tsx | 13 + .../ce/pages/AdminSettings/config/general.tsx | 82 + .../ce/pages/AdminSettings/config/index.ts | 2 + .../ce/pages/AdminSettings/config/types.ts | 34 +- .../src/ce/pages/AdminSettings/index.tsx | 18 +- .../pages/AppViewer/BackToHomeButton.tsx | 2 +- .../Upgrade/AccessControlUpgradePage.tsx | 70 +- .../ce/pages/Upgrade/AuditLogsUpgradePage.tsx | 22 +- app/client/src/ce/pages/Upgrade/Carousel.tsx | 13 +- app/client/src/ce/pages/Upgrade/Header.tsx | 7 +- .../src/ce/pages/Upgrade/UpgradePage.tsx | 11 +- app/client/src/ce/pages/UserAuth/Login.tsx | 52 +- app/client/src/ce/pages/UserAuth/SignUp.tsx | 43 +- app/client/src/ce/pages/workspace/Members.tsx | 11 +- .../workspace/WorkspaceInviteUsersForm.tsx | 4 +- app/client/src/ce/reducers/index.tsx | 4 + app/client/src/ce/reducers/tenantReducer.ts | 23 +- .../uiReducers/applicationsReducer.tsx | 15 +- app/client/src/ce/sagas/SuperUserSagas.tsx | 30 +- app/client/src/ce/sagas/index.tsx | 4 +- app/client/src/ce/sagas/tenantSagas.tsx | 5 +- .../src/ce/selectors/tenantSelectors.tsx | 19 + .../src/ce/utils/adminSettingsHelpers.ts | 14 + app/client/src/ce/utils/permissionHelpers.tsx | 49 +- .../autoHeight/AutoHeightContainer.test.tsx | 7 +- .../autoHeight/AutoHeightContainer.tsx | 34 +- .../autoHeight/AutoHeightContainerWrapper.tsx | 51 + .../AutoHeightLimitHandleGroup.tsx | 164 + .../components/autoHeightOverlay/constants.ts | 5 + .../src/components/autoHeightOverlay/hooks.ts | 144 + .../components/autoHeightOverlay/index.tsx | 398 ++ .../src/components/autoHeightOverlay/store.ts | 100 + .../src/components/autoHeightOverlay/types.ts | 10 + .../ui/AutoHeightLimitHandleBorder.test.tsx | 22 + .../ui/AutoHeightLimitHandleBorder.tsx | 24 + .../ui/AutoHeightLimitHandleDot.test.tsx | 22 + .../ui/AutoHeightLimitHandleDot.tsx | 23 + .../ui/AutoHeightLimitHandleLabel.test.tsx | 21 + .../ui/AutoHeightLimitHandleLabel.tsx | 23 + .../ui/AutoHeightLimitOverlayDisplay.test.tsx | 28 + .../ui/AutoHeightLimitOverlayDisplay.tsx | 20 + .../src/components/autoHeightOverlay/utils.ts | 15 + .../designSystems/appsmith/ModalComponent.tsx | 7 +- .../appsmith/autoLayout/FlexComponent.tsx | 12 +- .../appsmith/header/DeployLinkButton.tsx | 8 +- .../viewComponents/TextView/index.tsx | 2 +- .../editorComponents/ActionNameEditor.tsx | 2 + .../ActionRightPane/SuggestedWidgets.tsx | 3 +- .../ActionRightPane/index.tsx | 12 +- .../editorComponents/ApiResponseView.tsx | 5 +- .../components/editorComponents/Button.tsx | 10 +- .../CodeEditor/EditorConfig.ts | 8 +- .../CodeEditor/EvaluatedValuePopup.tsx | 4 +- .../CodeEditor/commandsHelper.ts | 2 +- .../CodeEditor/generateQuickCommands.tsx | 2 +- .../CodeEditor/hintHelpers.ts | 14 +- .../editorComponents/CodeEditor/index.tsx | 177 +- .../CodeEditor/markHelpers.ts | 41 + .../CodeEditor/styledComponents.ts | 15 +- .../editorComponents/Debugger/EntityLink.tsx | 4 +- .../Debugger/hooks/debuggerHooks.ts | 13 +- .../editorComponents/Debugger/index.tsx | 3 +- .../editorComponents/DropTargetComponent.tsx | 187 +- .../editorComponents/EditableText.tsx | 29 +- .../editorComponents/ErrorTooltip.tsx | 2 +- .../GlobalSearch/GlobalSearchHooks.tsx | 98 +- .../GlobalSearch/SnippetsDescription.tsx | 2 +- .../editorComponents/GlobalSearch/index.tsx | 61 +- .../GlobalSearch/useRecentEntities.tsx | 19 +- .../editorComponents/GlobalSearch/utils.tsx | 5 +- .../editorComponents/PreviewModeComponent.tsx | 22 - .../editorComponents/PropertyPaneSidebar.tsx | 65 +- .../editorComponents/ResizableComponent.tsx | 105 +- .../editorComponents/ResizableUtils.test.ts | 125 + .../{ResizableUtils.tsx => ResizableUtils.ts} | 27 + .../ResizeStyledComponents.tsx | 12 +- .../editorComponents/StoreAsDatasource.tsx | 12 +- .../form/ToggleComponentToJson.tsx | 46 +- .../form/fields/DropdownFieldWrapper.tsx | 2 + .../form/fields/DropdownWrapper.tsx | 2 + .../fields/EmbeddedDatasourcePathField.tsx | 11 +- .../form/fields/KeyValueFieldArray.tsx | 2 +- .../form/fields/RequestDropdownField.tsx | 1 + .../form/fields/SelectField.tsx | 2 + .../__tests__/KeyValueFieldArray.test.tsx | 4 +- .../formControls/KeyValueArrayControl.tsx | 98 +- .../formControls/KeyValueInputControl.tsx | 117 - .../src/components/formControls/utils.test.ts | 2 +- .../src/components/formControls/utils.ts | 29 +- .../propertyControls/ButtonControl.tsx | 2 +- .../propertyControls/ButtonListControl.tsx | 9 +- .../propertyControls/ChartDataControl.tsx | 30 +- .../ColorPickerComponentV2.test.tsx | 7 + .../ColorPickerComponentV2.tsx | 37 +- .../ColumnActionSelectorControl.tsx | 2 +- .../DraggableListComponent.tsx | 2 +- .../FieldConfigurationControl.tsx | 13 +- .../propertyControls/KeyValueComponent.tsx | 2 +- .../MenuButtonDynamicItemsControl.tsx | 201 + .../propertyControls/MenuItemsControl.tsx | 9 +- .../propertyControls/NumericInputControl.tsx | 6 + .../OpenConfigPanelControl.tsx | 74 + .../PrimaryColumnsControl.tsx | 9 +- .../PrimaryColumnsControlV2.tsx | 9 +- .../propertyControls/StyledControls.tsx | 28 +- .../propertyControls/TabControl.tsx | 12 +- .../src/components/propertyControls/index.ts | 7 + app/client/src/config.d.ts | 8 + app/client/src/constants/AppConstants.ts | 3 +- .../ActionConstants.tsx | 2 + .../src/constants/CanvasEditorConstants.tsx | 1 + app/client/src/constants/Colors.tsx | 5 +- app/client/src/constants/Datasource.ts | 2 + app/client/src/constants/DefaultTheme.tsx | 54 +- app/client/src/constants/Layers.tsx | 2 + .../constants/PropertyControlConstants.tsx | 34 +- app/client/src/constants/WidgetConstants.tsx | 32 +- app/client/src/constants/WidgetValidation.ts | 2 +- app/client/src/constants/defs/browser.json | 118 + app/client/src/constants/routes/appRoutes.ts | 3 +- app/client/src/constants/userConstants.ts | 2 + .../config/branding/UpgradeBanner.tsx | 3 + .../AdminSettings/config/branding/index.tsx | 1 + .../config/branding/useBrandingForm.tsx | 1 + .../ee/pages/AppViewer/BackToHomeButton.tsx | 3 + app/client/src/entities/Action/index.ts | 1 + app/client/src/entities/AppTheming/index.ts | 43 +- .../src/entities/AppTheming/utils.test.ts | 37 +- app/client/src/entities/AppTheming/utils.ts | 82 +- .../src/entities/DataTree/dataTreeAction.ts | 33 +- .../src/entities/DataTree/dataTreeFactory.ts | 161 +- .../DataTree/dataTreeJSAction.test.ts | 202 +- .../src/entities/DataTree/dataTreeJSAction.ts | 33 +- .../entities/DataTree/dataTreeWidget.test.ts | 125 +- .../src/entities/DataTree/dataTreeWidget.ts | 77 +- app/client/src/entities/DataTree/types.ts | 127 + app/client/src/entities/DataTree/utils.ts | 2 +- app/client/src/entities/Datasource/index.ts | 1 + .../src/entities/Engine/AppEditorEngine.ts | 8 +- app/client/src/entities/JSCollection/index.ts | 1 + .../Replay/ReplayEntity/ReplayCanvas.ts | 1 + .../src/entities/URLRedirect/URLAssembly.ts | 44 +- app/client/src/entities/Widget/utils.test.ts | 44 +- app/client/src/index.css | 4 + app/client/src/index.tsx | 24 +- app/client/src/navigation/FocusElements.ts | 128 +- app/client/src/navigation/FocusEntity.test.ts | 48 +- app/client/src/navigation/FocusEntity.ts | 99 +- .../src/pages/AppViewer/AppViewerHeader.tsx | 7 +- app/client/src/pages/AppViewer/index.tsx | 7 +- .../pages/Applications/ApplicationCard.tsx | 20 +- .../Applications/ForkApplicationModal.tsx | 13 +- .../Applications/ImportApplicationModal.tsx | 3 +- .../ImportApplicationModalOld.tsx | 3 +- app/client/src/pages/Applications/index.tsx | 287 +- .../Editor/APIEditor/ApiAuthentication.tsx | 26 +- .../pages/Editor/APIEditor/ApiRightPane.tsx | 27 +- .../Editor/APIEditor/CommonEditorForm.tsx | 24 +- .../APIEditor/GraphQL/VariableEditor.tsx | 2 +- .../src/pages/Editor/APIEditor/Pagination.tsx | 4 +- .../pages/Editor/APIEditor/PostBodyData.tsx | 2 +- .../AppSettings/DraggablePageList.tsx | 102 + .../AppSettings/GeneralSettings.tsx | 143 + .../AppSettings/PageSettings.tsx | 381 ++ .../AppSettings/SectionHeader.tsx | 47 + .../AppSettingsPane/AppSettings/index.tsx | 162 + .../Components/TextLoaderIcon.tsx | 33 + .../Editor/AppSettingsPane/PaneHeader.tsx | 45 + .../src/pages/Editor/AppSettingsPane/Utils.ts | 53 + .../pages/Editor/AppSettingsPane/index.tsx | 33 + .../pages/Editor/BottomBar/ManualUpgrades.tsx | 32 +- .../src/pages/Editor/BottomBar/index.tsx | 10 +- app/client/src/pages/Editor/Canvas.tsx | 4 +- .../pages/Editor/CanvasPropertyPane/index.tsx | 58 +- .../Editor/DataSourceEditor/Collapsible.tsx | 97 +- .../Editor/DataSourceEditor/Connected.tsx | 27 +- .../pages/Editor/DataSourceEditor/DBForm.tsx | 53 +- .../DataSourceEditor/DatasourceSection.tsx | 192 +- .../Editor/DataSourceEditor/FormTitle.tsx | 46 +- .../Editor/DataSourceEditor/JSONtoForm.tsx | 51 +- .../DataSourceEditor/NewActionButton.tsx | 5 +- .../RestAPIDatasourceForm.tsx | 134 +- .../SaveOrDiscardDatasourceModal.tsx | 76 + .../pages/Editor/DataSourceEditor/index.tsx | 299 +- .../EditorAppName/NavigationMenuData.ts | 84 +- .../EditorAppName/NavigationMenuItem.tsx | 8 +- .../src/pages/Editor/EditorAppName/index.tsx | 6 - app/client/src/pages/Editor/EditorHeader.tsx | 11 +- .../Editor/Explorer/Actions/ActionEntity.tsx | 21 +- .../Actions/ActionEntityContextMenu.tsx | 136 +- .../Explorer/Actions/MoreActionsMenu.tsx | 124 +- .../src/pages/Editor/Explorer/Datasources.tsx | 39 +- .../Datasources/DataSourceContextMenu.tsx | 79 +- .../Explorer/Datasources/DatasourceEntity.tsx | 30 +- .../Datasources/DatasourceStructure.tsx | 23 +- .../pages/Editor/Explorer/Entity/index.tsx | 53 +- .../Editor/Explorer/EntityExplorer.test.tsx | 78 +- .../pages/Editor/Explorer/EntityExplorer.tsx | 8 + .../pages/Editor/Explorer/Files/Submenu.tsx | 48 +- .../src/pages/Editor/Explorer/Files/index.tsx | 21 +- .../JSActions/JSActionContextMenu.tsx | 131 +- .../Explorer/JSActions/JSActionEntity.tsx | 20 +- .../Explorer/JSActions/MoreJSActionsMenu.tsx | 162 +- .../Explorer/Pages/AddPageContextMenu.tsx | 7 + .../Editor/Explorer/Pages/PageContextMenu.tsx | 120 +- .../src/pages/Editor/Explorer/Pages/index.tsx | 88 +- .../pages/Editor/Explorer/TreeDropdown.tsx | 2 + .../Explorer/Widgets/WidgetContextMenu.tsx | 23 +- .../Editor/Explorer/Widgets/WidgetEntity.tsx | 21 +- .../Editor/Explorer/Widgets/WidgetGroup.tsx | 12 +- .../Explorer/Widgets/useNavigateToWidget.ts | 19 +- .../pages/Editor/Explorer/Widgets/utils.ts | 11 +- .../src/pages/Editor/Explorer/common.tsx | 15 +- .../src/pages/Editor/Explorer/helpers.tsx | 7 +- app/client/src/pages/Editor/Explorer/hooks.ts | 5 +- .../src/pages/Editor/Explorer/index.tsx | 24 +- .../src/pages/Editor/Explorer/mockTestData.ts | 224 ++ .../FirstTimeUserOnboarding/Checklist.tsx | 3 +- .../IntroductionModal.tsx | 2 +- .../Editor/FirstTimeUserOnboarding/Tasks.tsx | 3 +- .../GeneratePageForm/GeneratePageForm.tsx | 4 +- .../GeneratePageForm/GoogleSheetForm.tsx | 6 +- .../components/GeneratePageForm/hooks.ts | 2 + .../GlobalHotKeys/GlobalHotKeys.test.tsx | 6 +- .../src/pages/Editor/GuidedTour/Boxed.tsx | 2 +- .../Editor/GuidedTour/DeviationModal.tsx | 2 +- .../src/pages/Editor/GuidedTour/app.json | 677 ++-- .../IntegrationEditor/ActiveDataSources.tsx | 11 + .../IntegrationEditor/DatasourceCard.tsx | 138 +- .../IntegrationEditor/DatasourceHome.tsx | 10 +- .../IntegrationsHomeScreen.tsx | 55 +- .../pages/Editor/IntegrationEditor/NewApi.tsx | 20 +- .../UnsupportedPluginDialog.tsx | 2 +- app/client/src/pages/Editor/JSEditor/Form.tsx | 32 +- .../Editor/JSEditor/JSFunctionSettings.tsx | 13 +- .../Editor/JSEditor/JSObjectNameEditor.tsx | 2 + .../src/pages/Editor/MainContainer.test.tsx | 5 +- app/client/src/pages/Editor/MainContainer.tsx | 17 +- .../Editor/MainContainerLayoutControl.tsx | 5 +- .../Editor/PageListSidebar/PageListItem.tsx | 120 - .../pages/Editor/PagesEditor/ContextMenu.tsx | 222 -- .../src/pages/Editor/PagesEditor/EditName.tsx | 128 - .../pages/Editor/PagesEditor/PageListItem.tsx | 325 -- .../src/pages/Editor/PagesEditor/index.tsx | 174 - .../Editor/PropertyPane/ConnectDataCTA.tsx | 2 +- .../PropertyPane/DraggableListControl.tsx | 59 + .../Editor/PropertyPane/EmptySearchResult.tsx | 6 +- .../PropertyPane/PanelPropertiesEditor.tsx | 144 +- .../Editor/PropertyPane/PropertyControl.tsx | 112 +- ...ator.tsx => PropertyControlsGenerator.tsx} | 112 +- .../Editor/PropertyPane/PropertyHelpLabel.tsx | 12 +- .../PropertyPane/PropertyPaneSearchInput.tsx | 107 + .../Editor/PropertyPane/PropertyPaneTab.tsx | 22 +- .../PropertyPaneTitle.test.tsx | 0 .../{ => PropertyPane}/PropertyPaneTitle.tsx | 38 +- .../Editor/PropertyPane/PropertyPaneView.tsx | 129 +- .../Editor/PropertyPane/PropertySection.tsx | 93 +- .../pages/Editor/PropertyPane/helpers.test.ts | 204 - .../src/pages/Editor/PropertyPane/helpers.ts | 58 +- .../PropertyPane/propertyPaneSearch.test.ts | 359 ++ .../Editor/PropertyPane/propertyPaneSearch.ts | 201 + .../Editor/QueryEditor/EditorJSONtoForm.tsx | 57 +- .../src/pages/Editor/QueryEditor/Table.tsx | 5 +- .../src/pages/Editor/QueryEditor/index.tsx | 13 +- .../pages/Editor/RequestConfirmationModal.tsx | 2 +- .../Editor/SaaSEditor/DatasourceCard.tsx | 13 +- .../Editor/SaaSEditor/DatasourceForm.tsx | 336 +- .../SaaSEditor/__data__/FinalState.json | 2 +- .../SaaSEditor/__data__/InitialState.json | 2 +- .../ThemePropertyPane/DeleteThemeModal.tsx | 39 +- .../ThemePropertyPane/SaveThemeModal.tsx | 72 +- .../ThemePropertyPane/ThemeBetaCard.tsx | 2 +- .../Editor/ThemePropertyPane/ThemeEditor.tsx | 297 +- .../controls/ThemeColorControl.tsx | 3 + .../controls/ThemeFontControl.tsx | 3 + .../WidgetsEditor/EmptyCanvasSection.tsx | 12 +- .../WidgetsEditor/PropertyPaneContainer.tsx | 14 +- .../src/pages/Editor/WidgetsEditor/index.tsx | 13 +- .../__tests__/QueryEditorTable.test.tsx | 20 + .../Editor/gitSync/QuickGitActions/index.tsx | 2 +- .../gitSync/ReconnectDatasourceModal.tsx | 2 +- .../gitSync/RepoLimitExceededErrorModal.tsx | 2 +- .../gitSync/components/ConflictInfo.tsx | 2 +- .../Editor/gitSync/components/DefaultTag.tsx | 2 +- .../gitSync/components/DeployPreview.tsx | 2 +- .../gitSync/components/GitConnectError.tsx | 2 +- app/client/src/pages/Editor/index.tsx | 27 +- app/client/src/pages/Editor/routes.tsx | 7 - .../src/pages/Home/LeftPaneBottomSection.tsx | 14 +- .../Settings/FormGroup/Accordion.test.tsx | 6 +- .../pages/Settings/FormGroup/Button.test.tsx | 16 +- .../src/pages/Settings/FormGroup/Button.tsx | 7 +- .../src/pages/Settings/FormGroup/Checkbox.tsx | 2 +- .../pages/Settings/FormGroup/ColorInput.tsx | 217 + .../pages/Settings/FormGroup/Group.test.tsx | 7 +- .../pages/Settings/FormGroup/ImageInput.tsx | 106 + .../pages/Settings/FormGroup/Link.test.tsx | 7 +- .../src/pages/Settings/FormGroup/Link.tsx | 5 +- .../pages/Settings/FormGroup/Radio.test.tsx | 80 + .../src/pages/Settings/FormGroup/Radio.tsx | 216 + .../Settings/FormGroup/TagInputField.test.tsx | 7 +- .../Settings/FormGroup/TagInputField.tsx | 2 +- .../pages/Settings/FormGroup/Text.test.tsx | 7 +- .../src/pages/Settings/FormGroup/Text.tsx | 2 +- .../Settings/FormGroup/TextInput.test.tsx | 3 +- .../pages/Settings/FormGroup/Toggle.test.tsx | 7 +- .../pages/Settings/FormGroup/common.test.tsx | 7 +- .../src/pages/Settings/FormGroup/group.tsx | 61 +- .../src/pages/Settings/RestartBanner.tsx | 2 +- .../src/pages/Settings/SaveSettings.tsx | 2 +- .../src/pages/Settings/WithSuperUserHoc.tsx | 3 +- app/client/src/pages/Settings/components.tsx | 2 +- .../Settings/config/branding/BrandingPage.tsx | 93 + .../Settings/config/branding/SettingsForm.tsx | 171 + .../config/branding/previews/AppPreview.tsx | 28 + .../branding/previews/DashboardPreview.tsx | 35 + .../branding/previews/DashboardThumbnail.tsx | 22 + .../config/branding/previews/EmailPreview.tsx | 56 + .../branding/previews/FaviconPreview.tsx | 33 + .../config/branding/previews/LinkPreview.tsx | 23 + .../config/branding/previews/LoginPreview.tsx | 50 + .../branding/previews/NotFoundPreview.tsx | 37 + .../config/branding/previews/PreviewBox.tsx | 29 + .../config/branding/previews/index.tsx | 74 + .../src/pages/Templates/ForkTemplate.tsx | 2 +- app/client/src/pages/UserAuth/Container.tsx | 38 + app/client/src/pages/UserAuth/FooterLinks.tsx | 45 +- .../src/pages/UserAuth/ForgotPassword.tsx | 30 +- .../src/pages/UserAuth/ResetPassword.tsx | 47 +- .../src/pages/UserAuth/StyledComponents.tsx | 1 - app/client/src/pages/UserAuth/index.tsx | 43 +- app/client/src/pages/common/AppHeader.tsx | 3 +- app/client/src/pages/common/AppRoute.tsx | 61 - .../common/CanvasArenas/StickyCanvasArena.tsx | 7 +- .../hooks/useAutoLayoutHighlights.ts | 2 +- .../hooks/useBlocksToBeDraggedOnCanvas.ts | 3 +- .../CanvasArenas/hooks/useCanvasDragging.ts | 3 +- app/client/src/pages/common/ClientError.tsx | 60 - app/client/src/pages/common/ErrorPage.tsx | 8 +- .../src/pages/common/ErrorPageHeader.tsx | 40 +- .../pages/common/ErrorPages/ClientError.tsx | 44 + .../src/pages/common/ErrorPages/Page.tsx | 31 + .../common/{ => ErrorPages}/PageNotFound.tsx | 49 +- .../pages/common/ErrorPages/ServerTimeout.tsx | 34 + .../common/ErrorPages/ServerUnavailable.tsx | 34 + app/client/src/pages/common/MobileSidebar.tsx | 9 +- app/client/src/pages/common/PageHeader.tsx | 32 +- app/client/src/pages/common/PageWrapper.tsx | 10 +- app/client/src/pages/common/ServerTimeout.tsx | 53 - .../src/pages/common/ServerUnavailable.tsx | 50 - .../src/pages/common/SharedUserList.tsx | 2 +- .../src/pages/common/datasourceAuth/index.tsx | 144 +- app/client/src/pages/setup/DetailsForm.tsx | 2 +- app/client/src/pages/setup/SignupSuccess.tsx | 3 +- app/client/src/pages/tests/slug.test.tsx | 13 +- .../pages/workspace/AppInviteUsersForm.tsx | 1 + .../workspace/DeleteConfirmationModal.tsx | 2 +- app/client/src/pages/workspace/settings.tsx | 69 +- .../autoHeightLayoutTreeReducer.ts | 6 +- .../canvasLevelsReducer.test.ts | 4 +- .../autoHeightReducers/canvasLevelsReducer.ts | 2 +- ...etsReducer.tsx => canvasWidgetsReducer.ts} | 7 +- .../entityReducers/datasourceReducer.ts | 50 + .../entityReducers/metaReducer/index.ts | 22 +- .../metaReducer/metaReducerUtils.ts | 27 + .../entityReducers/pageListReducer.tsx | 115 +- ...nfigReducer.tsx => widgetConfigReducer.ts} | 3 + .../evaluationReducers/triggerReducer.ts | 32 + .../src/reducers/uiReducers/apiPaneReducer.ts | 11 + .../uiReducers/appSettingsPaneReducer.ts | 44 + .../reducers/uiReducers/autoHeightReducer.ts | 29 + .../uiReducers/datasourceNameReducer.ts | 6 +- .../uiReducers/datasourcePaneReducer.ts | 34 +- .../reducers/uiReducers/debuggerReducer.ts | 24 +- .../uiReducers/editorContextReducer.ts | 110 +- .../src/reducers/uiReducers/editorReducer.tsx | 3 +- .../reducers/uiReducers/explorerReducer.ts | 47 +- .../src/reducers/uiReducers/gitSyncReducer.ts | 8 +- app/client/src/reducers/uiReducers/index.tsx | 4 + .../reducers/uiReducers/mainCanvasReducer.ts | 1 - .../uiReducers/propertyPaneReducer.tsx | 65 +- app/client/src/reflow/reflowHelpers.ts | 94 +- app/client/src/reflow/reflowTypes.ts | 8 +- app/client/src/reflow/reflowUtils.ts | 36 +- .../src/reflow/tests/reflowHelpers.test.js | 22 +- .../src/reflow/tests/reflowUtils.test.js | 35 +- app/client/src/resizable/resize/index.tsx | 63 +- .../src/resizable/resizenreflow/index.tsx | 57 +- .../ActionExecution/ActionExecutionSagas.ts | 4 +- .../GetCurrentLocationSaga.test.ts | 113 + .../ActionExecution/GetCurrentLocationSaga.ts | 62 +- .../sagas/ActionExecution/PluginActionSaga.ts | 114 +- .../src/sagas/ActionExecution/errorUtils.ts | 36 +- app/client/src/sagas/ApiPaneSagas.ts | 219 +- app/client/src/sagas/AppThemingSaga.tsx | 5 - app/client/src/sagas/ApplicationSagas.tsx | 28 +- .../sagas/CanvasSagas/DraggingCanvasSagas.ts | 53 +- app/client/src/sagas/DatasourcesSagas.ts | 207 +- app/client/src/sagas/DebuggerSagas.ts | 623 +-- app/client/src/sagas/ErrorSagas.tsx | 11 +- app/client/src/sagas/EvaluationsSaga.ts | 27 +- app/client/src/sagas/FormEvaluationSaga.ts | 2 +- app/client/src/sagas/GlobalSearchSagas.ts | 14 +- app/client/src/sagas/InitSagas.ts | 2 + app/client/src/sagas/JSPaneSagas.ts | 38 +- app/client/src/sagas/LintingSagas.ts | 9 +- app/client/src/sagas/ModalSagas.ts | 7 - app/client/src/sagas/NavigationSagas.ts | 174 +- app/client/src/sagas/PageSagas.tsx | 127 +- app/client/src/sagas/PluginSagas.ts | 8 + app/client/src/sagas/PostEvaluationSagas.ts | 137 +- app/client/src/sagas/PostLintingSagas.ts | 29 +- app/client/src/sagas/QueryPaneSagas.ts | 288 +- app/client/src/sagas/RecentEntitiesSagas.ts | 39 - app/client/src/sagas/ReplaySaga.ts | 13 +- app/client/src/sagas/SaaSPaneSagas.ts | 71 +- app/client/src/sagas/ThemeSaga.tsx | 25 +- .../handleAppLevelSocketEvents.tsx | 2 +- app/client/src/sagas/WidgetAdditionSagas.ts | 89 +- app/client/src/sagas/WidgetDeletionSagas.ts | 4 + app/client/src/sagas/WidgetOperationSagas.tsx | 341 +- app/client/src/sagas/WidgetOperationUtils.ts | 161 +- app/client/src/sagas/WidgetSelectionSagas.ts | 60 +- .../src/sagas/autoHeightSagas/batcher.ts | 41 + .../src/sagas/autoHeightSagas/containers.ts | 199 + .../src/sagas/autoHeightSagas/helpers.ts | 166 + app/client/src/sagas/autoHeightSagas/index.ts | 33 + .../src/sagas/autoHeightSagas/layoutTree.ts | 69 + .../src/sagas/autoHeightSagas/widgets.ts | 634 +++ app/client/src/sagas/editorContextSagas.ts | 84 +- app/client/src/sagas/selectors.tsx | 18 +- app/client/src/selectors/apiPaneSelectors.ts | 3 + .../selectors/appSettingsPaneSelectors.tsx | 10 + .../src/selectors/applicationSelectors.tsx | 10 +- .../src/selectors/autoHeightSelectors.ts | 7 + app/client/src/selectors/dataTreeSelectors.ts | 4 +- .../src/selectors/editorContextSelectors.ts | 135 +- app/client/src/selectors/editorSelectors.tsx | 173 +- app/client/src/selectors/entitiesSelector.ts | 9 +- app/client/src/selectors/explorerSelector.ts | 7 +- app/client/src/selectors/formSelectors.ts | 19 +- .../src/selectors/navigationSelectors.ts | 66 + .../src/selectors/onboardingSelectors.tsx | 10 +- .../src/selectors/pageListSelectors.tsx | 6 + .../src/selectors/propertyPaneSelectors.tsx | 87 +- app/client/src/selectors/ui.tsx | 17 + .../src/selectors/widgetSelectors.test.tsx | 27 + app/client/src/selectors/widgetSelectors.ts | 6 +- app/client/src/store.ts | 10 +- app/client/src/utils/AnalyticsUtil.tsx | 11 +- app/client/src/utils/AppsmithConsole.ts | 47 +- app/client/src/utils/BrandingUtils.ts | 181 + .../CallbackHandlerEventType.ts | 5 +- app/client/src/utils/DSLMigration.test.ts | 42 +- app/client/src/utils/DSLMigrations.ts | 220 +- .../src/utils/DSLMigrationsUtils.test.ts | 21 + app/client/src/utils/DatasourceSagaUtils.tsx | 22 + app/client/src/utils/DynamicBindingUtils.ts | 3 +- app/client/src/utils/WidgetFactory.tsx | 91 +- .../src/utils/WidgetFactoryHelpers.test.ts | 255 -- app/client/src/utils/WidgetFactoryHelpers.ts | 152 +- app/client/src/utils/WidgetFeatures.test.ts | 18 +- app/client/src/utils/WidgetFeatures.ts | 390 +- .../src/utils/WidgetLoadingStateUtils.test.ts | 1 + .../src/utils/WidgetRegisterHelpers.tsx | 43 +- app/client/src/utils/WidgetRegistry.tsx | 2 +- app/client/src/utils/autoHeight/constants.ts | 1 + .../src/utils/autoHeight/generateTree.test.ts | 22 +- .../src/utils/autoHeight/generateTree.ts | 24 +- app/client/src/utils/autoHeight/helpers.ts | 58 + .../src/utils/autoHeight/reflow.test.ts | 13 +- app/client/src/utils/autoHeight/reflow.ts | 174 +- .../autocomplete/AutocompleteSortRules.ts | 2 +- ...TernServer.ts => CodemirrorTernService.ts} | 17 +- .../utils/autocomplete/EntityDefinitions.ts | 2 +- .../src/utils/autocomplete/TernServer.test.ts | 24 +- .../utils/autocomplete/TernWorkerService.ts | 82 + .../src/utils/autocomplete/customDefUtils.ts | 8 +- .../dataTreeTypeDefCreator.test.ts | 2 +- .../autocomplete/dataTreeTypeDefCreator.ts | 2 +- .../utils/autocomplete/dataTypeSortRules.ts | 2 +- .../utils/autocomplete/keywordCompletion.ts | 2 +- app/client/src/utils/autocomplete/types.ts | 12 + app/client/src/utils/editorContextUtils.ts | 26 +- .../utils/formControl/FormControlRegistry.tsx | 11 - .../src/utils/formControl/formControlTypes.ts | 1 - app/client/src/utils/formhelpers.ts | 4 +- app/client/src/utils/getQueryParamsObject.ts | 7 +- app/client/src/utils/helpers.test.ts | 2 +- app/client/src/utils/helpers.tsx | 69 +- app/client/src/utils/history.ts | 16 +- .../src/utils/hooks/autoHeightUIHooks.ts | 25 + .../utils/hooks/useAllowEditorDragToSelect.ts | 4 +- .../src/utils/hooks/useBrandingTheme.ts | 38 + .../hooks/useCanvasMinHeightUpdateHook.ts | 2 +- .../utils/hooks/useClickToSelectWidget.tsx | 12 +- .../src/utils/hooks/useDynamicAppLayout.tsx | 62 +- .../hooks/usePositionedContainerZIndex.ts | 2 +- app/client/src/utils/hooks/useWidgetConfig.ts | 11 + .../src/utils/migrations/MenuButtonWidget.ts | 9 + .../utils/migrations/MigrateLabelPosition.ts | 16 + .../utils/migrations/ThemingMigration.test.ts | 2445 ++++++++++++ .../src/utils/migrations/ThemingMigrations.ts | 23 + .../utils/migrations/autoHeightMigrations.ts | 51 + app/client/src/utils/storage.ts | 3 +- .../src/utils/testPropertyPaneConfig.test.ts | 10 + app/client/src/utils/validation/common.ts | 4 +- .../src/widgets/AudioRecorderWidget/index.ts | 1 + .../AudioRecorderWidget/widget/index.tsx | 23 +- .../widgets/AudioWidget/component/index.tsx | 6 +- .../src/widgets/AudioWidget/widget/index.tsx | 117 +- .../BaseInputWidget/component/index.tsx | 32 +- .../widgets/BaseInputWidget/widget/index.tsx | 48 +- app/client/src/widgets/BaseWidget.tsx | 146 +- .../src/widgets/ButtonGroupWidget/index.ts | 1 + .../ButtonGroupWidget/widget/helpers.ts | 7 +- .../ButtonGroupWidget/widget/index.tsx | 51 +- app/client/src/widgets/ButtonWidget/index.ts | 1 + .../src/widgets/ButtonWidget/widget/index.tsx | 49 +- .../widgets/CameraWidget/component/index.tsx | 2 +- app/client/src/widgets/CameraWidget/index.ts | 1 + .../src/widgets/CameraWidget/widget/index.tsx | 40 +- app/client/src/widgets/CanvasWidget.tsx | 16 +- .../src/widgets/CategorySliderWidget/index.ts | 5 +- .../CategorySliderWidget/widget/index.tsx | 10 +- .../widget/propertyConfig/contentConfig.ts | 30 +- .../widget/propertyConfig/styleConfig.ts | 12 - .../widgets/ChartWidget/component/index.tsx | 15 +- .../src/widgets/ChartWidget/constants.ts | 3 +- app/client/src/widgets/ChartWidget/index.ts | 1 + .../src/widgets/ChartWidget/widget/index.tsx | 10 + .../ChartWidget/widget/propertyConfig.ts | 22 +- .../CheckboxGroupWidget/component/index.tsx | 16 +- .../src/widgets/CheckboxGroupWidget/index.ts | 9 +- .../CheckboxGroupWidget/widget/index.tsx | 85 +- .../CheckboxWidget/component/index.tsx | 6 + .../src/widgets/CheckboxWidget/index.ts | 7 + .../widgets/CheckboxWidget/widget/index.tsx | 39 +- .../widgets/CircularProgressWidget/index.ts | 1 + .../CircularProgressWidget/widget/index.tsx | 10 +- .../CodeScannerWidget/component/index.tsx | 4 +- .../src/widgets/CodeScannerWidget/index.ts | 1 + .../CodeScannerWidget/widget/index.tsx | 9 + .../widget/propertyConfig/contentConfig.ts | 12 + .../widget/propertyConfig/styleConfig.ts | 15 +- .../ContainerWidget/component/index.tsx | 2 - .../src/widgets/ContainerWidget/index.ts | 10 +- .../widgets/ContainerWidget/widget/index.tsx | 17 +- .../component/CurrencyCodeDropdown.tsx | 12 +- .../CurrencyInputWidget/component/index.tsx | 1 + .../src/widgets/CurrencyInputWidget/index.ts | 9 + .../CurrencyInputWidget/widget/index.tsx | 27 +- .../widgets/DatePickerWidget/widget/index.tsx | 2 +- .../DatePickerWidget2/component/index.tsx | 36 +- .../src/widgets/DatePickerWidget2/index.ts | 9 + .../DatePickerWidget2/widget/index.tsx | 97 +- .../DividerWidget/widget/index.test.tsx | 3 + .../widgets/DividerWidget/widget/index.tsx | 18 +- .../DocumentViewerWidget/widget/index.tsx | 12 +- .../src/widgets/DropdownWidget/index.ts | 1 + .../DropdownWidget/widget/index.test.tsx | 3 + .../widgets/DropdownWidget/widget/index.tsx | 12 +- .../src/widgets/FilePickerWidgetV2/index.ts | 1 + .../FilePickerWidgetV2/widget/index.tsx | 63 +- .../src/widgets/FormButtonWidget/index.ts | 1 + .../widgets/FormButtonWidget/widget/index.tsx | 9 + app/client/src/widgets/FormWidget/index.ts | 7 + .../src/widgets/FormWidget/widget/index.tsx | 7 + .../src/widgets/IconButtonWidget/index.ts | 1 + .../widgets/IconButtonWidget/widget/index.tsx | 31 +- .../widgets/IframeWidget/component/index.tsx | 2 +- app/client/src/widgets/IframeWidget/index.ts | 1 + .../src/widgets/IframeWidget/widget/index.tsx | 34 +- app/client/src/widgets/ImageWidget/index.ts | 1 + .../src/widgets/ImageWidget/widget/index.tsx | 30 +- app/client/src/widgets/InputWidget/index.ts | 1 + .../src/widgets/InputWidget/widget/index.tsx | 12 +- .../widgets/InputWidgetV2/component/index.tsx | 1 + app/client/src/widgets/InputWidgetV2/index.ts | 9 + .../widgets/InputWidgetV2/widget/index.tsx | 31 +- .../widgets/JSONFormWidget/component/Form.tsx | 59 +- .../JSONFormWidget/component/index.tsx | 49 +- .../component/useFixedFooter.ts | 7 +- .../src/widgets/JSONFormWidget/index.ts | 18 +- .../JSONFormWidget/schemaParser.test.ts | 505 ++- .../widgets/JSONFormWidget/schemaParser.ts | 85 +- .../widgets/JSONFormWidget/schemaTestData.ts | 58 + .../widgets/JSONFormWidget/widget/helper.ts | 14 +- .../widgets/JSONFormWidget/widget/index.tsx | 160 +- .../JSONFormWidget/widget/propertyConfig.ts | 16 +- .../widget/propertyConfig/helper.test.ts | 3 +- .../widget/propertyConfig/helper.ts | 4 +- .../propertyConfig/properties/common.ts | 2 +- .../widget/propertyConfig/properties/input.ts | 2 +- .../propertyConfig/properties/multiSelect.ts | 2 +- .../propertyConfig/properties/radioGroup.ts | 2 +- .../propertyConfig/properties/select.ts | 2 +- app/client/src/widgets/ListWidget/index.ts | 10 + .../src/widgets/ListWidget/widget/index.tsx | 94 +- .../ListWidget/widget/propertyConfig.ts | 20 +- .../src/widgets/MapChartWidget/index.ts | 1 + .../widgets/MapChartWidget/widget/index.tsx | 43 +- .../src/widgets/MapWidget/component/index.tsx | 1 + app/client/src/widgets/MapWidget/index.ts | 1 + .../src/widgets/MapWidget/widget/index.tsx | 38 +- .../MenuButtonWidget/component/index.tsx | 169 +- .../src/widgets/MenuButtonWidget/constants.ts | 101 +- .../src/widgets/MenuButtonWidget/index.ts | 3 + .../MenuButtonWidget/validations.test.ts | 83 + .../widgets/MenuButtonWidget/validations.ts | 45 + .../MenuButtonWidget/widget/helper.test.ts | 43 + .../widgets/MenuButtonWidget/widget/helper.ts | 34 + .../widgets/MenuButtonWidget/widget/index.tsx | 547 +-- .../childPanels/configureMenuItemsConfig.ts | 215 + .../childPanels/menuItemsConfig.ts | 130 + .../widget/propertyConfig/contentConfig.ts | 161 + .../widget/propertyConfig/propertyUtils.ts | 46 + .../widget/propertyConfig/styleConfig.ts | 178 + .../widgets/ModalWidget/component/index.tsx | 8 +- app/client/src/widgets/ModalWidget/index.ts | 12 +- .../src/widgets/ModalWidget/widget/index.tsx | 44 +- .../component/index.styled.tsx | 15 + .../MultiSelectTreeWidget/component/index.tsx | 20 +- .../widgets/MultiSelectTreeWidget/index.ts | 9 + .../MultiSelectTreeWidget/widget/index.tsx | 105 +- .../MultiSelectWidget/component/index.tsx | 6 +- .../src/widgets/MultiSelectWidget/index.ts | 1 + .../MultiSelectWidget/widget/index.tsx | 12 +- .../component/index.styled.tsx | 15 + .../MultiSelectWidgetV2/component/index.tsx | 20 +- .../src/widgets/MultiSelectWidgetV2/index.ts | 16 +- .../MultiSelectWidgetV2/widget/index.tsx | 114 +- .../NumberSliderWidget/component/Slider.tsx | 5 + .../src/widgets/NumberSliderWidget/index.ts | 3 +- .../widgets/NumberSliderWidget/validations.ts | 4 +- .../NumberSliderWidget/widget/index.tsx | 10 +- .../widget/propertyConfig/contentConfig.ts | 28 +- .../widget/propertyConfig/styleConfig.ts | 12 - .../component/ISDCodeDropdown.tsx | 13 +- .../PhoneInputWidget/component/index.tsx | 1 + .../src/widgets/PhoneInputWidget/index.ts | 9 + .../widgets/PhoneInputWidget/widget/index.tsx | 31 +- .../src/widgets/ProgressBarWidget/index.ts | 1 + .../ProgressBarWidget/widget/index.tsx | 8 + .../src/widgets/ProgressWidget/index.ts | 1 + .../widgets/ProgressWidget/widget/index.tsx | 18 +- .../QRGeneratorWidget/widget/index.tsx | 24 +- .../RadioGroupWidget/component/index.tsx | 26 +- .../src/widgets/RadioGroupWidget/index.ts | 7 + .../widgets/RadioGroupWidget/widget/index.tsx | 38 +- .../component/RangeSlider.tsx | 5 + .../src/widgets/RangeSliderWidget/index.ts | 3 +- .../widgets/RangeSliderWidget/validations.ts | 4 +- .../RangeSliderWidget/widget/index.tsx | 12 +- .../widget/propertyConfig/contentConfig.ts | 28 +- .../widget/propertyConfig/styleConfig.ts | 12 - app/client/src/widgets/RateWidget/index.ts | 7 + .../src/widgets/RateWidget/widget/index.tsx | 33 +- .../RichTextEditorWidget/component/index.tsx | 41 +- .../src/widgets/RichTextEditorWidget/index.ts | 9 + .../RichTextEditorWidget/widget/index.tsx | 37 +- .../SelectWidget/component/SelectButton.tsx | 1 + .../SelectWidget/component/index.styled.tsx | 33 +- .../widgets/SelectWidget/component/index.tsx | 33 +- app/client/src/widgets/SelectWidget/index.ts | 16 +- .../src/widgets/SelectWidget/widget/index.tsx | 85 +- .../component/index.styled.tsx | 15 + .../component/index.tsx | 16 +- .../widgets/SingleSelectTreeWidget/index.ts | 9 + .../SingleSelectTreeWidget/widget/index.tsx | 85 +- app/client/src/widgets/StatboxWidget/index.ts | 18 +- .../widgets/StatboxWidget/widget/index.tsx | 18 +- .../SwitchGroupWidget/component/index.tsx | 23 +- .../src/widgets/SwitchGroupWidget/index.ts | 7 + .../SwitchGroupWidget/widget/index.tsx | 68 +- .../widgets/SwitchWidget/component/index.tsx | 39 +- app/client/src/widgets/SwitchWidget/index.ts | 7 + .../src/widgets/SwitchWidget/widget/index.tsx | 39 +- .../widgets/TableWidget/component/Table.tsx | 1 + .../widgets/TableWidget/component/index.tsx | 2 + app/client/src/widgets/TableWidget/index.ts | 1 + .../src/widgets/TableWidget/widget/helpers.ts | 6 +- .../src/widgets/TableWidget/widget/index.tsx | 26 + .../TableWidget/widget/propertyConfig.ts | 36 +- .../TableWidgetV2/component/Constants.ts | 7 +- .../cellComponents/InlineCellEditor.tsx | 25 +- .../cellComponents/PlainTextCell.tsx | 35 +- .../component/header/actions/ActionItem.tsx | 2 +- .../header/actions/Utilities.test.ts | 3 +- .../actions/filter/FilterPaneContent.tsx | 13 +- .../component/header/actions/index.tsx | 4 +- .../src/widgets/TableWidgetV2/constants.ts | 10 + app/client/src/widgets/TableWidgetV2/index.ts | 1 + .../widgets/TableWidgetV2/widget/derived.js | 61 + .../TableWidgetV2/widget/derived.test.js | 2072 ++++++---- .../widgets/TableWidgetV2/widget/index.tsx | 65 +- .../propertyConfig/PanelConfig/Alignment.ts | 107 + .../propertyConfig/PanelConfig/Basic.ts | 10 +- .../PanelConfig/BorderAndShadow.ts | 4 +- .../propertyConfig/PanelConfig/Color.ts | 8 +- .../PanelConfig/ColumnControl.ts | 16 +- .../widget/propertyConfig/PanelConfig/Data.ts | 8 +- .../PanelConfig/DiscardButtonproperties.ts | 12 +- .../propertyConfig/PanelConfig/Events.ts | 22 +- .../propertyConfig/PanelConfig/General.ts | 14 +- .../widget/propertyConfig/PanelConfig/Icon.ts | 2 +- .../PanelConfig/ResponsiveBehavior.ts | 13 + .../PanelConfig/SaveButtonProperties.ts | 12 +- .../PanelConfig/TextFormatting.ts | 34 +- .../propertyConfig/PanelConfig/index.ts | 24 +- .../widget/propertyConfig/contentConfig.ts | 4 +- .../widget/propertyConfig/styleConfig.ts | 12 - .../widget/propertyUtils.test.ts | 53 + .../TableWidgetV2/widget/propertyUtils.ts | 17 + .../widgets/TableWidgetV2/widget/utilities.ts | 4 +- .../src/widgets/TabsMigrator/widget/index.tsx | 2 +- .../widgets/TabsWidget/component/index.tsx | 69 - app/client/src/widgets/TabsWidget/index.ts | 24 +- .../src/widgets/TabsWidget/widget/index.tsx | 64 +- .../TextWidget/component/FontLoader.tsx | 2 + .../component/filters/LinkFilter.ts | 14 + .../widgets/TextWidget/component/helpers.tsx | 15 + .../widgets/TextWidget/component/index.tsx | 10 +- app/client/src/widgets/TextWidget/index.ts | 7 + .../src/widgets/TextWidget/widget/index.tsx | 25 +- .../widgets/VideoWidget/component/index.tsx | 10 +- app/client/src/widgets/VideoWidget/index.ts | 1 + .../src/widgets/VideoWidget/widget/index.tsx | 111 +- app/client/src/widgets/WidgetUtils.test.ts | 230 +- app/client/src/widgets/WidgetUtils.ts | 123 +- .../widgets/components/LabelWithTooltip.tsx | 313 ++ app/client/src/widgets/constants.ts | 5 +- app/client/src/widgets/useDropdown.tsx | 25 +- app/client/src/widgets/withWidgetProps.tsx | 57 +- .../workers/Evaluation/HTTPRequestOverride.ts | 16 + .../src/workers/Evaluation/JSObject/index.ts | 415 +- .../src/workers/Evaluation/JSObject/utils.ts | 131 +- .../__tests__/dataTreeUtils.test.ts | 444 +++ .../Evaluation/__tests__/evaluate.test.ts | 2 +- .../Evaluation/__tests__/evaluation.test.ts | 69 +- .../__tests__/evaluationUtils.test.ts | 51 +- .../Evaluation/__tests__/validations.test.ts | 4 +- .../src/workers/Evaluation/dataTreeUtils.ts | 54 + app/client/src/workers/Evaluation/evaluate.ts | 24 +- .../workers/Evaluation/evaluation.worker.ts | 53 +- .../src/workers/Evaluation/evaluationUtils.ts | 7 +- .../src/workers/Evaluation/indirectEval.ts | 5 + .../src/workers/Evaluation/replayUtils.ts | 1 + app/client/src/workers/Evaluation/types.ts | 4 +- .../src/workers/Evaluation/validations.ts | 11 +- app/client/src/workers/Linting/types.ts | 3 - app/client/src/workers/Linting/utils.ts | 3 - app/client/src/workers/Tern/tern.worker.ts | 57 + .../workers/common/DataTreeEvaluator/index.ts | 79 +- .../mockData/ArrayAccessorTree.ts | 66 + .../mockData/NestedArrayAccessorTree.ts | 48 + .../mockData/mockUnEvalTree.ts | 109 +- .../workers/common/DataTreeEvaluator/test.ts | 41 +- .../DataTreeEvaluator/validationUtils.ts | 90 +- app/client/start-https.sh | 47 +- app/client/test/testCommon.ts | 33 +- app/client/typings/json-fn/index.d.ts | 1 - app/client/typings/lodash-move/index.d.ts | 1 - .../typings/react-base-table/index.d.ts | 2 - .../react-infinite-scroller/index.d.ts | 1 - app/client/yarn.lock | 990 ++--- app/rts/package.json | 27 +- app/rts/src/server.ts | 3 - app/rts/src/test/server.test.ts | 61 + app/rts/yarn.lock | 1470 +++---- .../external/constants/AnalyticsEvents.java | 15 + .../external/helpers/DataTypeStringUtils.java | 2 + .../external/helpers/MustacheHelper.java | 65 +- ...erUtils.java => RestAPIActivateUtils.java} | 2 +- .../external/models/MustacheBindingToken.java | 22 + .../plugins/BaseRestApiPluginExecutor.java | 6 +- .../plugins/SmartSubstitutionInterface.java | 8 +- .../helpers/DataTypeStringUtilsTest.java | 20 + .../external/helpers/MustacheHelperTest.java | 265 +- .../com/external/plugins/AmazonS3Plugin.java | 3 +- .../com/external/plugins/FirestorePlugin.java | 3 +- .../com/external/config/MethodConfig.java | 8 +- .../external/plugins/GoogleSheetsPlugin.java | 3 +- .../com/external/plugins/GraphQLPlugin.java | 20 +- .../src/main/resources/form.json | 4 +- .../com/external/plugins/MongoPlugin.java | 3 +- .../com/external/plugins/MssqlPlugin.java | 5 +- .../com/external/plugins/MySqlPlugin.java | 8 +- .../appsmith-plugins/postgresPlugin/pom.xml | 2 +- .../com/external/plugins/PostgresPlugin.java | 5 +- .../com/external/plugins/RestApiPlugin.java | 73 +- .../src/main/resources/form.json | 4 +- .../external/plugins/RestApiPluginTest.java | 262 ++ .../appsmith/server/acl/AclPermission.java | 1 + .../AuthenticationSuccessHandler.java | 6 +- .../ce/AuthenticationSuccessHandlerCE.java | 5 +- .../server/configurations/InstanceConfig.java | 23 +- .../server/configurations/SecurityConfig.java | 2 + .../server/configurations/SegmentConfig.java | 6 +- .../com/appsmith/server/constants/Entity.java | 1 + .../appsmith/server/constants/FieldName.java | 14 +- .../controllers/ce/ActionControllerCE.java | 5 +- .../ce/InstanceAdminControllerCE.java | 20 +- .../appsmith/server/domains/Application.java | 22 +- .../appsmith/server/dtos/UserProfileDTO.java | 3 + .../server/exceptions/AppsmithError.java | 32 +- .../server/featureflags/FeatureFlagEnum.java | 5 +- .../com/appsmith/server/helpers/DslUtils.java | 27 +- .../appsmith/server/helpers/PolicyUtils.java | 16 +- .../server/helpers/RedirectHelper.java | 5 +- .../appsmith/server/helpers/UserUtils.java | 6 +- .../server/helpers/ce/UserUtilsCE.java | 11 +- .../server/migrations/DatabaseChangelog.java | 39 +- .../server/migrations/DatabaseChangelog2.java | 2 +- .../CustomApplicationRepositoryImpl.java | 7 +- .../ce/CustomApplicationRepositoryCEImpl.java | 8 +- .../services/ActionCollectionServiceImpl.java | 9 +- .../services/ApplicationPageServiceImpl.java | 13 +- .../services/ApplicationServiceImpl.java | 9 +- .../ApplicationTemplateServiceImpl.java | 7 +- .../server/services/CommentServiceImpl.java | 8 +- .../services/CurlImporterServiceImpl.java | 6 +- .../DatasourceContextServiceImpl.java | 6 +- .../services/DatasourceServiceImpl.java | 9 +- .../server/services/GitServiceImpl.java | 18 +- .../services/LayoutActionServiceImpl.java | 9 +- .../services/LayoutCollectionServiceImpl.java | 8 +- .../server/services/LayoutServiceImpl.java | 6 +- .../server/services/NewActionServiceImpl.java | 13 +- .../server/services/NewPageServiceImpl.java | 8 +- .../services/PermissionGroupServiceImpl.java | 7 +- .../services/PostmanImporterServiceImpl.java | 6 +- .../server/services/ThemeServiceImpl.java | 15 +- .../services/UserWorkspaceServiceImpl.java | 8 +- .../server/services/WorkspaceServiceImpl.java | 8 +- .../ce/ActionCollectionServiceCEImpl.java | 36 +- .../services/ce/AnalyticsServiceCEImpl.java | 24 +- .../ce/ApplicationPageServiceCEImpl.java | 101 +- .../services/ce/ApplicationServiceCEImpl.java | 53 +- .../ce/ApplicationTemplateServiceCEImpl.java | 29 +- .../server/services/ce/AssetServiceCE.java | 4 +- .../services/ce/AssetServiceCEImpl.java | 62 +- .../server/services/ce/AstServiceCE.java | 3 +- .../server/services/ce/AstServiceCEImpl.java | 32 +- .../services/ce/CommentServiceCEImpl.java | 33 +- .../ce/CurlImporterServiceCEImpl.java | 8 +- .../ce/DatasourceContextServiceCEImpl.java | 10 +- .../services/ce/DatasourceServiceCE.java | 3 +- .../services/ce/DatasourceServiceCEImpl.java | 25 +- .../server/services/ce/GitServiceCEImpl.java | 55 +- .../ce/LayoutActionServiceCEImpl.java | 53 +- .../ce/LayoutCollectionServiceCEImpl.java | 65 +- .../services/ce/LayoutServiceCEImpl.java | 14 +- .../services/ce/NewActionServiceCE.java | 9 +- .../services/ce/NewActionServiceCEImpl.java | 593 ++- .../services/ce/NewPageServiceCEImpl.java | 36 +- .../services/ce/PermissionGroupServiceCE.java | 4 + .../ce/PermissionGroupServiceCEImpl.java | 65 +- .../ce/PostmanImporterServiceCEImpl.java | 8 +- .../services/ce/ThemeServiceCEImpl.java | 40 +- .../services/ce/UserDataServiceCEImpl.java | 2 +- .../server/services/ce/UserServiceCEImpl.java | 49 +- .../ce/UserWorkspaceServiceCEImpl.java | 29 +- .../services/ce/WorkspaceServiceCEImpl.java | 38 +- .../server/solutions/ActionPermission.java | 6 + .../solutions/ActionPermissionImpl.java | 8 + .../solutions/ApplicationFetcherImpl.java | 8 +- .../ApplicationForkingServiceImpl.java | 6 +- .../solutions/ApplicationPermission.java | 6 + .../solutions/ApplicationPermissionImpl.java | 8 + .../solutions/AuthenticationServiceImpl.java | 7 +- .../CreateDBTablePageSolutionImpl.java | 8 +- .../solutions/DatasourcePermission.java | 6 + .../solutions/DatasourcePermissionImpl.java | 8 + .../DatasourceStructureSolutionImpl.java | 5 +- .../DatasourceTriggerSolutionImpl.java | 6 +- .../solutions/EmailEventHandlerImpl.java | 5 +- .../ExamplesWorkspaceClonerImpl.java | 7 +- .../ImportExportApplicationServiceImpl.java | 12 +- .../ImportExportApplicationServiceImplV2.java | 62 + .../solutions/PageLoadActionsUtilImpl.java | 5 +- .../server/solutions/PagePermission.java | 6 + .../server/solutions/PagePermissionImpl.java | 8 + .../solutions/PermissionGroupPermission.java | 6 + .../PermissionGroupPermissionImpl.java | 8 + .../solutions/RefactoringSolutionImpl.java | 16 +- .../UserAndAccessManagementServiceImpl.java | 6 +- .../server/solutions/WorkspacePermission.java | 6 + .../solutions/WorkspacePermissionImpl.java | 8 + .../solutions/ce/ActionPermissionCE.java | 10 + .../solutions/ce/ActionPermissionCEImpl.java | 25 + .../ce/ApplicationFetcherCEImpl.java | 15 +- .../ce/ApplicationForkingServiceCEImpl.java | 19 +- .../solutions/ce/ApplicationPermissionCE.java | 13 + .../ce/ApplicationPermissionCEImpl.java | 40 + .../ce/AuthenticationServiceCEImpl.java | 14 +- .../ce/CreateDBTablePageSolutionCEImpl.java | 18 +- .../solutions/ce/DatasourcePermissionCE.java | 10 + .../ce/DatasourcePermissionCEImpl.java | 25 + .../ce/DatasourceStructureSolutionCEImpl.java | 7 +- .../ce/DatasourceTriggerSolutionCEImpl.java | 4 +- .../solutions/ce/EmailEventHandlerCEImpl.java | 5 +- .../server/solutions/ce/EnvManagerCE.java | 10 + .../server/solutions/ce/EnvManagerCEImpl.java | 94 +- .../ce/ExamplesWorkspaceClonerCEImpl.java | 8 +- .../ImportExportApplicationServiceCEImpl.java | 76 +- ...mportExportApplicationServiceCEImplV2.java | 2174 ++++++++++ .../ce/PageLoadActionsUtilCEImpl.java | 7 +- .../server/solutions/ce/PagePermissionCE.java | 10 + .../solutions/ce/PagePermissionCEImpl.java | 25 + .../ce/PermissionGroupPermissionCE.java | 11 + .../ce/PermissionGroupPermissionCEImpl.java | 30 + .../ce/PluginScheduledTaskCEImpl.java | 5 +- .../solutions/ce/RefactoringSolutionCE.java | 16 +- .../ce/RefactoringSolutionCEImpl.java | 168 +- .../UserAndAccessManagementServiceCEImpl.java | 24 +- .../solutions/ce/WorkspacePermissionCE.java | 11 + .../ce/WorkspacePermissionCEImpl.java | 30 + .../email/forgotPasswordTemplate.html | 429 +- ...inviteExistingUserToWorkspaceTemplate.html | 301 -- .../email/inviteUserCreatorTemplate.html | 301 -- .../resources/email/inviteUserTemplate.html | 123 + .../email/updateRoleExistingUserTemplate.html | 301 -- .../resources/email/welcomeUserTemplate.html | 756 ++-- .../main/resources/features/init-flags.yml | 10 + .../appsmith/server/helpers/DslUtilsTest.java | 94 + .../ActionCollectionServiceImplTest.java | 28 +- .../ApplicationTemplateServiceTest.java | 6 +- .../DatasourceContextServiceTest.java | 10 +- .../services/DatasourceServiceTest.java | 8 +- .../server/services/LayoutServiceTest.java | 18 +- .../server/services/NewPageServiceTest.java | 108 +- .../server/services/PageServiceTest.java | 18 +- .../server/services/UserServiceTest.java | 12 +- .../services/ce/ActionServiceCE_Test.java | 14 +- .../services/ce/GitServiceCEImplTest.java | 15 +- .../ce/NewActionServiceCEImplTest.java | 34 +- .../solutions/ApplicationFetcherUnitTest.java | 13 +- .../solutions/EmailEventHandlerTest.java | 5 +- .../server/solutions/EnvManagerTest.java | 25 +- .../ImportExportApplicationServiceTests.java | 29 +- ...ImportExportApplicationServiceV2Tests.java | 3513 +++++++++++++++++ .../ce/RefactoringSolutionCEImplTest.java | 21 +- app/shared/ast/index.ts | 5 +- app/shared/ast/src/index.ts | 96 +- app/shared/ast/src/jsObject/index.ts | 2 +- app/util/plugin-generation/package-lock.json | 12 +- .../AppsmithWidgetDevelopmentGuide.md | 2 +- contributions/CustomJsLibrary.md | 2 +- contributions/docs/CONTRIBUTING.md | 36 - contributions/docs/DB Integrations.md | 21 - contributions/docs/GlobalFunctions.md | 2 + contributions/docs/TestAutomation.md | 2 +- contributions/docs/UploadingAssets.md | 20 - contributions/docs/Widgets.md | 30 - contributions/docs/adding_guides.md | 20 - deploy/docker/entrypoint.sh | 7 +- deploy/docker/utils/package-lock.json | 12 +- deploy/helm/templates/ingress.yaml | 1 + static/images/how-it-works.svg | 1 + 1255 files changed, 52889 insertions(+), 16495 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/--documentation-improvement.yaml create mode 100644 .github/workflows/fat-migration.yml create mode 100644 .github/workflows/pr-test-file-check.yml create mode 100644 app/client/cypress/fixtures/Bugs/CheckboxGroupInListWidgetDsl.json create mode 100644 app/client/cypress/fixtures/DynamicHeightDefaultHeightdsl.json create mode 100644 app/client/cypress/fixtures/alignmentWithDynamicHeightDsl.json create mode 100644 app/client/cypress/fixtures/datePickerV2Updated_dsl.json create mode 100644 app/client/cypress/fixtures/dynamicHeightCanvasResizeDsl.json create mode 100644 app/client/cypress/fixtures/dynamicHeightContainerCheckboxdsl.json create mode 100644 app/client/cypress/fixtures/dynamicHeightContainerScrolldsl.json create mode 100644 app/client/cypress/fixtures/dynamicHeightContainerdsl.json create mode 100644 app/client/cypress/fixtures/dynamicHeightFormSwitchdsl.json create mode 100644 app/client/cypress/fixtures/dynamicHeightListDsl.json create mode 100644 app/client/cypress/fixtures/dynamicHeightStatboxdsl.json create mode 100644 app/client/cypress/fixtures/dynamicHeigthContainerFixedDsl.json create mode 100644 app/client/cypress/fixtures/dynamicTabWidgetdsl.json create mode 100644 app/client/cypress/fixtures/invisibleWidgetdsl.json create mode 100644 app/client/cypress/fixtures/jsonFormDynamicHeightDsl.json create mode 100644 app/client/cypress/fixtures/multipleContainerdsl.json create mode 100644 app/client/cypress/fixtures/selectMultiSelectTreeSelectWidgetDsl.json create mode 100644 app/client/cypress/fixtures/textWidgetDynamicdsl.json delete mode 100644 app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/BugTests/Bug16248_spec.ts create mode 100644 app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/BugTests/Bug18664_spec.ts create mode 100644 app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/BugTests/Bug18876_Spec.ts create mode 100644 app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/BugTests/DatasourceSchema_spec.ts create mode 100644 app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/BugTests/GitBugs.ts create mode 100644 app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DynamicHeight/DynamicHeight_Auto_Height_Limit_spec.js create mode 100644 app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DynamicHeight/DynamicHeight_Auto_Height_spec.js create mode 100644 app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DynamicHeight/DynamicHeight_CanvasHeight_resize_spec.js create mode 100644 app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DynamicHeight/DynamicHeight_Container_Scroll_spec.js create mode 100644 app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DynamicHeight/DynamicHeight_Container_collapse_undo_redoSpec.js create mode 100644 app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DynamicHeight/DynamicHeight_Form_With_SwitchGroup_spec.js create mode 100644 app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DynamicHeight/DynamicHeight_JsonForm_spec.js create mode 100644 app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DynamicHeight/DynamicHeight_List_spec.js create mode 100644 app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DynamicHeight/DynamicHeight_Multiple_Container_spec.js create mode 100644 app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DynamicHeight/DynamicHeight_Tab_spec.js create mode 100644 app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DynamicHeight/DynamicHeight_Text_Widget_spec.js create mode 100644 app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DynamicHeight/DynamicHeight_Text_With_Different_Size_spec.js create mode 100644 app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DynamicHeight/DynamicHeight_Visibility_spec.js rename app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Git/GitSync/{Deploy_spec.js => Deploy_spec.ts} (79%) rename app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Git/GitSync/{PreconnectionAppNameDeployMenu_spec.js => PreconnectionAppNameDeployMenu_spec.ts} (67%) create mode 100644 app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/IDE/Canvas_Context_Bug_Fixes.js create mode 100644 app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/IDE/Command_Click_Navigation_spec.js create mode 100644 app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/PropertyPane/PropertyPane_Search_spec.ts create mode 100644 app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/SettingsPane/GeneralSettings_spec.ts create mode 100644 app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/SettingsPane/PageSettings_spec.ts create mode 100644 app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Datepicker/DatePickerV2Updated_spec.js create mode 100644 app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Input/Input_OnFocus_OnBlur_spec.js create mode 100644 app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Others/StatBox_DragAndDrop_spec.js create mode 100644 app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Select/Select_TreeSelect_MultiSelect_OnFocus_OnBlur_spec.js create mode 100644 app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Tab/Tab_OnEvent_Navigation_spec.js create mode 100644 app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/JsFunctionExecution/Fetch_Spec.ts create mode 100644 app/client/cypress/integration/Smoke_TestSuite_Fat/ClientSideTests/Branding/Branding_spec.js create mode 100644 app/client/cypress/integration/Smoke_TestSuite_Fat/ClientSideTests/EmbedSettings/EmbedSettings_spec.js create mode 100644 app/client/cypress/support/Objects/ObjectsCore.ts create mode 100644 app/client/cypress/support/Pages/AppSettings/AppSettings.ts create mode 100644 app/client/cypress/support/Pages/AppSettings/GeneralSettings.ts create mode 100644 app/client/cypress/support/Pages/AppSettings/PageSettings.ts create mode 100644 app/client/cypress/support/Pages/AppSettings/ThemeSettings.ts create mode 100644 app/client/cypress/support/Pages/AppSettings/Utils.ts create mode 100644 app/client/public/static/img/appsmith-logo.svg rename app/client/public/{ => static/img}/favicon-orange.ico (100%) create mode 100644 app/client/src/actions/appSettingsPaneActions.ts create mode 100644 app/client/src/assets/svg/upgrade/access-control/prevent-accidental-damage.png create mode 100644 app/client/src/assets/svg/upgrade/access-control/restrict-public-exposure.png create mode 100644 app/client/src/assets/svg/upgrade/access-control/secure-apps-least-privilege.png create mode 100644 app/client/src/ce/pages/AdminSettings/config/branding/UpgradeBanner.tsx create mode 100644 app/client/src/ce/pages/AdminSettings/config/branding/index.tsx create mode 100644 app/client/src/ce/pages/AdminSettings/config/branding/useBrandingForm.tsx rename app/client/src/{ => ce}/pages/AppViewer/BackToHomeButton.tsx (91%) create mode 100644 app/client/src/components/autoHeight/AutoHeightContainerWrapper.tsx create mode 100644 app/client/src/components/autoHeightOverlay/AutoHeightLimitHandleGroup.tsx create mode 100644 app/client/src/components/autoHeightOverlay/constants.ts create mode 100644 app/client/src/components/autoHeightOverlay/hooks.ts create mode 100644 app/client/src/components/autoHeightOverlay/index.tsx create mode 100644 app/client/src/components/autoHeightOverlay/store.ts create mode 100644 app/client/src/components/autoHeightOverlay/types.ts create mode 100644 app/client/src/components/autoHeightOverlay/ui/AutoHeightLimitHandleBorder.test.tsx create mode 100644 app/client/src/components/autoHeightOverlay/ui/AutoHeightLimitHandleBorder.tsx create mode 100644 app/client/src/components/autoHeightOverlay/ui/AutoHeightLimitHandleDot.test.tsx create mode 100644 app/client/src/components/autoHeightOverlay/ui/AutoHeightLimitHandleDot.tsx create mode 100644 app/client/src/components/autoHeightOverlay/ui/AutoHeightLimitHandleLabel.test.tsx create mode 100644 app/client/src/components/autoHeightOverlay/ui/AutoHeightLimitHandleLabel.tsx create mode 100644 app/client/src/components/autoHeightOverlay/ui/AutoHeightLimitOverlayDisplay.test.tsx create mode 100644 app/client/src/components/autoHeightOverlay/ui/AutoHeightLimitOverlayDisplay.tsx create mode 100644 app/client/src/components/autoHeightOverlay/utils.ts delete mode 100644 app/client/src/components/editorComponents/PreviewModeComponent.tsx create mode 100644 app/client/src/components/editorComponents/ResizableUtils.test.ts rename app/client/src/components/editorComponents/{ResizableUtils.tsx => ResizableUtils.ts} (68%) delete mode 100644 app/client/src/components/formControls/KeyValueInputControl.tsx create mode 100644 app/client/src/components/propertyControls/MenuButtonDynamicItemsControl.tsx create mode 100644 app/client/src/components/propertyControls/OpenConfigPanelControl.tsx create mode 100644 app/client/src/config.d.ts create mode 100644 app/client/src/constants/Datasource.ts create mode 100644 app/client/src/ee/pages/AdminSettings/config/branding/UpgradeBanner.tsx create mode 100644 app/client/src/ee/pages/AdminSettings/config/branding/index.tsx create mode 100644 app/client/src/ee/pages/AdminSettings/config/branding/useBrandingForm.tsx create mode 100644 app/client/src/ee/pages/AppViewer/BackToHomeButton.tsx create mode 100644 app/client/src/entities/DataTree/types.ts create mode 100644 app/client/src/pages/Editor/AppSettingsPane/AppSettings/DraggablePageList.tsx create mode 100644 app/client/src/pages/Editor/AppSettingsPane/AppSettings/GeneralSettings.tsx create mode 100644 app/client/src/pages/Editor/AppSettingsPane/AppSettings/PageSettings.tsx create mode 100644 app/client/src/pages/Editor/AppSettingsPane/AppSettings/SectionHeader.tsx create mode 100644 app/client/src/pages/Editor/AppSettingsPane/AppSettings/index.tsx create mode 100644 app/client/src/pages/Editor/AppSettingsPane/Components/TextLoaderIcon.tsx create mode 100644 app/client/src/pages/Editor/AppSettingsPane/PaneHeader.tsx create mode 100644 app/client/src/pages/Editor/AppSettingsPane/Utils.ts create mode 100644 app/client/src/pages/Editor/AppSettingsPane/index.tsx create mode 100644 app/client/src/pages/Editor/DataSourceEditor/SaveOrDiscardDatasourceModal.tsx delete mode 100644 app/client/src/pages/Editor/PageListSidebar/PageListItem.tsx delete mode 100644 app/client/src/pages/Editor/PagesEditor/ContextMenu.tsx delete mode 100644 app/client/src/pages/Editor/PagesEditor/EditName.tsx delete mode 100644 app/client/src/pages/Editor/PagesEditor/PageListItem.tsx delete mode 100644 app/client/src/pages/Editor/PagesEditor/index.tsx create mode 100644 app/client/src/pages/Editor/PropertyPane/DraggableListControl.tsx rename app/client/src/pages/Editor/PropertyPane/{Generator.tsx => PropertyControlsGenerator.tsx} (52%) create mode 100644 app/client/src/pages/Editor/PropertyPane/PropertyPaneSearchInput.tsx rename app/client/src/pages/Editor/{ => PropertyPane}/PropertyPaneTitle.test.tsx (100%) rename app/client/src/pages/Editor/{ => PropertyPane}/PropertyPaneTitle.tsx (82%) delete mode 100644 app/client/src/pages/Editor/PropertyPane/helpers.test.ts create mode 100644 app/client/src/pages/Editor/PropertyPane/propertyPaneSearch.test.ts create mode 100644 app/client/src/pages/Editor/PropertyPane/propertyPaneSearch.ts create mode 100644 app/client/src/pages/Settings/FormGroup/ColorInput.tsx create mode 100644 app/client/src/pages/Settings/FormGroup/ImageInput.tsx create mode 100644 app/client/src/pages/Settings/FormGroup/Radio.test.tsx create mode 100644 app/client/src/pages/Settings/FormGroup/Radio.tsx create mode 100644 app/client/src/pages/Settings/config/branding/BrandingPage.tsx create mode 100644 app/client/src/pages/Settings/config/branding/SettingsForm.tsx create mode 100644 app/client/src/pages/Settings/config/branding/previews/AppPreview.tsx create mode 100644 app/client/src/pages/Settings/config/branding/previews/DashboardPreview.tsx create mode 100644 app/client/src/pages/Settings/config/branding/previews/DashboardThumbnail.tsx create mode 100644 app/client/src/pages/Settings/config/branding/previews/EmailPreview.tsx create mode 100644 app/client/src/pages/Settings/config/branding/previews/FaviconPreview.tsx create mode 100644 app/client/src/pages/Settings/config/branding/previews/LinkPreview.tsx create mode 100644 app/client/src/pages/Settings/config/branding/previews/LoginPreview.tsx create mode 100644 app/client/src/pages/Settings/config/branding/previews/NotFoundPreview.tsx create mode 100644 app/client/src/pages/Settings/config/branding/previews/PreviewBox.tsx create mode 100644 app/client/src/pages/Settings/config/branding/previews/index.tsx create mode 100644 app/client/src/pages/UserAuth/Container.tsx delete mode 100644 app/client/src/pages/common/AppRoute.tsx delete mode 100644 app/client/src/pages/common/ClientError.tsx create mode 100644 app/client/src/pages/common/ErrorPages/ClientError.tsx create mode 100644 app/client/src/pages/common/ErrorPages/Page.tsx rename app/client/src/pages/common/{ => ErrorPages}/PageNotFound.tsx (52%) create mode 100644 app/client/src/pages/common/ErrorPages/ServerTimeout.tsx create mode 100644 app/client/src/pages/common/ErrorPages/ServerUnavailable.tsx delete mode 100644 app/client/src/pages/common/ServerTimeout.tsx delete mode 100644 app/client/src/pages/common/ServerUnavailable.tsx rename app/client/src/reducers/entityReducers/{canvasWidgetsReducer.tsx => canvasWidgetsReducer.ts} (99%) create mode 100644 app/client/src/reducers/entityReducers/metaReducer/metaReducerUtils.ts rename app/client/src/reducers/entityReducers/{widgetConfigReducer.tsx => widgetConfigReducer.ts} (90%) create mode 100644 app/client/src/reducers/uiReducers/appSettingsPaneReducer.ts create mode 100644 app/client/src/reducers/uiReducers/autoHeightReducer.ts create mode 100644 app/client/src/sagas/ActionExecution/GetCurrentLocationSaga.test.ts create mode 100644 app/client/src/sagas/autoHeightSagas/batcher.ts create mode 100644 app/client/src/sagas/autoHeightSagas/containers.ts create mode 100644 app/client/src/sagas/autoHeightSagas/helpers.ts create mode 100644 app/client/src/sagas/autoHeightSagas/index.ts create mode 100644 app/client/src/sagas/autoHeightSagas/layoutTree.ts create mode 100644 app/client/src/sagas/autoHeightSagas/widgets.ts create mode 100644 app/client/src/selectors/appSettingsPaneSelectors.tsx create mode 100644 app/client/src/selectors/autoHeightSelectors.ts create mode 100644 app/client/src/selectors/navigationSelectors.ts create mode 100644 app/client/src/selectors/widgetSelectors.test.tsx create mode 100644 app/client/src/utils/BrandingUtils.ts create mode 100644 app/client/src/utils/DatasourceSagaUtils.tsx delete mode 100644 app/client/src/utils/WidgetFactoryHelpers.test.ts create mode 100644 app/client/src/utils/autoHeight/helpers.ts rename app/client/src/utils/autocomplete/{TernServer.ts => CodemirrorTernService.ts} (98%) create mode 100644 app/client/src/utils/autocomplete/TernWorkerService.ts create mode 100644 app/client/src/utils/autocomplete/types.ts create mode 100644 app/client/src/utils/hooks/autoHeightUIHooks.ts create mode 100644 app/client/src/utils/hooks/useBrandingTheme.ts create mode 100644 app/client/src/utils/hooks/useWidgetConfig.ts create mode 100644 app/client/src/utils/migrations/MigrateLabelPosition.ts create mode 100644 app/client/src/utils/migrations/ThemingMigration.test.ts create mode 100644 app/client/src/utils/migrations/autoHeightMigrations.ts create mode 100644 app/client/src/widgets/MenuButtonWidget/validations.test.ts create mode 100644 app/client/src/widgets/MenuButtonWidget/validations.ts create mode 100644 app/client/src/widgets/MenuButtonWidget/widget/helper.test.ts create mode 100644 app/client/src/widgets/MenuButtonWidget/widget/helper.ts create mode 100644 app/client/src/widgets/MenuButtonWidget/widget/propertyConfig/childPanels/configureMenuItemsConfig.ts create mode 100644 app/client/src/widgets/MenuButtonWidget/widget/propertyConfig/childPanels/menuItemsConfig.ts create mode 100644 app/client/src/widgets/MenuButtonWidget/widget/propertyConfig/contentConfig.ts create mode 100644 app/client/src/widgets/MenuButtonWidget/widget/propertyConfig/propertyUtils.ts create mode 100644 app/client/src/widgets/MenuButtonWidget/widget/propertyConfig/styleConfig.ts create mode 100644 app/client/src/widgets/TableWidgetV2/widget/propertyConfig/PanelConfig/Alignment.ts create mode 100644 app/client/src/widgets/TableWidgetV2/widget/propertyConfig/PanelConfig/ResponsiveBehavior.ts create mode 100644 app/client/src/widgets/TextWidget/component/filters/LinkFilter.ts create mode 100644 app/client/src/widgets/TextWidget/component/helpers.tsx create mode 100644 app/client/src/widgets/components/LabelWithTooltip.tsx create mode 100644 app/client/src/workers/Evaluation/HTTPRequestOverride.ts create mode 100644 app/client/src/workers/Evaluation/__tests__/dataTreeUtils.test.ts create mode 100644 app/client/src/workers/Evaluation/dataTreeUtils.ts create mode 100644 app/client/src/workers/Evaluation/indirectEval.ts create mode 100644 app/client/src/workers/Tern/tern.worker.ts delete mode 100644 app/client/typings/json-fn/index.d.ts delete mode 100644 app/client/typings/lodash-move/index.d.ts delete mode 100644 app/client/typings/react-base-table/index.d.ts delete mode 100644 app/client/typings/react-infinite-scroller/index.d.ts rename app/server/appsmith-interfaces/src/main/java/com/appsmith/external/helpers/restApiUtils/helpers/{TriggerUtils.java => RestAPIActivateUtils.java} (99%) create mode 100644 app/server/appsmith-interfaces/src/main/java/com/appsmith/external/models/MustacheBindingToken.java create mode 100644 app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ActionPermission.java create mode 100644 app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ActionPermissionImpl.java create mode 100644 app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ApplicationPermission.java create mode 100644 app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ApplicationPermissionImpl.java create mode 100644 app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/DatasourcePermission.java create mode 100644 app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/DatasourcePermissionImpl.java create mode 100644 app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ImportExportApplicationServiceImplV2.java create mode 100644 app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/PagePermission.java create mode 100644 app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/PagePermissionImpl.java create mode 100644 app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/PermissionGroupPermission.java create mode 100644 app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/PermissionGroupPermissionImpl.java create mode 100644 app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/WorkspacePermission.java create mode 100644 app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/WorkspacePermissionImpl.java create mode 100644 app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ce/ActionPermissionCE.java create mode 100644 app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ce/ActionPermissionCEImpl.java create mode 100644 app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ce/ApplicationPermissionCE.java create mode 100644 app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ce/ApplicationPermissionCEImpl.java create mode 100644 app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ce/DatasourcePermissionCE.java create mode 100644 app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ce/DatasourcePermissionCEImpl.java create mode 100644 app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ce/ImportExportApplicationServiceCEImplV2.java create mode 100644 app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ce/PagePermissionCE.java create mode 100644 app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ce/PagePermissionCEImpl.java create mode 100644 app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ce/PermissionGroupPermissionCE.java create mode 100644 app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ce/PermissionGroupPermissionCEImpl.java create mode 100644 app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ce/WorkspacePermissionCE.java create mode 100644 app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ce/WorkspacePermissionCEImpl.java delete mode 100644 app/server/appsmith-server/src/main/resources/email/inviteExistingUserToWorkspaceTemplate.html delete mode 100644 app/server/appsmith-server/src/main/resources/email/inviteUserCreatorTemplate.html create mode 100644 app/server/appsmith-server/src/main/resources/email/inviteUserTemplate.html delete mode 100644 app/server/appsmith-server/src/main/resources/email/updateRoleExistingUserTemplate.html create mode 100644 app/server/appsmith-server/src/test/java/com/appsmith/server/helpers/DslUtilsTest.java create mode 100644 app/server/appsmith-server/src/test/java/com/appsmith/server/solutions/ImportExportApplicationServiceV2Tests.java delete mode 100644 contributions/docs/CONTRIBUTING.md delete mode 100644 contributions/docs/DB Integrations.md delete mode 100644 contributions/docs/UploadingAssets.md delete mode 100644 contributions/docs/Widgets.md delete mode 100644 contributions/docs/adding_guides.md create mode 100644 static/images/how-it-works.svg diff --git a/.github/ISSUE_TEMPLATE/--documentation-improvement.yaml b/.github/ISSUE_TEMPLATE/--documentation-improvement.yaml deleted file mode 100644 index 4c3873919627..000000000000 --- a/.github/ISSUE_TEMPLATE/--documentation-improvement.yaml +++ /dev/null @@ -1,37 +0,0 @@ -name: 📖 Documentation Improvement -description: Suggest improvements to our documentation -title: "[Docs]: " -labels: [Documentation] -assignees: -- Nikhil-Nandagopal -- danciaclara -body: -- type: markdown - attributes: - value: | - Thanks for taking the time to fill out this documentation improvement request! -- type: checkboxes - attributes: - label: Is there an existing issue for this? - description: Please search to see if an issue realated to this already exists. - options: - - label: I have searched the existing issues - required: true -- type: input - attributes: - label: Documentation Link - description: Add a link to the page which needs improvement (if relevant) - validations: - required: false -- type: textarea - attributes: - label: Describe the problem - description: Is the documentation missing? Or is it confusing? Why is it confusing? - validations: - required: true -- type: textarea - attributes: - label: Describe the improvement - description: A clear and concise description of the improvement. - validations: - required: true diff --git a/.github/config.json b/.github/config.json index 9feed48b9a32..a10fac7a186f 100644 --- a/.github/config.json +++ b/.github/config.json @@ -1 +1 @@ -{"runners":[{"versioning":{"source":"milestones","type":"SemVer"},"prereleaseName":"alpha","issue":{"labels":{"Team Managers Pod":{"conditions":[{"label":"Settings","type":"hasLabel","value":true},{"label":"Git Version Control","type":"hasLabel","value":true},{"label":"Home Page","type":"hasLabel","value":true},{"label":"Import-Export-App","type":"hasLabel","value":true},{"label":"Invite users","type":"hasLabel","value":true},{"label":"Realtime Commenting","type":"hasLabel","value":true},{"label":"SSO","type":"hasLabel","value":true},{"label":"Multi User Realtime","type":"hasLabel","value":true},{"label":"Business Edition","type":"hasLabel","value":true},{"label":"RBAC","type":"hasLabel","value":true},{"label":"ABAC","type":"hasLabel","value":true},{"label":"Billing","type":"hasLabel","value":true},{"label":"Audit Logs","type":"hasLabel","value":true}],"requires":1},"New Developers Pod":{"conditions":[{"label":"Fork App","type":"hasLabel","value":true},{"label":"Omnibar","type":"hasLabel","value":true},{"label":"Onboarding","type":"hasLabel","value":true},{"label":"Telemetry","type":"hasLabel","value":true},{"label":"Entity Explorer","type":"hasLabel","value":true},{"label":"Generate Page","type":"hasLabel","value":true},{"label":"IDE","type":"hasLabel","value":true},{"label":"In App Comms","type":"hasLabel","value":true},{"label":"Sniping Mode","type":"hasLabel","value":true},{"label":"Design System","type":"hasLabel","value":true},{"label":"Example Apps","type":"hasLabel","value":true},{"label":"i18n","type":"hasLabel","value":true},{"label":"Welcome Screen","type":"hasLabel","value":true},{"label":"Templates","type":"hasLabel","value":true},{"label":"IDE Navigation","type":"hasLabel","value":true},{"label":"Login / Signup","type":"hasLabel","value":true},{"label":"Clean URLs","type":"hasLabel","value":true},{"label":"Embedding Apps","type":"hasLabel","value":true}],"requires":1},"BE Coders Pod":{"conditions":[{"label":"SAAS Plugins","type":"hasLabel","value":true},{"label":"SAAS Manager App","type":"hasLabel","value":true},{"label":"Data Platform Pod","type":"hasLabel","value":true},{"label":"Integrations Pod","type":"hasLabel","value":true}],"requires":1},"FE Coders Pod":{"conditions":[{"label":"JS Linting & Errors","type":"hasLabel","value":true},{"label":"Debugger","type":"hasLabel","value":true},{"label":"JS Snippets","type":"hasLabel","value":true},{"label":"Autocomplete","type":"hasLabel","value":true},{"label":"Evaluated Value","type":"hasLabel","value":true},{"label":"Slash Command","type":"hasLabel","value":true},{"label":"New JS Function","type":"hasLabel","value":true},{"label":"JS Promises","type":"hasLabel","value":true},{"label":"Function execution","type":"hasLabel","value":true},{"label":"JS Usability","type":"hasLabel","value":true},{"label":"Code Refactoring","type":"hasLabel","value":true},{"label":"storeValue","type":"hasLabel","value":true},{"label":"OnPageLoad","type":"hasLabel","value":true},{"label":"Framework Functions","type":"hasLabel","value":true},{"label":"AST","type":"hasLabel","value":true},{"label":"Code Editor","type":"hasLabel","value":true},{"label":"JS Objects","type":"hasLabel","value":true},{"label":"JS Evaluation","type":"hasLabel","value":true}],"requires":1},"App Viewers Pod":{"conditions":[{"label":"Button Widget","type":"hasLabel","value":true},{"label":"Chart Widget","type":"hasLabel","value":true},{"label":"Checkbox Widget","type":"hasLabel","value":true},{"label":"Container Widget","type":"hasLabel","value":true},{"label":"Date Picker Widget","type":"hasLabel","value":true},{"label":"Select Widget","type":"hasLabel","value":true},{"label":"File Picker Widget","type":"hasLabel","value":true},{"label":"Form Widget","type":"hasLabel","value":true},{"label":"Image Widget","type":"hasLabel","value":true},{"label":"Input Widget","type":"hasLabel","value":true},{"label":"List Widget","type":"hasLabel","value":true},{"label":"MultiSelect Widget","type":"hasLabel","value":true},{"label":"Map Widget","type":"hasLabel","value":true},{"label":"Modal Widget","type":"hasLabel","value":true},{"label":"Radio Widget","type":"hasLabel","value":true},{"label":"Rich Text Editor Widget","type":"hasLabel","value":true},{"label":"Tab Widget","type":"hasLabel","value":true},{"label":"Table Widget","type":"hasLabel","value":true},{"label":"Text Widget","type":"hasLabel","value":true},{"label":"Video Widget","type":"hasLabel","value":true},{"label":"iFrame","type":"hasLabel","value":true},{"label":"Menu Button","type":"hasLabel","value":true},{"label":"Rating","type":"hasLabel","value":true},{"label":"Widget Validation","type":"hasLabel","value":true},{"label":"reallabel","type":"hasLabel","value":true},{"label":"New Widget","type":"hasLabel","value":true},{"label":"Switch widget","type":"hasLabel","value":true},{"label":"Widget Styling","type":"hasLabel","value":true},{"label":"Audio Widget","type":"hasLabel","value":true},{"label":"Icon Button Widget","type":"hasLabel","value":true},{"label":"Checkbox Group widget","type":"hasLabel","value":true},{"label":"Stat Box Widget","type":"hasLabel","value":true},{"label":"Voice Recorder Widget","type":"hasLabel","value":true},{"label":"Calendar Widget","type":"hasLabel","value":true},{"label":"Menu Button Widget","type":"hasLabel","value":true},{"label":"Divider Widget","type":"hasLabel","value":true},{"label":"Rating Widget","type":"hasLabel","value":true},{"label":"App Navigation","type":"hasLabel","value":true},{"label":"View Mode","type":"hasLabel","value":true},{"label":"Widget Property","type":"hasLabel","value":true},{"label":"Document Viewer Widget","type":"hasLabel","value":true},{"label":"Radio Group Widget","type":"hasLabel","value":true},{"label":"Currency Input Widget","type":"hasLabel","value":true},{"label":"TreeSelect","type":"hasLabel","value":true},{"label":"MultiTree Select Widget","type":"hasLabel","value":true},{"label":"Phone Input Widget","type":"hasLabel","value":true},{"label":"JSON Form","type":"hasLabel","value":true},{"label":"All Widgets","type":"hasLabel","value":true},{"label":"App Theming","type":"hasLabel","value":true},{"label":"Button Group widget","type":"hasLabel","value":true},{"label":"Progress bar widget","type":"hasLabel","value":true},{"label":"Audio Recorder Widget","type":"hasLabel","value":true},{"label":"Camera Widget","type":"hasLabel","value":true},{"label":"Table Widget V2","type":"hasLabel","value":true},{"label":"Branding","type":"hasLabel","value":true},{"label":"Map Chart Widget","type":"hasLabel","value":true},{"label":"Code Scanner Widget","type":"hasLabel","value":true},{"label":"Widget keyboard accessibility","type":"hasLabel","value":true},{"label":"List Widget V2","type":"hasLabel","value":true}],"requires":1},"UI Builders Pod":{"conditions":[{"label":"Property Pane","type":"hasLabel","value":true},{"label":"Pages","type":"hasLabel","value":true},{"label":"Copy Paste","type":"hasLabel","value":true},{"label":"Drag & Drop","type":"hasLabel","value":true},{"label":"Undo/Redo","type":"hasLabel","value":true},{"label":"Responsive Viewport","type":"hasLabel","value":true},{"label":"Widgets Pane","type":"hasLabel","value":true},{"label":"UI Performance","type":"hasLabel","value":true},{"label":"Widget Grouping","type":"hasLabel","value":true},{"label":"Reflow & Resize","type":"hasLabel","value":true},{"label":"Canvas / Grid","type":"hasLabel","value":true},{"label":"Canvas Zooms","type":"hasLabel","value":true},{"label":"Frontend Libraries Upgrade","type":"hasLabel","value":true},{"label":"Dynamic Height","type":"hasLabel","value":true}],"requires":1},"User Education Pod":{"conditions":[{"label":"Content","type":"hasLabel","value":true},{"label":"Documentation","type":"hasLabel","value":true}],"requires":1},"DevOps Pod":{"conditions":[{"label":"Docker","type":"hasLabel","value":true},{"label":"Super Admin","type":"hasLabel","value":true},{"label":"Deployment","type":"hasLabel","value":true},{"label":"K8s","type":"hasLabel","value":true},{"label":"Email Config","type":"hasLabel","value":true},{"label":"Backup & Restore","type":"hasLabel","value":true}],"requires":1},"Design System Pod":{"conditions":[{"label":"Design System Pod","type":"hasLabel","value":true},{"label":"ads migration","type":"hasLabel","value":true}],"requires":1},"Data Platform Pod":{"conditions":[{"label":"Datasource Environments","type":"hasLabel","value":true},{"label":"Datatype issue","type":"hasLabel","value":true},{"label":"Entity Refactor","type":"hasLabel","value":true},{"label":"Core Query Execution","type":"hasLabel","value":true},{"label":"Query Management","type":"hasLabel","value":true},{"label":"Query Settings","type":"hasLabel","value":true},{"label":"SmartSubstitution","type":"hasLabel","value":true},{"label":"Query Generation","type":"hasLabel","value":true},{"label":"Query performance","type":"hasLabel","value":true},{"label":"Suggested Widgets","type":"hasLabel","value":true},{"label":"Page load executions","type":"hasLabel","value":true}],"requires":1},"Integrations Pod":{"conditions":[{"label":"New Datasource","type":"hasLabel","value":true},{"label":"Firestore","type":"hasLabel","value":true},{"label":"Google Sheets","type":"hasLabel","value":true},{"label":"Mongo","type":"hasLabel","value":true},{"label":"Redshift","type":"hasLabel","value":true},{"label":"snowflake","type":"hasLabel","value":true},{"label":"S3","type":"hasLabel","value":true},{"label":"Redis","type":"hasLabel","value":true},{"label":"Postgres","type":"hasLabel","value":true},{"label":"GraphQL Plugin","type":"hasLabel","value":true},{"label":"ArangoDB","type":"hasLabel","value":true},{"label":"MsSQL","type":"hasLabel","value":true},{"label":"REST API plugin","type":"hasLabel","value":true},{"label":"Elastic Search","type":"hasLabel","value":true},{"label":"OAuth","type":"hasLabel","value":true},{"label":"Airtable","type":"hasLabel","value":true},{"label":"CURL","type":"hasLabel","value":true},{"label":"DynamoDB","type":"hasLabel","value":true},{"label":"Zendesk","type":"hasLabel","value":true},{"label":"Hubspot","type":"hasLabel","value":true},{"label":"Query Forms","type":"hasLabel","value":true},{"label":"Twilio","type":"hasLabel","value":true},{"label":"MySQL","type":"hasLabel","value":true},{"label":"Connection pool","type":"hasLabel","value":true},{"label":"Datasources","type":"hasLabel","value":true}],"requires":1}}},"root":"."}],"labels":{"Tab Widget":{"color":"e2c76c","name":"Tab Widget","description":""},"Dont merge":{"color":"ADB39C","name":"Dont merge","description":""},"Epic":{"color":"3E4B9E","name":"Epic","description":"A zenhub epic that describes a project"},"Menu Button Widget":{"color":"235708","name":"Menu Button Widget","description":"Issues related to Menu Button widget"},"Checkbox Group widget":{"color":"D2ACD2","name":"Checkbox Group widget","description":"Issues related to Checkbox Group Widget"},"Input Widget":{"color":"ae65d8","name":"Input Widget","description":""},"Security":{"color":"99139C","name":"Security","description":""},"QA":{"color":"e2ca68","name":"QA","description":""},"Verified":{"color":"9bf416","name":"Verified","description":""},"Wont Fix":{"color":"ffffff","name":"Wont Fix","description":"This will not be worked on"},"MySQL":{"color":"c9ddc6","name":"MySQL","description":"Issues related to MySQL plugin"},"Development":{"color":"9F8A02","name":"Development","description":""},"Help Wanted":{"color":"008672","name":"Help Wanted","description":"Extra attention is needed"},"Home Page":{"color":"9c0c8e","name":"Home Page","description":"Issues related to the application home page"},"Rating Widget":{"color":"235708","name":"Rating Widget","description":"Issues related to the rating widget"},"Stat Box Widget":{"color":"f1c9ce","name":"Stat Box Widget","description":"Issues related to stat box"},"Enhancement":{"color":"a2eeef","name":"Enhancement","description":"New feature or request"},"Settings":{"color":"f7ff60","name":"Settings","description":"organization, team & user settings"},"Fork App":{"color":"5369db","name":"Fork App","description":"Issues related to forking apps"},"Container Widget":{"color":"19AD0D","name":"Container Widget","description":"Container widget"},"Papercut":{"color":"B562F6","name":"Papercut","description":""},"community":{"color":"dded34","name":"community","description":"issues reported by community members"},"Needs Design":{"color":"bfd4f2","name":"Needs Design","description":"needs design or changes to design"},"i18n":{"color":"1799b0","name":"i18n","description":"Represents issues that need to be tackled to handle internationalization"},"Rich Text Editor Widget":{"color":"f72cac","name":"Rich Text Editor Widget","description":""},"Onboarding":{"color":"d5794b","name":"Onboarding","description":"Issues related to onboarding new developers"},"Pages":{"color":"d7fd80","name":"Pages","description":"Issues related to configuring pages"},"skip-changelog":{"color":"06086F","name":"skip-changelog","description":"Adding this label to a PR prevents it from being listed in the changelog"},"Low":{"color":"79e53b","name":"Low","description":"An issue that is neither critical nor breaks a user flow"},"potential-duplicate":{"color":"d3cb2e","name":"potential-duplicate","description":"This label marks issues that are potential duplicates of already open issues"},"Audio Widget":{"color":"447B9A","name":"Audio Widget","description":"Issues related to Audio Widget"},"Firestore":{"color":"8078b0","name":"Firestore","description":"Issues related to the firestore Integration"},"New Widget":{"color":"be4cf2","name":"New Widget","description":"A request for a new widget"},"Performance":{"color":"d30e53","name":"Performance","description":"Page Load and evaluations"},"Modal Widget":{"color":"03846f","name":"Modal Widget","description":""},"UX Improvement":{"color":"f4a089","name":"UX Improvement","description":""},"S3":{"color":"8078b0","name":"S3","description":"Issues related to the S3 plugin"},"Release Blocker":{"color":"5756bf","name":"Release Blocker","description":"This issue must be resolved before the release"},"safari":{"color":"51C6AA","name":"safari","description":"Bugs seen on safari browser"},"Example Apps":{"color":"1799b0","name":"Example Apps","description":"Example apps created for new signups"},"MultiSelect Widget":{"color":"AB62D4","name":"MultiSelect Widget","description":"Issues related to MultiSelect Widget"},"Widget Styling":{"color":"37EA75","name":"Widget Styling","description":"all about widget styling"},"Calendar Widget":{"color":"8c6644","name":"Calendar Widget","description":""},"Website":{"color":"151720","name":"Website","description":"Related to www.appsmith.com website"},"Low effort":{"color":"8B59F0","name":"Low effort","description":"Something that'll take a few days to build"},"App Viewers Pod":{"color":"cd8ef9","name":"App Viewers Pod","description":"This label assigns issues to the app viewers pod"},"Checkbox Widget":{"color":"074ac6","name":"Checkbox Widget","description":""},"Spam":{"color":"620faf","name":"Spam","description":""},"Voice Recorder Widget":{"color":"85bc87","name":"Voice Recorder Widget","description":""},"Select Widget":{"color":"0c669e","name":"Select Widget","description":"Select or dropdown widget"},"Bug":{"color":"d73a4a","name":"Bug","description":"Something isn't working"},"Widget Validation":{"color":"6990BC","name":"Widget Validation","description":"Issues related to widget property validation"},"Generate Page":{"color":"f14274","name":"Generate Page","description":"Issures related to page generation"},"File Picker Widget":{"color":"6ae4f2","name":"File Picker Widget","description":""},"snowflake":{"color":"8078b0","name":"snowflake","description":"Issues related to the snowflake Integration"},"Automation":{"color":"CCAF60","name":"Automation","description":""},"hotfix":{"color":"BA3F1D","name":"hotfix","description":""},"Team Managers Pod":{"color":"bddb81","name":"Team Managers Pod","description":"Issues that team managers care about for the security and efficiency of their teams"},"Import-Export-App":{"color":"a7768a","name":"Import-Export-App","description":"Issues related to importing and exporting apps"},"High effort":{"color":"A7E87B","name":"High effort","description":"Something that'll take more than a month to build"},"Telemetry":{"color":"bc70f9","name":"Telemetry","description":"Issues related to instrumenting appsmith"},"Radio Widget":{"color":"91ef15","name":"Radio Widget","description":""},"Omnibar":{"color":"10b5ce","name":"Omnibar","description":"Issues related to the omnibar for navigation"},"Button Widget":{"color":"34efae","name":"Button Widget","description":""},"Switch widget":{"color":"33A8CE","name":"Switch widget","description":"The switch widget"},"Map Widget":{"color":"7eef7a","name":"Map Widget","description":""},"Task":{"color":"085630","name":"Task","description":"A simple Todo"},"Design System":{"color":"12b715","name":"Design System","description":"Design system"},"opera":{"color":"C63F5B","name":"opera","description":"Any issues identified on the opera browser"},"Login / Signup":{"color":"771e69","name":"Login / Signup","description":"Authentication flows"},"Image Widget":{"color":"8de8ad","name":"Image Widget","description":""},"firefox":{"color":"6d56e2","name":"firefox","description":""},"Property Pane":{"color":"b356ff","name":"Property Pane","description":"Issues related to the behaviour of the property pane"},"Deployment":{"color":"93491f","name":"Deployment","description":"Installation process of appsmith"},"Critical":{"color":"9b1b28","name":"Critical","description":"This issue needs immediate attention. Drop everything else"},"IDE":{"color":"61b2ee","name":"IDE","description":"Issues related to the IDE"},"Production":{"color":"b60205","name":"Production","description":""},"Dependencies":{"color":"0366d6","name":"Dependencies","description":"Pull requests that update a dependency file"},"Google Sheets":{"color":"8078b0","name":"Google Sheets","description":"Issues related to Google Sheets"},"Icon Button Widget":{"color":"D319CE","name":"Icon Button Widget","description":"Issues related to the icon button widget"},"Mongo":{"color":"8078b0","name":"Mongo","description":"Issues related to Mongo DB plugin"},"Documentation":{"color":"a8dff7","name":"Documentation","description":"Improvements or additions to documentation"},"TestGap":{"color":"f28253","name":"TestGap","description":"Issues identified for test plan improvement"},"keyboard shortcut":{"color":"0688B6","name":"keyboard shortcut","description":""},"Git Version Control":{"color":"C4568E","name":"Git Version Control","description":"Issues related to version control"},"Reopen":{"color":"897548","name":"Reopen","description":""},"Redshift":{"color":"8078b0","name":"Redshift","description":"Issues related to the redshift integration"},"Date Picker Widget":{"color":"ef1ce1","name":"Date Picker Widget","description":""},"Entity Explorer":{"color":"a2e2f9","name":"Entity Explorer","description":"Issues related to navigation using the entity explorer"},"JS Linting & Errors":{"color":"E56AA5","name":"JS Linting & Errors","description":"Issues related to JS Linting and errors"},"iFrame":{"color":"3CD1DB","name":"iFrame","description":"Issues related to iFrame"},"Stale":{"color":"ededed","name":"Stale","description":null},"Debugger":{"color":"e79062","name":"Debugger","description":"Issues related to the debugger"},"Quick effort":{"color":"95ED65","name":"Quick effort","description":"Something that'll take a few hours to build"},"Text Widget":{"color":"d130d1","name":"Text Widget","description":""},"Video Widget":{"color":"23dd4b","name":"Video Widget","description":""},"Datasources":{"color":"2cc0d4","name":"Datasources","description":"Issues related to configuring datasource on appsmith"},"error":{"color":"B66773","name":"error","description":"All issues connected to error messages"},"Form Widget":{"color":"09ed77","name":"Form Widget","description":""},"Needs Triaging":{"color":"e8b851","name":"Needs Triaging","description":"Needs attention from maintainers to triage"},"Autocomplete":{"color":"235708","name":"Autocomplete","description":"Issues related to the autocomplete"},"hacktoberfest":{"color":"0052cc","name":"hacktoberfest","description":"All issues that can be solved by the community during Hacktoberfest"},"Medium effort":{"color":"D31156","name":"Medium effort","description":"Something that'll take more than a week but less than a month to build"},"Release":{"color":"57e5e0","name":"Release","description":""},"High":{"color":"c94d14","name":"High","description":"This issue blocks a user from building or impacts a lot of users"},"UI Performance":{"color":"1799b0","name":"UI Performance","description":"Issues related to UI performance"},"UI Builders Pod":{"color":"517fba","name":"UI Builders Pod","description":"Issues that UI Builders face using appsmith"},"Deploy Preview":{"color":"bfdadc","name":"Deploy Preview","description":"Issues found in Deploy Preview"},"Needs Tests":{"color":"8ee263","name":"Needs Tests","description":"Needs automated tests to assert a feature/bug fix"},"Refactor":{"color":"B96662","name":"Refactor","description":"needs refactoring of code"},"Divider Widget":{"color":"235708","name":"Divider Widget","description":"Issues related to the divider widget"},"Table Widget":{"color":"2eead1","name":"Table Widget","description":""},"Needs More Info":{"color":"e54c10","name":"Needs More Info","description":"Needs additional information"},"Good First Issue":{"color":"7057ff","name":"Good First Issue","description":"Good for newcomers"},"UI Improvement":{"color":"9aeef4","name":"UI Improvement","description":""},"Backend":{"color":"d4c5f9","name":"Backend","description":"This marks the issue or pull request to reference server code"},"Frontend":{"color":"87c7f2","name":"Frontend","description":"This label marks the issue or pull request to reference client code"},"In App Comms":{"color":"9168f4","name":"In App Comms","description":"Issues around communication with appsmith instances"},"Chart Widget":{"color":"616ecc","name":"Chart Widget","description":""},"regression":{"color":"ffe5bc","name":"regression","description":""},"List Widget":{"color":"8508A0","name":"List Widget","description":"Issues related to the list widget"},"Duplicate":{"color":"cfd3d7","name":"Duplicate","description":"This issue or pull request already exists"},"JS Snippets":{"color":"8d62d2","name":"JS Snippets","description":"issues related to JS Snippets"},"Copy Paste":{"name":"Copy Paste","description":"Issues related to copy paste","color":"b4f0a9"},"Drag & Drop":{"name":"Drag & Drop","description":"Issues related to the drag & drop experience","color":"92115a"},"BE Coders Pod":{"color":"5d9848","name":"BE Coders Pod","description":"Issues related to users writing code to fetch and update data"},"FE Coders Pod":{"color":"a7effc","name":"FE Coders Pod","description":"Issues related to users writing javascript in appsmith"},"New Developers Pod":{"color":"6310da","name":"New Developers Pod","description":"Issues that new developers face while exploring the IDE"},"Sniping Mode":{"name":"Sniping Mode","description":"Issues related to sniping mode","color":"6310da"},"Redis":{"name":"Redis","description":"Issues related to Redis","color":"8078b0"},"New Datasource":{"color":"60b14c","name":"New Datasource","description":"Requests for new datasources"},"Evaluated Value":{"name":"Evaluated Value","description":"Issues related to evaluated values","color":"39f6e7"},"Undo/Redo":{"name":"Undo/Redo","description":"Issues related to undo/redo","color":"f25880"},"App Navigation":{"name":"App Navigation","description":"Issues related to the topbar navigation and configuring it","color":"12b715"},"Responsive Viewport":{"color":"12b715","name":"Responsive Viewport","description":"Issues seen on different viewports like mobile"},"Widgets Pane":{"name":"Widgets Pane","description":"Issues related to the discovery and organisation of widgets","color":"ad5d78"},"Invite users":{"color":"1799b0","name":"Invite users","description":"Invite users flow and any associated actions"},"View Mode":{"color":"1799b0","name":"View Mode","description":"Issues related to the view mode"},"User Education Pod":{"name":"User Education Pod","description":"Issues related to user education","color":"1799b0"},"Content":{"name":"Content","description":"For content related topics i.e blogs, templates, videos","color":"a8dff7"},"Embedding Apps":{"name":"Embedding Apps","description":"Issues related to embedding","color":"26ef4f"},"Slash Command":{"name":"Slash Command","description":"Issues related to the slash command","color":"a0608e"},"Widget Property":{"name":"Widget Property","description":"Issues related to adding / modifying widget properties across widgets","color":"5e92cb"},"Windows":{"name":"Windows","description":"Issues related exclusively to Windows systems","color":"b4cb8a"},"Old App Issues":{"name":"Old App Issues","description":"Issues related to apps old apps a few weeks old and app issues in stale browser session","color":"87ab18"},"Document Viewer Widget":{"name":"Document Viewer Widget","description":"Issues related to Document Viewer Widget","color":"899d4b"},"Radio Group Widget":{"name":"Radio Group Widget","description":"Issues related to radio group widget","color":"b68495"},"Super Admin":{"name":"Super Admin","description":"Issues related to the super admin page","color":"aa95cf"},"Postgres":{"name":"Postgres","description":"Postgres related issues","color":"8078b0"},"REST API plugin":{"name":"REST API plugin","description":"REST API plugin related issues","color":"8078b0"},"New JS Function":{"name":"New JS Function","description":"Issues related to adding a JS Function","color":"8e8aa4"},"Cannot Reproduce Issue":{"color":"93c9cc","name":"Cannot Reproduce Issue","description":"Issues that cannot be reproduced"},"Widget Grouping":{"name":"Widget Grouping","description":"Issues related to Widget Grouping","color":"a49951"},"K8s":{"name":"K8s","description":"Kubernetes related issues","color":"5f318a"},"Docker":{"name":"Docker","description":"Issues related to docker","color":"89b808"},"Camera Widget":{"name":"Camera Widget","description":"Issues and enhancements related to camera widget","color":"e6038e"},"SAAS Plugins":{"name":"SAAS Plugins","description":"Issues related to SAAS Plugins","color":"ef9c9d"},"JS Promises":{"name":"JS Promises","description":"Issues related to promises","color":"d7771f"},"OnPageLoad":{"name":"OnPageLoad","description":"OnPageLoad issues on functions and queries","color":"50559d"},"Function execution":{"name":"Function execution","description":"JS function execution","color":"a302b0"},"JS Usability":{"name":"JS Usability","description":"usability issues with JS editor and JS elsewhere","color":"a302b0"},"Currency Input Widget":{"name":"Currency Input Widget","description":"Issues related to currency input widget","color":"b2164f"},"TreeSelect":{"name":"TreeSelect","description":"Issues related to TreeSelect Widget","color":"a1633e"},"MultiTree Select Widget":{"name":"MultiTree Select Widget","description":"Issues related to MultiTree Select Widget","color":"a1633e"},"Welcome Screen":{"name":"Welcome Screen","description":"Issues related to the welcome screen","color":"3897be"},"Realtime Commenting":{"color":"a70b86","name":"Realtime Commenting","description":"In-app communication between teams"},"Phone Input Widget":{"name":"Phone Input Widget","description":"Issues related to the Phone Input widget","color":"a70b86"},"JSON Form":{"name":"JSON Form","description":"Issue / features related to the JSON form wiget","color":"46b209"},"All Widgets":{"name":"All Widgets","description":"Issues related to all widgets","color":"972b36"},"V1":{"name":"V1","description":"V1","color":"67ab2e"},"Reflow & Resize":{"name":"Reflow & Resize","description":"All issues related to reflow and resize experience","color":"748a13"},"App Theming":{"name":"App Theming","description":"Items that are related to the App level theming controls epic","color":"8bf430"},"SSO":{"name":"SSO","description":"Issues, requests and enhancements around Single sign-on.","color":"bf019b"},"Multi User Realtime":{"name":"Multi User Realtime","description":"Issues related to multiple users using or editing an application","color":"e7b6ce"},"Templates":{"name":"Templates","description":"Issues related to Templates","color":"c3b541"},"Ready for design":{"name":"Ready for design","description":"this issue is ready for design: it contains clear problem statements and other required information","color":"ebf442"},"Support":{"name":"Support","description":"Issues created by the A-force team to address user queries","color":"1740f3"},"Button Group widget":{"name":"Button Group widget","description":"Issue and enhancements related to the button group widget","color":"f17025"},"GraphQL Plugin":{"name":"GraphQL Plugin","description":"Issues related to GraphQL plugin","color":"8078b0"},"DevOps Pod":{"name":"DevOps Pod","description":"Issues related to devops","color":"d956c7"},"medium":{"name":"medium","description":"Issues that frustrate users due to poor UX","color":"23dfd9"},"ArangoDB":{"name":"ArangoDB","description":"Issues related to arangoDB","color":"8078b0"},"Code Refactoring":{"name":"Code Refactoring","description":"Issues related to code refactoring","color":"76310e"},"Progress bar widget":{"name":"Progress bar widget","description":"To track issues related to progress bar","color":"2d7abf"},"Audio Recorder Widget":{"name":"Audio Recorder Widget","description":"Issues related to Audio Recorder Widget","color":"9accef"},"Airtable":{"name":"Airtable","description":"Issues for Airtable","color":"60885f"},"RBAC":{"name":"RBAC","description":"Issues, requests and enhancements around RBAC.","color":"9211c3"},"Canvas / Grid":{"name":"Canvas / Grid","description":"Issues related to the canvas","color":"16b092"},"Email Config":{"name":"Email Config","description":"Issues related to configuring the email service","color":"2a21d1"},"CURL":{"name":"CURL","description":"Issues related to CURL impor","color":"60885f"},"Canvas Zooms":{"name":"Canvas Zooms","description":"Issues related to zooming the canvas","color":"e6038e"},"business":{"name":"business","description":"Features that will be a part of our business edition","color":"cd59eb"},"Action Pod":{"name":"Action Pod","description":"","color":"ee2e36"},"AutomationGap1":{"color":"a5e07c","name":"AutomationGap1","description":"Issues that needs automated tests"},"A-Force11":{"name":"A-Force11","description":"Issues raised by A-Force team","color":"d667b6"},"A-Force":{"name":"A-Force","description":"Issues raised by A-Force team","color":"274ecc"},"Business Edition":{"name":"Business Edition","description":"Features that will be a part of our business edition","color":"55184d"},"storeValue":{"name":"storeValue","description":"Issues related to the store value function","color":"5d3e66"},"Tests":{"name":"Tests","description":"test item","color":"1c6990"},"DynamoDB":{"name":"DynamoDB","description":"Issues that are related to DynamoDB should have this label","color":"60885f"},"Design System Pod":{"name":"Design System Pod","description":"Design system related issues","color":"6d1c11"},"ABAC":{"color":"e009a5","name":"ABAC","description":"User permissions and access controls"},"Backup & Restore":{"name":"Backup & Restore","description":"Issues related to backup and restore","color":"86874d"},"ads migration":{"name":"ads migration","description":"All issues related to Appsmith design system migration","color":"6d1c11"},"Billing":{"name":"Billing","description":"Billing infrastructure and flows for Business Edition and Trial users","color":"41dd97"},"Datatype issue":{"name":"Datatype issue","description":"Issues that have risen because data types weren't handled","color":"60885f"},"OAuth":{"name":"OAuth","description":"OAuth related bugs or features","color":"60885f"},"Table Widget V2":{"name":"Table Widget V2","description":"Issues related to Table Widget V2","color":"3a7192"},"AST":{"name":"AST","description":"Issues related to maintaining AST logic","color":"418fa4"},"IDE Navigation":{"name":"IDE Navigation","description":"Issues/feature requests related to IDE navigation, and context switching","color":"bc0cba"},"Query performance":{"name":"Query performance","description":"Issues that have to do with lack in performance of query execution","color":"e4d966"},"SAAS Manager App":{"name":"SAAS Manager App","description":"Issues with the SAAS manager app","color":"d427db"},"Twilio":{"name":"Twilio","description":"Issues related to Twilio integration","color":"23ba8d"},"Hubspot":{"name":"Hubspot","description":"Issues related to Hubspot integration","color":"60885f"},"Zendesk":{"name":"Zendesk","description":"Issues related to Zendesk integration","color":"60885f"},"Entity Refactor":{"name":"Entity Refactor","description":"Issues related to refactor logic","color":"418fa4"},"Branding":{"name":"Branding","description":"All issues under branding and whitelabelling appsmith ecosystem","color":"7aaaf1"},"Map Chart Widget":{"name":"Map Chart Widget","description":"Issues related to Map Chart Widgets","color":"c8397f"},"Product Catchup":{"name":"Product Catchup","description":"Issues created in the product catchup","color":"29cd2c"},"Framework Functions":{"name":"Framework Functions","description":"Issues related to internal functions like showAlert(), navigateTo() etc...","color":"c25a09"},"Frontend Libraries Upgrade":{"name":"Frontend Libraries Upgrade","description":"Issues related to frontend libraries upgrade","color":"ede1fc"},"Audit Logs":{"name":"Audit Logs","description":"Audit trails to ensure data security","color":"f3fd62"},"MsSQL":{"name":"MsSQL","description":"Issues related to MsSQL plugin","color":"8078b0"},"Data Platform Pod":{"name":"Data Platform Pod","description":"Issues related to the underlying data platform","color":"3f8c3a"},"Integrations Pod":{"name":"Integrations Pod","description":"Issues related to a specific integration","color":"5dbbb1"},"Datasource Environments":{"name":"Datasource Environments","description":"Issues related to datasource environments","color":"bb7a14"},"Elastic Search":{"name":"Elastic Search","description":"Issues related to the elastic search datasource","color":"8078b0"},"Core Query Execution":{"color":"418fa4","name":"Core Query Execution","description":"Issues related to the execution of all queries"},"Query Management":{"name":"Query Management","description":"Issues related to the CRUD of actions or queries","color":"6a5b42"},"Query Settings":{"name":"Query Settings","description":"Issues related to the settings of all queries","color":"c7da7a"},"Code Editor":{"name":"Code Editor","description":"Issues related to the code editor","color":"4ca16e"},"Query Forms":{"color":"12b253","name":"Query Forms","description":"Isuses related to the query forms"},"JS Objects":{"color":"22962c","name":"JS Objects","description":"Issues related to JS Objects"},"JS Evaluation":{"color":"22962c","name":"JS Evaluation","description":"Issues related to JS evaluation on the platform"},"SmartSubstitution":{"name":"SmartSubstitution","description":"Issues related to Smart substitution of mustache bindings in queries","color":"e4d966"},"Query Generation":{"name":"Query Generation","description":"Issues related to query generation","color":"e4d966"},"Suggested Widgets":{"name":"Suggested Widgets","description":"Issues related to suggesting widgets based on query response","color":"e4d966"},"Page load executions":{"name":"Page load executions","description":"Issues related to page load execution","color":"5696b2"},"Code Scanner Widget":{"name":"Code Scanner Widget","description":"Issues related to code scanner widget","color":"9bc1a0"},"Dynamic Height":{"name":"Dynamic Height","description":"Issues related to dynamic height of widgets","color":"9bc1a0"},"Clean URLs":{"name":"Clean URLs","description":"Issues related to clean URLs epic","color":"112623"},"Widget keyboard accessibility":{"name":"Widget keyboard accessibility","description":"All issues related to keyboard accessibility in widgets","color":"b626fd"},"Connection pool":{"name":"Connection pool","description":"issues to do with connection pooling of various plugins","color":"94fe36"},"List Widget V2":{"name":"List Widget V2","description":"Issues related to the list widget v2","color":"adaaf7"}},"success":true} \ No newline at end of file +{"runners":[{"versioning":{"source":"milestones","type":"SemVer"},"prereleaseName":"alpha","issue":{"labels":{"Team Managers Pod":{"conditions":[{"label":"Settings","type":"hasLabel","value":true},{"label":"Git Version Control","type":"hasLabel","value":true},{"label":"Home Page","type":"hasLabel","value":true},{"label":"Import-Export-App","type":"hasLabel","value":true},{"label":"Invite users","type":"hasLabel","value":true},{"label":"Realtime Commenting","type":"hasLabel","value":true},{"label":"SSO","type":"hasLabel","value":true},{"label":"Multi User Realtime","type":"hasLabel","value":true},{"label":"Business Edition","type":"hasLabel","value":true},{"label":"RBAC","type":"hasLabel","value":true},{"label":"ABAC","type":"hasLabel","value":true},{"label":"Billing","type":"hasLabel","value":true},{"label":"Audit Logs","type":"hasLabel","value":true}],"requires":1},"New Developers Pod":{"conditions":[{"label":"Fork App","type":"hasLabel","value":true},{"label":"Omnibar","type":"hasLabel","value":true},{"label":"Onboarding","type":"hasLabel","value":true},{"label":"Telemetry","type":"hasLabel","value":true},{"label":"Entity Explorer","type":"hasLabel","value":true},{"label":"Generate Page","type":"hasLabel","value":true},{"label":"IDE","type":"hasLabel","value":true},{"label":"In App Comms","type":"hasLabel","value":true},{"label":"Sniping Mode","type":"hasLabel","value":true},{"label":"Design System","type":"hasLabel","value":true},{"label":"Example Apps","type":"hasLabel","value":true},{"label":"i18n","type":"hasLabel","value":true},{"label":"Welcome Screen","type":"hasLabel","value":true},{"label":"Templates","type":"hasLabel","value":true},{"label":"IDE Navigation","type":"hasLabel","value":true},{"label":"Login / Signup","type":"hasLabel","value":true},{"label":"Clean URLs","type":"hasLabel","value":true},{"label":"Embedding Apps","type":"hasLabel","value":true}],"requires":1},"BE Coders Pod":{"conditions":[{"label":"SAAS Plugins","type":"hasLabel","value":true},{"label":"SAAS Manager App","type":"hasLabel","value":true},{"label":"Data Platform Pod","type":"hasLabel","value":true},{"label":"Integrations Pod","type":"hasLabel","value":true}],"requires":1},"FE Coders Pod":{"conditions":[{"label":"JS Linting & Errors","type":"hasLabel","value":true},{"label":"Debugger","type":"hasLabel","value":true},{"label":"JS Snippets","type":"hasLabel","value":true},{"label":"Autocomplete","type":"hasLabel","value":true},{"label":"Evaluated Value","type":"hasLabel","value":true},{"label":"Slash Command","type":"hasLabel","value":true},{"label":"New JS Function","type":"hasLabel","value":true},{"label":"JS Promises","type":"hasLabel","value":true},{"label":"Function execution","type":"hasLabel","value":true},{"label":"JS Usability","type":"hasLabel","value":true},{"label":"Code Refactoring","type":"hasLabel","value":true},{"label":"storeValue","type":"hasLabel","value":true},{"label":"OnPageLoad","type":"hasLabel","value":true},{"label":"Framework Functions","type":"hasLabel","value":true},{"label":"AST","type":"hasLabel","value":true},{"label":"Code Editor","type":"hasLabel","value":true},{"label":"JS Objects","type":"hasLabel","value":true},{"label":"JS Evaluation","type":"hasLabel","value":true}],"requires":1},"App Viewers Pod":{"conditions":[{"label":"Button Widget","type":"hasLabel","value":true},{"label":"Chart Widget","type":"hasLabel","value":true},{"label":"Checkbox Widget","type":"hasLabel","value":true},{"label":"Container Widget","type":"hasLabel","value":true},{"label":"Date Picker Widget","type":"hasLabel","value":true},{"label":"Select Widget","type":"hasLabel","value":true},{"label":"File Picker Widget","type":"hasLabel","value":true},{"label":"Form Widget","type":"hasLabel","value":true},{"label":"Image Widget","type":"hasLabel","value":true},{"label":"Input Widget","type":"hasLabel","value":true},{"label":"List Widget","type":"hasLabel","value":true},{"label":"MultiSelect Widget","type":"hasLabel","value":true},{"label":"Map Widget","type":"hasLabel","value":true},{"label":"Modal Widget","type":"hasLabel","value":true},{"label":"Radio Widget","type":"hasLabel","value":true},{"label":"Rich Text Editor Widget","type":"hasLabel","value":true},{"label":"Tab Widget","type":"hasLabel","value":true},{"label":"Table Widget","type":"hasLabel","value":true},{"label":"Text Widget","type":"hasLabel","value":true},{"label":"Video Widget","type":"hasLabel","value":true},{"label":"iFrame","type":"hasLabel","value":true},{"label":"Menu Button","type":"hasLabel","value":true},{"label":"Rating","type":"hasLabel","value":true},{"label":"Widget Validation","type":"hasLabel","value":true},{"label":"reallabel","type":"hasLabel","value":true},{"label":"New Widget","type":"hasLabel","value":true},{"label":"Switch widget","type":"hasLabel","value":true},{"label":"Widget Styling","type":"hasLabel","value":true},{"label":"Audio Widget","type":"hasLabel","value":true},{"label":"Icon Button Widget","type":"hasLabel","value":true},{"label":"Checkbox Group widget","type":"hasLabel","value":true},{"label":"Stat Box Widget","type":"hasLabel","value":true},{"label":"Voice Recorder Widget","type":"hasLabel","value":true},{"label":"Calendar Widget","type":"hasLabel","value":true},{"label":"Menu Button Widget","type":"hasLabel","value":true},{"label":"Divider Widget","type":"hasLabel","value":true},{"label":"Rating Widget","type":"hasLabel","value":true},{"label":"App Navigation","type":"hasLabel","value":true},{"label":"View Mode","type":"hasLabel","value":true},{"label":"Widget Property","type":"hasLabel","value":true},{"label":"Document Viewer Widget","type":"hasLabel","value":true},{"label":"Radio Group Widget","type":"hasLabel","value":true},{"label":"Currency Input Widget","type":"hasLabel","value":true},{"label":"TreeSelect","type":"hasLabel","value":true},{"label":"MultiTree Select Widget","type":"hasLabel","value":true},{"label":"Phone Input Widget","type":"hasLabel","value":true},{"label":"JSON Form","type":"hasLabel","value":true},{"label":"All Widgets","type":"hasLabel","value":true},{"label":"App Theming","type":"hasLabel","value":true},{"label":"Button Group widget","type":"hasLabel","value":true},{"label":"Progress bar widget","type":"hasLabel","value":true},{"label":"Audio Recorder Widget","type":"hasLabel","value":true},{"label":"Camera Widget","type":"hasLabel","value":true},{"label":"Table Widget V2","type":"hasLabel","value":true},{"label":"Branding","type":"hasLabel","value":true},{"label":"Map Chart Widget","type":"hasLabel","value":true},{"label":"Code Scanner Widget","type":"hasLabel","value":true},{"label":"Widget keyboard accessibility","type":"hasLabel","value":true},{"label":"List Widget V2","type":"hasLabel","value":true}],"requires":1},"UI Builders Pod":{"conditions":[{"label":"Property Pane","type":"hasLabel","value":true},{"label":"Pages","type":"hasLabel","value":true},{"label":"Copy Paste","type":"hasLabel","value":true},{"label":"Drag & Drop","type":"hasLabel","value":true},{"label":"Undo/Redo","type":"hasLabel","value":true},{"label":"Responsive Viewport","type":"hasLabel","value":true},{"label":"Widgets Pane","type":"hasLabel","value":true},{"label":"UI Performance","type":"hasLabel","value":true},{"label":"Widget Grouping","type":"hasLabel","value":true},{"label":"Reflow & Resize","type":"hasLabel","value":true},{"label":"Canvas / Grid","type":"hasLabel","value":true},{"label":"Canvas Zooms","type":"hasLabel","value":true},{"label":"Frontend Libraries Upgrade","type":"hasLabel","value":true},{"label":"Auto Height","type":"hasLabel","value":true}],"requires":1},"User Education Pod":{"conditions":[{"label":"Content","type":"hasLabel","value":true},{"label":"Documentation","type":"hasLabel","value":true}],"requires":1},"DevOps Pod":{"conditions":[{"label":"Docker","type":"hasLabel","value":true},{"label":"Super Admin","type":"hasLabel","value":true},{"label":"Deployment","type":"hasLabel","value":true},{"label":"K8s","type":"hasLabel","value":true},{"label":"Email Config","type":"hasLabel","value":true},{"label":"Backup & Restore","type":"hasLabel","value":true}],"requires":1},"Design System Pod":{"conditions":[{"label":"Design System Pod","type":"hasLabel","value":true},{"label":"ads migration","type":"hasLabel","value":true},{"label":"ads deduplication ","type":"hasLabel","value":true}],"requires":1},"Data Platform Pod":{"conditions":[{"label":"Datasource Environments","type":"hasLabel","value":true},{"label":"Datatype issue","type":"hasLabel","value":true},{"label":"Entity Refactor","type":"hasLabel","value":true},{"label":"Core Query Execution","type":"hasLabel","value":true},{"label":"Query Management","type":"hasLabel","value":true},{"label":"Query Settings","type":"hasLabel","value":true},{"label":"SmartSubstitution","type":"hasLabel","value":true},{"label":"Query Generation","type":"hasLabel","value":true},{"label":"Query performance","type":"hasLabel","value":true},{"label":"Suggested Widgets","type":"hasLabel","value":true},{"label":"Page load executions","type":"hasLabel","value":true}],"requires":1},"Integrations Pod":{"conditions":[{"label":"New Datasource","type":"hasLabel","value":true},{"label":"Firestore","type":"hasLabel","value":true},{"label":"Google Sheets","type":"hasLabel","value":true},{"label":"Mongo","type":"hasLabel","value":true},{"label":"Redshift","type":"hasLabel","value":true},{"label":"snowflake","type":"hasLabel","value":true},{"label":"S3","type":"hasLabel","value":true},{"label":"Redis","type":"hasLabel","value":true},{"label":"Postgres","type":"hasLabel","value":true},{"label":"GraphQL Plugin","type":"hasLabel","value":true},{"label":"ArangoDB","type":"hasLabel","value":true},{"label":"MsSQL","type":"hasLabel","value":true},{"label":"REST API plugin","type":"hasLabel","value":true},{"label":"Elastic Search","type":"hasLabel","value":true},{"label":"OAuth","type":"hasLabel","value":true},{"label":"Airtable","type":"hasLabel","value":true},{"label":"CURL","type":"hasLabel","value":true},{"label":"DynamoDB","type":"hasLabel","value":true},{"label":"Zendesk","type":"hasLabel","value":true},{"label":"Hubspot","type":"hasLabel","value":true},{"label":"Query Forms","type":"hasLabel","value":true},{"label":"Twilio","type":"hasLabel","value":true},{"label":"MySQL","type":"hasLabel","value":true},{"label":"Connection pool","type":"hasLabel","value":true},{"label":"Datasources","type":"hasLabel","value":true}],"requires":1}}},"root":"."}],"labels":{"Tab Widget":{"color":"e2c76c","name":"Tab Widget","description":""},"Dont merge":{"color":"ADB39C","name":"Dont merge","description":""},"Epic":{"color":"3E4B9E","name":"Epic","description":"A zenhub epic that describes a project"},"Menu Button Widget":{"color":"235708","name":"Menu Button Widget","description":"Issues related to Menu Button widget"},"Checkbox Group widget":{"color":"D2ACD2","name":"Checkbox Group widget","description":"Issues related to Checkbox Group Widget"},"Input Widget":{"color":"ae65d8","name":"Input Widget","description":""},"Security":{"color":"99139C","name":"Security","description":""},"QA":{"color":"e2ca68","name":"QA","description":""},"Verified":{"color":"9bf416","name":"Verified","description":""},"Wont Fix":{"color":"ffffff","name":"Wont Fix","description":"This will not be worked on"},"MySQL":{"color":"c9ddc6","name":"MySQL","description":"Issues related to MySQL plugin"},"Development":{"color":"9F8A02","name":"Development","description":""},"Help Wanted":{"color":"008672","name":"Help Wanted","description":"Extra attention is needed"},"Home Page":{"color":"9c0c8e","name":"Home Page","description":"Issues related to the application home page"},"Rating Widget":{"color":"235708","name":"Rating Widget","description":"Issues related to the rating widget"},"Stat Box Widget":{"color":"f1c9ce","name":"Stat Box Widget","description":"Issues related to stat box"},"Enhancement":{"color":"a2eeef","name":"Enhancement","description":"New feature or request"},"Settings":{"color":"f7ff60","name":"Settings","description":"organization, team & user settings"},"Fork App":{"color":"5369db","name":"Fork App","description":"Issues related to forking apps"},"Container Widget":{"color":"19AD0D","name":"Container Widget","description":"Container widget"},"Papercut":{"color":"B562F6","name":"Papercut","description":""},"community":{"color":"dded34","name":"community","description":"issues reported by community members"},"Needs Design":{"color":"bfd4f2","name":"Needs Design","description":"needs design or changes to design"},"i18n":{"color":"1799b0","name":"i18n","description":"Represents issues that need to be tackled to handle internationalization"},"Rich Text Editor Widget":{"color":"f72cac","name":"Rich Text Editor Widget","description":""},"Onboarding":{"color":"d5794b","name":"Onboarding","description":"Issues related to onboarding new developers"},"Pages":{"color":"d7fd80","name":"Pages","description":"Issues related to configuring pages"},"skip-changelog":{"color":"06086F","name":"skip-changelog","description":"Adding this label to a PR prevents it from being listed in the changelog"},"Low":{"color":"79e53b","name":"Low","description":"An issue that is neither critical nor breaks a user flow"},"potential-duplicate":{"color":"d3cb2e","name":"potential-duplicate","description":"This label marks issues that are potential duplicates of already open issues"},"Audio Widget":{"color":"447B9A","name":"Audio Widget","description":"Issues related to Audio Widget"},"Firestore":{"color":"8078b0","name":"Firestore","description":"Issues related to the firestore Integration"},"New Widget":{"color":"be4cf2","name":"New Widget","description":"A request for a new widget"},"Performance":{"color":"d30e53","name":"Performance","description":"Page Load and evaluations"},"Modal Widget":{"color":"03846f","name":"Modal Widget","description":""},"UX Improvement":{"color":"f4a089","name":"UX Improvement","description":""},"S3":{"color":"8078b0","name":"S3","description":"Issues related to the S3 plugin"},"Release Blocker":{"color":"5756bf","name":"Release Blocker","description":"This issue must be resolved before the release"},"safari":{"color":"51C6AA","name":"safari","description":"Bugs seen on safari browser"},"Example Apps":{"color":"1799b0","name":"Example Apps","description":"Example apps created for new signups"},"MultiSelect Widget":{"color":"AB62D4","name":"MultiSelect Widget","description":"Issues related to MultiSelect Widget"},"Widget Styling":{"color":"37EA75","name":"Widget Styling","description":"all about widget styling"},"Calendar Widget":{"color":"8c6644","name":"Calendar Widget","description":""},"Website":{"color":"151720","name":"Website","description":"Related to www.appsmith.com website"},"Low effort":{"color":"8B59F0","name":"Low effort","description":"Something that'll take a few days to build"},"App Viewers Pod":{"color":"cd8ef9","name":"App Viewers Pod","description":"This label assigns issues to the app viewers pod"},"Checkbox Widget":{"color":"074ac6","name":"Checkbox Widget","description":""},"Spam":{"color":"620faf","name":"Spam","description":""},"Voice Recorder Widget":{"color":"85bc87","name":"Voice Recorder Widget","description":""},"Select Widget":{"color":"0c669e","name":"Select Widget","description":"Select or dropdown widget"},"Bug":{"color":"d73a4a","name":"Bug","description":"Something isn't working"},"Widget Validation":{"color":"6990BC","name":"Widget Validation","description":"Issues related to widget property validation"},"Generate Page":{"color":"f14274","name":"Generate Page","description":"Issures related to page generation"},"File Picker Widget":{"color":"6ae4f2","name":"File Picker Widget","description":""},"snowflake":{"color":"8078b0","name":"snowflake","description":"Issues related to the snowflake Integration"},"Automation":{"color":"CCAF60","name":"Automation","description":""},"hotfix":{"color":"BA3F1D","name":"hotfix","description":""},"Team Managers Pod":{"color":"bddb81","name":"Team Managers Pod","description":"Issues that team managers care about for the security and efficiency of their teams"},"Import-Export-App":{"color":"a7768a","name":"Import-Export-App","description":"Issues related to importing and exporting apps"},"High effort":{"color":"A7E87B","name":"High effort","description":"Something that'll take more than a month to build"},"Telemetry":{"color":"bc70f9","name":"Telemetry","description":"Issues related to instrumenting appsmith"},"Radio Widget":{"color":"91ef15","name":"Radio Widget","description":""},"Omnibar":{"color":"10b5ce","name":"Omnibar","description":"Issues related to the omnibar for navigation"},"Button Widget":{"color":"34efae","name":"Button Widget","description":""},"Switch widget":{"color":"33A8CE","name":"Switch widget","description":"The switch widget"},"Map Widget":{"color":"7eef7a","name":"Map Widget","description":""},"Task":{"color":"085630","name":"Task","description":"A simple Todo"},"Design System":{"color":"12b715","name":"Design System","description":"Design system"},"opera":{"color":"C63F5B","name":"opera","description":"Any issues identified on the opera browser"},"Login / Signup":{"color":"771e69","name":"Login / Signup","description":"Authentication flows"},"Image Widget":{"color":"8de8ad","name":"Image Widget","description":""},"firefox":{"color":"6d56e2","name":"firefox","description":""},"Property Pane":{"color":"b356ff","name":"Property Pane","description":"Issues related to the behaviour of the property pane"},"Deployment":{"color":"93491f","name":"Deployment","description":"Installation process of appsmith"},"Critical":{"color":"9b1b28","name":"Critical","description":"This issue needs immediate attention. Drop everything else"},"IDE":{"color":"61b2ee","name":"IDE","description":"Issues related to the IDE"},"Production":{"color":"b60205","name":"Production","description":""},"Dependencies":{"color":"0366d6","name":"Dependencies","description":"Pull requests that update a dependency file"},"Google Sheets":{"color":"8078b0","name":"Google Sheets","description":"Issues related to Google Sheets"},"Icon Button Widget":{"color":"D319CE","name":"Icon Button Widget","description":"Issues related to the icon button widget"},"Mongo":{"color":"8078b0","name":"Mongo","description":"Issues related to Mongo DB plugin"},"Documentation":{"color":"a8dff7","name":"Documentation","description":"Improvements or additions to documentation"},"TestGap":{"color":"f28253","name":"TestGap","description":"Issues identified for test plan improvement"},"keyboard shortcut":{"color":"0688B6","name":"keyboard shortcut","description":""},"Git Version Control":{"color":"C4568E","name":"Git Version Control","description":"Issues related to version control"},"Reopen":{"color":"897548","name":"Reopen","description":""},"Redshift":{"color":"8078b0","name":"Redshift","description":"Issues related to the redshift integration"},"Date Picker Widget":{"color":"ef1ce1","name":"Date Picker Widget","description":""},"Entity Explorer":{"color":"a2e2f9","name":"Entity Explorer","description":"Issues related to navigation using the entity explorer"},"JS Linting & Errors":{"color":"E56AA5","name":"JS Linting & Errors","description":"Issues related to JS Linting and errors"},"iFrame":{"color":"3CD1DB","name":"iFrame","description":"Issues related to iFrame"},"Stale":{"color":"ededed","name":"Stale","description":null},"Debugger":{"color":"e79062","name":"Debugger","description":"Issues related to the debugger"},"Quick effort":{"color":"95ED65","name":"Quick effort","description":"Something that'll take a few hours to build"},"Text Widget":{"color":"d130d1","name":"Text Widget","description":""},"Video Widget":{"color":"23dd4b","name":"Video Widget","description":""},"Datasources":{"color":"2cc0d4","name":"Datasources","description":"Issues related to configuring datasource on appsmith"},"error":{"color":"B66773","name":"error","description":"All issues connected to error messages"},"Form Widget":{"color":"09ed77","name":"Form Widget","description":""},"Needs Triaging":{"color":"e8b851","name":"Needs Triaging","description":"Needs attention from maintainers to triage"},"Autocomplete":{"color":"235708","name":"Autocomplete","description":"Issues related to the autocomplete"},"hacktoberfest":{"color":"0052cc","name":"hacktoberfest","description":"All issues that can be solved by the community during Hacktoberfest"},"Medium effort":{"color":"D31156","name":"Medium effort","description":"Something that'll take more than a week but less than a month to build"},"Release":{"color":"57e5e0","name":"Release","description":""},"High":{"color":"c94d14","name":"High","description":"This issue blocks a user from building or impacts a lot of users"},"UI Performance":{"color":"1799b0","name":"UI Performance","description":"Issues related to UI performance"},"UI Builders Pod":{"color":"517fba","name":"UI Builders Pod","description":"Issues that UI Builders face using appsmith"},"Deploy Preview":{"color":"bfdadc","name":"Deploy Preview","description":"Issues found in Deploy Preview"},"Needs Tests":{"color":"8ee263","name":"Needs Tests","description":"Needs automated tests to assert a feature/bug fix"},"Refactor":{"color":"B96662","name":"Refactor","description":"needs refactoring of code"},"Divider Widget":{"color":"235708","name":"Divider Widget","description":"Issues related to the divider widget"},"Table Widget":{"color":"2eead1","name":"Table Widget","description":""},"Needs More Info":{"color":"e54c10","name":"Needs More Info","description":"Needs additional information"},"Good First Issue":{"color":"7057ff","name":"Good First Issue","description":"Good for newcomers"},"UI Improvement":{"color":"9aeef4","name":"UI Improvement","description":""},"Backend":{"color":"d4c5f9","name":"Backend","description":"This marks the issue or pull request to reference server code"},"Frontend":{"color":"87c7f2","name":"Frontend","description":"This label marks the issue or pull request to reference client code"},"In App Comms":{"color":"9168f4","name":"In App Comms","description":"Issues around communication with appsmith instances"},"Chart Widget":{"color":"616ecc","name":"Chart Widget","description":""},"regression":{"color":"ffe5bc","name":"regression","description":""},"List Widget":{"color":"8508A0","name":"List Widget","description":"Issues related to the list widget"},"Duplicate":{"color":"cfd3d7","name":"Duplicate","description":"This issue or pull request already exists"},"JS Snippets":{"color":"8d62d2","name":"JS Snippets","description":"issues related to JS Snippets"},"Copy Paste":{"name":"Copy Paste","description":"Issues related to copy paste","color":"b4f0a9"},"Drag & Drop":{"name":"Drag & Drop","description":"Issues related to the drag & drop experience","color":"92115a"},"BE Coders Pod":{"color":"5d9848","name":"BE Coders Pod","description":"Issues related to users writing code to fetch and update data"},"FE Coders Pod":{"color":"a7effc","name":"FE Coders Pod","description":"Issues related to users writing javascript in appsmith"},"New Developers Pod":{"color":"6310da","name":"New Developers Pod","description":"Issues that new developers face while exploring the IDE"},"Sniping Mode":{"name":"Sniping Mode","description":"Issues related to sniping mode","color":"6310da"},"Redis":{"name":"Redis","description":"Issues related to Redis","color":"8078b0"},"New Datasource":{"color":"60b14c","name":"New Datasource","description":"Requests for new datasources"},"Evaluated Value":{"name":"Evaluated Value","description":"Issues related to evaluated values","color":"39f6e7"},"Undo/Redo":{"name":"Undo/Redo","description":"Issues related to undo/redo","color":"f25880"},"App Navigation":{"name":"App Navigation","description":"Issues related to the topbar navigation and configuring it","color":"12b715"},"Responsive Viewport":{"color":"12b715","name":"Responsive Viewport","description":"Issues seen on different viewports like mobile"},"Widgets Pane":{"name":"Widgets Pane","description":"Issues related to the discovery and organisation of widgets","color":"ad5d78"},"Invite users":{"color":"1799b0","name":"Invite users","description":"Invite users flow and any associated actions"},"View Mode":{"color":"1799b0","name":"View Mode","description":"Issues related to the view mode"},"User Education Pod":{"name":"User Education Pod","description":"Issues related to user education","color":"1799b0"},"Content":{"name":"Content","description":"For content related topics i.e blogs, templates, videos","color":"a8dff7"},"Embedding Apps":{"name":"Embedding Apps","description":"Issues related to embedding","color":"26ef4f"},"Slash Command":{"name":"Slash Command","description":"Issues related to the slash command","color":"a0608e"},"Widget Property":{"name":"Widget Property","description":"Issues related to adding / modifying widget properties across widgets","color":"5e92cb"},"Windows":{"name":"Windows","description":"Issues related exclusively to Windows systems","color":"b4cb8a"},"Old App Issues":{"name":"Old App Issues","description":"Issues related to apps old apps a few weeks old and app issues in stale browser session","color":"87ab18"},"Document Viewer Widget":{"name":"Document Viewer Widget","description":"Issues related to Document Viewer Widget","color":"899d4b"},"Radio Group Widget":{"name":"Radio Group Widget","description":"Issues related to radio group widget","color":"b68495"},"Super Admin":{"name":"Super Admin","description":"Issues related to the super admin page","color":"aa95cf"},"Postgres":{"name":"Postgres","description":"Postgres related issues","color":"8078b0"},"REST API plugin":{"name":"REST API plugin","description":"REST API plugin related issues","color":"8078b0"},"New JS Function":{"name":"New JS Function","description":"Issues related to adding a JS Function","color":"8e8aa4"},"Cannot Reproduce Issue":{"color":"93c9cc","name":"Cannot Reproduce Issue","description":"Issues that cannot be reproduced"},"Widget Grouping":{"name":"Widget Grouping","description":"Issues related to Widget Grouping","color":"a49951"},"K8s":{"name":"K8s","description":"Kubernetes related issues","color":"5f318a"},"Docker":{"name":"Docker","description":"Issues related to docker","color":"89b808"},"Camera Widget":{"name":"Camera Widget","description":"Issues and enhancements related to camera widget","color":"e6038e"},"SAAS Plugins":{"name":"SAAS Plugins","description":"Issues related to SAAS Plugins","color":"ef9c9d"},"JS Promises":{"name":"JS Promises","description":"Issues related to promises","color":"d7771f"},"OnPageLoad":{"name":"OnPageLoad","description":"OnPageLoad issues on functions and queries","color":"50559d"},"Function execution":{"name":"Function execution","description":"JS function execution","color":"a302b0"},"JS Usability":{"name":"JS Usability","description":"usability issues with JS editor and JS elsewhere","color":"a302b0"},"Currency Input Widget":{"name":"Currency Input Widget","description":"Issues related to currency input widget","color":"b2164f"},"TreeSelect":{"name":"TreeSelect","description":"Issues related to TreeSelect Widget","color":"a1633e"},"MultiTree Select Widget":{"name":"MultiTree Select Widget","description":"Issues related to MultiTree Select Widget","color":"a1633e"},"Welcome Screen":{"name":"Welcome Screen","description":"Issues related to the welcome screen","color":"3897be"},"Realtime Commenting":{"color":"a70b86","name":"Realtime Commenting","description":"In-app communication between teams"},"Phone Input Widget":{"name":"Phone Input Widget","description":"Issues related to the Phone Input widget","color":"a70b86"},"JSON Form":{"name":"JSON Form","description":"Issue / features related to the JSON form wiget","color":"46b209"},"All Widgets":{"name":"All Widgets","description":"Issues related to all widgets","color":"972b36"},"V1":{"name":"V1","description":"V1","color":"67ab2e"},"Reflow & Resize":{"name":"Reflow & Resize","description":"All issues related to reflow and resize experience","color":"748a13"},"App Theming":{"name":"App Theming","description":"Items that are related to the App level theming controls epic","color":"8bf430"},"SSO":{"name":"SSO","description":"Issues, requests and enhancements around Single sign-on.","color":"bf019b"},"Multi User Realtime":{"name":"Multi User Realtime","description":"Issues related to multiple users using or editing an application","color":"e7b6ce"},"Templates":{"name":"Templates","description":"Issues related to Templates","color":"c3b541"},"Ready for design":{"name":"Ready for design","description":"this issue is ready for design: it contains clear problem statements and other required information","color":"ebf442"},"Support":{"name":"Support","description":"Issues created by the A-force team to address user queries","color":"1740f3"},"Button Group widget":{"name":"Button Group widget","description":"Issue and enhancements related to the button group widget","color":"f17025"},"GraphQL Plugin":{"name":"GraphQL Plugin","description":"Issues related to GraphQL plugin","color":"8078b0"},"DevOps Pod":{"name":"DevOps Pod","description":"Issues related to devops","color":"d956c7"},"medium":{"name":"medium","description":"Issues that frustrate users due to poor UX","color":"23dfd9"},"ArangoDB":{"name":"ArangoDB","description":"Issues related to arangoDB","color":"8078b0"},"Code Refactoring":{"name":"Code Refactoring","description":"Issues related to code refactoring","color":"76310e"},"Progress bar widget":{"name":"Progress bar widget","description":"To track issues related to progress bar","color":"2d7abf"},"Audio Recorder Widget":{"name":"Audio Recorder Widget","description":"Issues related to Audio Recorder Widget","color":"9accef"},"Airtable":{"name":"Airtable","description":"Issues for Airtable","color":"60885f"},"RBAC":{"name":"RBAC","description":"Issues, requests and enhancements around RBAC.","color":"9211c3"},"Canvas / Grid":{"name":"Canvas / Grid","description":"Issues related to the canvas","color":"16b092"},"Email Config":{"name":"Email Config","description":"Issues related to configuring the email service","color":"2a21d1"},"CURL":{"name":"CURL","description":"Issues related to CURL impor","color":"60885f"},"Canvas Zooms":{"name":"Canvas Zooms","description":"Issues related to zooming the canvas","color":"e6038e"},"business":{"name":"business","description":"Features that will be a part of our business edition","color":"cd59eb"},"Action Pod":{"name":"Action Pod","description":"","color":"ee2e36"},"AutomationGap1":{"color":"a5e07c","name":"AutomationGap1","description":"Issues that needs automated tests"},"A-Force11":{"name":"A-Force11","description":"Issues raised by A-Force team","color":"d667b6"},"A-Force":{"name":"A-Force","description":"Issues raised by A-Force team","color":"274ecc"},"Business Edition":{"name":"Business Edition","description":"Features that will be a part of our business edition","color":"55184d"},"storeValue":{"name":"storeValue","description":"Issues related to the store value function","color":"5d3e66"},"Tests":{"name":"Tests","description":"test item","color":"1c6990"},"DynamoDB":{"name":"DynamoDB","description":"Issues that are related to DynamoDB should have this label","color":"60885f"},"Design System Pod":{"name":"Design System Pod","description":"Design system related issues","color":"6d1c11"},"ABAC":{"color":"e009a5","name":"ABAC","description":"User permissions and access controls"},"Backup & Restore":{"name":"Backup & Restore","description":"Issues related to backup and restore","color":"86874d"},"ads migration":{"name":"ads migration","description":"All issues related to Appsmith design system migration","color":"6d1c11"},"Billing":{"name":"Billing","description":"Billing infrastructure and flows for Business Edition and Trial users","color":"41dd97"},"Datatype issue":{"name":"Datatype issue","description":"Issues that have risen because data types weren't handled","color":"60885f"},"OAuth":{"name":"OAuth","description":"OAuth related bugs or features","color":"60885f"},"Table Widget V2":{"name":"Table Widget V2","description":"Issues related to Table Widget V2","color":"3a7192"},"AST":{"name":"AST","description":"Issues related to maintaining AST logic","color":"418fa4"},"IDE Navigation":{"name":"IDE Navigation","description":"Issues/feature requests related to IDE navigation, and context switching","color":"bc0cba"},"Query performance":{"name":"Query performance","description":"Issues that have to do with lack in performance of query execution","color":"e4d966"},"SAAS Manager App":{"name":"SAAS Manager App","description":"Issues with the SAAS manager app","color":"d427db"},"Twilio":{"name":"Twilio","description":"Issues related to Twilio integration","color":"23ba8d"},"Hubspot":{"name":"Hubspot","description":"Issues related to Hubspot integration","color":"60885f"},"Zendesk":{"name":"Zendesk","description":"Issues related to Zendesk integration","color":"60885f"},"Entity Refactor":{"name":"Entity Refactor","description":"Issues related to refactor logic","color":"418fa4"},"Branding":{"name":"Branding","description":"All issues under branding and whitelabelling appsmith ecosystem","color":"7aaaf1"},"Map Chart Widget":{"name":"Map Chart Widget","description":"Issues related to Map Chart Widgets","color":"c8397f"},"Product Catchup":{"name":"Product Catchup","description":"Issues created in the product catchup","color":"29cd2c"},"Framework Functions":{"name":"Framework Functions","description":"Issues related to internal functions like showAlert(), navigateTo() etc...","color":"c25a09"},"Frontend Libraries Upgrade":{"name":"Frontend Libraries Upgrade","description":"Issues related to frontend libraries upgrade","color":"ede1fc"},"Audit Logs":{"name":"Audit Logs","description":"Audit trails to ensure data security","color":"f3fd62"},"MsSQL":{"name":"MsSQL","description":"Issues related to MsSQL plugin","color":"8078b0"},"Data Platform Pod":{"name":"Data Platform Pod","description":"Issues related to the underlying data platform","color":"3f8c3a"},"Integrations Pod":{"name":"Integrations Pod","description":"Issues related to a specific integration","color":"5dbbb1"},"Datasource Environments":{"name":"Datasource Environments","description":"Issues related to datasource environments","color":"bb7a14"},"Elastic Search":{"name":"Elastic Search","description":"Issues related to the elastic search datasource","color":"8078b0"},"Core Query Execution":{"color":"418fa4","name":"Core Query Execution","description":"Issues related to the execution of all queries"},"Query Management":{"name":"Query Management","description":"Issues related to the CRUD of actions or queries","color":"6a5b42"},"Query Settings":{"name":"Query Settings","description":"Issues related to the settings of all queries","color":"c7da7a"},"Code Editor":{"name":"Code Editor","description":"Issues related to the code editor","color":"4ca16e"},"Query Forms":{"color":"12b253","name":"Query Forms","description":"Isuses related to the query forms"},"JS Objects":{"color":"22962c","name":"JS Objects","description":"Issues related to JS Objects"},"JS Evaluation":{"color":"22962c","name":"JS Evaluation","description":"Issues related to JS evaluation on the platform"},"SmartSubstitution":{"name":"SmartSubstitution","description":"Issues related to Smart substitution of mustache bindings in queries","color":"e4d966"},"Query Generation":{"name":"Query Generation","description":"Issues related to query generation","color":"e4d966"},"Suggested Widgets":{"name":"Suggested Widgets","description":"Issues related to suggesting widgets based on query response","color":"e4d966"},"Page load executions":{"name":"Page load executions","description":"Issues related to page load execution","color":"5696b2"},"Code Scanner Widget":{"name":"Code Scanner Widget","description":"Issues related to code scanner widget","color":"9bc1a0"},"Clean URLs":{"name":"Clean URLs","description":"Issues related to clean URLs epic","color":"112623"},"Widget keyboard accessibility":{"name":"Widget keyboard accessibility","description":"All issues related to keyboard accessibility in widgets","color":"b626fd"},"Connection pool":{"name":"Connection pool","description":"issues to do with connection pooling of various plugins","color":"94fe36"},"List Widget V2":{"name":"List Widget V2","description":"Issues related to the list widget v2","color":"adaaf7"},"Auto Height":{"name":"Auto Height","description":"Issues related to dynamic height of widgets","color":"5149cf"},"cypress_failed_test":{"name":"cypress_failed_test","description":"Cypress failed tests","color":"4745d5"},"ads deduplication ":{"name":"ads deduplication ","description":"Replacing component with ADS components","color":"741fa1"}},"success":true} \ No newline at end of file diff --git a/.github/workflows/fat-migration.yml b/.github/workflows/fat-migration.yml new file mode 100644 index 000000000000..2f612d31bebe --- /dev/null +++ b/.github/workflows/fat-migration.yml @@ -0,0 +1,782 @@ +name: Test, build and push Docker Image Fat migration + +on: + # This line enables manual triggering of this workflow. + workflow_dispatch: + + + +jobs: + buildClient: + # If the build has been triggered manually via workflow_dispatch or via a push to protected branches + # then we don't check for the PR approved state + if: | + github.event_name == 'workflow_dispatch' || + github.event_name == 'push' || + (github.event_name == 'pull_request_review' && + github.event.review.state == 'approved' && + github.event.pull_request.head.repo.full_name == github.repository) + runs-on: ubuntu-latest + defaults: + run: + working-directory: app/client + shell: bash + + steps: + # Checkout the code + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + + # Checkout the code + - name: Checkout the merged commit from PR and base branch + if: github.event_name == 'pull_request_review' + uses: actions/checkout@v2 + with: + fetch-depth: 0 + ref: refs/pull/${{ github.event.pull_request.number }}/merge + + - name: Checkout the head commit of the branch + if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' + uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Figure out the PR number + run: echo ${{ github.event.pull_request.number }} + + # Timestamp will be used to create cache key + - id: timestamp + run: echo "::set-output name=timestamp::$(date +'%Y-%m-%dT%H:%M:%S')" + + # In case this is second attempt try restoring status of the prior attempt from cache + - name: Restore the previous run result + uses: actions/cache@v2 + with: + path: | + ~/run_result + key: ${{ github.run_id }}-${{ github.job }}-${{ steps.timestamp.outputs.timestamp }} + restore-keys: | + ${{ github.run_id }}-${{ github.job }}- + + # Fetch prior run result + - name: Get the previous run result + id: run_result + run: cat ~/run_result 2>/dev/null || echo 'default' + + # Incase of prior failure run the job + - if: steps.run_result.outputs.run_result != 'success' + run: echo "I'm alive!" && exit 0 + + # Set status = success + - run: echo "::set-output name=run_result::success" > ~/run_result + + - name: Use Node.js 16.14.0 + if: steps.run_result.outputs.run_result != 'success' + uses: actions/setup-node@v1 + with: + node-version: "16.14.0" + + - name: Get yarn cache directory path + if: steps.run_result.outputs.run_result != 'success' + id: yarn-dep-cache-dir-path + run: echo "::set-output name=dir::$(yarn cache dir)" + + # Retrieve npm dependencies from cache. After a successful run, these dependencies are cached again + - name: Cache npm dependencies + if: steps.run_result.outputs.run_result != 'success' + id: yarn-dep-cache + uses: actions/cache@v2 + env: + cache-name: cache-yarn-dependencies + with: + path: | + ${{ steps.yarn-dep-cache-dir-path.outputs.dir }} + key: ${{ runner.os }}-yarn-dep-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn-dep- + + # Install all the dependencies + - name: Install dependencies + if: steps.run_result.outputs.run_result != 'success' + run: yarn install --frozen-lockfile + + - name: Set the build environment based on the branch + if: steps.run_result.outputs.run_result != 'success' + id: vars + run: | + echo "::set-output name=REACT_APP_ENVIRONMENT::DEVELOPMENT" + if [[ "${{github.ref}}" == "refs/heads/master" ]]; then + echo "::set-output name=REACT_APP_ENVIRONMENT::PRODUCTION" + fi + if [[ "${{github.ref}}" == "refs/heads/release" ]]; then + echo "::set-output name=REACT_APP_ENVIRONMENT::STAGING" + fi + # Since this is an unreleased build, we set the version to incremented version number with + # a `-SNAPSHOT` suffix. + latest_released_version="$(git tag --list 'v*' --sort=-version:refname | head -1)" + echo "latest_released_version = $latest_released_version" + next_version="$(echo "$latest_released_version" | awk -F. -v OFS=. '{ $NF++; print }')" + echo "next_version = $next_version" + echo ::set-output name=version::$next_version-SNAPSHOT + + # We burn React environment & the Segment analytics key into the build itself. + # This is to ensure that we don't need to configure it in each installation + - name: Create the bundle + if: steps.run_result.outputs.run_result != 'success' + run: | + if [[ $GITHUB_REF == "refs/heads/release" ]]; then + REACT_APP_SEGMENT_CE_KEY=${{ secrets.APPSMITH_SEGMENT_CE_KEY_RELEASE }} + else + REACT_APP_SEGMENT_CE_KEY=${{ secrets.APPSMITH_SEGMENT_CE_KEY }} + fi + REACT_APP_ENVIRONMENT=${{steps.vars.outputs.REACT_APP_ENVIRONMENT}} \ + REACT_APP_FUSIONCHARTS_LICENSE_KEY=${{ secrets.APPSMITH_FUSIONCHARTS_LICENSE_KEY }} \ + REACT_APP_SEGMENT_CE_KEY="$REACT_APP_SEGMENT_CE_KEY" \ + SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }} \ + REACT_APP_VERSION_ID=${{ steps.vars.outputs.version }} \ + REACT_APP_VERSION_RELEASE_DATE=$(date -u '+%Y-%m-%dT%H:%M:%SZ') \ + REACT_APP_VERSION_EDITION="Community" \ + REACT_APP_INTERCOM_APP_ID=${{ secrets.APPSMITH_INTERCOM_ID }} \ + yarn build + ls -l build + + # Restore the previous built bundle if present. If not push the newly built into the cache + - name: Restore the previous bundle + uses: actions/cache@v2 + with: + path: | + app/client/build/ + key: ${{ github.run_id }}-${{ github.job }}-${{ steps.timestamp.outputs.timestamp }} + restore-keys: | + ${{ github.run_id }}-${{ github.job }} + + # Upload the build artifact so that it can be used by the test & deploy job in the workflow + - name: Upload react build bundle + uses: actions/upload-artifact@v2 + with: + name: client-build + path: app/client/build/ + + # Set status = success + - run: echo "::set-output name=run_result::success" > ~/run_result + + buildServer: + defaults: + run: + working-directory: app/server + runs-on: ubuntu-latest + # Only run this workflow for internally triggered events + if: | + github.event_name == 'workflow_dispatch' || + github.event_name == 'push' || + (github.event_name == 'pull_request_review' && + github.event.review.state == 'approved' && + github.event.pull_request.head.repo.full_name == github.repository) + + # Service containers to run with this job. Required for running tests + services: + # Label used to access the service container + redis: + # Docker Hub image for Redis + image: redis + ports: + # Opens tcp port 6379 on the host and service container + - 6379:6379 + mongo: + image: mongo + ports: + - 27017:27017 + + steps: + # Checkout the code + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + + # Timestamp will be used to create cache key + - id: timestamp + run: echo "::set-output name=timestamp::$(date +'%Y-%m-%dT%H:%M:%S')" + + # In case this is second attempt try restoring status of the prior attempt from cache + - name: Restore the previous run result + uses: actions/cache@v2 + with: + path: | + ~/run_result + key: ${{ github.run_id }}-${{ github.job }}-${{ steps.timestamp.outputs.timestamp }} + restore-keys: | + ${{ github.run_id }}-${{ github.job }}- + + # Fetch prior run result + - name: Get the previous run result + id: run_result + run: cat ~/run_result 2>/dev/null || echo 'default' + + # Incase of prior failure run the job + - if: steps.run_result.outputs.run_result != 'success' + run: echo "I'm alive!" && exit 0 + + # Setup Java + - name: Set up JDK 1.11 + if: steps.run_result.outputs.run_result != 'success' + uses: actions/setup-java@v1 + with: + java-version: "11.0.10" + + # Retrieve maven dependencies from cache. After a successful run, these dependencies are cached again + - name: Cache maven dependencies + if: steps.run_result.outputs.run_result != 'success' + uses: actions/cache@v2 + env: + cache-name: cache-maven-dependencies + with: + # maven dependencies are stored in `~/.m2` on Linux/macOS + path: ~/.m2 + key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} + restore-keys: ${{ runner.os }}-m2 + + # Here, the GITHUB_REF is of type /refs/head/. We extract branch_name from this by removing the + # first 11 characters. This can be used to build images for several branches + # Since this is an unreleased build, we get the latest released version number, increment the minor number in it, + # append a `-SNAPSHOT` at it's end to prepare the snapshot version number. This is used as the project's version. + - name: Get the version to tag the Docker image + if: steps.run_result.outputs.run_result != 'success' + id: vars + run: | + # Since this is an unreleased build, we set the version to incremented version number with a + # `-SNAPSHOT` suffix. + latest_released_version="$(git tag --list 'v*' --sort=-version:refname | head -1)" + echo "latest_released_version = $latest_released_version" + next_version="$(echo "$latest_released_version" | awk -F. -v OFS=. '{ $NF++; print }')" + echo "next_version = $next_version" + echo ::set-output name=version::$next_version-SNAPSHOT + echo ::set-output name=tag::$(echo ${GITHUB_REF:11}) + + - name: Test and Build package + if: steps.run_result.outputs.run_result != 'success' + env: + APPSMITH_MONGODB_URI: "mongodb://localhost:27017/mobtools" + APPSMITH_REDIS_URL: "redis://127.0.0.1:6379" + APPSMITH_ENCRYPTION_PASSWORD: "password" + APPSMITH_ENCRYPTION_SALT: "salt" + APPSMITH_IS_SELF_HOSTED: false + APPSMITH_GIT_ROOT: "./container-volumes/git-storage" + APPSMITH_AUDITLOG_ENABLED: true + working-directory: app/server + run: | + mvn --batch-mode versions:set \ + -DnewVersion=${{ steps.vars.outputs.version }} \ + -DgenerateBackupPoms=false \ + -DprocessAllModules=true + ./build.sh -DskipTests + ls -l dist + + # Restore the previous built bundle if present. If not push the newly built into the cache + - name: Restore the previous bundle + uses: actions/cache@v2 + with: + path: | + app/server/dist/ + key: ${{ github.run_id }}-${{ github.job }}-${{ steps.timestamp.outputs.timestamp }} + restore-keys: | + ${{ github.run_id }}-${{ github.job }} + + # Upload the build artifact so that it can be used by the test & deploy job in the workflow + - name: Upload server build bundle + uses: actions/upload-artifact@v2 + with: + name: server-build + path: app/server/dist/ + + - run: echo "::set-output name=run_result::success" > ~/run_result + + buildRts: + defaults: + run: + working-directory: app/rts + runs-on: ubuntu-latest + # Only run this workflow for internally triggered events + if: | + github.event_name == 'workflow_dispatch' || + github.event_name == 'push' || + (github.event_name == 'pull_request_review' && + github.event.review.state == 'approved' && + github.event.pull_request.head.repo.full_name == github.repository) + + steps: + # Checkout the code + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + + # Timestamp will be used to create cache key + - id: timestamp + run: echo "::set-output name=timestamp::$(date +'%Y-%m-%dT%H:%M:%S')" + + # In case this is second attempt try restoring status of the prior attempt from cache + - name: Restore the previous run result + uses: actions/cache@v2 + with: + path: | + ~/run_result + key: ${{ github.run_id }}-${{ github.job }}-${{ steps.timestamp.outputs.timestamp }} + restore-keys: | + ${{ github.run_id }}-${{ github.job }}- + + # Fetch prior run result + - name: Get the previous run result + id: run_result + run: cat ~/run_result 2>/dev/null || echo 'default' + + # Incase of prior failure run the job + - if: steps.run_result.outputs.run_result != 'success' + run: echo "I'm alive!" && exit 0 + + - name: Use Node.js 16.14.0 + if: steps.run_result.outputs.run_result != 'success' + uses: actions/setup-node@v1 + with: + node-version: "16.14.0" + + # Here, the GITHUB_REF is of type /refs/head/. We extract branch_name from this by removing the + # first 11 characters. This can be used to build images for several branches + # Since this is an unreleased build, we get the latest released version number, increment the minor number in it, + # append a `-SNAPSHOT` at it's end to prepare the snapshot version number. This is used as the project's version. + - name: Get the version to tag the Docker image + if: steps.run_result.outputs.run_result != 'success' + id: vars + run: | + # Since this is an unreleased build, we set the version to incremented version number with a + # `-SNAPSHOT` suffix. + latest_released_version="$(git tag --list 'v*' --sort=-version:refname | head -1)" + echo "latest_released_version = $latest_released_version" + next_version="$(echo "$latest_released_version" | awk -F. -v OFS=. '{ $NF++; print }')" + echo "next_version = $next_version" + echo ::set-output name=version::$next_version-SNAPSHOT + echo ::set-output name=tag::$(echo ${GITHUB_REF:11}) + + - name: Build + if: steps.run_result.outputs.run_result != 'success' + run: | + echo 'export const VERSION = "${{ steps.vars.outputs.version }}"' > src/version.js + yarn build + + # Restore the previous built bundle if present. If not push the newly built into the cache + - name: Restore the previous bundle + uses: actions/cache@v2 + with: + path: | + app/rts/dist/ + key: ${{ github.run_id }}-${{ github.job }}-${{ steps.timestamp.outputs.timestamp }} + restore-keys: | + ${{ github.run_id }}-${{ github.job }} + + # Tar the bundles to speed up the upload & download process + - name: Tar the rts bundles + run: | + ls -al dist/node_modules/@shared/ast + tar -cvf rts-dist.tar dist + # Upload the build artifacts and dependencies so that it can be used by the test & deploy job in other workflows + - name: Upload rts build bundle + uses: actions/upload-artifact@v2 + with: + name: rts-dist + path: app/rts/rts-dist.tar + + # Set status = success + - run: echo "::set-output name=run_result::success" > ~/run_result + + + fat-container-test: + needs: [buildClient, buildServer, buildRts] + # Only run if the build step is successful + # If the build has been triggered manually via workflow_dispatch or via a push to protected branches + # then we don't check for the PR approved state + if: | + success() && + (github.event_name == 'workflow_dispatch' || + github.event_name == 'push' || + (github.event_name == 'pull_request_review' && + github.event.review.state == 'approved' && + github.event.pull_request.head.repo.full_name == github.repository)) + runs-on: ubuntu-latest + defaults: + run: + shell: bash + strategy: + fail-fast: false + matrix: + job: [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 ] + + # Service containers to run with this job. Required for running tests + services: + # Label used to access the service container + redis: + # Docker Hub image for Redis + image: redis + ports: + # Opens tcp port 6379 on the host and service container + - 6379:6379 + mongo: + image: mongo + ports: + - 27017:27017 + + steps: + # Checkout the code + - name: Checkout the merged commit from PR and base branch + if: github.event_name == 'pull_request_review' + uses: actions/checkout@v2 + with: + ref: refs/pull/${{ github.event.pull_request.number }}/merge + + - name: Checkout the head commit of the branch + if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' + uses: actions/checkout@v2 + + # Timestamp will be used to create cache key + - id: timestamp + run: echo "::set-output name=timestamp::$(date +'%Y-%m-%dT%H:%M:%S')" + + # In case this is second attempt try restoring status of the prior attempt from cache + - name: Restore the previous run result + uses: martijnhols/actions-cache@v3.0.2 + with: + path: | + ~/run_result + key: ${{ github.run_id }}-${{ github.job }}-${{ matrix.job }} + restore-keys: | + ${{ github.run_id }}-${{ github.job }}-${{ matrix.job }} + + # Fetch prior run result + - name: Get the previous run result + id: run_result + run: cat ~/run_result 2>/dev/null || echo 'default' + + # In case this is second attempt try restoring failed tests + - name: Restore the previous failed combine result + if: steps.run_result.outputs.run_result == 'failedtest' + uses: martijnhols/actions-cache/restore@v3 + with: + path: | + ~/combined_failed_spec_fat + key: ${{ github.run_id }}-"ui-test-result" + restore-keys: | + ${{ github.run_id }}-${{ github.job }} + + # failed_spec_env will contain list of all failed specs + # We are using evnironment variable instead of regular to support multiline + - name: Get failed_spec + if: steps.run_result.outputs.run_result == 'failedtest' + run: | + failed_spec_env=$(cat ~/combined_failed_spec_fat) + echo "failed_spec_env<> $GITHUB_ENV + echo "$failed_spec_env" >> $GITHUB_ENV + echo "EOF" >> $GITHUB_ENV + + - if: steps.run_result.outputs.run_result != 'success' && steps.run_result.outputs.run_result != 'failedtest' + run: echo "Starting full run" && exit 0 + + - if: steps.run_result.outputs.run_result == 'failedtest' + run: echo "Rerunning failed tests" && exit 0 + + - name: cat run_result + run: echo ${{ steps.run_result.outputs.run_result }} + + # Setup Java + - name: Set up JDK 1.11 + if: steps.run_result.outputs.run_result != 'success' + uses: actions/setup-java@v1 + with: + java-version: "11.0.10" + + - name: Download the react build artifact + uses: actions/download-artifact@v2 + with: + name: client-build + path: app/client/build + + - name: Download the server build artifact + uses: actions/download-artifact@v2 + with: + name: server-build + path: app/server/dist + + - name: Download the rts build artifact + uses: actions/download-artifact@v2 + with: + name: rts-dist + path: app/rts/dist + + - name: Untar the rts folder + run: | + tar -xvf app/rts/dist/rts-dist.tar -C app/rts/ + echo "Cleaning up the tar files" + rm app/rts/dist/rts-dist.tar + + # We don't use Depot Docker builds because it's faster for local Docker images to be built locally. + # It's slower and more expensive to build these Docker images on Depot and download it back to the CI node. + - name: Build docker image + if: steps.run_result.outputs.run_result != 'success' + working-directory: "." + run: | + docker build -t fatcontainer . + + - name: Create folder + if: steps.run_result.outputs.run_result != 'success' + env: + APPSMITH_LICENSE_KEY: ${{ secrets.APPSMITH_LICENSE_KEY }} + working-directory: "." + run: | + mkdir -p fatcontainerlocal/stacks/configuration/ + mkdir -p fatcontainerlocal/oldstack + + - name: Download S3 image + uses: keithweaver/aws-s3-github-action@v1.0.0 + with: + command: cp + source: s3://ci-assets--appsmith/ + destination: /home/runner/work/appsmith/appsmith/fatcontainerlocal/oldstack + aws_access_key_id: ${{ secrets.S3_CI_ASSETS_ACCESS_KEY_ID }} + aws_secret_access_key: ${{ secrets.S3_CI_ASSETS_SECRET_ACCESS_KEY }} + aws_region: ap-south-1 + flags: --recursive + + - name: Load docker image + if: steps.run_result.outputs.run_result != 'success' + env: + APPSMITH_LICENSE_KEY: ${{ secrets.APPSMITH_LICENSE_KEY }} + working-directory: "." + run: | + mkdir -p ~/git-server/keys + mkdir -p ~/git-server/repos + docker run --name test-event-driver -d -p 2222:22 -p 5001:5001 -p 3306:3306 \ + -p 5432:5432 -p 28017:27017 -p 25:25 --privileged --pid=host --ipc=host --volume /:/host -v ~/git-server/keys:/git-server/keys \ + -v ~/git-server/repos:/git-server/repos appsmith/test-event-driver:latest + cd fatcontainerlocal + docker run -d --name appsmith -p 80:80 -p 9001:9001 \ + -v "$PWD/stacks:/appsmith-stacks" -e APPSMITH_LICENSE_KEY=$APPSMITH_LICENSE_KEY \ + -e APPSMITH_CLOUD_SERVICES_BASE_URL=http://host.docker.internal:5001 \ + -e APPSMITH_AUDITLOG_ENABLED=true \ + fatcontainer + + - name: Use Node.js 16.14.0 + if: steps.run_result.outputs.run_result != 'success' + uses: actions/setup-node@v1 + with: + node-version: "16.14.0" + + # Install all the dependencies + - name: Install dependencies + if: steps.run_result.outputs.run_result != 'success' + run: | + cd app/client + yarn install + + - name: Setting up the cypress tests + if: steps.run_result.outputs.run_result != 'success' + shell: bash + env: + APPSMITH_SSL_CERTIFICATE: ${{ secrets.APPSMITH_SSL_CERTIFICATE }} + APPSMITH_SSL_KEY: ${{ secrets.APPSMITH_SSL_KEY }} + CYPRESS_URL: ${{ secrets.CYPRESS_URL }} + CYPRESS_USERNAME: ${{ secrets.CYPRESS_USERNAME }} + CYPRESS_PASSWORD: ${{ secrets.CYPRESS_PASSWORD }} + CYPRESS_TESTUSERNAME1: ${{ secrets.CYPRESS_TESTUSERNAME1 }} + CYPRESS_TESTPASSWORD1: ${{ secrets.CYPRESS_TESTPASSWORD1 }} + CYPRESS_TESTUSERNAME2: ${{ secrets.CYPRESS_TESTUSERNAME2 }} + CYPRESS_TESTPASSWORD2: ${{ secrets.CYPRESS_TESTPASSWORD1 }} + CYPRESS_TESTUSERNAME3: ${{ secrets.CYPRESS_TESTUSERNAME3 }} + CYPRESS_TESTPASSWORD3: ${{ secrets.CYPRESS_TESTPASSWORD3 }} + CYPRESS_TESTUSERNAME4: ${{ secrets.CYPRESS_TESTUSERNAME4 }} + CYPRESS_TESTPASSWORD4: ${{ secrets.CYPRESS_TESTPASSWORD4 }} + CYPRESS_S3_ACCESS_KEY: ${{ secrets.CYPRESS_S3_ACCESS_KEY }} + CYPRESS_S3_SECRET_KEY: ${{ secrets.CYPRESS_S3_SECRET_KEY }} + CYPRESS_GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.CYPRESS_GITHUB_PERSONAL_ACCESS_TOKEN }} + CYPRESS_TEST_GITHUB_USER_NAME: ${{ secrets.CYPRESS_TEST_GITHUB_USER_NAME }} + CYPRESS_APPSMITH_OAUTH2_GOOGLE_CLIENT_ID: ${{ secrets.CYPRESS_APPSMITH_OAUTH2_GOOGLE_CLIENT_ID }} + CYPRESS_APPSMITH_OAUTH2_GOOGLE_CLIENT_SECRET: ${{ secrets.CYPRESS_APPSMITH_OAUTH2_GOOGLE_CLIENT_SECRET }} + CYPRESS_APPSMITH_OAUTH2_GITHUB_CLIENT_ID: ${{ secrets.CYPRESS_APPSMITH_OAUTH2_GITHUB_CLIENT_ID }} + CYPRESS_APPSMITH_OAUTH2_GITHUB_CLIENT_SECRET: ${{ secrets.CYPRESS_APPSMITH_OAUTH2_GITHUB_CLIENT_SECRET }} + APPSMITH_DISABLE_TELEMETRY: true + APPSMITH_GOOGLE_MAPS_API_KEY: ${{ secrets.APPSMITH_GOOGLE_MAPS_API_KEY }} + POSTGRES_PASSWORD: postgres + run: | + cd app/client + chmod a+x ./cypress/setup-test-fat.sh + ./cypress/setup-test-fat.sh + + - uses: browser-actions/setup-chrome@latest + with: + chrome-version: stable + - run: | + echo "BROWSER_PATH=$(which chrome)" >> $GITHUB_ENV + + - name: Run the cypress test + if: steps.run_result.outputs.run_result != 'success' && steps.run_result.outputs.run_result != 'failedtest' + uses: cypress-io/github-action@v2 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} + CYPRESS_PROJECT_ID: ${{ secrets.CYPRESS_PROJECT_ID }} + CYPRESS_USERNAME: ${{ secrets.CYPRESS_USERNAME }} + CYPRESS_PASSWORD: ${{ secrets.CYPRESS_PASSWORD }} + CYPRESS_TESTUSERNAME1: ${{ secrets.CYPRESS_TESTUSERNAME1 }} + CYPRESS_TESTPASSWORD1: ${{ secrets.CYPRESS_TESTPASSWORD1 }} + CYPRESS_TESTUSERNAME2: ${{ secrets.CYPRESS_TESTUSERNAME2 }} + CYPRESS_TESTPASSWORD2: ${{ secrets.CYPRESS_TESTPASSWORD1 }} + CYPRESS_TESTUSERNAME3: ${{ secrets.CYPRESS_TESTUSERNAME3 }} + CYPRESS_TESTPASSWORD3: ${{ secrets.CYPRESS_TESTPASSWORD3 }} + CYPRESS_TESTUSERNAME4: ${{ secrets.CYPRESS_TESTUSERNAME4 }} + CYPRESS_TESTPASSWORD4: ${{ secrets.CYPRESS_TESTPASSWORD4 }} + CYPRESS_S3_ACCESS_KEY: ${{ secrets.CYPRESS_S3_ACCESS_KEY }} + CYPRESS_S3_SECRET_KEY: ${{ secrets.CYPRESS_S3_SECRET_KEY }} + CYPRESS_GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.CYPRESS_GITHUB_PERSONAL_ACCESS_TOKEN }} + CYPRESS_TEST_GITHUB_USER_NAME: ${{ secrets.CYPRESS_TEST_GITHUB_USER_NAME }} + CYPRESS_APPSMITH_OAUTH2_GOOGLE_CLIENT_ID: ${{ secrets.CYPRESS_APPSMITH_OAUTH2_GOOGLE_CLIENT_ID }} + CYPRESS_APPSMITH_OAUTH2_GOOGLE_CLIENT_SECRET: ${{ secrets.CYPRESS_APPSMITH_OAUTH2_GOOGLE_CLIENT_SECRET }} + CYPRESS_APPSMITH_OAUTH2_GITHUB_CLIENT_ID: ${{ secrets.CYPRESS_APPSMITH_OAUTH2_GITHUB_CLIENT_ID }} + CYPRESS_APPSMITH_OAUTH2_GITHUB_CLIENT_SECRET: ${{ secrets.CYPRESS_APPSMITH_OAUTH2_GITHUB_CLIENT_SECRET }} + APPSMITH_DISABLE_TELEMETRY: true + APPSMITH_GOOGLE_MAPS_API_KEY: ${{ secrets.APPSMITH_GOOGLE_MAPS_API_KEY }} + COMMIT_INFO_MESSAGE: ${{ github.event.pull_request.title }} + with: + browser: ${{ env.BROWSER_PATH }} + headless: true + record: true + install: false + parallel: true + config-file: cypress_fat.json + group: "Electrons on Github Action Fat Container" + spec: "cypress/integration/Smoke_TestSuite_Fat/**/*" + working-directory: app/client + # tag will be either "push" or "pull_request" + tag: ${{ github.event_name }} + env: "NODE_ENV=development" + + # Incase of second attemtp only run failed specs + - name: Run the cypress test with failed tests + if: steps.run_result.outputs.run_result == 'failedtest' + uses: cypress-io/github-action@v2 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} + CYPRESS_PROJECT_ID: ${{ secrets.CYPRESS_PROJECT_ID }} + CYPRESS_USERNAME: ${{ secrets.CYPRESS_USERNAME }} + CYPRESS_PASSWORD: ${{ secrets.CYPRESS_PASSWORD }} + CYPRESS_TESTUSERNAME1: ${{ secrets.CYPRESS_TESTUSERNAME1 }} + CYPRESS_TESTPASSWORD1: ${{ secrets.CYPRESS_TESTPASSWORD1 }} + CYPRESS_TESTUSERNAME2: ${{ secrets.CYPRESS_TESTUSERNAME2 }} + CYPRESS_TESTPASSWORD2: ${{ secrets.CYPRESS_TESTPASSWORD1 }} + CYPRESS_TESTUSERNAME3: ${{ secrets.CYPRESS_TESTUSERNAME3 }} + CYPRESS_TESTPASSWORD3: ${{ secrets.CYPRESS_TESTPASSWORD3 }} + CYPRESS_TESTUSERNAME4: ${{ secrets.CYPRESS_TESTUSERNAME4 }} + CYPRESS_TESTPASSWORD4: ${{ secrets.CYPRESS_TESTPASSWORD4 }} + CYPRESS_S3_ACCESS_KEY: ${{ secrets.CYPRESS_S3_ACCESS_KEY }} + CYPRESS_S3_SECRET_KEY: ${{ secrets.CYPRESS_S3_SECRET_KEY }} + CYPRESS_GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.CYPRESS_GITHUB_PERSONAL_ACCESS_TOKEN }} + CYPRESS_TEST_GITHUB_USER_NAME: ${{ secrets.CYPRESS_TEST_GITHUB_USER_NAME }} + CYPRESS_APPSMITH_OAUTH2_GOOGLE_CLIENT_ID: ${{ secrets.CYPRESS_APPSMITH_OAUTH2_GOOGLE_CLIENT_ID }} + CYPRESS_APPSMITH_OAUTH2_GOOGLE_CLIENT_SECRET: ${{ secrets.CYPRESS_APPSMITH_OAUTH2_GOOGLE_CLIENT_SECRET }} + CYPRESS_APPSMITH_OAUTH2_GITHUB_CLIENT_ID: ${{ secrets.CYPRESS_APPSMITH_OAUTH2_GITHUB_CLIENT_ID }} + CYPRESS_APPSMITH_OAUTH2_GITHUB_CLIENT_SECRET: ${{ secrets.CYPRESS_APPSMITH_OAUTH2_GITHUB_CLIENT_SECRET }} + APPSMITH_DISABLE_TELEMETRY: true + APPSMITH_GOOGLE_MAPS_API_KEY: ${{ secrets.APPSMITH_GOOGLE_MAPS_API_KEY }} + COMMIT_INFO_MESSAGE: ${{ github.event.pull_request.title }} + with: + browser: ${{ env.BROWSER_PATH }} + headless: true + record: true + install: false + parallel: true + config-file: cypress_fat.json + group: "Electrons on Github Action" + spec: "cypress/integration/Smoke_TestSuite_Fat/**/*" + working-directory: app/client + # tag will be either "push" or "pull_request" + tag: ${{ github.event_name }} + env: "NODE_ENV=development" + + # Set status = failedtest + - name: Set fail if there are test failures + if: failure() + run: echo "::set-output name=run_result::failedtest" > ~/run_result + + # Create a directory ~/failed_spec_fat and add a dummy file + # This will ensure upload and download steps are successfull + - name: Create directories for failed tests + if: always() + run: | + mkdir -p ~/failed_spec_fat + echo "empty" >> ~/failed_spec_fat/dummy-${{ matrix.job }} + + # add list failed tests to a file + - name: Incase of test failures copy them to a file + if: failure() + run: | + cd ${{ github.workspace }}/app/client/cypress/ + find screenshots -type d|grep -i spec |sed 's/screenshots/cypress\/integration/g' > ~/failed_spec_fat/failed_spec_fat-${{ matrix.job }} + + # Upload failed test list using common path for all matrix job + - name: Upload failed test list artifact + if: always() + uses: actions/upload-artifact@v2 + with: + name: failed-spec-fat + path: ~/failed_spec_fat + + # Force store previous run result to cache + - name: Store the previous run result + if: failure() + uses: martijnhols/actions-cache/save@v3 + with: + path: | + ~/run_result + key: ${{ github.run_id }}-${{ github.job }}-${{ matrix.job }} + restore-keys: | + ${{ github.run_id }}-${{ github.job }}-${{ matrix.job }} + + # Force store previous failed test list to cache + - name: Store the previous failed test result + if: failure() + uses: martijnhols/actions-cache/save@v3 + with: + path: | + ~/failed_spec_fat + key: ${{ github.run_id }}-${{ github.job }}-${{ matrix.job }} + restore-keys: | + ${{ github.run_id }}-${{ github.job }}-${{ matrix.job }} + + # Upload the screenshots as artifacts if there's a failure + - uses: actions/upload-artifact@v1 + if: failure() + with: + name: cypress-screenshots-${{ matrix.job }} + path: app/client/cypress/screenshots/ + + - name: Restore the previous bundle + uses: actions/cache@v2 + with: + path: | + app/client/cypress/snapshots/ + key: ${{ github.run_id }}-${{ github.job }}-${{ matrix.job }} + restore-keys: | + ${{ github.run_id }}-${{ github.job }}-${{ matrix.job }} + + # Upload the snapshots as artifacts for layout validation + - uses: actions/upload-artifact@v1 + with: + name: cypress-snapshots-visualRegression + path: app/client/cypress/snapshots/ + + # Upload the log artifact so that it can be used by the test & deploy job in the workflow + - name: Upload server logs bundle on failure + uses: actions/upload-artifact@v2 + if: failure() + with: + name: server-logs-${{ matrix.job }} + path: app/server/server-logs.log + + # Set status = success + - run: echo "::set-output name=run_result::success" > ~/run_result \ No newline at end of file diff --git a/.github/workflows/integration-tests-command.yml b/.github/workflows/integration-tests-command.yml index dcda9cb00153..f1ca434bef42 100644 --- a/.github/workflows/integration-tests-command.yml +++ b/.github/workflows/integration-tests-command.yml @@ -19,7 +19,7 @@ jobs: Workflow: `${{ github.workflow }}`. Commit: `${{ github.event.client_payload.slash_command.args.named.sha }}`. PR: ${{ github.event.client_payload.pull_request.number }}. - Perf tests will be available at + Perf tests will be available at server-build: name: server-build @@ -67,7 +67,7 @@ jobs: strategy: fail-fast: false matrix: - job: [0, 1] + job: [0, 1, 2, 3, 4] # Service containers to run with this job. Required for running tests services: @@ -245,6 +245,10 @@ jobs: CYPRESS_TESTPASSWORD1: ${{ secrets.CYPRESS_TESTPASSWORD1 }} CYPRESS_TESTUSERNAME2: ${{ secrets.CYPRESS_TESTUSERNAME2 }} CYPRESS_TESTPASSWORD2: ${{ secrets.CYPRESS_TESTPASSWORD1 }} + CYPRESS_TESTUSERNAME3: ${{ secrets.CYPRESS_TESTUSERNAME3 }} + CYPRESS_TESTPASSWORD3: ${{ secrets.CYPRESS_TESTPASSWORD3 }} + CYPRESS_TESTUSERNAME4: ${{ secrets.CYPRESS_TESTUSERNAME4 }} + CYPRESS_TESTPASSWORD4: ${{ secrets.CYPRESS_TESTPASSWORD4 }} CYPRESS_S3_ACCESS_KEY: ${{ secrets.CYPRESS_S3_ACCESS_KEY }} CYPRESS_S3_SECRET_KEY: ${{ secrets.CYPRESS_S3_SECRET_KEY }} CYPRESS_GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.CYPRESS_GITHUB_PERSONAL_ACCESS_TOKEN }} @@ -280,6 +284,10 @@ jobs: CYPRESS_TESTPASSWORD1: ${{ secrets.CYPRESS_TESTPASSWORD1 }} CYPRESS_TESTUSERNAME2: ${{ secrets.CYPRESS_TESTUSERNAME2 }} CYPRESS_TESTPASSWORD2: ${{ secrets.CYPRESS_TESTPASSWORD1 }} + CYPRESS_TESTUSERNAME3: ${{ secrets.CYPRESS_TESTUSERNAME3 }} + CYPRESS_TESTPASSWORD3: ${{ secrets.CYPRESS_TESTPASSWORD3 }} + CYPRESS_TESTUSERNAME4: ${{ secrets.CYPRESS_TESTUSERNAME4 }} + CYPRESS_TESTPASSWORD4: ${{ secrets.CYPRESS_TESTPASSWORD4 }} CYPRESS_S3_ACCESS_KEY: ${{ secrets.CYPRESS_S3_ACCESS_KEY }} CYPRESS_S3_SECRET_KEY: ${{ secrets.CYPRESS_S3_SECRET_KEY }} CYPRESS_GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.CYPRESS_GITHUB_PERSONAL_ACCESS_TOKEN }} @@ -319,6 +327,10 @@ jobs: CYPRESS_TESTPASSWORD1: ${{ secrets.CYPRESS_TESTPASSWORD1 }} CYPRESS_TESTUSERNAME2: ${{ secrets.CYPRESS_TESTUSERNAME2 }} CYPRESS_TESTPASSWORD2: ${{ secrets.CYPRESS_TESTPASSWORD1 }} + CYPRESS_TESTUSERNAME3: ${{ secrets.CYPRESS_TESTUSERNAME3 }} + CYPRESS_TESTPASSWORD3: ${{ secrets.CYPRESS_TESTPASSWORD3 }} + CYPRESS_TESTUSERNAME4: ${{ secrets.CYPRESS_TESTUSERNAME4 }} + CYPRESS_TESTPASSWORD4: ${{ secrets.CYPRESS_TESTPASSWORD4 }} CYPRESS_S3_ACCESS_KEY: ${{ secrets.CYPRESS_S3_ACCESS_KEY }} CYPRESS_S3_SECRET_KEY: ${{ secrets.CYPRESS_S3_SECRET_KEY }} CYPRESS_GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.CYPRESS_GITHUB_PERSONAL_ACCESS_TOKEN }} @@ -375,7 +387,7 @@ jobs: if: failure() run: | cd ${{ github.workspace }}/app/client/cypress/ - find screenshots -type d|grep -i spec |sed 's/screenshots/cypress\/integration/g' > ~/failed_spec_fat/failed_spec-${{ matrix.job }} + find screenshots -type d|grep -i spec |sed 's/screenshots/cypress\/integration/g' > ~/failed_spec_fat/failed_spec_fat-${{ matrix.job }} # Upload failed test list using common path for all matrix job - name: Upload failed test list artifact @@ -590,20 +602,17 @@ jobs: # Run the server in the background and redirect logs to a log file ./scripts/start-dev-server.sh &> server-logs.log & - - name: Wait for 30 seconds for server to start + - name: Wait for 30s and check if server is running if: steps.run_result.outputs.run_result != 'success' run: | sleep 30s - - - name: Exit if Server hasnt started - if: steps.run_result.outputs.run_result != 'success' - run: | - if [[ `ps -ef | grep "server-.*-SNAPSHOT" | grep java |wc -l` == 0 ]]; then - echo "Server Not Started"; - exit 1; - else - echo "Server Found"; - fi + if lsof -i :8080; then + echo "Server Found" + else + echo "Server Not Started. Printing logs from server process" + cat app/server/nohup.out + exit 1 + fi - name: Installing Yarn serve if: steps.run_result.outputs.run_result != 'success' @@ -632,6 +641,10 @@ jobs: CYPRESS_TESTPASSWORD1: ${{ secrets.CYPRESS_TESTPASSWORD1 }} CYPRESS_TESTUSERNAME2: ${{ secrets.CYPRESS_TESTUSERNAME2 }} CYPRESS_TESTPASSWORD2: ${{ secrets.CYPRESS_TESTPASSWORD1 }} + CYPRESS_TESTUSERNAME3: ${{ secrets.CYPRESS_TESTUSERNAME3 }} + CYPRESS_TESTPASSWORD3: ${{ secrets.CYPRESS_TESTPASSWORD3 }} + CYPRESS_TESTUSERNAME4: ${{ secrets.CYPRESS_TESTUSERNAME4 }} + CYPRESS_TESTPASSWORD4: ${{ secrets.CYPRESS_TESTPASSWORD4 }} CYPRESS_S3_ACCESS_KEY: ${{ secrets.CYPRESS_S3_ACCESS_KEY }} CYPRESS_S3_SECRET_KEY: ${{ secrets.CYPRESS_S3_SECRET_KEY }} CYPRESS_GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.CYPRESS_GITHUB_PERSONAL_ACCESS_TOKEN }} @@ -665,6 +678,10 @@ jobs: CYPRESS_TESTPASSWORD1: ${{ secrets.CYPRESS_TESTPASSWORD1 }} CYPRESS_TESTUSERNAME2: ${{ secrets.CYPRESS_TESTUSERNAME2 }} CYPRESS_TESTPASSWORD2: ${{ secrets.CYPRESS_TESTPASSWORD1 }} + CYPRESS_TESTUSERNAME3: ${{ secrets.CYPRESS_TESTUSERNAME3 }} + CYPRESS_TESTPASSWORD3: ${{ secrets.CYPRESS_TESTPASSWORD3 }} + CYPRESS_TESTUSERNAME4: ${{ secrets.CYPRESS_TESTUSERNAME4 }} + CYPRESS_TESTPASSWORD4: ${{ secrets.CYPRESS_TESTPASSWORD4 }} CYPRESS_S3_ACCESS_KEY: ${{ secrets.CYPRESS_S3_ACCESS_KEY }} CYPRESS_S3_SECRET_KEY: ${{ secrets.CYPRESS_S3_SECRET_KEY }} CYPRESS_GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.CYPRESS_GITHUB_PERSONAL_ACCESS_TOKEN }} @@ -703,6 +720,10 @@ jobs: CYPRESS_TESTPASSWORD1: ${{ secrets.CYPRESS_TESTPASSWORD1 }} CYPRESS_TESTUSERNAME2: ${{ secrets.CYPRESS_TESTUSERNAME2 }} CYPRESS_TESTPASSWORD2: ${{ secrets.CYPRESS_TESTPASSWORD1 }} + CYPRESS_TESTUSERNAME3: ${{ secrets.CYPRESS_TESTUSERNAME3 }} + CYPRESS_TESTPASSWORD3: ${{ secrets.CYPRESS_TESTPASSWORD3 }} + CYPRESS_TESTUSERNAME4: ${{ secrets.CYPRESS_TESTUSERNAME4 }} + CYPRESS_TESTPASSWORD4: ${{ secrets.CYPRESS_TESTPASSWORD4 }} CYPRESS_S3_ACCESS_KEY: ${{ secrets.CYPRESS_S3_ACCESS_KEY }} CYPRESS_S3_SECRET_KEY: ${{ secrets.CYPRESS_S3_SECRET_KEY }} CYPRESS_GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.CYPRESS_GITHUB_PERSONAL_ACCESS_TOKEN }} @@ -804,8 +825,18 @@ jobs: # Set status = success - run: echo "::set-output name=run_result::success" > ~/run_result + perf-test: + needs: [client-build, server-build, rts-build] + # Only run if the build step is successful + if: success() + name: perf-test + uses: ./.github/workflows/perf-test.yml + secrets: inherit + with: + pr: ${{ github.event.client_payload.pull_request.number }} + ui-test-result: - needs: [ui-test, fat-container-test] + needs: [ui-test, fat-container-test, perf-test] # Only run if the ui-test with matrices step is successful if: always() runs-on: ubuntu-latest @@ -954,7 +985,7 @@ jobs: run: echo "$PAYLOAD_CONTEXT" - name: Check ui-test set status - if: needs.ui-test.result != 'success' || needs.fat-container-test.result != 'success' + if: needs.ui-test.result != 'success' || needs.fat-container-test.result != 'success' || needs.perf-test.result != 'success' run: exit 1 package: @@ -1025,12 +1056,3 @@ jobs: return result; } - perf-test: - needs: [client-build, server-build, rts-build] - # Only run if the build step is successful - if: success() - name: perf-test - uses: ./.github/workflows/perf-test.yml - secrets: inherit - with: - pr: ${{ github.event.client_payload.pull_request.number }} diff --git a/.github/workflows/label-checker.yml b/.github/workflows/label-checker.yml index be005b4b4f66..f2fab9a88d65 100644 --- a/.github/workflows/label-checker.yml +++ b/.github/workflows/label-checker.yml @@ -19,5 +19,5 @@ jobs: - uses: docker://agilepathway/pull-request-label-checker:latest with: # Needs to have a Test Plan Approved label if not part of any of the pods below - any_of: Test Plan Approved,App Viewers Pod,User Education Pod,New Developers Pod,Team Managers Pod,UI Builders Pod,Performance,Release Promotion,CI,Design System Pod,DevOps Pod + any_of: Test Plan Approved,App Viewers Pod,User Education Pod,New Developers Pod,Team Managers Pod,UI Builders Pod,Performance,Release Promotion,CI,Design System Pod,DevOps Pod,skip-testPlan repo_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/perf-test.yml b/.github/workflows/perf-test.yml index 561dab896269..e2218a96a4b6 100644 --- a/.github/workflows/perf-test.yml +++ b/.github/workflows/perf-test.yml @@ -140,6 +140,8 @@ jobs: APPSMITH_CLOUD_SERVICES_USERNAME: "" APPSMITH_CLOUD_SERVICES_PASSWORD: "" APPSMITH_GIT_ROOT: "./container-volumes/git-storage" + APPSMITH_LICENSE_KEY: ${{ secrets.APPSMITH_LICENSE_KEY }} + APPSMITH_ENVFILE_PATH: /tmp/dummy.env run: | ls -l ls -l scripts/ @@ -147,27 +149,33 @@ jobs: # Run the server in the background and redirect logs to a log file ./scripts/start-dev-server.sh &> server-logs.log & - - name: Wait for 30 seconds for server to start + - name: Installing Yarn serve if: steps.run_result.outputs.run_result != 'success' run: | - sleep 30s + yarn global add serve + echo "$(yarn global bin)" >> $GITHUB_PATH - - name: Exit if Server hasnt started + - name: Load docker image if: steps.run_result.outputs.run_result != 'success' + env: + APPSMITH_LICENSE_KEY: ${{ secrets.APPSMITH_LICENSE_KEY }} + working-directory: "." run: | - if [[ `ps -ef | grep "server-.*-SNAPSHOT" | grep java |wc -l` == 0 ]]; then - echo "Server Not Started"; - exit 1; - else - echo "Server Found"; - fi + mkdir -p ~/git-server/keys + mkdir -p ~/git-server/repos + docker run --name test-event-driver -d -p 2222:22 -p 5001:5001 -p 3306:3306 \ + -p 5432:5432 -p 28017:27017 -p 25:25 --privileged --pid=host --ipc=host --volume /:/host -v ~/git-server/keys:/git-server/keys \ + -v ~/git-server/repos:/git-server/repos appsmith/test-event-driver:latest + - - name: Installing Yarn serve + # Start rts + - name: Start RTS Server if: steps.run_result.outputs.run_result != 'success' + working-directory: ./app/rts run: | - yarn global add serve - echo "$(yarn global bin)" >> $GITHUB_PATH - + cp .env.example .env + ./start-server.sh & + - name: Setting up the perf tests if: steps.run_result.outputs.run_result != 'success' shell: bash @@ -181,6 +189,10 @@ jobs: CYPRESS_TESTPASSWORD1: ${{ secrets.CYPRESS_TESTPASSWORD1 }} CYPRESS_TESTUSERNAME2: ${{ secrets.CYPRESS_TESTUSERNAME2 }} CYPRESS_TESTPASSWORD2: ${{ secrets.CYPRESS_TESTPASSWORD1 }} + CYPRESS_TESTUSERNAME3: ${{ secrets.CYPRESS_TESTUSERNAME3 }} + CYPRESS_TESTPASSWORD3: ${{ secrets.CYPRESS_TESTPASSWORD3 }} + CYPRESS_TESTUSERNAME4: ${{ secrets.CYPRESS_TESTUSERNAME4 }} + CYPRESS_TESTPASSWORD4: ${{ secrets.CYPRESS_TESTPASSWORD4 }} CYPRESS_S3_ACCESS_KEY: ${{ secrets.CYPRESS_S3_ACCESS_KEY }} CYPRESS_S3_SECRET_KEY: ${{ secrets.CYPRESS_S3_SECRET_KEY }} CYPRESS_APPSMITH_OAUTH2_GOOGLE_CLIENT_ID: ${{ secrets.CYPRESS_APPSMITH_OAUTH2_GOOGLE_CLIENT_ID }} @@ -214,6 +226,17 @@ jobs: shell: bash run: yarn install --frozen-lockfile + - name: Exit if Server hasnt started + if: steps.run_result.outputs.run_result != 'success' + run: | + if lsof -i :8080; then + echo "Server Found" + else + echo "Server Not Started. Printing logs from server process" + cat app/server/nohup.out + exit 1 + fi + - name: Change test script permissions if: steps.run_result.outputs.run_result != 'success' working-directory: app/client/perf diff --git a/.github/workflows/perf-tests-command.yml b/.github/workflows/perf-tests-command.yml index 4b1828e54e46..46d9899b61fe 100644 --- a/.github/workflows/perf-tests-command.yml +++ b/.github/workflows/perf-tests-command.yml @@ -19,7 +19,7 @@ jobs: Workflow: `${{ github.workflow }}`. Commit: `${{ github.event.client_payload.slash_command.args.named.sha }}`. PR: ${{ github.event.client_payload.pull_request.number }}. - Perf tests will be available at + Perf tests will be available at server-build: name: server-build uses: ./.github/workflows/server-build.yml diff --git a/.github/workflows/pr-labeler.yml b/.github/workflows/pr-labeler.yml index 2b96fe7f7346..2d853d5120a1 100644 --- a/.github/workflows/pr-labeler.yml +++ b/.github/workflows/pr-labeler.yml @@ -12,21 +12,4 @@ jobs: steps: - uses: appsmithorg/labeler@master env: - GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" - - - name: Get changed files using defaults - id: changed-files - uses: trilom/file-changes-action@v1.2.3 - continue-on-error: true - - - name: Run step when a file changes - uses: actions-ecosystem/action-create-comment@v1 - env: - GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" - if: | - contains(steps.changed-files.outputs.files_modified, 'cypress') == false && - contains(steps.changed-files.outputs.files_modified, 'test') == false - with: - github_token: ${{ secrets.github_token }} - body: | - Unable to find test scripts. Please add necessary tests to the PR. \ No newline at end of file + GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" \ No newline at end of file diff --git a/.github/workflows/pr-test-file-check.yml b/.github/workflows/pr-test-file-check.yml new file mode 100644 index 000000000000..5305bc927dc1 --- /dev/null +++ b/.github/workflows/pr-test-file-check.yml @@ -0,0 +1,33 @@ +name: Check for test files + +on: + pull_request: + branches: [ release ] + types: [ opened, reopened, edited ] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Get changed files using defaults + id: changed-files + uses: trilom/file-changes-action@v1.2.3 + continue-on-error: true + + - name: Ger labels of the PR + id: labels + uses: joerick/pr-labels-action@v1.0.6 + continue-on-error: true + + - name: Comment if test files not found + uses: actions-ecosystem/action-create-comment@v1 + env: + GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" + if: | + contains(fromJson('["cypress", "test"]'), toJson(steps.changed-files.outputs.files_modified)) == false && + contains(fromJson('["Bug", "Enhancement", "Pod"]'), toJson(steps.labels.outputs.labels)) == true + with: + github_token: ${{ secrets.github_token }} + body: | + Unable to find test scripts. Please add necessary tests to the PR. \ No newline at end of file diff --git a/.github/workflows/test-build-docker-image.yml b/.github/workflows/test-build-docker-image.yml index fdf1e6fb3cf9..3a29c318f567 100644 --- a/.github/workflows/test-build-docker-image.yml +++ b/.github/workflows/test-build-docker-image.yml @@ -553,6 +553,10 @@ jobs: CYPRESS_TESTPASSWORD1: ${{ secrets.CYPRESS_TESTPASSWORD1 }} CYPRESS_TESTUSERNAME2: ${{ secrets.CYPRESS_TESTUSERNAME2 }} CYPRESS_TESTPASSWORD2: ${{ secrets.CYPRESS_TESTPASSWORD1 }} + CYPRESS_TESTUSERNAME3: ${{ secrets.CYPRESS_TESTUSERNAME3 }} + CYPRESS_TESTPASSWORD3: ${{ secrets.CYPRESS_TESTPASSWORD3 }} + CYPRESS_TESTUSERNAME4: ${{ secrets.CYPRESS_TESTUSERNAME4 }} + CYPRESS_TESTPASSWORD4: ${{ secrets.CYPRESS_TESTPASSWORD4 }} CYPRESS_S3_ACCESS_KEY: ${{ secrets.CYPRESS_S3_ACCESS_KEY }} CYPRESS_S3_SECRET_KEY: ${{ secrets.CYPRESS_S3_SECRET_KEY }} CYPRESS_APPSMITH_OAUTH2_GOOGLE_CLIENT_ID: ${{ secrets.CYPRESS_APPSMITH_OAUTH2_GOOGLE_CLIENT_ID }} @@ -637,7 +641,7 @@ jobs: strategy: fail-fast: false matrix: - job: [0, 1] + job: [0, 1, 2, 3, 4] # Service containers to run with this job. Required for running tests services: @@ -817,6 +821,10 @@ jobs: CYPRESS_TESTPASSWORD1: ${{ secrets.CYPRESS_TESTPASSWORD1 }} CYPRESS_TESTUSERNAME2: ${{ secrets.CYPRESS_TESTUSERNAME2 }} CYPRESS_TESTPASSWORD2: ${{ secrets.CYPRESS_TESTPASSWORD1 }} + CYPRESS_TESTUSERNAME3: ${{ secrets.CYPRESS_TESTUSERNAME3 }} + CYPRESS_TESTPASSWORD3: ${{ secrets.CYPRESS_TESTPASSWORD3 }} + CYPRESS_TESTUSERNAME4: ${{ secrets.CYPRESS_TESTUSERNAME4 }} + CYPRESS_TESTPASSWORD4: ${{ secrets.CYPRESS_TESTPASSWORD4 }} CYPRESS_S3_ACCESS_KEY: ${{ secrets.CYPRESS_S3_ACCESS_KEY }} CYPRESS_S3_SECRET_KEY: ${{ secrets.CYPRESS_S3_SECRET_KEY }} CYPRESS_GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.CYPRESS_GITHUB_PERSONAL_ACCESS_TOKEN }} @@ -852,6 +860,10 @@ jobs: CYPRESS_TESTPASSWORD1: ${{ secrets.CYPRESS_TESTPASSWORD1 }} CYPRESS_TESTUSERNAME2: ${{ secrets.CYPRESS_TESTUSERNAME2 }} CYPRESS_TESTPASSWORD2: ${{ secrets.CYPRESS_TESTPASSWORD1 }} + CYPRESS_TESTUSERNAME3: ${{ secrets.CYPRESS_TESTUSERNAME3 }} + CYPRESS_TESTPASSWORD3: ${{ secrets.CYPRESS_TESTPASSWORD3 }} + CYPRESS_TESTUSERNAME4: ${{ secrets.CYPRESS_TESTUSERNAME4 }} + CYPRESS_TESTPASSWORD4: ${{ secrets.CYPRESS_TESTPASSWORD4 }} CYPRESS_S3_ACCESS_KEY: ${{ secrets.CYPRESS_S3_ACCESS_KEY }} CYPRESS_S3_SECRET_KEY: ${{ secrets.CYPRESS_S3_SECRET_KEY }} CYPRESS_GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.CYPRESS_GITHUB_PERSONAL_ACCESS_TOKEN }} @@ -891,6 +903,10 @@ jobs: CYPRESS_TESTPASSWORD1: ${{ secrets.CYPRESS_TESTPASSWORD1 }} CYPRESS_TESTUSERNAME2: ${{ secrets.CYPRESS_TESTUSERNAME2 }} CYPRESS_TESTPASSWORD2: ${{ secrets.CYPRESS_TESTPASSWORD1 }} + CYPRESS_TESTUSERNAME3: ${{ secrets.CYPRESS_TESTUSERNAME3 }} + CYPRESS_TESTPASSWORD3: ${{ secrets.CYPRESS_TESTPASSWORD3 }} + CYPRESS_TESTUSERNAME4: ${{ secrets.CYPRESS_TESTUSERNAME4 }} + CYPRESS_TESTPASSWORD4: ${{ secrets.CYPRESS_TESTPASSWORD4 }} CYPRESS_S3_ACCESS_KEY: ${{ secrets.CYPRESS_S3_ACCESS_KEY }} CYPRESS_S3_SECRET_KEY: ${{ secrets.CYPRESS_S3_SECRET_KEY }} CYPRESS_GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.CYPRESS_GITHUB_PERSONAL_ACCESS_TOKEN }} @@ -1255,6 +1271,10 @@ jobs: CYPRESS_TESTPASSWORD1: ${{ secrets.CYPRESS_TESTPASSWORD1 }} CYPRESS_TESTUSERNAME2: ${{ secrets.CYPRESS_TESTUSERNAME2 }} CYPRESS_TESTPASSWORD2: ${{ secrets.CYPRESS_TESTPASSWORD1 }} + CYPRESS_TESTUSERNAME3: ${{ secrets.CYPRESS_TESTUSERNAME3 }} + CYPRESS_TESTPASSWORD3: ${{ secrets.CYPRESS_TESTPASSWORD3 }} + CYPRESS_TESTUSERNAME4: ${{ secrets.CYPRESS_TESTUSERNAME4 }} + CYPRESS_TESTPASSWORD4: ${{ secrets.CYPRESS_TESTPASSWORD4 }} CYPRESS_S3_ACCESS_KEY: ${{ secrets.CYPRESS_S3_ACCESS_KEY }} CYPRESS_S3_SECRET_KEY: ${{ secrets.CYPRESS_S3_SECRET_KEY }} CYPRESS_GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.CYPRESS_GITHUB_PERSONAL_ACCESS_TOKEN }} @@ -1288,6 +1308,10 @@ jobs: CYPRESS_TESTPASSWORD1: ${{ secrets.CYPRESS_TESTPASSWORD1 }} CYPRESS_TESTUSERNAME2: ${{ secrets.CYPRESS_TESTUSERNAME2 }} CYPRESS_TESTPASSWORD2: ${{ secrets.CYPRESS_TESTPASSWORD1 }} + CYPRESS_TESTUSERNAME3: ${{ secrets.CYPRESS_TESTUSERNAME3 }} + CYPRESS_TESTPASSWORD3: ${{ secrets.CYPRESS_TESTPASSWORD3 }} + CYPRESS_TESTUSERNAME4: ${{ secrets.CYPRESS_TESTUSERNAME4 }} + CYPRESS_TESTPASSWORD4: ${{ secrets.CYPRESS_TESTPASSWORD4 }} CYPRESS_S3_ACCESS_KEY: ${{ secrets.CYPRESS_S3_ACCESS_KEY }} CYPRESS_S3_SECRET_KEY: ${{ secrets.CYPRESS_S3_SECRET_KEY }} CYPRESS_GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.CYPRESS_GITHUB_PERSONAL_ACCESS_TOKEN }} @@ -1326,6 +1350,10 @@ jobs: CYPRESS_TESTPASSWORD1: ${{ secrets.CYPRESS_TESTPASSWORD1 }} CYPRESS_TESTUSERNAME2: ${{ secrets.CYPRESS_TESTUSERNAME2 }} CYPRESS_TESTPASSWORD2: ${{ secrets.CYPRESS_TESTPASSWORD1 }} + CYPRESS_TESTUSERNAME3: ${{ secrets.CYPRESS_TESTUSERNAME3 }} + CYPRESS_TESTPASSWORD3: ${{ secrets.CYPRESS_TESTPASSWORD3 }} + CYPRESS_TESTUSERNAME4: ${{ secrets.CYPRESS_TESTUSERNAME4 }} + CYPRESS_TESTPASSWORD4: ${{ secrets.CYPRESS_TESTPASSWORD4 }} CYPRESS_S3_ACCESS_KEY: ${{ secrets.CYPRESS_S3_ACCESS_KEY }} CYPRESS_S3_SECRET_KEY: ${{ secrets.CYPRESS_S3_SECRET_KEY }} CYPRESS_GITHUB_PERSONAL_ACCESS_TOKEN: ${{ secrets.CYPRESS_GITHUB_PERSONAL_ACCESS_TOKEN }} @@ -1476,7 +1504,7 @@ jobs: # Force save the failed spec list into a cache - name: Store the combined run result - if: needs.ui-test.result + if: needs.ui-test.result != 'success' uses: martijnhols/actions-cache/save@v3 with: path: | @@ -1487,7 +1515,7 @@ jobs: # Force save the fat failed spec list into a cache - name: Store the combined run result for fat - if: needs.ui-test.result + if: needs.fat-container-test.result != 'success' uses: martijnhols/actions-cache/save@v3 with: path: | @@ -1499,7 +1527,7 @@ jobs: # Upload combined failed spec list to a file # This is done for debugging. - name: upload combined failed spec - if: needs.ui-test.result + if: needs.ui-test.result != 'success' uses: actions/upload-artifact@v2 with: name: combined_failed_spec @@ -1508,7 +1536,7 @@ jobs: # Upload combined failed fat spec list to a file # This is done for debugging. - name: upload combined failed spec - if: needs.ui-test.result + if: needs.fat-container-test.result != 'success' uses: actions/upload-artifact@v2 with: name: combined_failed_spec_fat diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ef78d7888ea2..8252ed59e735 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -21,7 +21,7 @@ We welcome all feature requests, whether it's to add new functionality to an exi File your feature request through GitHub Issues using the [Feature Request](https://github.com/appsmithorg/appsmith/issues/new?assignees=Nikhil-Nandagopal&labels=Enhancement&template=--feature-request.yaml&title=%5BFeature%5D%3A+) template. #### 📝 Improve the documentation -In the process of shipping features quickly, we may forget to keep our docs up to date. You can help by suggesting improvements to our documentation using the [Documentation Improvement](https://github.com/appsmithorg/appsmith/issues/new?assignees=Nikhil-Nandagopal&labels=Documentation&template=--documentation-improvement.yaml&title=%5BDocs%5D%3A+) template or dive right into our [Docs Contribution Guide](contributions/docs/CONTRIBUTING.md)! +In the process of shipping features quickly, we may forget to keep our docs up to date. You can help by suggesting improvements to our documentation using the [Documentation templates](https://github.com/appsmithorg/appsmith-docs/issues/new/choose) or dive right into our [Docs Contribution Guide](https://github.com/appsmithorg/appsmith-docs/blob/main/CONTRIBUTING.md)! #### ⚙️ Close a Bug / Feature issue We welcome contributions that help make appsmith bug free & improve the experience of our users. You can also find issues tagged [Good First Issues](https://github.com/appsmithorg/appsmith/issues?q=is%3Aopen+is%3Aissue+label%3A%22Good+First+Issue%22+bug). Check out our [Code Contribution Guide](contributions/CodeContributionsGuidelines.md) to begin. diff --git a/README.md b/README.md index b7ff6db36236..aa2f8577d40e 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,10 @@ YouTubeTemplates - + + +
+How Appsmith Works --- @@ -165,8 +168,8 @@ Lets build great software together. [![sharat87](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/120119?v=4&w=50&h=50&mask=circle)](https://github.com/sharat87) [![riodeuno](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/103687?v=4&w=50&h=50&mask=circle)](https://github.com/riodeuno) [![vicky-primathon](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/67091118?v=4&w=50&h=50&mask=circle)](https://github.com/vicky-primathon) -[![satbir121](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/39981226?v=4&w=50&h=50&mask=circle)](https://github.com/satbir121) [![akash-codemonk](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/67054171?v=4&w=50&h=50&mask=circle)](https://github.com/akash-codemonk) +[![satbir121](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/39981226?v=4&w=50&h=50&mask=circle)](https://github.com/satbir121) [![nidhi-nair](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/5298848?v=4&w=50&h=50&mask=circle)](https://github.com/nidhi-nair) [![Tooluloope](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/31691737?v=4&w=50&h=50&mask=circle)](https://github.com/Tooluloope) [![sumitsum](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/1757421?v=4&w=50&h=50&mask=circle)](https://github.com/sumitsum) @@ -181,61 +184,62 @@ Lets build great software together. [![Rishabh-Rathod](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/23132741?v=4&w=50&h=50&mask=circle)](https://github.com/Rishabh-Rathod) [![sbalaji1192](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/5328605?v=4&w=50&h=50&mask=circle)](https://github.com/sbalaji1192) [![ohansFavour](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/46670083?v=4&w=50&h=50&mask=circle)](https://github.com/ohansFavour) +[![ankitakinger](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/28362912?v=4&w=50&h=50&mask=circle)](https://github.com/ankitakinger) [![Aishwarya-U-R](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/91450662?v=4&w=50&h=50&mask=circle)](https://github.com/Aishwarya-U-R) [![Irongade](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/37867493?v=4&w=50&h=50&mask=circle)](https://github.com/Irongade) [![prsidhu](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/5424788?v=4&w=50&h=50&mask=circle)](https://github.com/prsidhu) [![pranavkanade](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/13262095?v=4&w=50&h=50&mask=circle)](https://github.com/pranavkanade) [![somangshu](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/11089579?v=4&w=50&h=50&mask=circle)](https://github.com/somangshu) -[![ankitakinger](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/28362912?v=4&w=50&h=50&mask=circle)](https://github.com/ankitakinger) [![ApekshaBhosale](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/7846888?v=4&w=50&h=50&mask=circle)](https://github.com/ApekshaBhosale) -[![sidhantgoel](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/3933675?v=4&w=50&h=50&mask=circle)](https://github.com/sidhantgoel) [![SatishGandham](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/441914?v=4&w=50&h=50&mask=circle)](https://github.com/SatishGandham) +[![sidhantgoel](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/3933675?v=4&w=50&h=50&mask=circle)](https://github.com/sidhantgoel) [![yatinappsmith](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/84702014?v=4&w=50&h=50&mask=circle)](https://github.com/yatinappsmith) [![rahulramesha](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/71900764?v=4&w=50&h=50&mask=circle)](https://github.com/rahulramesha) [![IAmAnubhavSaini](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/1573771?v=4&w=50&h=50&mask=circle)](https://github.com/IAmAnubhavSaini) [![marks0351](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/35134347?v=4&w=50&h=50&mask=circle)](https://github.com/marks0351) [![albinAppsmith](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/87797149?v=4&w=50&h=50&mask=circle)](https://github.com/albinAppsmith) -[![ayushpahwa](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/8526215?v=4&w=50&h=50&mask=circle)](https://github.com/ayushpahwa) [![Parthvi12](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/80334441?v=4&w=50&h=50&mask=circle)](https://github.com/Parthvi12) +[![ayushpahwa](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/8526215?v=4&w=50&h=50&mask=circle)](https://github.com/ayushpahwa) +[![AmanAgarwal041](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/7565635?v=4&w=50&h=50&mask=circle)](https://github.com/AmanAgarwal041) [![ashit-rath](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/88306433?v=4&w=50&h=50&mask=circle)](https://github.com/ashit-rath) [![areyabhishek](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/30255708?v=4&w=50&h=50&mask=circle)](https://github.com/areyabhishek) -[![AmanAgarwal041](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/7565635?v=4&w=50&h=50&mask=circle)](https://github.com/AmanAgarwal041) -[![rimildeyjsr](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/10229595?v=4&w=50&h=50&mask=circle)](https://github.com/rimildeyjsr) -[![cokoghenun](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/17744578?v=4&w=50&h=50&mask=circle)](https://github.com/cokoghenun) [![ankurrsinghal](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/17961105?v=4&w=50&h=50&mask=circle)](https://github.com/ankurrsinghal) +[![rimildeyjsr](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/10229595?v=4&w=50&h=50&mask=circle)](https://github.com/rimildeyjsr) [![vishnu-gp](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/9128194?v=4&w=50&h=50&mask=circle)](https://github.com/vishnu-gp) +[![cokoghenun](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/17744578?v=4&w=50&h=50&mask=circle)](https://github.com/cokoghenun) [![keyurparalkar](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/14138515?v=4&w=50&h=50&mask=circle)](https://github.com/keyurparalkar) [![vihar](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/16307796?v=4&w=50&h=50&mask=circle)](https://github.com/vihar) [![eco-monk](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/66776129?v=4&w=50&h=50&mask=circle)](https://github.com/eco-monk) [![ChandanBalajiBP](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/104058110?v=4&w=50&h=50&mask=circle)](https://github.com/ChandanBalajiBP) [![souma-ghosh](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/103924539?v=4&w=50&h=50&mask=circle)](https://github.com/souma-ghosh) +[![berzerkeer](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/74818788?v=4&w=50&h=50&mask=circle)](https://github.com/berzerkeer) [![subrata71](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/3524599?v=4&w=50&h=50&mask=circle)](https://github.com/subrata71) [![dhruvikn](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/22471214?v=4&w=50&h=50&mask=circle)](https://github.com/dhruvikn) [![sum35h](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/20785806?v=4&w=50&h=50&mask=circle)](https://github.com/sum35h) [![tanvibhakta](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/13763558?v=4&w=50&h=50&mask=circle)](https://github.com/tanvibhakta) -[![berzerkeer](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/74818788?v=4&w=50&h=50&mask=circle)](https://github.com/berzerkeer) -[![nsarupr](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/20905988?v=4&w=50&h=50&mask=circle)](https://github.com/nsarupr) -[![sondermanish](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/107841575?v=4&w=50&h=50&mask=circle)](https://github.com/sondermanish) [![sneha122](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/30018882?v=4&w=50&h=50&mask=circle)](https://github.com/sneha122) +[![nsarupr](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/20905988?v=4&w=50&h=50&mask=circle)](https://github.com/nsarupr) +[![iamrkcheers](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/16760643?v=4&w=50&h=50&mask=circle)](https://github.com/iamrkcheers) [![pratapaprasanna](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/15846947?v=4&w=50&h=50&mask=circle)](https://github.com/pratapaprasanna) +[![sondermanish](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/107841575?v=4&w=50&h=50&mask=circle)](https://github.com/sondermanish) [![Pranay105](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/48308728?v=4&w=50&h=50&mask=circle)](https://github.com/Pranay105) -[![vaibh1297](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/40293928?v=4&w=50&h=50&mask=circle)](https://github.com/vaibh1297) [![ankitsrivas14](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/67647761?v=4&w=50&h=50&mask=circle)](https://github.com/ankitsrivas14) +[![vaibh1297](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/40293928?v=4&w=50&h=50&mask=circle)](https://github.com/vaibh1297) +[![NilanshBansal](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/25542733?v=4&w=50&h=50&mask=circle)](https://github.com/NilanshBansal) +[![ravikp7](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/13567359?v=4&w=50&h=50&mask=circle)](https://github.com/ravikp7) [![kocharrahul7](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/20532920?v=4&w=50&h=50&mask=circle)](https://github.com/kocharrahul7) [![rohitagarwal88](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/890915?v=4&w=50&h=50&mask=circle)](https://github.com/rohitagarwal88) [![ramsaptami](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/79509062?v=4&w=50&h=50&mask=circle)](https://github.com/ramsaptami) [![AS-Laguna](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/101155659?v=4&w=50&h=50&mask=circle)](https://github.com/AS-Laguna) -[![NilanshBansal](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/25542733?v=4&w=50&h=50&mask=circle)](https://github.com/NilanshBansal) +[![gitstart-appsmith](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/114667275?v=4&w=50&h=50&mask=circle)](https://github.com/gitstart-appsmith) +[![vivonk](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/25587962?v=4&w=50&h=50&mask=circle)](https://github.com/vivonk) [![RakshaKShetty](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/45958978?v=4&w=50&h=50&mask=circle)](https://github.com/RakshaKShetty) -[![ravikp7](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/13567359?v=4&w=50&h=50&mask=circle)](https://github.com/ravikp7) -[![iamrkcheers](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/16760643?v=4&w=50&h=50&mask=circle)](https://github.com/iamrkcheers) +[![Rhitottam](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/4265002?v=4&w=50&h=50&mask=circle)](https://github.com/Rhitottam) [![Rishabhkaul](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/1650391?v=4&w=50&h=50&mask=circle)](https://github.com/Rishabhkaul) [![rohan-arthur](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/94514895?v=4&w=50&h=50&mask=circle)](https://github.com/rohan-arthur) -[![somnathdasadhikari](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/17508328?v=4&w=50&h=50&mask=circle)](https://github.com/somnathdasadhikari) +[![appsmithguru](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/116803038?v=4&w=50&h=50&mask=circle)](https://github.com/appsmithguru) [![hiteshjoshi](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/217911?v=4&w=50&h=50&mask=circle)](https://github.com/hiteshjoshi) [![rlnorthcutt](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/673633?v=4&w=50&h=50&mask=circle)](https://github.com/rlnorthcutt) -[![vuiets](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/1571370?v=4&w=50&h=50&mask=circle)](https://github.com/vuiets) -[![Rhitottam](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/4265002?v=4&w=50&h=50&mask=circle)](https://github.com/Rhitottam) [![tomjose92](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/5265702?v=4&w=50&h=50&mask=circle)](https://github.com/tomjose92) [![ginilpg](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/8299030?v=4&w=50&h=50&mask=circle)](https://github.com/ginilpg) [![sribalajig](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/15727421?v=4&w=50&h=50&mask=circle)](https://github.com/sribalajig) @@ -255,7 +259,7 @@ Lets build great software together. [![Vidushi-Gupta](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/55969597?v=4&w=50&h=50&mask=circle)](https://github.com/Vidushi-Gupta) [![aakashDesign](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/65771350?v=4&w=50&h=50&mask=circle)](https://github.com/aakashDesign) [![appsmith-bot](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/74705725?v=4&w=50&h=50&mask=circle)](https://github.com/appsmith-bot) -[![shastryblr](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/79321954?v=4&w=50&h=50&mask=circle)](https://github.com/shastryblr) +[![shastry-gg](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/79321954?v=4&w=50&h=50&mask=circle)](https://github.com/shastry-gg) [![AnandiKulkarni](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/80756091?v=4&w=50&h=50&mask=circle)](https://github.com/AnandiKulkarni) [![momcilo-appsmith](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/81744497?v=4&w=50&h=50&mask=circle)](https://github.com/momcilo-appsmith) [![shwetha-ramesh](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/92293815?v=4&w=50&h=50&mask=circle)](https://github.com/shwetha-ramesh) @@ -272,8 +276,8 @@ Lets build great software together. [![laveena-en](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/109572422?v=4&w=50&h=50&mask=circle)](https://github.com/laveena-en) [![kamakshibhat-appsmith](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/113327629?v=4&w=50&h=50&mask=circle)](https://github.com/kamakshibhat-appsmith) [![felix-appsmith](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/114161539?v=4&w=50&h=50&mask=circle)](https://github.com/felix-appsmith) -[![gitstart-appsmith](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/114667275?v=4&w=50&h=50&mask=circle)](https://github.com/gitstart-appsmith) -[![appsmithguru](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/116803038?v=4&w=50&h=50&mask=circle)](https://github.com/appsmithguru) +[![Vijetha-Kaja](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/119562824?v=4&w=50&h=50&mask=circle)](https://github.com/Vijetha-Kaja) +[![AnandShahs123](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/119847783?v=4&w=50&h=50&mask=circle)](https://github.com/AnandShahs123) [![rishabhsaxena](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/1944800?v=4&w=50&h=50&mask=circle)](https://github.com/rishabhsaxena) [![wmdev0808](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/82799722?v=4&w=50&h=50&mask=circle)](https://github.com/wmdev0808) [![techbhavin](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/58818598?v=4&w=50&h=50&mask=circle)](https://github.com/techbhavin) @@ -324,6 +328,7 @@ Lets build great software together. [![akbansa](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/13042781?v=4&w=50&h=50&mask=circle)](https://github.com/akbansa) [![bharat-patodi](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/16771172?v=4&w=50&h=50&mask=circle)](https://github.com/bharat-patodi) [![Bhavin789](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/15359575?v=4&w=50&h=50&mask=circle)](https://github.com/Bhavin789) +[![bhuvanaindukuri](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/29200083?v=4&w=50&h=50&mask=circle)](https://github.com/bhuvanaindukuri) [![donno2048](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/61805754?v=4&w=50&h=50&mask=circle)](https://github.com/donno2048) [![reachtokish](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/10685261?v=4&w=50&h=50&mask=circle)](https://github.com/reachtokish) [![nuwan94](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/25694570?v=4&w=50&h=50&mask=circle)](https://github.com/nuwan94) @@ -352,7 +357,6 @@ Lets build great software together. [![anvaravind](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/12210689?v=4&w=50&h=50&mask=circle)](https://github.com/anvaravind) [![ari-hacks](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/36749814?v=4&w=50&h=50&mask=circle)](https://github.com/ari-hacks) [![ashwanisindhu1](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/549817?v=4&w=50&h=50&mask=circle)](https://github.com/ashwanisindhu1) -[![bhuvanaindukuri](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/29200083?v=4&w=50&h=50&mask=circle)](https://github.com/bhuvanaindukuri) [![Caitlin-Fotheringham](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/49273562?v=4&w=50&h=50&mask=circle)](https://github.com/Caitlin-Fotheringham) [![Chiradeep-Banik](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/76490903?v=4&w=50&h=50&mask=circle)](https://github.com/Chiradeep-Banik) [![chrismaeda](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/1022718?v=4&w=50&h=50&mask=circle)](https://github.com/chrismaeda) @@ -387,9 +391,11 @@ Lets build great software together. [![pushkar1393](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/31943620?v=4&w=50&h=50&mask=circle)](https://github.com/pushkar1393) [![imor](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/1666073?v=4&w=50&h=50&mask=circle)](https://github.com/imor) [![RishiKumarRay](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/87641376?v=4&w=50&h=50&mask=circle)](https://github.com/RishiKumarRay) +[![Rooney30](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/103701369?v=4&w=50&h=50&mask=circle)](https://github.com/Rooney30) [![Saket2](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/49346036?v=4&w=50&h=50&mask=circle)](https://github.com/Saket2) [![withshubh](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/25361949?v=4&w=50&h=50&mask=circle)](https://github.com/withshubh) [![smrutiparida](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/5945400?v=4&w=50&h=50&mask=circle)](https://github.com/smrutiparida) +[![somnathdasadhikari](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/17508328?v=4&w=50&h=50&mask=circle)](https://github.com/somnathdasadhikari) [![srijanshetty](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/1744347?v=4&w=50&h=50&mask=circle)](https://github.com/srijanshetty) [![Sufiyan1997](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/22118049?v=4&w=50&h=50&mask=circle)](https://github.com/Sufiyan1997) [![rayrny](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/48341341?v=4&w=50&h=50&mask=circle)](https://github.com/rayrny) @@ -408,7 +414,7 @@ Lets build great software together. [![zimkjh](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/31986639?v=4&w=50&h=50&mask=circle)](https://github.com/zimkjh) [![kyteinsky](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/20724224?v=4&w=50&h=50&mask=circle)](https://github.com/kyteinsky) [![lifeneedspassion](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/7675485?v=4&w=50&h=50&mask=circle)](https://github.com/lifeneedspassion) -[![geek-nupur](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/35266222?v=4&w=50&h=50&mask=circle)](https://github.com/geek-nupur) +[![nupur-singhal1992](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/35266222?v=4&w=50&h=50&mask=circle)](https://github.com/nupur-singhal1992) [![nzidol](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/13618973?v=4&w=50&h=50&mask=circle)](https://github.com/nzidol) [![onifs10](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/48095055?v=4&w=50&h=50&mask=circle)](https://github.com/onifs10) diff --git a/app/client/build.sh b/app/client/build.sh index 2f625ec5c4d7..9a2480b1012b 100755 --- a/app/client/build.sh +++ b/app/client/build.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash set -e @@ -8,5 +8,11 @@ echo "Sentry Auth Token: $SENTRY_AUTH_TOKEN" REACT_APP_SENTRY_RELEASE=$GIT_SHA REACT_APP_CLIENT_LOG_LEVEL=ERROR EXTEND_ESLINT=true craco --max-old-space-size=4096 build --config craco.build.config.js -rm ./build/static/js/*.js.map + +if [ "$GITHUB_REPOSITORY" == "appsmithorg/appsmith-ee" ]; then + echo "Deleting sourcemaps for EE" + rm ./build/static/js/*.js.map + rm ./build/static/js/*.js.map.gz +fi + echo "build finished" diff --git a/app/client/craco.build.config.js b/app/client/craco.build.config.js index 9d5f3b698e6a..608c5e731fb0 100644 --- a/app/client/craco.build.config.js +++ b/app/client/craco.build.config.js @@ -15,7 +15,7 @@ plugins.push( swSrc: "./src/serviceWorker.js", mode: "development", swDest: "./pageService.js", - maximumFileSizeToCacheInBytes: 10 * 1024 * 1024, + maximumFileSizeToCacheInBytes: 11 * 1024 * 1024, }), ); diff --git a/app/client/cypress.env.json b/app/client/cypress.env.json index b78ee35858bf..3f1601590342 100644 --- a/app/client/cypress.env.json +++ b/app/client/cypress.env.json @@ -1,5 +1,5 @@ { - "MySQL":1, - "Mongo":1, + "MySQL": 1, + "Mongo": 1, "Edition": 0 -} +} \ No newline at end of file diff --git a/app/client/cypress/fixtures/Bugs/CheckboxGroupInListWidgetDsl.json b/app/client/cypress/fixtures/Bugs/CheckboxGroupInListWidgetDsl.json new file mode 100644 index 000000000000..70a0677350d5 --- /dev/null +++ b/app/client/cypress/fixtures/Bugs/CheckboxGroupInListWidgetDsl.json @@ -0,0 +1,857 @@ +{ + "dsl": { + "widgetName": "MainContainer", + "backgroundColor": "none", + "rightColumn": 4896.0, + "snapColumns": 64.0, + "detachFromLayout": true, + "widgetId": "0", + "topRow": 0.0, + "bottomRow": 560.0, + "containerStyle": "none", + "snapRows": 125.0, + "parentRowSpace": 1.0, + "type": "CANVAS_WIDGET", + "canExtend": true, + "version": 69.0, + "minHeight": 1292.0, + "dynamicTriggerPathList": [], + "parentColumnSpace": 1.0, + "dynamicBindingPathList": [], + "leftColumn": 0.0, + "children": [ + { + "template": { + "Image1": { + "isVisible": true, + "defaultImage": "https://assets.appsmith.com/widgets/default.png", + "imageShape": "RECTANGLE", + "maxZoomLevel": 1.0, + "enableRotation": false, + "enableDownload": false, + "objectFit": "cover", + "image": "{{List1.listData.map((currentItem) => currentItem.img)}}", + "widgetName": "Image1", + "version": 1.0, + "animateLoading": true, + "type": "IMAGE_WIDGET", + "hideCard": false, + "isDeprecated": false, + "displayName": "Image", + "key": "n1szm8g77z", + "iconSVG": "/static/media/icon.52d8fb963abcb95c79b10f1553389f22.svg", + "boxShadow": "none", + "dynamicBindingPathList": [ + { + "key": "image" + }, + { + "key": "borderRadius" + } + ], + "dynamicTriggerPathList": [], + "widgetId": "852qrq5vm1", + "renderMode": "CANVAS", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "isLoading": false, + "leftColumn": 0.0, + "rightColumn": 16.0, + "topRow": 0.0, + "bottomRow": 8.0, + "parentId": "7z0hh0zvos" + }, + "Text1": { + "isVisible": true, + "text": "{{List1.listData.map((currentItem) => currentItem.name)}}", + "fontSize": "1rem", + "fontStyle": "BOLD", + "textAlign": "LEFT", + "textColor": "#231F20", + "widgetName": "Text1", + "shouldTruncate": false, + "overflow": "NONE", + "version": 1.0, + "animateLoading": true, + "minDynamicHeight": 4.0, + "maxDynamicHeight": 9000.0, + "dynamicHeight": "AUTO_HEIGHT", + "searchTags": [ + "typography", + "paragraph", + "label" + ], + "type": "TEXT_WIDGET", + "hideCard": false, + "isDeprecated": false, + "displayName": "Text", + "key": "tf4er66bom", + "iconSVG": "/static/media/icon.97c59b523e6f70ba6f40a10fc2c7c5b5.svg", + "textStyle": "HEADING", + "boxShadow": "none", + "dynamicBindingPathList": [ + { + "key": "text" + }, + { + "key": "truncateButtonColor" + }, + { + "key": "fontFamily" + }, + { + "key": "borderRadius" + } + ], + "dynamicTriggerPathList": [], + "widgetId": "1sfn18h25j", + "renderMode": "CANVAS", + "truncateButtonColor": "{{appsmith.theme.colors.primaryColor}}", + "fontFamily": "{{appsmith.theme.fontFamily.appFont}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "isLoading": false, + "leftColumn": 16.0, + "rightColumn": 28.0, + "topRow": 0.0, + "bottomRow": 4.0, + "parentId": "7z0hh0zvos" + }, + "Text2": { + "isVisible": true, + "text": "{{List1.listData.map((currentItem) => currentItem.id)}}", + "fontSize": "1rem", + "fontStyle": "BOLD", + "textAlign": "LEFT", + "textColor": "#231F20", + "widgetName": "Text2", + "shouldTruncate": false, + "overflow": "NONE", + "version": 1.0, + "animateLoading": true, + "minDynamicHeight": 4.0, + "maxDynamicHeight": 9000.0, + "dynamicHeight": "AUTO_HEIGHT", + "searchTags": [ + "typography", + "paragraph", + "label" + ], + "type": "TEXT_WIDGET", + "hideCard": false, + "isDeprecated": false, + "displayName": "Text", + "key": "tf4er66bom", + "iconSVG": "/static/media/icon.97c59b523e6f70ba6f40a10fc2c7c5b5.svg", + "textStyle": "BODY", + "boxShadow": "none", + "dynamicBindingPathList": [ + { + "key": "text" + }, + { + "key": "truncateButtonColor" + }, + { + "key": "fontFamily" + }, + { + "key": "borderRadius" + } + ], + "dynamicTriggerPathList": [], + "widgetId": "ekx7bft1ux", + "renderMode": "CANVAS", + "truncateButtonColor": "{{appsmith.theme.colors.primaryColor}}", + "fontFamily": "{{appsmith.theme.fontFamily.appFont}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "isLoading": false, + "leftColumn": 16.0, + "rightColumn": 24.0, + "topRow": 4.0, + "bottomRow": 8.0, + "parentId": "7z0hh0zvos" + }, + "CheckboxGroup1": { + "isVisible": true, + "animateLoading": true, + "labelTextSize": "0.875rem", + "options": [ + { + "label": "Blue", + "value": "BLUE" + }, + { + "label": "Green", + "value": "GREEN" + }, + { + "label": "Red", + "value": "RED" + } + ], + "defaultSelectedValues": [ + "BLUE" + ], + "isDisabled": false, + "isInline": true, + "isRequired": false, + "labelText": "Label", + "labelPosition": "Top", + "labelAlignment": "left", + "labelWidth": 5.0, + "widgetName": "CheckboxGroup1", + "version": 2.0, + "minDynamicHeight": 4.0, + "maxDynamicHeight": 9000.0, + "dynamicHeight": "AUTO_HEIGHT", + "type": "CHECKBOX_GROUP_WIDGET", + "hideCard": false, + "isDeprecated": false, + "displayName": "Checkbox Group", + "key": "2ge4cq00z3", + "iconSVG": "/static/media/icon.ecb3847950c4515966ef642a32758afb.svg", + "widgetId": "30pveks53r", + "renderMode": "CANVAS", + "accentColor": "{{appsmith.theme.colors.primaryColor}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "isLoading": false, + "parentColumnSpace": 4.2734375, + "parentRowSpace": 10.0, + "leftColumn": 35.0, + "rightColumn": 58.0, + "topRow": 0.0, + "bottomRow": 6.0, + "parentId": "7z0hh0zvos", + "dynamicBindingPathList": [ + { + "key": "accentColor" + }, + { + "key": "borderRadius" + } + ], + "logBlackList": { + "isVisible": true, + "animateLoading": true, + "labelTextSize": true, + "options": true, + "defaultSelectedValues": true, + "isDisabled": true, + "isInline": true, + "isRequired": true, + "labelText": true, + "labelPosition": true, + "labelAlignment": true, + "labelWidth": true, + "widgetName": true, + "version": true, + "minDynamicHeight": true, + "maxDynamicHeight": true, + "dynamicHeight": true, + "searchTags": true, + "type": true, + "hideCard": true, + "isDeprecated": true, + "replacement": true, + "displayName": true, + "key": true, + "iconSVG": true, + "isCanvas": true, + "minHeight": true, + "widgetId": true, + "renderMode": true, + "accentColor": true, + "borderRadius": true, + "isLoading": true, + "parentColumnSpace": true, + "parentRowSpace": true, + "leftColumn": true, + "rightColumn": true, + "topRow": true, + "bottomRow": true, + "parentId": true, + "dynamicBindingPathList": true + } + } + }, + "boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}", + "widgetName": "List1", + "listData": [ + { + "id": "001", + "name": "Blue", + "img": "https://assets.appsmith.com/widgets/default.png" + }, + { + "id": "002", + "name": "Green", + "img": "https://assets.appsmith.com/widgets/default.png" + }, + { + "id": "003", + "name": "Red", + "img": "https://assets.appsmith.com/widgets/default.png" + } + ], + "isCanvas": true, + "displayName": "List", + "iconSVG": "/static/media/icon.9925ee17dee37bf1ba7374412563a8a7.svg", + "topRow": 8.0, + "bottomRow": 48.0, + "parentRowSpace": 10.0, + "type": "LIST_WIDGET", + "hideCard": false, + "gridGap": 0.0, + "animateLoading": true, + "parentColumnSpace": 12.5625, + "dynamicTriggerPathList": [], + "leftColumn": 18.0, + "dynamicBindingPathList": [ + { + "key": "accentColor" + }, + { + "key": "borderRadius" + }, + { + "key": "boxShadow" + }, + { + "key": "template.Image1.image" + }, + { + "key": "template.Text1.text" + }, + { + "key": "template.Text2.text" + } + ], + "gridType": "vertical", + "enhancements": true, + "children": [ + { + "boxShadow": "none", + "widgetName": "Canvas1", + "displayName": "Canvas", + "topRow": 0.0, + "bottomRow": 400.0, + "parentRowSpace": 1.0, + "type": "CANVAS_WIDGET", + "canExtend": false, + "hideCard": true, + "dropDisabled": true, + "openParentPropertyPane": true, + "minHeight": 400.0, + "noPad": true, + "parentColumnSpace": 1.0, + "leftColumn": 0.0, + "dynamicBindingPathList": [ + { + "key": "borderRadius" + }, + { + "key": "accentColor" + } + ], + "children": [ + { + "boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}", + "widgetName": "Container1", + "borderColor": "#E0DEDE", + "disallowCopy": true, + "isCanvas": true, + "displayName": "Container", + "iconSVG": "/static/media/icon.1977dca3370505e2db3a8e44cfd54907.svg", + "searchTags": [ + "div", + "parent", + "group" + ], + "topRow": 0.0, + "bottomRow": 12.0, + "dragDisabled": true, + "type": "CONTAINER_WIDGET", + "hideCard": false, + "openParentPropertyPane": true, + "shouldScrollContents": true, + "isDeletable": false, + "animateLoading": true, + "leftColumn": 0.0, + "dynamicBindingPathList": [ + { + "key": "borderRadius" + }, + { + "key": "boxShadow" + } + ], + "children": [ + { + "boxShadow": "none", + "widgetName": "Canvas2", + "displayName": "Canvas", + "topRow": 0.0, + "bottomRow": 150.0, + "parentRowSpace": 1.0, + "type": "CANVAS_WIDGET", + "canExtend": false, + "hideCard": true, + "minHeight": 150.0, + "parentColumnSpace": 1.0, + "leftColumn": 0.0, + "dynamicBindingPathList": [ + { + "key": "borderRadius" + }, + { + "key": "accentColor" + } + ], + "children": [ + { + "boxShadow": "none", + "widgetName": "Image1", + "displayName": "Image", + "iconSVG": "/static/media/icon.52d8fb963abcb95c79b10f1553389f22.svg", + "topRow": 0.0, + "bottomRow": 8.0, + "type": "IMAGE_WIDGET", + "hideCard": false, + "animateLoading": true, + "dynamicTriggerPathList": [], + "imageShape": "RECTANGLE", + "dynamicBindingPathList": [ + { + "key": "image" + }, + { + "key": "borderRadius" + } + ], + "leftColumn": 0.0, + "defaultImage": "https://assets.appsmith.com/widgets/default.png", + "key": "n1szm8g77z", + "image": "{{currentItem.img}}", + "isDeprecated": false, + "rightColumn": 16.0, + "objectFit": "cover", + "widgetId": "852qrq5vm1", + "logBlackList": { + "isVisible": true, + "defaultImage": true, + "imageShape": true, + "maxZoomLevel": true, + "enableRotation": true, + "enableDownload": true, + "objectFit": true, + "image": true, + "widgetName": true, + "version": true, + "animateLoading": true, + "searchTags": true, + "type": true, + "hideCard": true, + "isDeprecated": true, + "replacement": true, + "displayName": true, + "key": true, + "iconSVG": true, + "isCanvas": true, + "boxShadow": true, + "dynamicBindingPathList": true, + "dynamicTriggerPathList": true, + "minHeight": true, + "widgetId": true, + "renderMode": true, + "borderRadius": true, + "isLoading": true, + "parentColumnSpace": true, + "parentRowSpace": true, + "leftColumn": true, + "rightColumn": true, + "topRow": true, + "bottomRow": true, + "parentId": true + }, + "isVisible": true, + "version": 1.0, + "parentId": "7z0hh0zvos", + "renderMode": "CANVAS", + "isLoading": false, + "maxZoomLevel": 1.0, + "enableDownload": false, + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "enableRotation": false + }, + { + "boxShadow": "none", + "widgetName": "Text1", + "displayName": "Text", + "iconSVG": "/static/media/icon.97c59b523e6f70ba6f40a10fc2c7c5b5.svg", + "searchTags": [ + "typography", + "paragraph", + "label" + ], + "topRow": 0.0, + "bottomRow": 4.0, + "type": "TEXT_WIDGET", + "hideCard": false, + "animateLoading": true, + "overflow": "NONE", + "dynamicTriggerPathList": [], + "fontFamily": "{{appsmith.theme.fontFamily.appFont}}", + "dynamicBindingPathList": [ + { + "key": "text" + }, + { + "key": "truncateButtonColor" + }, + { + "key": "fontFamily" + }, + { + "key": "borderRadius" + } + ], + "leftColumn": 16.0, + "shouldTruncate": false, + "truncateButtonColor": "{{appsmith.theme.colors.primaryColor}}", + "text": "{{currentItem.name}}", + "key": "tf4er66bom", + "isDeprecated": false, + "rightColumn": 28.0, + "textAlign": "LEFT", + "dynamicHeight": "AUTO_HEIGHT", + "widgetId": "1sfn18h25j", + "logBlackList": { + "isVisible": true, + "text": true, + "fontSize": true, + "fontStyle": true, + "textAlign": true, + "textColor": true, + "widgetName": true, + "shouldTruncate": true, + "overflow": true, + "version": true, + "animateLoading": true, + "minDynamicHeight": true, + "maxDynamicHeight": true, + "dynamicHeight": true, + "searchTags": true, + "type": true, + "hideCard": true, + "isDeprecated": true, + "replacement": true, + "displayName": true, + "key": true, + "iconSVG": true, + "isCanvas": true, + "textStyle": true, + "boxShadow": true, + "dynamicBindingPathList": true, + "dynamicTriggerPathList": true, + "minHeight": true, + "widgetId": true, + "renderMode": true, + "truncateButtonColor": true, + "fontFamily": true, + "borderRadius": true, + "isLoading": true, + "parentColumnSpace": true, + "parentRowSpace": true, + "leftColumn": true, + "rightColumn": true, + "topRow": true, + "bottomRow": true, + "parentId": true + }, + "isVisible": true, + "fontStyle": "BOLD", + "textColor": "#231F20", + "version": 1.0, + "parentId": "7z0hh0zvos", + "renderMode": "CANVAS", + "isLoading": false, + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "maxDynamicHeight": 9000.0, + "fontSize": "1rem", + "textStyle": "HEADING", + "minDynamicHeight": 4.0 + }, + { + "boxShadow": "none", + "widgetName": "Text2", + "displayName": "Text", + "iconSVG": "/static/media/icon.97c59b523e6f70ba6f40a10fc2c7c5b5.svg", + "searchTags": [ + "typography", + "paragraph", + "label" + ], + "topRow": 4.0, + "bottomRow": 9.0, + "type": "TEXT_WIDGET", + "hideCard": false, + "animateLoading": true, + "overflow": "NONE", + "dynamicTriggerPathList": [], + "fontFamily": "{{appsmith.theme.fontFamily.appFont}}", + "dynamicBindingPathList": [ + { + "key": "text" + }, + { + "key": "truncateButtonColor" + }, + { + "key": "fontFamily" + }, + { + "key": "borderRadius" + } + ], + "leftColumn": 16.0, + "shouldTruncate": false, + "truncateButtonColor": "{{appsmith.theme.colors.primaryColor}}", + "text": "{{currentItem.id}}", + "key": "tf4er66bom", + "isDeprecated": false, + "rightColumn": 24.0, + "textAlign": "LEFT", + "dynamicHeight": "AUTO_HEIGHT", + "widgetId": "ekx7bft1ux", + "logBlackList": { + "isVisible": true, + "text": true, + "fontSize": true, + "fontStyle": true, + "textAlign": true, + "textColor": true, + "widgetName": true, + "shouldTruncate": true, + "overflow": true, + "version": true, + "animateLoading": true, + "minDynamicHeight": true, + "maxDynamicHeight": true, + "dynamicHeight": true, + "searchTags": true, + "type": true, + "hideCard": true, + "isDeprecated": true, + "replacement": true, + "displayName": true, + "key": true, + "iconSVG": true, + "isCanvas": true, + "textStyle": true, + "boxShadow": true, + "dynamicBindingPathList": true, + "dynamicTriggerPathList": true, + "minHeight": true, + "widgetId": true, + "renderMode": true, + "truncateButtonColor": true, + "fontFamily": true, + "borderRadius": true, + "isLoading": true, + "parentColumnSpace": true, + "parentRowSpace": true, + "leftColumn": true, + "rightColumn": true, + "topRow": true, + "bottomRow": true, + "parentId": true + }, + "isVisible": true, + "fontStyle": "BOLD", + "textColor": "#231F20", + "version": 1.0, + "parentId": "7z0hh0zvos", + "renderMode": "CANVAS", + "isLoading": false, + "originalTopRow": 4.0, + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "maxDynamicHeight": 9000.0, + "originalBottomRow": 9.0, + "fontSize": "1rem", + "textStyle": "BODY", + "minDynamicHeight": 4.0 + }, + { + "widgetName": "CheckboxGroup1", + "displayName": "Checkbox Group", + "iconSVG": "/static/media/icon.ecb3847950c4515966ef642a32758afb.svg", + "labelText": "Label", + "topRow": 0.0, + "bottomRow": 13.0, + "parentRowSpace": 10.0, + "labelWidth": 5.0, + "type": "CHECKBOX_GROUP_WIDGET", + "hideCard": false, + "animateLoading": true, + "parentColumnSpace": 4.2734375, + "leftColumn": 35.0, + "dynamicBindingPathList": [ + { + "key": "accentColor" + }, + { + "key": "borderRadius" + } + ], + "labelPosition": "Top", + "options": [ + { + "label": "Blue", + "value": "BLUE" + }, + { + "label": "Green", + "value": "GREEN" + }, + { + "label": "Red", + "value": "RED" + } + ], + "isDisabled": false, + "key": "2ge4cq00z3", + "labelTextSize": "0.875rem", + "isRequired": false, + "isDeprecated": false, + "rightColumn": 58.0, + "defaultSelectedValues": [ + "BLUE" + ], + "dynamicHeight": "AUTO_HEIGHT", + "widgetId": "30pveks53r", + "accentColor": "{{appsmith.theme.colors.primaryColor}}", + "logBlackList": { + "isVisible": true, + "animateLoading": true, + "labelTextSize": true, + "options": true, + "defaultSelectedValues": true, + "isDisabled": true, + "isInline": true, + "isRequired": true, + "labelText": true, + "labelPosition": true, + "labelAlignment": true, + "labelWidth": true, + "widgetName": true, + "version": true, + "minDynamicHeight": true, + "maxDynamicHeight": true, + "dynamicHeight": true, + "searchTags": true, + "type": true, + "hideCard": true, + "isDeprecated": true, + "replacement": true, + "displayName": true, + "key": true, + "iconSVG": true, + "isCanvas": true, + "minHeight": true, + "widgetId": true, + "renderMode": true, + "accentColor": true, + "borderRadius": true, + "isLoading": true, + "parentColumnSpace": true, + "parentRowSpace": true, + "leftColumn": true, + "rightColumn": true, + "topRow": true, + "bottomRow": true, + "parentId": true, + "dynamicBindingPathList": true + }, + "isVisible": true, + "version": 2.0, + "parentId": "7z0hh0zvos", + "labelAlignment": "left", + "renderMode": "CANVAS", + "isLoading": false, + "originalTopRow": 0.0, + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "maxDynamicHeight": 9000.0, + "originalBottomRow": 6.0, + "isInline": true, + "minDynamicHeight": 4.0 + } + ], + "key": "8xilm9v7l4", + "isDeprecated": false, + "detachFromLayout": true, + "widgetId": "7z0hh0zvos", + "accentColor": "{{appsmith.theme.colors.primaryColor}}", + "containerStyle": "none", + "isVisible": true, + "version": 1.0, + "parentId": "s5iecr6n8i", + "renderMode": "CANVAS", + "isLoading": false, + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}" + } + ], + "borderWidth": "1", + "key": "h9n2njehd3", + "disablePropertyPane": true, + "backgroundColor": "white", + "isDeprecated": false, + "rightColumn": 64.0, + "dynamicHeight": "FIXED", + "widgetId": "s5iecr6n8i", + "containerStyle": "card", + "isVisible": true, + "version": 1.0, + "parentId": "9iszxoqodt", + "renderMode": "CANVAS", + "isLoading": false, + "disabledWidgetFeatures": [ + "dynamicHeight" + ], + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "maxDynamicHeight": 9000.0, + "minDynamicHeight": 10.0 + } + ], + "key": "8xilm9v7l4", + "isDeprecated": false, + "rightColumn": 301.5, + "detachFromLayout": true, + "widgetId": "9iszxoqodt", + "accentColor": "{{appsmith.theme.colors.primaryColor}}", + "containerStyle": "none", + "isVisible": true, + "version": 1.0, + "parentId": "3c9elfu0p5", + "renderMode": "CANVAS", + "isLoading": false, + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}" + } + ], + "privateWidgets": { + "undefined": true + }, + "key": "5a7y7jpy4y", + "backgroundColor": "transparent", + "isDeprecated": false, + "rightColumn": 42.0, + "itemBackgroundColor": "#FFFFFF", + "widgetId": "3c9elfu0p5", + "accentColor": "{{appsmith.theme.colors.primaryColor}}", + "isVisible": true, + "parentId": "0", + "renderMode": "CANVAS", + "isLoading": false, + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}" + } + ] + } +} \ No newline at end of file diff --git a/app/client/cypress/fixtures/ContextSwitching.json b/app/client/cypress/fixtures/ContextSwitching.json index 27542360cb49..cce3f6bae1b1 100644 --- a/app/client/cypress/fixtures/ContextSwitching.json +++ b/app/client/cypress/fixtures/ContextSwitching.json @@ -1 +1 @@ -{"clientSchemaVersion":1.0,"serverSchemaVersion":6.0,"exportedApplication":{"name":"ContextSwitching","isPublic":false,"pages":[{"id":"Page1","isDefault":true}],"publishedPages":[{"id":"Page1","isDefault":true}],"viewMode":false,"appIsExample":false,"unreadCommentThreads":0.0,"color":"#C2DAF0","icon":"email","slug":"contextswitching","evaluationVersion":2.0,"applicationVersion":2.0,"isManualUpdate":false,"deleted":false},"datasourceList":[{"name":"Appsmith","pluginId":"restapi-plugin","messages":[],"isAutoGenerated":false,"deleted":false,"gitSyncId":"5df9cd9109c7073d4ce37d49_61bb76c6cd5d7045095281f7"},{"name":"Appsmith Image Assets","pluginId":"amazons3-plugin","messages":[],"isAutoGenerated":false,"deleted":false,"gitSyncId":"5df9cd9109c7073d4ce37d49_61bb76c6cd5d7045095281f9"},{"name":"Github","pluginId":"restapi-plugin","messages":[],"isAutoGenerated":false,"deleted":false,"gitSyncId":"5df9cd9109c7073d4ce37d49_61bb76c5cd5d704509527eb3"},{"name":"Internal DB","pluginId":"postgres-plugin","messages":[],"isAutoGenerated":false,"deleted":false,"gitSyncId":"5df9cd9109c7073d4ce37d49_61bb76c5cd5d704509527ec7"},{"name":"Movies","pluginId":"mongo-plugin","messages":[],"isAutoGenerated":false,"deleted":false,"gitSyncId":"5df9cd9109c7073d4ce37d49_61bb76c7cd5d7045095285e2"}],"pageList":[{"unpublishedPage":{"name":"Page1","slug":"page1","layouts":[{"viewMode":false,"dsl":{"widgetName":"MainContainer","backgroundColor":"none","rightColumn":4896.0,"snapColumns":64.0,"detachFromLayout":true,"widgetId":"0","topRow":0.0,"bottomRow":880.0,"containerStyle":"none","snapRows":125.0,"parentRowSpace":1.0,"type":"CANVAS_WIDGET","canExtend":true,"version":63.0,"minHeight":1292.0,"dynamicTriggerPathList":[],"parentColumnSpace":1.0,"dynamicBindingPathList":[],"leftColumn":0.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":10.0,"bottomRow":14.0,"parentRowSpace":10.0,"type":"BUTTON_WIDGET","hideCard":false,"animateLoading":true,"parentColumnSpace":15.6875,"leftColumn":5.0,"dynamicBindingPathList":[{"key":"buttonColor"},{"key":"borderRadius"}],"text":"Submit","isDisabled":false,"key":"s011uot4ul","isDeprecated":false,"rightColumn":21.0,"isDefaultClickDisabled":true,"widgetId":"h1np8mutzo","isVisible":true,"recaptchaType":"V3","version":1.0,"parentId":"0","renderMode":"CANVAS","isLoading":false,"disabledWhenInvalid":false,"borderRadius":"{{appsmith.theme.borderRadius.appBorderRadius}}","buttonVariant":"PRIMARY","placement":"CENTER"},{"boxShadow":"none","widgetName":"Input1","displayName":"Input","iconSVG":"/static/media/icon.9f505595da61a34f563dba82adeb06ec.svg","searchTags":["form","text input","number","textarea"],"topRow":5.0,"bottomRow":9.0,"parentRowSpace":10.0,"labelWidth":5.0,"autoFocus":false,"type":"INPUT_WIDGET_V2","hideCard":false,"animateLoading":true,"parentColumnSpace":16.3125,"resetOnSubmit":true,"leftColumn":2.0,"dynamicBindingPathList":[{"key":"accentColor"},{"key":"borderRadius"}],"labelPosition":"Left","labelStyle":"","inputType":"TEXT","isDisabled":false,"key":"2qsnrpp5zu","labelTextSize":"0.875rem","isRequired":false,"isDeprecated":false,"rightColumn":25.0,"widgetId":"onii9k4vfq","accentColor":"{{appsmith.theme.colors.primaryColor}}","isVisible":true,"label":"Label","version":2.0,"parentId":"0","labelAlignment":"left","renderMode":"CANVAS","isLoading":false,"borderRadius":"{{appsmith.theme.borderRadius.appBorderRadius}}","iconAlign":"left","defaultText":""},{"boxShadow":"none","widgetName":"Image1","displayName":"Image","iconSVG":"/static/media/icon.52d8fb963abcb95c79b10f1553389f22.svg","topRow":16.0,"bottomRow":45.0,"parentRowSpace":10.0,"type":"IMAGE_WIDGET","hideCard":false,"animateLoading":true,"parentColumnSpace":16.3125,"imageShape":"RECTANGLE","leftColumn":2.0,"dynamicBindingPathList":[{"key":"borderRadius"}],"defaultImage":"https://assets.appsmith.com/widgets/default.png","key":"r8ob443ol9","image":"","isDeprecated":false,"rightColumn":25.0,"objectFit":"cover","widgetId":"leg9shh821","isVisible":true,"version":1.0,"parentId":"0","renderMode":"CANVAS","isLoading":false,"maxZoomLevel":1.0,"enableDownload":false,"borderRadius":"{{appsmith.theme.borderRadius.appBorderRadius}}","enableRotation":false},{"boxShadow":"{{appsmith.theme.boxShadow.appBoxShadow}}","widgetName":"Chart1","allowScroll":false,"displayName":"Chart","iconSVG":"/static/media/icon.6adbe31ed817fc4bfd66f9f0a6fc105c.svg","searchTags":["graph","visuals","visualisations"],"topRow":5.0,"bottomRow":45.0,"parentRowSpace":10.0,"type":"CHART_WIDGET","hideCard":false,"chartData":{"w8tod7yb6x":{"seriesName":"Sales","data":[{"x":"Product1","y":20000.0},{"x":"Product2","y":22000.0},{"x":"Product3","y":32000.0}]}},"animateLoading":true,"fontFamily":"{{appsmith.theme.fontFamily.appFont}}","parentColumnSpace":16.3125,"leftColumn":26.0,"dynamicBindingPathList":[{"key":"borderRadius"},{"key":"boxShadow"},{"key":"accentColor"},{"key":"fontFamily"}],"customFusionChartConfig":{"type":"column2d","dataSource":{"data":[{"label":"Product1","value":20000.0},{"label":"Product2","value":22000.0},{"label":"Product3","value":32000.0}],"chart":{"caption":"Sales Report","xAxisName":"Product Line","yAxisName":"Revenue($)","theme":"fusion","alignCaptionWithCanvas":1.0,"captionFontSize":"24","captionAlignment":"center","captionPadding":"20","captionFontColor":"#231F20","legendIconSides":"4","legendIconBgAlpha":"100","legendIconAlpha":"100","legendPosition":"top","canvasPadding":"0","chartLeftMargin":"20","chartTopMargin":"10","chartRightMargin":"40","chartBottomMargin":"10","xAxisNameFontSize":"14","labelFontSize":"12","labelFontColor":"#716E6E","xAxisNameFontColor":"#716E6E","yAxisNameFontSize":"14","yAxisValueFontSize":"12","yAxisValueFontColor":"#716E6E","yAxisNameFontColor":"#716E6E"}}},"key":"7nrtlw0qwh","isDeprecated":false,"rightColumn":61.0,"widgetId":"hj3wvardz8","accentColor":"{{appsmith.theme.colors.primaryColor}}","isVisible":true,"version":1.0,"parentId":"0","labelOrientation":"auto","renderMode":"CANVAS","isLoading":false,"yAxisName":"Revenue($)","chartName":"Sales Report","borderRadius":"{{appsmith.theme.borderRadius.appBorderRadius}}","xAxisName":"Product Line","chartType":"COLUMN_CHART"},{"boxShadow":"{{appsmith.theme.boxShadow.appBoxShadow}}","widgetName":"Container1","borderColor":"transparent","isCanvas":true,"displayName":"Container","iconSVG":"/static/media/icon.1977dca3370505e2db3a8e44cfd54907.svg","searchTags":["div","parent","group"],"topRow":46.0,"bottomRow":86.0,"parentRowSpace":10.0,"type":"CONTAINER_WIDGET","hideCard":false,"animateLoading":true,"parentColumnSpace":16.3125,"leftColumn":2.0,"dynamicBindingPathList":[{"key":"borderRadius"},{"key":"boxShadow"}],"children":[{"boxShadow":"none","widgetName":"Canvas1","displayName":"Canvas","topRow":0.0,"bottomRow":390.0,"parentRowSpace":1.0,"type":"CANVAS_WIDGET","canExtend":false,"hideCard":true,"minHeight":400.0,"parentColumnSpace":1.0,"leftColumn":0.0,"dynamicBindingPathList":[{"key":"borderRadius"},{"key":"accentColor"}],"children":[{"widgetName":"Text1","displayName":"Text","iconSVG":"/static/media/icon.97c59b523e6f70ba6f40a10fc2c7c5b5.svg","searchTags":["typography","paragraph","label"],"topRow":8.0,"bottomRow":29.0,"parentRowSpace":10.0,"type":"TEXT_WIDGET","hideCard":false,"animateLoading":true,"overflow":"NONE","fontFamily":"{{appsmith.theme.fontFamily.appFont}}","parentColumnSpace":14.7255859375,"dynamicTriggerPathList":[],"leftColumn":16.0,"dynamicBindingPathList":[{"key":"fontFamily"},{"key":"borderRadius"}],"shouldTruncate":false,"truncateButtonColor":"#FFC13D","text":"Label","key":"6i9fc2hoj5","isDeprecated":false,"rightColumn":47.0,"textAlign":"CENTER","widgetId":"hiejqpgenc","isVisible":true,"fontStyle":"BOLD","textColor":"#231F20","version":1.0,"parentId":"hto4699x1l","renderMode":"CANVAS","isLoading":false,"borderRadius":"{{appsmith.theme.borderRadius.appBorderRadius}}","fontSize":"3rem"}],"key":"30ud2srz1w","isDeprecated":false,"rightColumn":391.5,"detachFromLayout":true,"widgetId":"hto4699x1l","accentColor":"{{appsmith.theme.colors.primaryColor}}","containerStyle":"none","isVisible":true,"version":1.0,"parentId":"lfbrjo1aae","renderMode":"CANVAS","isLoading":false,"borderRadius":"{{appsmith.theme.borderRadius.appBorderRadius}}"}],"borderWidth":"0","key":"l2gxaxceyf","backgroundColor":"#FFFFFF","isDeprecated":false,"rightColumn":61.0,"widgetId":"lfbrjo1aae","containerStyle":"card","isVisible":true,"version":1.0,"parentId":"0","renderMode":"CANVAS","isLoading":false,"borderRadius":"{{appsmith.theme.borderRadius.appBorderRadius}}"}]},"layoutOnLoadActions":[],"validOnPageLoadActions":true,"id":"Page1","deleted":false,"policies":[],"userPermissions":[]}],"userPermissions":[],"policies":[]},"publishedPage":{"name":"Page1","slug":"page1","layouts":[{"viewMode":false,"dsl":{"widgetName":"MainContainer","backgroundColor":"none","rightColumn":1224.0,"snapColumns":16.0,"detachFromLayout":true,"widgetId":"0","topRow":0.0,"bottomRow":1250.0,"containerStyle":"none","snapRows":33.0,"parentRowSpace":1.0,"type":"CANVAS_WIDGET","canExtend":true,"version":4.0,"minHeight":1292.0,"dynamicTriggerPathList":[],"parentColumnSpace":1.0,"dynamicBindingPathList":[],"leftColumn":0.0,"children":[]},"validOnPageLoadActions":true,"id":"Page1","deleted":false,"policies":[],"userPermissions":[]}],"userPermissions":[],"policies":[]},"deleted":false,"gitSyncId":"630714bdb3409d60b4bf4ab5_630714bdb3409d60b4bf4ab7"}],"actionList":[{"pluginType":"API","pluginId":"restapi-plugin","unpublishedAction":{"name":"Rest_Api_1","datasource":{"name":"Appsmith","pluginId":"restapi-plugin","messages":[],"isAutoGenerated":false,"id":"Appsmith","deleted":false,"policies":[],"userPermissions":[]},"pageId":"Page1","actionConfiguration":{"timeoutInMillisecond":10000.0,"paginationType":"NONE","path":"/users","headers":[{"key":"dd","value":"{{SQL_Query.data}}"},{"key":"content-type","value":"application/json"}],"encodeParamsToggle":true,"queryParameters":[],"bodyFormData":[],"httpMethod":"GET","pluginSpecifiedTemplates":[{"value":true}],"formData":{"apiContentType":"application/json"}},"executeOnLoad":false,"dynamicBindingPathList":[{"key":"headers[0].value"}],"isValid":true,"invalids":[],"messages":[],"jsonPathKeys":["appsmith.URL.queryParams.key","appsmith.URL.fullPath","SQL_Query.data"],"userSetOnLoad":false,"confirmBeforeExecute":false,"policies":[],"userPermissions":[]},"publishedAction":{"datasource":{"messages":[],"isAutoGenerated":false,"deleted":false,"policies":[],"userPermissions":[]},"messages":[],"userSetOnLoad":false,"confirmBeforeExecute":false,"policies":[],"userPermissions":[]},"id":"Page1_Rest_Api_1","deleted":false,"gitSyncId":"630714bdb3409d60b4bf4ab5_63105a16e84aab6f3c7d16ba"},{"pluginType":"JS","pluginId":"js-plugin","unpublishedAction":{"name":"myFun1","fullyQualifiedName":"JSObject1.myFun1","datasource":{"name":"UNUSED_DATASOURCE","pluginId":"js-plugin","messages":[],"isAutoGenerated":false,"deleted":false,"policies":[],"userPermissions":[]},"pageId":"Page1","collectionId":"Page1_JSObject1","actionConfiguration":{"timeoutInMillisecond":10000.0,"paginationType":"NONE","encodeParamsToggle":true,"body":"() => {\n return Button1.text;\n}","jsArguments":[],"isAsync":false},"executeOnLoad":false,"clientSideExecution":true,"dynamicBindingPathList":[{"key":"body"}],"isValid":true,"invalids":[],"messages":[],"jsonPathKeys":["() => {\n return Button1.text;\n}"],"userSetOnLoad":false,"confirmBeforeExecute":false,"policies":[],"userPermissions":[]},"publishedAction":{"datasource":{"messages":[],"isAutoGenerated":false,"deleted":false,"policies":[],"userPermissions":[]},"messages":[],"userSetOnLoad":false,"confirmBeforeExecute":false,"policies":[],"userPermissions":[]},"id":"Page1_JSObject1.myFun1","deleted":false,"gitSyncId":"630714bdb3409d60b4bf4ab5_6319706b93a914550ebf3cbe"},{"pluginType":"JS","pluginId":"js-plugin","unpublishedAction":{"name":"myFun1","fullyQualifiedName":"JSObject2.myFun1","datasource":{"name":"UNUSED_DATASOURCE","pluginId":"js-plugin","messages":[],"isAutoGenerated":false,"deleted":false,"policies":[],"userPermissions":[]},"pageId":"Page1","collectionId":"Page1_JSObject2","actionConfiguration":{"timeoutInMillisecond":10000.0,"paginationType":"NONE","encodeParamsToggle":true,"body":"() => {}","jsArguments":[],"isAsync":false},"executeOnLoad":false,"clientSideExecution":true,"dynamicBindingPathList":[{"key":"body"}],"isValid":true,"invalids":[],"messages":[],"jsonPathKeys":["() => {}"],"userSetOnLoad":false,"confirmBeforeExecute":false,"policies":[],"userPermissions":[]},"publishedAction":{"datasource":{"messages":[],"isAutoGenerated":false,"deleted":false,"policies":[],"userPermissions":[]},"messages":[],"userSetOnLoad":false,"confirmBeforeExecute":false,"policies":[],"userPermissions":[]},"id":"Page1_JSObject2.myFun1","deleted":false,"gitSyncId":"630714bdb3409d60b4bf4ab5_6319753a8eed172d1cad67d5"},{"pluginType":"API","pluginId":"restapi-plugin","unpublishedAction":{"name":"Rest_Api_2","datasource":{"name":"Github","pluginId":"restapi-plugin","messages":[],"isAutoGenerated":false,"id":"Github","deleted":false,"policies":[],"userPermissions":[]},"pageId":"Page1","actionConfiguration":{"timeoutInMillisecond":10000.0,"paginationType":"NONE","headers":[{"key":"abc"}],"encodeParamsToggle":true,"queryParameters":[],"bodyFormData":[],"httpMethod":"GET","pluginSpecifiedTemplates":[{"value":true}],"formData":{"apiContentType":"none"}},"executeOnLoad":false,"dynamicBindingPathList":[],"isValid":true,"invalids":[],"messages":[],"jsonPathKeys":[],"userSetOnLoad":false,"confirmBeforeExecute":false,"policies":[],"userPermissions":[]},"publishedAction":{"datasource":{"messages":[],"isAutoGenerated":false,"deleted":false,"policies":[],"userPermissions":[]},"messages":[],"userSetOnLoad":false,"confirmBeforeExecute":false,"policies":[],"userPermissions":[]},"id":"Page1_Rest_Api_2","deleted":false,"gitSyncId":"630714bdb3409d60b4bf4ab5_6319886c8eed172d1cad6880"},{"pluginType":"API","pluginId":"graphql-plugin","unpublishedAction":{"name":"Graphql_Query","datasource":{"name":"DEFAULT_GRAPHQL_DATASOURCE","pluginId":"graphql-plugin","invalids":["No datasource configuration found. Please configure it and try again."],"messages":[],"isAutoGenerated":false,"deleted":false,"policies":[],"userPermissions":[]},"pageId":"Page1","actionConfiguration":{"timeoutInMillisecond":10000.0,"paginationType":"NONE","headers":[{"key":"content-type","value":"application/json"}],"encodeParamsToggle":true,"queryParameters":[],"body":"{\n\tname: $name\n}","httpMethod":"POST","pluginSpecifiedTemplates":[{"value":true},{"value":"{\n\tname: \"Hey\",\n\t\n}"},{"value":{}}],"formData":{"apiContentType":"application/json"}},"executeOnLoad":false,"dynamicBindingPathList":[],"isValid":true,"invalids":[],"messages":[],"jsonPathKeys":[],"userSetOnLoad":false,"confirmBeforeExecute":false,"policies":[],"userPermissions":[]},"publishedAction":{"datasource":{"messages":[],"isAutoGenerated":false,"deleted":false,"policies":[],"userPermissions":[]},"messages":[],"userSetOnLoad":false,"confirmBeforeExecute":false,"policies":[],"userPermissions":[]},"id":"Page1_Graphql_Query","deleted":false,"gitSyncId":"630714bdb3409d60b4bf4ab5_631eb376a831a05a31ef635b"},{"pluginType":"DB","pluginId":"amazons3-plugin","unpublishedAction":{"name":"S3_Query","datasource":{"name":"Appsmith Image Assets","pluginId":"amazons3-plugin","messages":[],"isAutoGenerated":false,"id":"Appsmith Image Assets","deleted":false,"policies":[],"userPermissions":[]},"pageId":"Page1","actionConfiguration":{"timeoutInMillisecond":10000.0,"paginationType":"NONE","encodeParamsToggle":true,"formData":{"command":{"data":"LIST"},"bucket":{"data":"sss"},"path":{"data":""},"create":{"dataType":{"data":"YES"},"expiry":{"data":"5"}},"body":{"data":""},"list":{"prefix":{"data":""},"where":{"data":{"condition":"AND","children":[{"condition":"EQ"}]}},"signedUrl":{"data":"NO"},"expiry":{"data":"5"},"unSignedUrl":{"data":"YES"},"sortBy":{"data":[{"column":"","order":"Ascending"}]}},"read":{"dataType":{"data":"YES"}},"smartSubstitution":{"data":true}}},"executeOnLoad":false,"dynamicBindingPathList":[],"isValid":true,"invalids":[],"messages":[],"jsonPathKeys":[],"userSetOnLoad":false,"confirmBeforeExecute":false,"policies":[],"userPermissions":[]},"publishedAction":{"datasource":{"messages":[],"isAutoGenerated":false,"deleted":false,"policies":[],"userPermissions":[]},"messages":[],"userSetOnLoad":false,"confirmBeforeExecute":false,"policies":[],"userPermissions":[]},"id":"Page1_S3_Query","deleted":false,"gitSyncId":"630714bdb3409d60b4bf4ab5_632c2543ec371b142c33d149"},{"pluginType":"DB","pluginId":"mongo-plugin","unpublishedAction":{"name":"Mongo_Query","datasource":{"name":"Movies","pluginId":"mongo-plugin","messages":[],"isAutoGenerated":false,"id":"Movies","deleted":false,"policies":[],"userPermissions":[]},"pageId":"Page1","actionConfiguration":{"paginationType":"NONE","encodeParamsToggle":true,"formData":{"command":{"data":"FIND"},"aggregate":{"limit":{"data":"10"}},"delete":{"limit":{"data":"SINGLE"}},"updateMany":{"limit":{"data":"SINGLE"}},"smartSubstitution":{"data":true},"misc":{"formToNativeQuery":{"data":"{\n \"find\": \"null\",\n \"limit\": 10,\n \"batchSize\": 10\n}\n","status":"SUCCESS"}}}},"executeOnLoad":false,"isValid":true,"invalids":[],"messages":[],"jsonPathKeys":[],"userSetOnLoad":false,"confirmBeforeExecute":false,"policies":[],"userPermissions":[]},"publishedAction":{"datasource":{"messages":[],"isAutoGenerated":false,"deleted":false,"policies":[],"userPermissions":[]},"messages":[],"userSetOnLoad":false,"confirmBeforeExecute":false,"policies":[],"userPermissions":[]},"id":"Page1_Mongo_Query","deleted":false,"gitSyncId":"630714bdb3409d60b4bf4ab5_632c25654b8d9c0c2da23eee"},{"pluginType":"DB","pluginId":"postgres-plugin","unpublishedAction":{"name":"SQL_Query","datasource":{"name":"Internal DB","pluginId":"postgres-plugin","messages":[],"isAutoGenerated":false,"id":"Internal DB","deleted":false,"policies":[],"userPermissions":[]},"pageId":"Page1","actionConfiguration":{"timeoutInMillisecond":10000.0,"paginationType":"NONE","encodeParamsToggle":true,"body":"SELECT * FROM users ORDER BY id LIMIT 10;","pluginSpecifiedTemplates":[{"value":true}]},"executeOnLoad":false,"dynamicBindingPathList":[],"isValid":true,"invalids":[],"messages":[],"jsonPathKeys":[],"userSetOnLoad":false,"confirmBeforeExecute":false,"policies":[],"userPermissions":[]},"publishedAction":{"datasource":{"messages":[],"isAutoGenerated":false,"deleted":false,"policies":[],"userPermissions":[]},"messages":[],"userSetOnLoad":false,"confirmBeforeExecute":false,"policies":[],"userPermissions":[]},"id":"Page1_SQL_Query","deleted":false,"gitSyncId":"630714bdb3409d60b4bf4ab5_632c2609ec371b142c33d14f"}],"actionCollectionList":[{"unpublishedCollection":{"name":"JSObject1","pageId":"Page1","pluginId":"js-plugin","pluginType":"JS","actions":[],"archivedActions":[],"body":"export default {\n\tmyVar1: [],\n\tmyVar2: {},\n\tmyFun1: () => {\n\t\treturn Button1.text;\n\t}\n}","variables":[{"name":"myVar1","value":"[]"},{"name":"myVar2","value":"{}"}]},"id":"Page1_JSObject1","deleted":false,"gitSyncId":"630714bdb3409d60b4bf4ab5_6319706b93a914550ebf3cc2"},{"unpublishedCollection":{"name":"JSObject2","pageId":"Page1","pluginId":"js-plugin","pluginType":"JS","actions":[],"archivedActions":[],"body":"export default {\n\tmyVar1: [],\n\tmyVar2: {},\n\tmyFun1: () => {\n\t\t\n\t\t//write code here\n\t},\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n}","variables":[{"name":"myVar1","value":"[]"},{"name":"myVar2","value":"{}"}]},"id":"Page1_JSObject2","deleted":false,"gitSyncId":"630714bdb3409d60b4bf4ab5_6319753a8eed172d1cad67d9"}],"updatedResources":{"actionList":["Rest_Api_2##ENTITY_SEPARATOR##Page1","Graphql_Query##ENTITY_SEPARATOR##Page1","JSObject2.myFun1##ENTITY_SEPARATOR##Page1","Mongo_Query##ENTITY_SEPARATOR##Page1","Rest_Api_1##ENTITY_SEPARATOR##Page1","S3_Query##ENTITY_SEPARATOR##Page1","JSObject1.myFun1##ENTITY_SEPARATOR##Page1","SQL_Query##ENTITY_SEPARATOR##Page1"],"pageList":["Page1"],"actionCollectionList":["JSObject1##ENTITY_SEPARATOR##Page1","JSObject2##ENTITY_SEPARATOR##Page1"]},"editModeTheme":{"name":"Default","displayName":"Modern","isSystemTheme":true,"deleted":false},"publishedTheme":{"name":"Default","displayName":"Modern","isSystemTheme":true,"deleted":false}} +{"clientSchemaVersion":1.0,"serverSchemaVersion":6.0,"exportedApplication":{"name":"ContextSwitching (1)","isPublic":false,"pages":[{"id":"Page1","isDefault":true}],"publishedPages":[{"id":"Page1","isDefault":true}],"viewMode":false,"appIsExample":false,"unreadCommentThreads":0.0,"color":"#C2DAF0","icon":"email","slug":"contextswitching-1","evaluationVersion":2.0,"applicationVersion":2.0,"isManualUpdate":false,"deleted":false},"datasourceList":[{"name":"Appsmith","pluginId":"restapi-plugin","messages":[],"isAutoGenerated":false,"deleted":false,"gitSyncId":"5df9cd9109c7073d4ce37d49_61bb76c6cd5d7045095281f7"},{"name":"Appsmith Image Assets","pluginId":"amazons3-plugin","messages":[],"isAutoGenerated":false,"deleted":false,"gitSyncId":"5df9cd9109c7073d4ce37d49_61bb76c6cd5d7045095281f9"},{"name":"Github","pluginId":"restapi-plugin","messages":[],"isAutoGenerated":false,"deleted":false,"gitSyncId":"5df9cd9109c7073d4ce37d49_61bb76c5cd5d704509527eb3"},{"name":"Internal DB","pluginId":"postgres-plugin","messages":[],"isAutoGenerated":false,"deleted":false,"gitSyncId":"5df9cd9109c7073d4ce37d49_61bb76c5cd5d704509527ec7"},{"name":"Movies","pluginId":"mongo-plugin","messages":[],"isAutoGenerated":false,"deleted":false,"gitSyncId":"5df9cd9109c7073d4ce37d49_61bb76c7cd5d7045095285e2"}],"pageList":[{"unpublishedPage":{"name":"Page1","slug":"page1","layouts":[{"viewMode":false,"dsl":{"widgetName":"MainContainer","backgroundColor":"none","rightColumn":4896.0,"snapColumns":64.0,"detachFromLayout":true,"widgetId":"0","topRow":0.0,"bottomRow":1290.0,"containerStyle":"none","snapRows":125.0,"parentRowSpace":1.0,"type":"CANVAS_WIDGET","canExtend":true,"version":70.0,"minHeight":1292.0,"dynamicTriggerPathList":[],"parentColumnSpace":1.0,"dynamicBindingPathList":[],"leftColumn":0.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":10.0,"bottomRow":14.0,"parentRowSpace":10.0,"type":"BUTTON_WIDGET","hideCard":false,"animateLoading":true,"parentColumnSpace":15.6875,"leftColumn":5.0,"dynamicBindingPathList":[{"key":"buttonColor"},{"key":"borderRadius"}],"text":"Submit","isDisabled":false,"key":"s011uot4ul","isDeprecated":false,"rightColumn":21.0,"isDefaultClickDisabled":true,"widgetId":"h1np8mutzo","isVisible":true,"recaptchaType":"V3","version":1.0,"parentId":"0","renderMode":"CANVAS","isLoading":false,"disabledWhenInvalid":false,"borderRadius":"{{appsmith.theme.borderRadius.appBorderRadius}}","buttonVariant":"PRIMARY","placement":"CENTER"},{"boxShadow":"none","widgetName":"Input1","displayName":"Input","iconSVG":"/static/media/icon.9f505595da61a34f563dba82adeb06ec.svg","searchTags":["form","text input","number","textarea"],"topRow":5.0,"bottomRow":9.0,"parentRowSpace":10.0,"labelWidth":5.0,"autoFocus":false,"type":"INPUT_WIDGET_V2","hideCard":false,"animateLoading":true,"parentColumnSpace":16.3125,"resetOnSubmit":true,"leftColumn":2.0,"dynamicBindingPathList":[{"key":"accentColor"},{"key":"borderRadius"}],"labelPosition":"Left","labelStyle":"","inputType":"TEXT","isDisabled":false,"key":"2qsnrpp5zu","labelTextSize":"0.875rem","isRequired":false,"isDeprecated":false,"rightColumn":25.0,"dynamicHeight":"FIXED","widgetId":"onii9k4vfq","accentColor":"{{appsmith.theme.colors.primaryColor}}","isVisible":true,"label":"Label","version":2.0,"parentId":"0","labelAlignment":"left","renderMode":"CANVAS","isLoading":false,"borderRadius":"{{appsmith.theme.borderRadius.appBorderRadius}}","maxDynamicHeight":9000.0,"iconAlign":"left","defaultText":"","minDynamicHeight":4.0},{"boxShadow":"none","widgetName":"Image1","displayName":"Image","iconSVG":"/static/media/icon.52d8fb963abcb95c79b10f1553389f22.svg","topRow":16.0,"bottomRow":45.0,"parentRowSpace":10.0,"type":"IMAGE_WIDGET","hideCard":false,"animateLoading":true,"parentColumnSpace":16.3125,"imageShape":"RECTANGLE","leftColumn":2.0,"dynamicBindingPathList":[{"key":"borderRadius"}],"defaultImage":"https://assets.appsmith.com/widgets/default.png","key":"r8ob443ol9","image":"","isDeprecated":false,"rightColumn":25.0,"objectFit":"cover","widgetId":"leg9shh821","isVisible":true,"version":1.0,"parentId":"0","renderMode":"CANVAS","isLoading":false,"maxZoomLevel":1.0,"enableDownload":false,"borderRadius":"{{appsmith.theme.borderRadius.appBorderRadius}}","enableRotation":false},{"boxShadow":"{{appsmith.theme.boxShadow.appBoxShadow}}","widgetName":"Chart1","allowScroll":false,"displayName":"Chart","iconSVG":"/static/media/icon.6adbe31ed817fc4bfd66f9f0a6fc105c.svg","searchTags":["graph","visuals","visualisations"],"topRow":5.0,"bottomRow":45.0,"parentRowSpace":10.0,"type":"CHART_WIDGET","hideCard":false,"chartData":{"w8tod7yb6x":{"seriesName":"Sales","data":[{"x":"Product1","y":20000.0},{"x":"Product2","y":22000.0},{"x":"Product3","y":32000.0}]}},"animateLoading":true,"fontFamily":"{{appsmith.theme.fontFamily.appFont}}","parentColumnSpace":16.3125,"leftColumn":26.0,"dynamicBindingPathList":[{"key":"borderRadius"},{"key":"boxShadow"},{"key":"accentColor"},{"key":"fontFamily"}],"customFusionChartConfig":{"type":"column2d","dataSource":{"data":[{"label":"Product1","value":20000.0},{"label":"Product2","value":22000.0},{"label":"Product3","value":32000.0}],"chart":{"caption":"Sales Report","xAxisName":"Product Line","yAxisName":"Revenue($)","theme":"fusion","alignCaptionWithCanvas":1.0,"captionFontSize":"24","captionAlignment":"center","captionPadding":"20","captionFontColor":"#231F20","legendIconSides":"4","legendIconBgAlpha":"100","legendIconAlpha":"100","legendPosition":"top","canvasPadding":"0","chartLeftMargin":"20","chartTopMargin":"10","chartRightMargin":"40","chartBottomMargin":"10","xAxisNameFontSize":"14","labelFontSize":"12","labelFontColor":"#716E6E","xAxisNameFontColor":"#716E6E","yAxisNameFontSize":"14","yAxisValueFontSize":"12","yAxisValueFontColor":"#716E6E","yAxisNameFontColor":"#716E6E"}}},"key":"7nrtlw0qwh","isDeprecated":false,"rightColumn":61.0,"widgetId":"hj3wvardz8","accentColor":"{{appsmith.theme.colors.primaryColor}}","isVisible":true,"version":1.0,"parentId":"0","labelOrientation":"auto","renderMode":"CANVAS","isLoading":false,"yAxisName":"Revenue($)","chartName":"Sales Report","borderRadius":"{{appsmith.theme.borderRadius.appBorderRadius}}","xAxisName":"Product Line","chartType":"COLUMN_CHART"},{"boxShadow":"{{appsmith.theme.boxShadow.appBoxShadow}}","widgetName":"Container1","borderColor":"transparent","isCanvas":true,"displayName":"Container","iconSVG":"/static/media/icon.1977dca3370505e2db3a8e44cfd54907.svg","searchTags":["div","parent","group"],"topRow":46.0,"bottomRow":86.0,"parentRowSpace":10.0,"type":"CONTAINER_WIDGET","hideCard":false,"animateLoading":true,"parentColumnSpace":16.3125,"leftColumn":2.0,"dynamicBindingPathList":[{"key":"borderRadius"},{"key":"boxShadow"}],"children":[{"boxShadow":"none","widgetName":"Canvas1","displayName":"Canvas","topRow":0.0,"bottomRow":390.0,"parentRowSpace":1.0,"type":"CANVAS_WIDGET","canExtend":false,"hideCard":true,"minHeight":400.0,"parentColumnSpace":1.0,"leftColumn":0.0,"dynamicBindingPathList":[{"key":"borderRadius"},{"key":"accentColor"}],"children":[{"widgetName":"Text1","displayName":"Text","iconSVG":"/static/media/icon.97c59b523e6f70ba6f40a10fc2c7c5b5.svg","searchTags":["typography","paragraph","label"],"topRow":8.0,"bottomRow":29.0,"parentRowSpace":10.0,"type":"TEXT_WIDGET","hideCard":false,"animateLoading":true,"overflow":"NONE","fontFamily":"{{appsmith.theme.fontFamily.appFont}}","parentColumnSpace":14.7255859375,"dynamicTriggerPathList":[],"leftColumn":16.0,"dynamicBindingPathList":[{"key":"fontFamily"},{"key":"borderRadius"}],"shouldTruncate":false,"truncateButtonColor":"#FFC13D","text":"Label","key":"6i9fc2hoj5","isDeprecated":false,"rightColumn":47.0,"textAlign":"CENTER","dynamicHeight":"FIXED","widgetId":"hiejqpgenc","isVisible":true,"fontStyle":"BOLD","textColor":"#231F20","version":1.0,"parentId":"hto4699x1l","renderMode":"CANVAS","isLoading":false,"borderRadius":"{{appsmith.theme.borderRadius.appBorderRadius}}","maxDynamicHeight":9000.0,"fontSize":"3rem","minDynamicHeight":4.0}],"key":"30ud2srz1w","isDeprecated":false,"rightColumn":391.5,"detachFromLayout":true,"widgetId":"hto4699x1l","accentColor":"{{appsmith.theme.colors.primaryColor}}","containerStyle":"none","isVisible":true,"version":1.0,"parentId":"lfbrjo1aae","renderMode":"CANVAS","isLoading":false,"borderRadius":"{{appsmith.theme.borderRadius.appBorderRadius}}"}],"borderWidth":"0","key":"l2gxaxceyf","backgroundColor":"#FFFFFF","isDeprecated":false,"rightColumn":61.0,"dynamicHeight":"FIXED","widgetId":"lfbrjo1aae","containerStyle":"card","isVisible":true,"version":1.0,"parentId":"0","renderMode":"CANVAS","isLoading":false,"borderRadius":"{{appsmith.theme.borderRadius.appBorderRadius}}","maxDynamicHeight":9000.0,"minDynamicHeight":4.0},{"boxShadow":"none","widgetName":"Modal1","isCanvas":true,"displayName":"Modal","iconSVG":"/static/media/icon.4975978e9a961fb0bfb4e38de7ecc7c5.svg","searchTags":["dialog","popup","notification"],"topRow":32.0,"bottomRow":272.0,"parentRowSpace":10.0,"type":"MODAL_WIDGET","hideCard":false,"shouldScrollContents":true,"animateLoading":true,"parentColumnSpace":16.3125,"leftColumn":20.0,"dynamicBindingPathList":[{"key":"borderRadius"}],"children":[{"widgetName":"Canvas2","displayName":"Canvas","topRow":0.0,"bottomRow":240.0,"parentRowSpace":1.0,"type":"CANVAS_WIDGET","canExtend":true,"hideCard":true,"shouldScrollContents":false,"minHeight":240.0,"parentColumnSpace":1.0,"leftColumn":0.0,"dynamicBindingPathList":[],"children":[{"boxShadow":"none","widgetName":"IconButton1","onClick":"{{closeModal('Modal1')}}","buttonColor":"{{appsmith.theme.colors.primaryColor}}","displayName":"Icon Button","iconSVG":"/static/media/icon.1a0c634ac75f9fa6b6ae7a8df882a3ba.svg","searchTags":["click","submit"],"topRow":0.0,"bottomRow":4.0,"type":"ICON_BUTTON_WIDGET","hideCard":false,"animateLoading":true,"leftColumn":58.0,"dynamicBindingPathList":[{"key":"buttonColor"},{"key":"borderRadius"}],"iconSize":24.0,"isDisabled":false,"key":"h6asgvi0vd","isDeprecated":false,"rightColumn":64.0,"iconName":"cross","widgetId":"8gwy85xlqd","isVisible":true,"version":1.0,"parentId":"pcni4dljt9","renderMode":"CANVAS","isLoading":false,"borderRadius":"{{appsmith.theme.borderRadius.appBorderRadius}}","buttonVariant":"TERTIARY"},{"widgetName":"Text2","displayName":"Text","iconSVG":"/static/media/icon.97c59b523e6f70ba6f40a10fc2c7c5b5.svg","searchTags":["typography","paragraph","label"],"topRow":1.0,"bottomRow":5.0,"type":"TEXT_WIDGET","hideCard":false,"animateLoading":true,"overflow":"NONE","fontFamily":"{{appsmith.theme.fontFamily.appFont}}","leftColumn":1.0,"dynamicBindingPathList":[{"key":"truncateButtonColor"},{"key":"fontFamily"},{"key":"borderRadius"}],"shouldTruncate":false,"truncateButtonColor":"{{appsmith.theme.colors.primaryColor}}","text":"Modal Title","key":"ymfamodlxd","isDeprecated":false,"rightColumn":41.0,"textAlign":"LEFT","dynamicHeight":"AUTO_HEIGHT","widgetId":"lc1ovalrv0","isVisible":true,"fontStyle":"BOLD","textColor":"#231F20","version":1.0,"parentId":"pcni4dljt9","renderMode":"CANVAS","isLoading":false,"borderRadius":"{{appsmith.theme.borderRadius.appBorderRadius}}","maxDynamicHeight":9000.0,"fontSize":"1.25rem","minDynamicHeight":4.0},{"resetFormOnClick":false,"boxShadow":"none","widgetName":"Button2","onClick":"{{closeModal('Modal1')}}","buttonColor":"{{appsmith.theme.colors.primaryColor}}","displayName":"Button","iconSVG":"/static/media/icon.cca026338f1c8eb6df8ba03d084c2fca.svg","searchTags":["click","submit"],"topRow":18.0,"bottomRow":22.0,"type":"BUTTON_WIDGET","hideCard":false,"animateLoading":true,"leftColumn":31.0,"dynamicBindingPathList":[{"key":"buttonColor"},{"key":"borderRadius"}],"text":"Close","isDisabled":false,"key":"g4h46nuarm","isDeprecated":false,"rightColumn":47.0,"isDefaultClickDisabled":true,"widgetId":"86cyr9be1d","buttonStyle":"PRIMARY","isVisible":true,"recaptchaType":"V3","version":1.0,"parentId":"pcni4dljt9","renderMode":"CANVAS","isLoading":false,"disabledWhenInvalid":false,"borderRadius":"{{appsmith.theme.borderRadius.appBorderRadius}}","buttonVariant":"SECONDARY","placement":"CENTER"},{"resetFormOnClick":false,"boxShadow":"none","widgetName":"Button3","buttonColor":"{{appsmith.theme.colors.primaryColor}}","displayName":"Button","iconSVG":"/static/media/icon.cca026338f1c8eb6df8ba03d084c2fca.svg","searchTags":["click","submit"],"topRow":18.0,"bottomRow":22.0,"type":"BUTTON_WIDGET","hideCard":false,"animateLoading":true,"leftColumn":47.0,"dynamicBindingPathList":[{"key":"buttonColor"},{"key":"borderRadius"}],"text":"Confirm","isDisabled":false,"key":"g4h46nuarm","isDeprecated":false,"rightColumn":63.0,"isDefaultClickDisabled":true,"widgetId":"laiw2q2841","buttonStyle":"PRIMARY_BUTTON","isVisible":true,"recaptchaType":"V3","version":1.0,"parentId":"pcni4dljt9","renderMode":"CANVAS","isLoading":false,"disabledWhenInvalid":false,"borderRadius":"{{appsmith.theme.borderRadius.appBorderRadius}}","buttonVariant":"PRIMARY","placement":"CENTER"}],"isDisabled":false,"key":"pt1a71cj3d","isDeprecated":false,"rightColumn":391.5,"detachFromLayout":true,"widgetId":"pcni4dljt9","isVisible":true,"version":1.0,"parentId":"xhf80p6oeo","renderMode":"CANVAS","isLoading":false}],"key":"0cohv4e2yc","height":240.0,"isDeprecated":false,"rightColumn":44.0,"detachFromLayout":true,"dynamicHeight":"AUTO_HEIGHT","widgetId":"xhf80p6oeo","canOutsideClickClose":true,"canEscapeKeyClose":true,"version":2.0,"parentId":"0","renderMode":"CANVAS","isLoading":false,"borderRadius":"{{appsmith.theme.borderRadius.appBorderRadius}}","maxDynamicHeight":9000.0,"width":456.0,"minDynamicHeight":24.0}]},"layoutOnLoadActions":[],"layoutOnLoadActionErrors":[],"validOnPageLoadActions":true,"id":"Page1","deleted":false,"policies":[],"userPermissions":[]}],"userPermissions":[],"policies":[]},"publishedPage":{"name":"Page1","slug":"page1","layouts":[{"viewMode":false,"dsl":{"widgetName":"MainContainer","backgroundColor":"none","rightColumn":1224.0,"snapColumns":16.0,"detachFromLayout":true,"widgetId":"0","topRow":0.0,"bottomRow":1250.0,"containerStyle":"none","snapRows":33.0,"parentRowSpace":1.0,"type":"CANVAS_WIDGET","canExtend":true,"version":4.0,"minHeight":1292.0,"dynamicTriggerPathList":[],"parentColumnSpace":1.0,"dynamicBindingPathList":[],"leftColumn":0.0,"children":[]},"validOnPageLoadActions":true,"id":"Page1","deleted":false,"policies":[],"userPermissions":[]}],"userPermissions":[],"policies":[]},"deleted":false,"gitSyncId":"630714bdb3409d60b4bf4ab5_630714bdb3409d60b4bf4ab7"}],"actionList":[{"pluginType":"API","pluginId":"restapi-plugin","unpublishedAction":{"name":"Rest_Api_1","datasource":{"name":"Appsmith","pluginId":"restapi-plugin","messages":[],"isAutoGenerated":false,"id":"Appsmith","deleted":false,"policies":[],"userPermissions":[]},"pageId":"Page1","actionConfiguration":{"timeoutInMillisecond":10000.0,"paginationType":"NONE","path":"/users","headers":[{"key":"dd","value":"{{SQL_Query.data}}"},{"key":"content-type","value":"application/json"}],"encodeParamsToggle":true,"queryParameters":[],"bodyFormData":[],"httpMethod":"GET","selfReferencingDataPaths":[],"pluginSpecifiedTemplates":[{"value":true}],"formData":{"apiContentType":"application/json"}},"executeOnLoad":false,"dynamicBindingPathList":[{"key":"headers[0].value"}],"isValid":true,"invalids":[],"messages":[],"jsonPathKeys":["appsmith.URL.queryParams.key","appsmith.URL.fullPath","SQL_Query.data"],"userSetOnLoad":false,"confirmBeforeExecute":false,"policies":[],"userPermissions":[]},"publishedAction":{"datasource":{"messages":[],"isAutoGenerated":false,"deleted":false,"policies":[],"userPermissions":[]},"messages":[],"userSetOnLoad":false,"confirmBeforeExecute":false,"policies":[],"userPermissions":[]},"id":"Page1_Rest_Api_1","deleted":false,"gitSyncId":"630714bdb3409d60b4bf4ab5_63105a16e84aab6f3c7d16ba"},{"pluginType":"API","pluginId":"graphql-plugin","unpublishedAction":{"name":"Graphql_Query","datasource":{"name":"DEFAULT_GRAPHQL_DATASOURCE","pluginId":"graphql-plugin","invalids":["No datasource configuration found. Please configure it and try again."],"messages":[],"isAutoGenerated":false,"deleted":false,"policies":[],"userPermissions":[]},"pageId":"Page1","actionConfiguration":{"timeoutInMillisecond":10000.0,"paginationType":"NONE","headers":[{"key":"content-type","value":"application/json"}],"encodeParamsToggle":true,"queryParameters":[],"body":"{\n\tname: $name\n}","httpMethod":"POST","selfReferencingDataPaths":[],"pluginSpecifiedTemplates":[{"value":true},{"value":"{\n\tname: \"Hey\",\n\t\n}"},{"value":{}}],"formData":{"apiContentType":"application/json"}},"executeOnLoad":false,"dynamicBindingPathList":[],"isValid":true,"invalids":[],"messages":[],"jsonPathKeys":[],"userSetOnLoad":false,"confirmBeforeExecute":false,"policies":[],"userPermissions":[]},"publishedAction":{"datasource":{"messages":[],"isAutoGenerated":false,"deleted":false,"policies":[],"userPermissions":[]},"messages":[],"userSetOnLoad":false,"confirmBeforeExecute":false,"policies":[],"userPermissions":[]},"id":"Page1_Graphql_Query","deleted":false,"gitSyncId":"630714bdb3409d60b4bf4ab5_631eb376a831a05a31ef635b"},{"pluginType":"JS","pluginId":"js-plugin","unpublishedAction":{"name":"myFun1","fullyQualifiedName":"JSObject2.myFun1","datasource":{"name":"UNUSED_DATASOURCE","pluginId":"js-plugin","messages":[],"isAutoGenerated":false,"deleted":false,"policies":[],"userPermissions":[]},"pageId":"Page1","collectionId":"Page1_JSObject2","actionConfiguration":{"timeoutInMillisecond":10000.0,"paginationType":"NONE","encodeParamsToggle":true,"body":"() => {}","selfReferencingDataPaths":[],"jsArguments":[],"isAsync":false},"executeOnLoad":false,"clientSideExecution":true,"dynamicBindingPathList":[{"key":"body"}],"isValid":true,"invalids":[],"messages":[],"jsonPathKeys":["() => {}"],"userSetOnLoad":false,"confirmBeforeExecute":false,"policies":[],"userPermissions":[]},"publishedAction":{"datasource":{"messages":[],"isAutoGenerated":false,"deleted":false,"policies":[],"userPermissions":[]},"messages":[],"userSetOnLoad":false,"confirmBeforeExecute":false,"policies":[],"userPermissions":[]},"id":"Page1_JSObject2.myFun1","deleted":false,"gitSyncId":"630714bdb3409d60b4bf4ab5_6319753a8eed172d1cad67d5"},{"pluginType":"API","pluginId":"restapi-plugin","unpublishedAction":{"name":"Rest_Api_2","datasource":{"name":"Github","pluginId":"restapi-plugin","messages":[],"isAutoGenerated":false,"id":"Github","deleted":false,"policies":[],"userPermissions":[]},"pageId":"Page1","actionConfiguration":{"timeoutInMillisecond":10000.0,"paginationType":"NONE","headers":[{"key":"abc"}],"encodeParamsToggle":true,"queryParameters":[],"bodyFormData":[],"httpMethod":"GET","selfReferencingDataPaths":[],"pluginSpecifiedTemplates":[{"value":true}],"formData":{"apiContentType":"none"}},"executeOnLoad":false,"dynamicBindingPathList":[],"isValid":true,"invalids":[],"messages":[],"jsonPathKeys":[],"userSetOnLoad":false,"confirmBeforeExecute":false,"policies":[],"userPermissions":[]},"publishedAction":{"datasource":{"messages":[],"isAutoGenerated":false,"deleted":false,"policies":[],"userPermissions":[]},"messages":[],"userSetOnLoad":false,"confirmBeforeExecute":false,"policies":[],"userPermissions":[]},"id":"Page1_Rest_Api_2","deleted":false,"gitSyncId":"630714bdb3409d60b4bf4ab5_6319886c8eed172d1cad6880"},{"pluginType":"DB","pluginId":"amazons3-plugin","unpublishedAction":{"name":"S3_Query","datasource":{"name":"Appsmith Image Assets","pluginId":"amazons3-plugin","messages":[],"isAutoGenerated":false,"id":"Appsmith Image Assets","deleted":false,"policies":[],"userPermissions":[]},"pageId":"Page1","actionConfiguration":{"timeoutInMillisecond":10000.0,"paginationType":"NONE","encodeParamsToggle":true,"selfReferencingDataPaths":[],"formData":{"command":{"data":"LIST"},"bucket":{"data":"sss"},"path":{"data":""},"create":{"dataType":{"data":"YES"},"expiry":{"data":"5"}},"body":{"data":""},"list":{"prefix":{"data":""},"where":{"data":{"condition":"AND","children":[{"condition":"EQ"}]}},"signedUrl":{"data":"NO"},"expiry":{"data":"5"},"unSignedUrl":{"data":"YES"},"sortBy":{"data":[{"column":"","order":"Ascending"}]}},"read":{"dataType":{"data":"YES"}},"smartSubstitution":{"data":true}}},"executeOnLoad":false,"dynamicBindingPathList":[],"isValid":true,"invalids":[],"messages":[],"jsonPathKeys":[],"userSetOnLoad":false,"confirmBeforeExecute":false,"policies":[],"userPermissions":[]},"publishedAction":{"datasource":{"messages":[],"isAutoGenerated":false,"deleted":false,"policies":[],"userPermissions":[]},"messages":[],"userSetOnLoad":false,"confirmBeforeExecute":false,"policies":[],"userPermissions":[]},"id":"Page1_S3_Query","deleted":false,"gitSyncId":"630714bdb3409d60b4bf4ab5_632c2543ec371b142c33d149"},{"pluginType":"JS","pluginId":"js-plugin","unpublishedAction":{"name":"myFun1","fullyQualifiedName":"JSObject1.myFun1","datasource":{"name":"UNUSED_DATASOURCE","pluginId":"js-plugin","messages":[],"isAutoGenerated":false,"deleted":false,"policies":[],"userPermissions":[]},"pageId":"Page1","collectionId":"Page1_JSObject1","actionConfiguration":{"timeoutInMillisecond":10000.0,"paginationType":"NONE","encodeParamsToggle":true,"body":"() => {\n return Button1.text;\n}","selfReferencingDataPaths":[],"jsArguments":[],"isAsync":false},"executeOnLoad":false,"clientSideExecution":true,"dynamicBindingPathList":[{"key":"body"}],"isValid":true,"invalids":[],"messages":[],"jsonPathKeys":["() => {\n return Button1.text;\n}"],"userSetOnLoad":false,"confirmBeforeExecute":false,"policies":[],"userPermissions":[]},"publishedAction":{"datasource":{"messages":[],"isAutoGenerated":false,"deleted":false,"policies":[],"userPermissions":[]},"messages":[],"userSetOnLoad":false,"confirmBeforeExecute":false,"policies":[],"userPermissions":[]},"id":"Page1_JSObject1.myFun1","deleted":false,"gitSyncId":"630714bdb3409d60b4bf4ab5_6319706b93a914550ebf3cbe"},{"pluginType":"DB","pluginId":"postgres-plugin","unpublishedAction":{"name":"SQL_Query","datasource":{"name":"Internal DB","pluginId":"postgres-plugin","messages":[],"isAutoGenerated":false,"id":"Internal DB","deleted":false,"policies":[],"userPermissions":[]},"pageId":"Page1","actionConfiguration":{"timeoutInMillisecond":10000.0,"paginationType":"NONE","encodeParamsToggle":true,"body":"SELECT * FROM users ORDER BY id LIMIT 10;","selfReferencingDataPaths":[],"pluginSpecifiedTemplates":[{"value":true}]},"executeOnLoad":false,"dynamicBindingPathList":[],"isValid":true,"invalids":[],"messages":[],"jsonPathKeys":[],"userSetOnLoad":false,"confirmBeforeExecute":false,"policies":[],"userPermissions":[]},"publishedAction":{"datasource":{"messages":[],"isAutoGenerated":false,"deleted":false,"policies":[],"userPermissions":[]},"messages":[],"userSetOnLoad":false,"confirmBeforeExecute":false,"policies":[],"userPermissions":[]},"id":"Page1_SQL_Query","deleted":false,"gitSyncId":"630714bdb3409d60b4bf4ab5_632c2609ec371b142c33d14f"},{"pluginType":"DB","pluginId":"mongo-plugin","unpublishedAction":{"name":"Mongo_Query","datasource":{"name":"Movies","pluginId":"mongo-plugin","messages":[],"isAutoGenerated":false,"id":"Movies","deleted":false,"policies":[],"userPermissions":[]},"pageId":"Page1","actionConfiguration":{"paginationType":"NONE","encodeParamsToggle":true,"selfReferencingDataPaths":[],"formData":{"command":{"data":"FIND"},"aggregate":{"limit":{"data":"10"}},"delete":{"limit":{"data":"SINGLE"}},"updateMany":{"limit":{"data":"SINGLE"}},"smartSubstitution":{"data":true},"misc":{"formToNativeQuery":{"data":"{\n \"find\": \"null\",\n \"limit\": 10,\n \"batchSize\": 10\n}\n","status":"SUCCESS"}}}},"executeOnLoad":false,"isValid":true,"invalids":[],"messages":[],"jsonPathKeys":[],"userSetOnLoad":false,"confirmBeforeExecute":false,"policies":[],"userPermissions":[]},"publishedAction":{"datasource":{"messages":[],"isAutoGenerated":false,"deleted":false,"policies":[],"userPermissions":[]},"messages":[],"userSetOnLoad":false,"confirmBeforeExecute":false,"policies":[],"userPermissions":[]},"id":"Page1_Mongo_Query","deleted":false,"gitSyncId":"630714bdb3409d60b4bf4ab5_632c25654b8d9c0c2da23eee"}],"actionCollectionList":[{"unpublishedCollection":{"name":"JSObject1","pageId":"Page1","pluginId":"js-plugin","pluginType":"JS","actions":[],"archivedActions":[],"body":"export default {\n\tmyVar1: [],\n\tmyVar2: {},\n\tmyFun1: () => {\n\t\treturn Button1.text;\n\t}\n}","variables":[{"name":"myVar1","value":"[]"},{"name":"myVar2","value":"{}"}],"userPermissions":[]},"id":"Page1_JSObject1","deleted":false,"gitSyncId":"630714bdb3409d60b4bf4ab5_6319706b93a914550ebf3cc2"},{"unpublishedCollection":{"name":"JSObject2","pageId":"Page1","pluginId":"js-plugin","pluginType":"JS","actions":[],"archivedActions":[],"body":"export default {\n\tmyVar1: [],\n\tmyVar2: {},\n\tmyFun1: () => {\n\t\t\n\t\t//write code here\n\t},\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n\t\n}","variables":[{"name":"myVar1","value":"[]"},{"name":"myVar2","value":"{}"}],"userPermissions":[]},"id":"Page1_JSObject2","deleted":false,"gitSyncId":"630714bdb3409d60b4bf4ab5_6319753a8eed172d1cad67d9"}],"updatedResources":{"actionList":["Rest_Api_2##ENTITY_SEPARATOR##Page1","Graphql_Query##ENTITY_SEPARATOR##Page1","JSObject2.myFun1##ENTITY_SEPARATOR##Page1","Mongo_Query##ENTITY_SEPARATOR##Page1","Rest_Api_1##ENTITY_SEPARATOR##Page1","S3_Query##ENTITY_SEPARATOR##Page1","JSObject1.myFun1##ENTITY_SEPARATOR##Page1","SQL_Query##ENTITY_SEPARATOR##Page1"],"pageList":["Page1"],"actionCollectionList":["JSObject1##ENTITY_SEPARATOR##Page1","JSObject2##ENTITY_SEPARATOR##Page1"]},"editModeTheme":{"name":"Default","displayName":"Modern","isSystemTheme":true,"deleted":false},"publishedTheme":{"name":"Default","displayName":"Modern","isSystemTheme":true,"deleted":false}} \ No newline at end of file diff --git a/app/client/cypress/fixtures/DynamicHeightDefaultHeightdsl.json b/app/client/cypress/fixtures/DynamicHeightDefaultHeightdsl.json new file mode 100644 index 000000000000..1796571638c0 --- /dev/null +++ b/app/client/cypress/fixtures/DynamicHeightDefaultHeightdsl.json @@ -0,0 +1,157 @@ +{ + "dsl": { + "widgetName": "MainContainer", + "backgroundColor": "none", + "rightColumn": 4896, + "snapColumns": 64, + "detachFromLayout": true, + "widgetId": "0", + "topRow": 0, + "bottomRow": 490, + "containerStyle": "none", + "snapRows": 125, + "parentRowSpace": 1, + "type": "CANVAS_WIDGET", + "canExtend": true, + "version": 69, + "minHeight": 1292, + "dynamicTriggerPathList": [], + "parentColumnSpace": 1, + "dynamicBindingPathList": [], + "leftColumn": 0, + "children": [ + { + "isVisible": true, + "backgroundColor": "#FFFFFF", + "widgetName": "Container1", + "containerStyle": "card", + "borderColor": "#E0DEDE", + "borderWidth": "1", + "boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}", + "animateLoading": true, + "children": [ + { + "isVisible": true, + "widgetName": "Canvas1", + "version": 1, + "detachFromLayout": true, + "type": "CANVAS_WIDGET", + "hideCard": true, + "isDeprecated": false, + "displayName": "Canvas", + "key": "v2o6lv3nzy", + "containerStyle": "none", + "canExtend": false, + "children": [ + { + "isVisible": true, + "animateLoading": true, + "text": "Submit", + "buttonVariant": "PRIMARY", + "placement": "CENTER", + "widgetName": "Button1", + "isDisabled": false, + "isDefaultClickDisabled": true, + "disabledWhenInvalid": false, + "resetFormOnClick": false, + "recaptchaType": "V3", + "version": 1, + "searchTags": [ + "click", + "submit" + ], + "type": "BUTTON_WIDGET", + "hideCard": false, + "isDeprecated": false, + "displayName": "Button", + "key": "314dya6t5f", + "iconSVG": "/static/media/icon.cca026338f1c8eb6df8ba03d084c2fca.svg", + "widgetId": "ky4p2dinmv", + "renderMode": "CANVAS", + "buttonColor": "{{appsmith.theme.colors.primaryColor}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "boxShadow": "none", + "isLoading": false, + "parentColumnSpace": 3.42578125, + "parentRowSpace": 10, + "leftColumn": 19, + "rightColumn": 35, + "topRow": 29, + "bottomRow": 33, + "parentId": "3dktwb98ur", + "dynamicBindingPathList": [ + { + "key": "buttonColor" + }, + { + "key": "borderRadius" + } + ] + } + ], + "minHeight": 350, + "widgetId": "3dktwb98ur", + "renderMode": "CANVAS", + "boxShadow": "none", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "accentColor": "{{appsmith.theme.colors.primaryColor}}", + "isLoading": false, + "parentColumnSpace": 1, + "parentRowSpace": 1, + "leftColumn": 0, + "rightColumn": 239.25, + "topRow": 0, + "bottomRow": 350, + "parentId": "vu6m5e6y57", + "dynamicBindingPathList": [ + { + "key": "borderRadius" + }, + { + "key": "accentColor" + } + ] + } + ], + "version": 1, + "minDynamicHeight": 10, + "maxDynamicHeight": 9000, + "dynamicHeight": "AUTO_HEIGHT", + "shouldScrollContents": true, + "searchTags": [ + "div", + "parent", + "group" + ], + "type": "CONTAINER_WIDGET", + "hideCard": false, + "isDeprecated": false, + "displayName": "Container", + "key": "48gjvjaig3", + "iconSVG": "/static/media/icon.1977dca3370505e2db3a8e44cfd54907.svg", + "isCanvas": true, + "widgetId": "vu6m5e6y57", + "renderMode": "CANVAS", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "isLoading": false, + "parentColumnSpace": 9.96875, + "parentRowSpace": 10, + "leftColumn": 17, + "rightColumn": 41, + "topRow": 6, + "bottomRow": 41, + "parentId": "0", + "dynamicBindingPathList": [ + { + "key": "borderRadius" + }, + { + "key": "boxShadow" + } + ], + "originalTopRow": 6, + "originalBottomRow": 16 + } + ] + } +} \ No newline at end of file diff --git a/app/client/cypress/fixtures/alignmentWithDynamicHeightDsl.json b/app/client/cypress/fixtures/alignmentWithDynamicHeightDsl.json new file mode 100644 index 000000000000..2bfc5b47140b --- /dev/null +++ b/app/client/cypress/fixtures/alignmentWithDynamicHeightDsl.json @@ -0,0 +1,426 @@ +{ + "dsl": { + "widgetName": "MainContainer", + "backgroundColor": "none", + "rightColumn": 1224, + "snapColumns": 64, + "detachFromLayout": true, + "widgetId": "0", + "topRow": 0, + "bottomRow": 2670, + "containerStyle": "none", + "snapRows": 121, + "parentRowSpace": 1, + "type": "CANVAS_WIDGET", + "canExtend": true, + "version": 69, + "minHeight": 1220, + "parentColumnSpace": 1, + "dynamicBindingPathList": [], + "leftColumn": 0, + "children": [ + { + "widgetName": "Text1", + "displayName": "Text", + "iconSVG": "/static/media/icon.97c59b523e6f70ba6f40a10fc2c7c5b5.svg", + "searchTags": [ + "typography", + "paragraph", + "label" + ], + "topRow": 17, + "bottomRow": 82, + "parentRowSpace": 10, + "type": "TEXT_WIDGET", + "hideCard": false, + "animateLoading": true, + "overflow": "NONE", + "fontFamily": "{{appsmith.theme.fontFamily.appFont}}", + "parentColumnSpace": 15.265625, + "dynamicTriggerPathList": [], + "leftColumn": 5, + "dynamicBindingPathList": [ + { + "key": "truncateButtonColor" + }, + { + "key": "fontFamily" + }, + { + "key": "borderRadius" + }, + { + "key": "text" + } + ], + "shouldTruncate": false, + "truncateButtonColor": "{{appsmith.theme.colors.primaryColor}}", + "text": "{{appsmith.store.text}}", + "key": "9ccwuxni06", + "isDeprecated": false, + "rightColumn": 57, + "textAlign": "LEFT", + "dynamicHeight": "AUTO_HEIGHT", + "widgetId": "l3kr1x82zd", + "isVisible": true, + "fontStyle": "BOLD", + "textColor": "#231F20", + "version": 1, + "parentId": "0", + "renderMode": "CANVAS", + "isLoading": false, + "originalTopRow": 17, + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "maxDynamicHeight": 9000, + "originalBottomRow": 22, + "fontSize": "1rem", + "minDynamicHeight": 4 + }, + { + "widgetName": "Text3", + "displayName": "Text", + "iconSVG": "/static/media/icon.97c59b523e6f70ba6f40a10fc2c7c5b5.svg", + "searchTags": [ + "typography", + "paragraph", + "label" + ], + "topRow": 83, + "bottomRow": 165, + "parentRowSpace": 10, + "type": "TEXT_WIDGET", + "hideCard": false, + "animateLoading": true, + "overflow": "NONE", + "fontFamily": "{{appsmith.theme.fontFamily.appFont}}", + "parentColumnSpace": 15.265625, + "dynamicTriggerPathList": [], + "leftColumn": 27, + "dynamicBindingPathList": [ + { + "key": "truncateButtonColor" + }, + { + "key": "fontFamily" + }, + { + "key": "borderRadius" + }, + { + "key": "text" + } + ], + "shouldTruncate": false, + "truncateButtonColor": "{{appsmith.theme.colors.primaryColor}}", + "text": "{{appsmith.store.text}}", + "key": "9ccwuxni06", + "isDeprecated": false, + "rightColumn": 64, + "textAlign": "LEFT", + "dynamicHeight": "AUTO_HEIGHT", + "widgetId": "o3bwh27p3h", + "isVisible": true, + "fontStyle": "BOLD", + "textColor": "#231F20", + "version": 1, + "parentId": "0", + "renderMode": "CANVAS", + "isLoading": false, + "originalTopRow": 23, + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "maxDynamicHeight": 9000, + "originalBottomRow": 28, + "fontSize": "1rem", + "minDynamicHeight": 4 + }, + { + "resetFormOnClick": false, + "boxShadow": "none", + "widgetName": "Button2", + "onClick": "{{storeValue('text', `Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s,`)}}", + "buttonColor": "{{appsmith.theme.colors.primaryColor}}", + "dynamicPropertyPathList": [ + { + "key": "onClick" + } + ], + "displayName": "Button", + "iconSVG": "/static/media/icon.cca026338f1c8eb6df8ba03d084c2fca.svg", + "searchTags": [ + "click", + "submit" + ], + "topRow": 89, + "bottomRow": 93, + "parentRowSpace": 10, + "type": "BUTTON_WIDGET", + "hideCard": false, + "animateLoading": true, + "parentColumnSpace": 15.265625, + "dynamicTriggerPathList": [ + { + "key": "onClick" + } + ], + "leftColumn": 5, + "dynamicBindingPathList": [ + { + "key": "buttonColor" + }, + { + "key": "borderRadius" + } + ], + "text": "Small", + "isDisabled": false, + "key": "gpamqfjowi", + "isDeprecated": false, + "rightColumn": 21, + "isDefaultClickDisabled": true, + "widgetId": "pgpxxkqql0", + "isVisible": true, + "recaptchaType": "V3", + "version": 1, + "parentId": "0", + "renderMode": "CANVAS", + "isLoading": false, + "originalTopRow": 29, + "disabledWhenInvalid": false, + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "originalBottomRow": 33, + "buttonVariant": "PRIMARY", + "placement": "CENTER" + }, + { + "resetFormOnClick": false, + "boxShadow": "none", + "widgetName": "Button1", + "onClick": "{{storeValue('text', `Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum`)}}", + "buttonColor": "{{appsmith.theme.colors.primaryColor}}", + "dynamicPropertyPathList": [ + { + "key": "onClick" + } + ], + "displayName": "Button", + "iconSVG": "/static/media/icon.cca026338f1c8eb6df8ba03d084c2fca.svg", + "searchTags": [ + "click", + "submit" + ], + "topRow": 101, + "bottomRow": 105, + "parentRowSpace": 10, + "type": "BUTTON_WIDGET", + "hideCard": false, + "animateLoading": true, + "parentColumnSpace": 15.265625, + "dynamicTriggerPathList": [ + { + "key": "onClick" + } + ], + "leftColumn": 4, + "dynamicBindingPathList": [ + { + "key": "buttonColor" + }, + { + "key": "borderRadius" + } + ], + "text": "Medium", + "isDisabled": false, + "key": "gpamqfjowi", + "isDeprecated": false, + "rightColumn": 20, + "isDefaultClickDisabled": true, + "widgetId": "oh3y8w2j1p", + "isVisible": true, + "recaptchaType": "V3", + "version": 1, + "parentId": "0", + "renderMode": "CANVAS", + "isLoading": false, + "originalTopRow": 34, + "disabledWhenInvalid": false, + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "originalBottomRow": 38, + "buttonVariant": "PRIMARY", + "placement": "CENTER" + }, + { + "resetFormOnClick": false, + "boxShadow": "none", + "widgetName": "Button1Copy", + "onClick": "{{storeValue('text', `Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.\n\nWhy do we use it?\nIt is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like).\n\n\nWhere does it come from?\nContrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of \"de Finibus Bonorum et Malorum\" (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, \"Lorem ipsum dolor sit amet..\", comes from a line in section 1.10.32.\n\nThe standard chunk of Lorem Ipsum used since the 1500s is reproduced below for those interested. Sections 1.10.32 and 1.10.33 from \"de Finibus Bonorum et Malorum\" by Cicero are also reproduced in their exact original form, accompanied by English versions from the 1914 translation by H. Rackham.\n\nWhere can I get some?\nThere are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by injected humour, or randomised words which don't look even slightly believable. If you are going to use a passage of Lorem Ipsum, you need to be sure there isn't anything embarrassing hidden in the middle of text. All the Lorem Ipsum generators on the Internet tend to repeat predefined chunks as necessary, making this the first true generator on the Internet. It uses a dictionary of over 200 Latin words, combined with a handful of model senten`)}}", + "buttonColor": "{{appsmith.theme.colors.primaryColor}}", + "dynamicPropertyPathList": [ + { + "key": "onClick" + } + ], + "displayName": "Button", + "iconSVG": "/static/media/icon.cca026338f1c8eb6df8ba03d084c2fca.svg", + "searchTags": [ + "click", + "submit" + ], + "topRow": 167, + "bottomRow": 171, + "parentRowSpace": 10, + "type": "BUTTON_WIDGET", + "hideCard": false, + "animateLoading": true, + "parentColumnSpace": 15.265625, + "dynamicTriggerPathList": [ + { + "key": "onClick" + } + ], + "leftColumn": 28, + "dynamicBindingPathList": [ + { + "key": "buttonColor" + }, + { + "key": "borderRadius" + } + ], + "text": "Large", + "isDisabled": false, + "key": "gpamqfjowi", + "isDeprecated": false, + "rightColumn": 44, + "isDefaultClickDisabled": true, + "widgetId": "ter388gte5", + "isVisible": true, + "recaptchaType": "V3", + "version": 1, + "parentId": "0", + "renderMode": "CANVAS", + "isLoading": false, + "originalTopRow": 39, + "disabledWhenInvalid": false, + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "originalBottomRow": 43, + "buttonVariant": "PRIMARY", + "placement": "CENTER" + }, + { + "widgetName": "Text2", + "displayName": "Text", + "iconSVG": "/static/media/icon.97c59b523e6f70ba6f40a10fc2c7c5b5.svg", + "searchTags": [ + "typography", + "paragraph", + "label" + ], + "topRow": 181, + "bottomRow": 251, + "parentRowSpace": 10, + "type": "TEXT_WIDGET", + "hideCard": false, + "animateLoading": true, + "overflow": "NONE", + "fontFamily": "{{appsmith.theme.fontFamily.appFont}}", + "parentColumnSpace": 15.265625, + "dynamicTriggerPathList": [], + "leftColumn": 5, + "dynamicBindingPathList": [ + { + "key": "truncateButtonColor" + }, + { + "key": "fontFamily" + }, + { + "key": "borderRadius" + }, + { + "key": "text" + } + ], + "shouldTruncate": false, + "truncateButtonColor": "{{appsmith.theme.colors.primaryColor}}", + "text": "{{appsmith.store.text}}", + "key": "9ccwuxni06", + "isDeprecated": false, + "rightColumn": 53, + "textAlign": "LEFT", + "dynamicHeight": "AUTO_HEIGHT", + "widgetId": "22trl91ovs", + "isVisible": true, + "fontStyle": "BOLD", + "textColor": "#231F20", + "version": 1, + "parentId": "0", + "renderMode": "CANVAS", + "isLoading": false, + "originalTopRow": 44, + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "maxDynamicHeight": 9000, + "originalBottomRow": 49, + "fontSize": "1rem", + "minDynamicHeight": 4 + }, + { + "widgetName": "Text4", + "displayName": "Text", + "iconSVG": "/static/media/icon.97c59b523e6f70ba6f40a10fc2c7c5b5.svg", + "searchTags": [ + "typography", + "paragraph", + "label" + ], + "topRow": 187, + "bottomRow": 259, + "parentRowSpace": 10, + "type": "TEXT_WIDGET", + "hideCard": false, + "animateLoading": true, + "overflow": "NONE", + "fontFamily": "{{appsmith.theme.fontFamily.appFont}}", + "parentColumnSpace": 15.265625, + "dynamicTriggerPathList": [], + "leftColumn": 16, + "dynamicBindingPathList": [ + { + "key": "truncateButtonColor" + }, + { + "key": "fontFamily" + }, + { + "key": "borderRadius" + }, + { + "key": "text" + } + ], + "shouldTruncate": false, + "truncateButtonColor": "{{appsmith.theme.colors.primaryColor}}", + "text": "{{appsmith.store.text}}", + "key": "9ccwuxni06", + "isDeprecated": false, + "rightColumn": 60, + "textAlign": "LEFT", + "dynamicHeight": "AUTO_HEIGHT", + "widgetId": "wdz3pjzsi5", + "isVisible": true, + "fontStyle": "BOLD", + "textColor": "#231F20", + "version": 1, + "parentId": "0", + "renderMode": "CANVAS", + "isLoading": false, + "originalTopRow": 50, + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "maxDynamicHeight": 9000, + "originalBottomRow": 55, + "fontSize": "1rem", + "minDynamicHeight": 4 + } + ] + } +} \ No newline at end of file diff --git a/app/client/cypress/fixtures/datePickerV2Updated_dsl.json b/app/client/cypress/fixtures/datePickerV2Updated_dsl.json new file mode 100644 index 000000000000..61125cb2bc52 --- /dev/null +++ b/app/client/cypress/fixtures/datePickerV2Updated_dsl.json @@ -0,0 +1,80 @@ +{ + "dsl": { + "widgetName": "MainContainer", + "backgroundColor": "none", + "rightColumn": 4896, + "snapColumns": 64, + "detachFromLayout": true, + "widgetId": "0", + "topRow": 0, + "bottomRow": 1290, + "containerStyle": "none", + "snapRows": 125, + "parentRowSpace": 1, + "type": "CANVAS_WIDGET", + "canExtend": true, + "version": 70, + "minHeight": 1292, + "dynamicTriggerPathList": [], + "parentColumnSpace": 1, + "dynamicBindingPathList": [], + "leftColumn": 0, + "children": [ + { + "boxShadow": "none", + "widgetName": "DatePicker1", + "minDate": "1920-12-31T18:30:00.000Z", + "dateFormat": "YYYY-MM-DD HH:mm", + "displayName": "DatePicker", + "iconSVG": "/static/media/icon.300e5ab8e2e1c26c7a0bad06116842b7.svg", + "searchTags": [ + "calendar" + ], + "topRow": 17, + "bottomRow": 24, + "shortcuts": false, + "parentRowSpace": 10, + "labelWidth": 5, + "type": "DATE_PICKER_WIDGET2", + "hideCard": false, + "animateLoading": true, + "parentColumnSpace": 12.5625, + "leftColumn": 17, + "dynamicBindingPathList": [ + { + "key": "accentColor" + }, + { + "key": "borderRadius" + } + ], + "labelPosition": "Top", + "isDisabled": false, + "key": "g3ofr85rxv", + "labelTextSize": "0.875rem", + "isRequired": false, + "defaultDate": "2022-12-07T10:48:26.097Z", + "isDeprecated": false, + "rightColumn": 37, + "dynamicHeight": "FIXED", + "widgetId": "0ssaj11vm0", + "accentColor": "{{appsmith.theme.colors.primaryColor}}", + "isVisible": true, + "datePickerType": "DATE_PICKER", + "label": "Label", + "version": 2, + "parentId": "0", + "labelAlignment": "left", + "renderMode": "CANVAS", + "isLoading": false, + "timePrecision": "minute", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "maxDynamicHeight": 9000, + "firstDayOfWeek": 0, + "closeOnSelection": true, + "maxDate": "2121-12-31T18:29:00.000Z", + "minDynamicHeight": 4 + } + ] + } +} \ No newline at end of file diff --git a/app/client/cypress/fixtures/dynamicHeightCanvasResizeDsl.json b/app/client/cypress/fixtures/dynamicHeightCanvasResizeDsl.json new file mode 100644 index 000000000000..d828f228108e --- /dev/null +++ b/app/client/cypress/fixtures/dynamicHeightCanvasResizeDsl.json @@ -0,0 +1,302 @@ +{ + "dsl": { + "widgetName": "MainContainer", + "backgroundColor": "none", + "rightColumn": 4896, + "snapColumns": 64, + "detachFromLayout": true, + "widgetId": "0", + "topRow": 0, + "bottomRow": 460, + "containerStyle": "none", + "snapRows": 125, + "parentRowSpace": 1, + "type": "CANVAS_WIDGET", + "canExtend": true, + "version": 69, + "minHeight": 1292, + "dynamicTriggerPathList": [], + "parentColumnSpace": 1, + "dynamicBindingPathList": [], + "leftColumn": 0, + "children": [ + { + "isVisible": true, + "backgroundColor": "#FFFFFF", + "widgetName": "Container1", + "containerStyle": "card", + "borderColor": "#E0DEDE", + "borderWidth": "1", + "boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}", + "animateLoading": true, + "children": [ + { + "isVisible": true, + "widgetName": "Canvas1", + "version": 1, + "detachFromLayout": true, + "type": "CANVAS_WIDGET", + "hideCard": true, + "isDeprecated": false, + "displayName": "Canvas", + "key": "1bw00vqtdm", + "containerStyle": "none", + "canExtend": false, + "children": [ + { + "isVisible": true, + "backgroundColor": "#FFFFFF", + "widgetName": "Container2", + "containerStyle": "card", + "borderColor": "#E0DEDE", + "borderWidth": "1", + "boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}", + "animateLoading": true, + "children": [ + { + "isVisible": true, + "widgetName": "Canvas2", + "version": 1, + "detachFromLayout": true, + "type": "CANVAS_WIDGET", + "hideCard": true, + "isDeprecated": false, + "displayName": "Canvas", + "key": "1bw00vqtdm", + "containerStyle": "none", + "canExtend": false, + "children": [], + "minHeight": 100, + "widgetId": "s1a0qnthhm", + "renderMode": "CANVAS", + "isLoading": false, + "parentColumnSpace": 1, + "parentRowSpace": 1, + "leftColumn": 0, + "rightColumn": 138.29296875, + "topRow": 0, + "bottomRow": 100, + "parentId": "1ioppex86e", + "dynamicBindingPathList": [] + } + ], + "version": 1, + "minDynamicHeight": 10, + "maxDynamicHeight": 9000, + "dynamicHeight": "AUTO_HEIGHT", + "shouldScrollContents": true, + "searchTags": [ + "div", + "parent", + "group" + ], + "type": "CONTAINER_WIDGET", + "hideCard": false, + "isDeprecated": false, + "displayName": "Container", + "key": "pjyxbucclq", + "iconSVG": "/static/media/icon.1977dca3370505e2db3a8e44cfd54907.svg", + "isCanvas": true, + "widgetId": "1ioppex86e", + "renderMode": "CANVAS", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "isLoading": false, + "parentColumnSpace": 5.76220703125, + "parentRowSpace": 10, + "leftColumn": 19, + "rightColumn": 43, + "topRow": 1, + "bottomRow": 11, + "parentId": "7cshqwb2zr", + "dynamicBindingPathList": [ + { + "key": "borderRadius" + }, + { + "key": "boxShadow" + } + ] + }, + { + "isVisible": true, + "backgroundColor": "#FFFFFF", + "widgetName": "Container3", + "containerStyle": "card", + "borderColor": "#E0DEDE", + "borderWidth": "1", + "boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}", + "animateLoading": true, + "children": [ + { + "isVisible": true, + "widgetName": "Canvas3", + "version": 1, + "detachFromLayout": true, + "type": "CANVAS_WIDGET", + "hideCard": true, + "isDeprecated": false, + "displayName": "Canvas", + "key": "1bw00vqtdm", + "containerStyle": "none", + "canExtend": false, + "children": [], + "minHeight": 100, + "widgetId": "g38egg6jl0", + "renderMode": "CANVAS", + "isLoading": false, + "parentColumnSpace": 1, + "parentRowSpace": 1, + "leftColumn": 0, + "rightColumn": 138.29296875, + "topRow": 0, + "bottomRow": 100, + "parentId": "b9oq4d1he7", + "dynamicBindingPathList": [] + } + ], + "version": 1, + "minDynamicHeight": 10, + "maxDynamicHeight": 9000, + "dynamicHeight": "AUTO_HEIGHT", + "shouldScrollContents": true, + "searchTags": [ + "div", + "parent", + "group" + ], + "type": "CONTAINER_WIDGET", + "hideCard": false, + "isDeprecated": false, + "displayName": "Container", + "key": "pjyxbucclq", + "iconSVG": "/static/media/icon.1977dca3370505e2db3a8e44cfd54907.svg", + "isCanvas": true, + "widgetId": "b9oq4d1he7", + "renderMode": "CANVAS", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "isLoading": false, + "parentColumnSpace": 5.76220703125, + "parentRowSpace": 10, + "leftColumn": 19, + "rightColumn": 43, + "topRow": 17, + "bottomRow": 27, + "parentId": "7cshqwb2zr", + "dynamicBindingPathList": [ + { + "key": "borderRadius" + }, + { + "key": "boxShadow" + } + ] + }, + { + "isVisible": true, + "text": "Label", + "fontSize": "1rem", + "fontStyle": "BOLD", + "textAlign": "LEFT", + "textColor": "#231F20", + "widgetName": "Text1", + "shouldTruncate": false, + "overflow": "NONE", + "version": 1, + "animateLoading": true, + "minDynamicHeight": 4, + "maxDynamicHeight": 9000, + "dynamicHeight": "AUTO_HEIGHT", + "searchTags": [ + "typography", + "paragraph", + "label" + ], + "type": "TEXT_WIDGET", + "hideCard": false, + "isDeprecated": false, + "displayName": "Text", + "key": "sypv6avexm", + "iconSVG": "/static/media/icon.97c59b523e6f70ba6f40a10fc2c7c5b5.svg", + "widgetId": "bbxx87ygze", + "renderMode": "CANVAS", + "truncateButtonColor": "{{appsmith.theme.colors.primaryColor}}", + "fontFamily": "{{appsmith.theme.fontFamily.appFont}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "isLoading": false, + "parentColumnSpace": 5.76220703125, + "parentRowSpace": 10, + "leftColumn": 23, + "rightColumn": 39, + "topRow": 12, + "bottomRow": 16, + "parentId": "7cshqwb2zr", + "dynamicBindingPathList": [ + { + "key": "truncateButtonColor" + }, + { + "key": "fontFamily" + }, + { + "key": "borderRadius" + } + ] + } + ], + "minHeight": 290, + "widgetId": "7cshqwb2zr", + "renderMode": "CANVAS", + "isLoading": false, + "parentColumnSpace": 1, + "parentRowSpace": 1, + "leftColumn": 0, + "rightColumn": 239.25, + "topRow": 0, + "bottomRow": 290, + "parentId": "ngknct6sch", + "dynamicBindingPathList": [] + } + ], + "version": 1, + "minDynamicHeight": 10, + "maxDynamicHeight": 9000, + "dynamicHeight": "AUTO_HEIGHT", + "shouldScrollContents": true, + "searchTags": [ + "div", + "parent", + "group" + ], + "type": "CONTAINER_WIDGET", + "hideCard": false, + "isDeprecated": false, + "displayName": "Container", + "key": "pjyxbucclq", + "iconSVG": "/static/media/icon.1977dca3370505e2db3a8e44cfd54907.svg", + "isCanvas": true, + "widgetId": "ngknct6sch", + "renderMode": "CANVAS", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "isLoading": false, + "parentColumnSpace": 9.96875, + "parentRowSpace": 10, + "leftColumn": 13, + "rightColumn": 52, + "topRow": 6, + "bottomRow": 35, + "parentId": "0", + "dynamicBindingPathList": [ + { + "key": "borderRadius" + }, + { + "key": "boxShadow" + } + ], + "originalBottomRow": 51, + "originalTopRow": 6, + "dynamicTriggerPathList": [] + } + ] + } +} \ No newline at end of file diff --git a/app/client/cypress/fixtures/dynamicHeightContainerCheckboxdsl.json b/app/client/cypress/fixtures/dynamicHeightContainerCheckboxdsl.json new file mode 100644 index 000000000000..a945fec10649 --- /dev/null +++ b/app/client/cypress/fixtures/dynamicHeightContainerCheckboxdsl.json @@ -0,0 +1,170 @@ +{ + "dsl": { + "widgetName": "MainContainer", + "backgroundColor": "none", + "rightColumn": 4896, + "snapColumns": 64, + "detachFromLayout": true, + "widgetId": "0", + "topRow": 0, + "bottomRow": 1290, + "containerStyle": "none", + "snapRows": 125, + "parentRowSpace": 1, + "type": "CANVAS_WIDGET", + "canExtend": true, + "version": 69, + "minHeight": 1292, + "dynamicTriggerPathList": [], + "parentColumnSpace": 1, + "dynamicBindingPathList": [], + "leftColumn": 0, + "children": [ + { + "boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}", + "widgetName": "Container1", + "borderColor": "#E0DEDE", + "isCanvas": true, + "displayName": "Container", + "iconSVG": "/static/media/icon.1977dca3370505e2db3a8e44cfd54907.svg", + "searchTags": [ + "div", + "parent", + "group" + ], + "topRow": 6, + "bottomRow": 16, + "parentRowSpace": 10, + "type": "CONTAINER_WIDGET", + "hideCard": false, + "shouldScrollContents": true, + "animateLoading": true, + "parentColumnSpace": 11.9375, + "leftColumn": 16, + "dynamicBindingPathList": [ + { + "key": "borderRadius" + }, + { + "key": "boxShadow" + } + ], + "children": [ + { + "boxShadow": "none", + "widgetName": "Canvas1", + "displayName": "Canvas", + "topRow": 0, + "bottomRow": 100, + "parentRowSpace": 1, + "type": "CANVAS_WIDGET", + "canExtend": false, + "hideCard": true, + "minHeight": 100, + "parentColumnSpace": 1, + "leftColumn": 0, + "dynamicBindingPathList": [ + { + "key": "borderRadius" + }, + { + "key": "accentColor" + } + ], + "children": [ + { + "isVisible": true, + "animateLoading": true, + "labelTextSize": "0.875rem", + "options": [ + { + "label": "Blue", + "value": "BLUE" + }, + { + "label": "Green", + "value": "GREEN" + }, + { + "label": "Red", + "value": "RED" + } + ], + "defaultSelectedValues": [ + "BLUE" + ], + "isDisabled": false, + "isInline": true, + "isRequired": false, + "labelText": "Label", + "labelPosition": "Top", + "labelAlignment": "left", + "labelWidth": 5, + "widgetName": "CheckboxGroup1", + "version": 2, + "minDynamicHeight": 4, + "maxDynamicHeight": 9000, + "dynamicHeight": "AUTO_HEIGHT", + "type": "CHECKBOX_GROUP_WIDGET", + "hideCard": false, + "isDeprecated": false, + "displayName": "Checkbox Group", + "key": "px8e5kndcb", + "iconSVG": "/static/media/icon.ecb3847950c4515966ef642a32758afb.svg", + "widgetId": "li1gq4tzny", + "renderMode": "CANVAS", + "accentColor": "{{appsmith.theme.colors.primaryColor}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "isLoading": false, + "parentColumnSpace": 3.42578125, + "parentRowSpace": 10, + "leftColumn": 18, + "rightColumn": 41, + "topRow": 0, + "bottomRow": 6, + "parentId": "tbezx4vcxu", + "dynamicBindingPathList": [ + { + "key": "accentColor" + }, + { + "key": "borderRadius" + } + ] + } + ], + "key": "49f4d77rwd", + "isDeprecated": false, + "rightColumn": 286.5, + "detachFromLayout": true, + "widgetId": "tbezx4vcxu", + "accentColor": "{{appsmith.theme.colors.primaryColor}}", + "containerStyle": "none", + "isVisible": true, + "version": 1, + "parentId": "57nv0ufxq1", + "renderMode": "CANVAS", + "isLoading": false, + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}" + } + ], + "borderWidth": "1", + "key": "g4phrz9m3l", + "backgroundColor": "#FFFFFF", + "isDeprecated": false, + "rightColumn": 40, + "dynamicHeight": "AUTO_HEIGHT", + "widgetId": "57nv0ufxq1", + "containerStyle": "card", + "isVisible": true, + "version": 1, + "parentId": "0", + "renderMode": "CANVAS", + "isLoading": false, + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "maxDynamicHeight": 9000, + "minDynamicHeight": 10 + } + ] + } +} \ No newline at end of file diff --git a/app/client/cypress/fixtures/dynamicHeightContainerScrolldsl.json b/app/client/cypress/fixtures/dynamicHeightContainerScrolldsl.json new file mode 100644 index 000000000000..113119211edc --- /dev/null +++ b/app/client/cypress/fixtures/dynamicHeightContainerScrolldsl.json @@ -0,0 +1,165 @@ +{ + "dsl": { + "widgetName": "MainContainer", + "backgroundColor": "none", + "rightColumn": 4896, + "snapColumns": 64, + "detachFromLayout": true, + "widgetId": "0", + "topRow": 0, + "bottomRow": 460, + "containerStyle": "none", + "snapRows": 125, + "parentRowSpace": 1, + "type": "CANVAS_WIDGET", + "canExtend": true, + "version": 69, + "minHeight": 1292, + "dynamicTriggerPathList": [], + "parentColumnSpace": 1, + "dynamicBindingPathList": [], + "leftColumn": 0, + "children": [ + { + "isVisible": true, + "backgroundColor": "#FFFFFF", + "widgetName": "Container1", + "containerStyle": "card", + "borderColor": "#E0DEDE", + "borderWidth": "1", + "boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}", + "animateLoading": true, + "children": [ + { + "isVisible": true, + "widgetName": "Canvas1", + "version": 1, + "detachFromLayout": true, + "type": "CANVAS_WIDGET", + "hideCard": true, + "isDeprecated": false, + "displayName": "Canvas", + "key": "pbbivorh4u", + "containerStyle": "none", + "canExtend": false, + "children": [ + { + "isVisible": true, + "text": "Cypress Test scenariosCypress Test scenariosCypress Test scenariosCypress Test scenariosCypress Test scenariosCypress Test scenariosCypress Test scenariosCypress Test scenariosCypress Test scenariosCypress Test scenariosCypress Test scenariosCypress Test scenariosCypress Test scenariosCypress Test scenariosCypress Test scenariosCypress Test scenariosCypress Test scenariosCypress Test scenarios", + "fontSize": "1rem", + "fontStyle": "BOLD", + "textAlign": "LEFT", + "textColor": "#231F20", + "widgetName": "Text1", + "shouldTruncate": false, + "overflow": "NONE", + "version": 1, + "animateLoading": true, + "minDynamicHeight": 4, + "maxDynamicHeight": 9000, + "dynamicHeight": "AUTO_HEIGHT", + "searchTags": [ + "typography", + "paragraph", + "label" + ], + "type": "TEXT_WIDGET", + "hideCard": false, + "isDeprecated": false, + "displayName": "Text", + "key": "qz9rm4m6mt", + "iconSVG": "/static/media/icon.97c59b523e6f70ba6f40a10fc2c7c5b5.svg", + "widgetId": "cvjp04zqa6", + "renderMode": "CANVAS", + "truncateButtonColor": "{{appsmith.theme.colors.primaryColor}}", + "fontFamily": "{{appsmith.theme.fontFamily.appFont}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "isLoading": false, + "parentColumnSpace": 3.42578125, + "parentRowSpace": 10, + "leftColumn": 18, + "rightColumn": 34, + "topRow": 2, + "bottomRow": 143, + "parentId": "y2r0w8mmas", + "dynamicBindingPathList": [ + { + "key": "truncateButtonColor" + }, + { + "key": "fontFamily" + }, + { + "key": "borderRadius" + } + ], + "dynamicTriggerPathList": [], + "originalTopRow": 2, + "originalBottomRow": 6 + } + ], + "minHeight": 1450, + "widgetId": "y2r0w8mmas", + "renderMode": "CANVAS", + "boxShadow": "none", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "accentColor": "{{appsmith.theme.colors.primaryColor}}", + "isLoading": false, + "parentColumnSpace": 1, + "parentRowSpace": 1, + "leftColumn": 0, + "rightColumn": 239.25, + "topRow": 0, + "bottomRow": 1450, + "parentId": "uls70b9gd6", + "dynamicBindingPathList": [ + { + "key": "borderRadius" + }, + { + "key": "accentColor" + } + ] + } + ], + "version": 1, + "minDynamicHeight": 10, + "maxDynamicHeight": 9000, + "dynamicHeight": "FIXED", + "shouldScrollContents": true, + "searchTags": [ + "div", + "parent", + "group" + ], + "type": "CONTAINER_WIDGET", + "hideCard": false, + "isDeprecated": false, + "displayName": "Container", + "key": "vkaake96j7", + "iconSVG": "/static/media/icon.1977dca3370505e2db3a8e44cfd54907.svg", + "isCanvas": true, + "widgetId": "uls70b9gd6", + "renderMode": "CANVAS", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "isLoading": false, + "parentColumnSpace": 9.96875, + "parentRowSpace": 10, + "leftColumn": 17, + "rightColumn": 41, + "topRow": 8, + "bottomRow": 18, + "parentId": "0", + "dynamicBindingPathList": [ + { + "key": "borderRadius" + }, + { + "key": "boxShadow" + } + ], + "dynamicTriggerPathList": [] + } + ] + } +} \ No newline at end of file diff --git a/app/client/cypress/fixtures/dynamicHeightContainerdsl.json b/app/client/cypress/fixtures/dynamicHeightContainerdsl.json new file mode 100644 index 000000000000..a59bd714fb47 --- /dev/null +++ b/app/client/cypress/fixtures/dynamicHeightContainerdsl.json @@ -0,0 +1,109 @@ +{ + "dsl": { + "widgetName": "MainContainer", + "backgroundColor": "none", + "rightColumn": 4896, + "snapColumns": 64, + "detachFromLayout": true, + "widgetId": "0", + "topRow": 0, + "bottomRow": 1292, + "containerStyle": "none", + "snapRows": 125, + "parentRowSpace": 1, + "type": "CANVAS_WIDGET", + "canExtend": true, + "version": 69, + "minHeight": 1292, + "dynamicTriggerPathList": [], + "parentColumnSpace": 1, + "dynamicBindingPathList": [], + "leftColumn": 0, + "children": [ + { + "isVisible": true, + "backgroundColor": "#FFFFFF", + "widgetName": "Container1", + "containerStyle": "card", + "borderColor": "#E0DEDE", + "borderWidth": "1", + "boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}", + "animateLoading": true, + "children": [ + { + "isVisible": true, + "widgetName": "Canvas1", + "version": 1, + "detachFromLayout": true, + "type": "CANVAS_WIDGET", + "hideCard": true, + "isDeprecated": false, + "displayName": "Canvas", + "key": "49f4d77rwd", + "containerStyle": "none", + "canExtend": false, + "children": [], + "minHeight": 100, + "widgetId": "tbezx4vcxu", + "renderMode": "CANVAS", + "boxShadow": "none", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "accentColor": "{{appsmith.theme.colors.primaryColor}}", + "isLoading": false, + "parentColumnSpace": 1, + "parentRowSpace": 1, + "leftColumn": 0, + "rightColumn": 286.5, + "topRow": 0, + "bottomRow": 100, + "parentId": "57nv0ufxq1", + "dynamicBindingPathList": [ + { + "key": "borderRadius" + }, + { + "key": "accentColor" + } + ] + } + ], + "version": 1, + "minDynamicHeight": 10, + "maxDynamicHeight": 9000, + "dynamicHeight": "AUTO_HEIGHT", + "shouldScrollContents": true, + "searchTags": [ + "div", + "parent", + "group" + ], + "type": "CONTAINER_WIDGET", + "hideCard": false, + "isDeprecated": false, + "displayName": "Container", + "key": "g4phrz9m3l", + "iconSVG": "/static/media/icon.1977dca3370505e2db3a8e44cfd54907.svg", + "isCanvas": true, + "widgetId": "57nv0ufxq1", + "renderMode": "CANVAS", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "isLoading": false, + "parentColumnSpace": 11.9375, + "parentRowSpace": 10, + "leftColumn": 20, + "rightColumn": 44, + "topRow": 23, + "bottomRow": 33, + "parentId": "0", + "dynamicBindingPathList": [ + { + "key": "borderRadius" + }, + { + "key": "boxShadow" + } + ] + } + ] + } +} \ No newline at end of file diff --git a/app/client/cypress/fixtures/dynamicHeightFormSwitchdsl.json b/app/client/cypress/fixtures/dynamicHeightFormSwitchdsl.json new file mode 100644 index 000000000000..37bb35fb2609 --- /dev/null +++ b/app/client/cypress/fixtures/dynamicHeightFormSwitchdsl.json @@ -0,0 +1,481 @@ +{ + "dsl": { + "widgetName": "MainContainer", + "backgroundColor": "none", + "rightColumn": 4896, + "snapColumns": 64, + "detachFromLayout": true, + "widgetId": "0", + "topRow": 0, + "bottomRow": 1290, + "containerStyle": "none", + "snapRows": 125, + "parentRowSpace": 1, + "type": "CANVAS_WIDGET", + "canExtend": true, + "version": 69, + "minHeight": 1292, + "dynamicTriggerPathList": [], + "parentColumnSpace": 1, + "dynamicBindingPathList": [], + "leftColumn": 0, + "children": [ + { + "isVisible": true, + "minDynamicHeight": 4, + "maxDynamicHeight": 9000, + "dynamicHeight": "FIXED", + "shouldScrollContents": true, + "borderColor": "#E0DEDE", + "borderWidth": "1", + "animateLoading": true, + "widgetName": "Form1", + "backgroundColor": "#FFFFFF", + "children": [ + { + "isVisible": true, + "widgetName": "Canvas1", + "version": 1, + "detachFromLayout": true, + "type": "CANVAS_WIDGET", + "hideCard": true, + "isDeprecated": false, + "displayName": "Canvas", + "key": "74zfu9g19a", + "containerStyle": "none", + "canExtend": false, + "children": [ + { + "isVisible": true, + "minDynamicHeight": 4, + "maxDynamicHeight": 9000, + "dynamicHeight": "FIXED", + "overflow": "NONE", + "text": "Form", + "fontSize": "1.25rem", + "fontStyle": "BOLD", + "textAlign": "LEFT", + "textColor": "#231F20", + "widgetName": "Text1", + "shouldTruncate": false, + "version": 1, + "animateLoading": true, + "searchTags": [ + "typography", + "paragraph", + "label" + ], + "type": "TEXT_WIDGET", + "hideCard": false, + "isDeprecated": false, + "displayName": "Text", + "key": "qjy37lgbc3", + "iconSVG": "/static/media/icon.97c59b523e6f70ba6f40a10fc2c7c5b5.svg", + "widgetId": "8c07j99hsy", + "renderMode": "CANVAS", + "truncateButtonColor": "{{appsmith.theme.colors.primaryColor}}", + "fontFamily": "{{appsmith.theme.fontFamily.appFont}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "isLoading": false, + "leftColumn": 1.5, + "rightColumn": 25.5, + "topRow": 1, + "bottomRow": 5, + "parentId": "5fswq5m00l", + "dynamicBindingPathList": [ + { + "key": "truncateButtonColor" + }, + { + "key": "fontFamily" + }, + { + "key": "borderRadius" + } + ] + }, + { + "isVisible": true, + "minDynamicHeight": 4, + "maxDynamicHeight": 9000, + "dynamicHeight": "FIXED", + "widgetName": "SwitchGroup1", + "options": [ + { + "label": "Blue", + "value": "BLUE" + }, + { + "label": "Green", + "value": "GREEN" + }, + { + "label": "Red", + "value": "RED" + } + ], + "defaultSelectedValues": [ + "BLUE" + ], + "isDisabled": false, + "isRequired": false, + "isInline": true, + "animateLoading": true, + "alignment": "left", + "labelText": "Label", + "labelPosition": "Left", + "labelAlignment": "left", + "labelWidth": 5, + "version": 1, + "labelTextSize": "0.875rem", + "type": "SWITCH_GROUP_WIDGET", + "hideCard": false, + "isDeprecated": false, + "displayName": "Switch Group", + "key": "gu5yyvgl2n", + "iconSVG": "/static/media/icon.c98225eee52c61080cd91042d88545c8.svg", + "isCanvas": false, + "widgetId": "k6h69sgn79", + "renderMode": "CANVAS", + "accentColor": "{{appsmith.theme.colors.primaryColor}}", + "isLoading": false, + "parentColumnSpace": 4.1640625, + "parentRowSpace": 10, + "leftColumn": 9, + "rightColumn": 35, + "topRow": 27, + "bottomRow": 31, + "parentId": "5fswq5m00l", + "dynamicBindingPathList": [ + { + "key": "accentColor" + } + ], + "onSelectionChange": "{{showModal('Modal1')}}", + "dynamicTriggerPathList": [ + { + "key": "onSelectionChange" + } + ] + } + ], + "minHeight": 310, + "widgetId": "5fswq5m00l", + "renderMode": "CANVAS", + "boxShadow": "none", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "accentColor": "{{appsmith.theme.colors.primaryColor}}", + "isLoading": false, + "parentColumnSpace": 1, + "parentRowSpace": 1, + "leftColumn": 0, + "rightColumn": 286.5, + "topRow": 0, + "bottomRow": 310, + "parentId": "qx47bk6wx8", + "dynamicBindingPathList": [ + { + "key": "borderRadius" + }, + { + "key": "accentColor" + } + ] + } + ], + "searchTags": [ + "group" + ], + "type": "FORM_WIDGET", + "hideCard": false, + "isDeprecated": false, + "displayName": "Form", + "key": "d7jgl29xqe", + "iconSVG": "/static/media/icon.ea3e08d130e59c56867ae40114c10eed.svg", + "isCanvas": true, + "widgetId": "qx47bk6wx8", + "renderMode": "CANVAS", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}", + "isLoading": false, + "parentColumnSpace": 11.9375, + "parentRowSpace": 10, + "leftColumn": 14, + "rightColumn": 38, + "topRow": 10, + "bottomRow": 43, + "parentId": "0", + "dynamicBindingPathList": [ + { + "key": "borderRadius" + }, + { + "key": "boxShadow" + } + ], + "dynamicTriggerPathList": [] + }, + { + "minDynamicHeight": 4, + "maxDynamicHeight": 9000, + "dynamicHeight": "AUTO_HEIGHT", + "shouldScrollContents": true, + "width": 456, + "height": 240, + "canEscapeKeyClose": true, + "animateLoading": true, + "detachFromLayout": true, + "canOutsideClickClose": true, + "widgetName": "Modal1", + "children": [ + { + "isVisible": true, + "widgetName": "Canvas2", + "version": 1, + "detachFromLayout": true, + "type": "CANVAS_WIDGET", + "hideCard": true, + "isDeprecated": false, + "displayName": "Canvas", + "key": "74zfu9g19a", + "canExtend": true, + "isDisabled": false, + "shouldScrollContents": false, + "children": [ + { + "isVisible": true, + "iconName": "cross", + "buttonVariant": "TERTIARY", + "isDisabled": false, + "widgetName": "IconButton1", + "version": 1, + "animateLoading": true, + "searchTags": [ + "click", + "submit" + ], + "type": "ICON_BUTTON_WIDGET", + "hideCard": false, + "isDeprecated": false, + "displayName": "Icon Button", + "key": "oipik9z0jf", + "iconSVG": "/static/media/icon.1a0c634ac75f9fa6b6ae7a8df882a3ba.svg", + "buttonColor": "{{appsmith.theme.colors.primaryColor}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "iconSize": 24, + "widgetId": "h84z4pqq7d", + "renderMode": "CANVAS", + "boxShadow": "none", + "isLoading": false, + "leftColumn": 58, + "rightColumn": 64, + "topRow": 0, + "bottomRow": 4, + "parentId": "b8h15fewxw", + "dynamicBindingPathList": [ + { + "key": "buttonColor" + }, + { + "key": "borderRadius" + } + ], + "onClick": "{{closeModal('Modal1')}}" + }, + { + "isVisible": true, + "minDynamicHeight": 4, + "maxDynamicHeight": 9000, + "dynamicHeight": "FIXED", + "overflow": "NONE", + "text": "Modal Title", + "fontSize": "1.5rem", + "fontStyle": "BOLD", + "textAlign": "LEFT", + "textColor": "#231F20", + "widgetName": "Text2", + "shouldTruncate": false, + "version": 1, + "animateLoading": true, + "searchTags": [ + "typography", + "paragraph", + "label" + ], + "type": "TEXT_WIDGET", + "hideCard": false, + "isDeprecated": false, + "displayName": "Text", + "key": "qjy37lgbc3", + "iconSVG": "/static/media/icon.97c59b523e6f70ba6f40a10fc2c7c5b5.svg", + "widgetId": "j98rl5e7a1", + "renderMode": "CANVAS", + "truncateButtonColor": "{{appsmith.theme.colors.primaryColor}}", + "fontFamily": "{{appsmith.theme.fontFamily.appFont}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "isLoading": false, + "leftColumn": 1, + "rightColumn": 41, + "topRow": 1, + "bottomRow": 5, + "parentId": "b8h15fewxw", + "dynamicBindingPathList": [ + { + "key": "truncateButtonColor" + }, + { + "key": "fontFamily" + }, + { + "key": "borderRadius" + } + ] + }, + { + "isVisible": true, + "animateLoading": true, + "text": "Close", + "buttonVariant": "SECONDARY", + "placement": "CENTER", + "widgetName": "Button1", + "isDisabled": false, + "isDefaultClickDisabled": true, + "disabledWhenInvalid": false, + "resetFormOnClick": false, + "recaptchaType": "V3", + "version": 1, + "searchTags": [ + "click", + "submit" + ], + "type": "BUTTON_WIDGET", + "hideCard": false, + "isDeprecated": false, + "displayName": "Button", + "key": "8fy9at549y", + "iconSVG": "/static/media/icon.cca026338f1c8eb6df8ba03d084c2fca.svg", + "buttonStyle": "PRIMARY", + "widgetId": "w3gw0z0hul", + "renderMode": "CANVAS", + "buttonColor": "{{appsmith.theme.colors.primaryColor}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "boxShadow": "none", + "isLoading": false, + "leftColumn": 31, + "rightColumn": 47, + "topRow": 18, + "bottomRow": 22, + "parentId": "b8h15fewxw", + "dynamicBindingPathList": [ + { + "key": "buttonColor" + }, + { + "key": "borderRadius" + } + ], + "onClick": "{{closeModal('Modal1')}}" + }, + { + "isVisible": true, + "animateLoading": true, + "text": "Confirm", + "buttonVariant": "PRIMARY", + "placement": "CENTER", + "widgetName": "Button2", + "isDisabled": false, + "isDefaultClickDisabled": true, + "disabledWhenInvalid": false, + "resetFormOnClick": false, + "recaptchaType": "V3", + "version": 1, + "searchTags": [ + "click", + "submit" + ], + "type": "BUTTON_WIDGET", + "hideCard": false, + "isDeprecated": false, + "displayName": "Button", + "key": "8fy9at549y", + "iconSVG": "/static/media/icon.cca026338f1c8eb6df8ba03d084c2fca.svg", + "buttonStyle": "PRIMARY_BUTTON", + "widgetId": "dcky71d9uv", + "renderMode": "CANVAS", + "buttonColor": "{{appsmith.theme.colors.primaryColor}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "boxShadow": "none", + "isLoading": false, + "leftColumn": 47, + "rightColumn": 63, + "topRow": 18, + "bottomRow": 22, + "parentId": "b8h15fewxw", + "dynamicBindingPathList": [ + { + "key": "buttonColor" + }, + { + "key": "borderRadius" + } + ] + } + ], + "minHeight": 0, + "widgetId": "b8h15fewxw", + "renderMode": "CANVAS", + "boxShadow": "none", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "accentColor": "{{appsmith.theme.colors.primaryColor}}", + "isLoading": false, + "parentColumnSpace": 1, + "parentRowSpace": 1, + "leftColumn": 0, + "rightColumn": 0, + "topRow": 0, + "bottomRow": 0, + "parentId": "d0r5dcpeqm", + "dynamicBindingPathList": [ + { + "key": "borderRadius" + }, + { + "key": "accentColor" + } + ] + } + ], + "version": 2, + "searchTags": [ + "dialog", + "popup", + "notification" + ], + "type": "MODAL_WIDGET", + "hideCard": false, + "isDeprecated": false, + "displayName": "Modal", + "key": "1y0e1crldi", + "iconSVG": "/static/media/icon.4975978e9a961fb0bfb4e38de7ecc7c5.svg", + "isCanvas": true, + "widgetId": "d0r5dcpeqm", + "renderMode": "CANVAS", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "boxShadow": "none", + "isLoading": false, + "parentColumnSpace": 1, + "parentRowSpace": 1, + "leftColumn": 0, + "rightColumn": 0, + "topRow": 0, + "bottomRow": 0, + "parentId": "0", + "dynamicBindingPathList": [ + { + "key": "borderRadius" + } + ] + } + ] + } +} \ No newline at end of file diff --git a/app/client/cypress/fixtures/dynamicHeightListDsl.json b/app/client/cypress/fixtures/dynamicHeightListDsl.json new file mode 100644 index 000000000000..690492449888 --- /dev/null +++ b/app/client/cypress/fixtures/dynamicHeightListDsl.json @@ -0,0 +1,634 @@ +{ + "dsl": { + "widgetName": "MainContainer", + "backgroundColor": "none", + "rightColumn": 4896, + "snapColumns": 64, + "detachFromLayout": true, + "widgetId": "0", + "topRow": 0, + "bottomRow": 500, + "containerStyle": "none", + "snapRows": 125, + "parentRowSpace": 1, + "type": "CANVAS_WIDGET", + "canExtend": true, + "version": 69, + "minHeight": 1292, + "dynamicTriggerPathList": [], + "parentColumnSpace": 1, + "dynamicBindingPathList": [], + "leftColumn": 0, + "children": [ + { + "isVisible": true, + "backgroundColor": "transparent", + "itemBackgroundColor": "#FFFFFF", + "animateLoading": true, + "gridType": "vertical", + "template": { + "Image1": { + "isVisible": true, + "defaultImage": "https://assets.appsmith.com/widgets/default.png", + "imageShape": "RECTANGLE", + "maxZoomLevel": 1, + "enableRotation": false, + "enableDownload": false, + "objectFit": "cover", + "image": "{{List1.listData.map((currentItem) => currentItem.img)}}", + "widgetName": "Image1", + "version": 1, + "animateLoading": true, + "type": "IMAGE_WIDGET", + "hideCard": false, + "isDeprecated": false, + "displayName": "Image", + "key": "nqfe0za35m", + "iconSVG": "/static/media/icon.52d8fb963abcb95c79b10f1553389f22.svg", + "boxShadow": "none", + "dynamicBindingPathList": [ + { + "key": "image" + }, + { + "key": "borderRadius" + } + ], + "dynamicTriggerPathList": [], + "widgetId": "xd0fne6182", + "renderMode": "CANVAS", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "isLoading": false, + "leftColumn": 0, + "rightColumn": 16, + "topRow": 0, + "bottomRow": 8, + "parentId": "h9zxkabruq" + }, + "Text1": { + "isVisible": true, + "text": "{{List1.listData.map((currentItem) => currentItem.name)}}", + "fontSize": "1rem", + "fontStyle": "BOLD", + "textAlign": "LEFT", + "textColor": "#231F20", + "widgetName": "Text1", + "shouldTruncate": false, + "overflow": "NONE", + "version": 1, + "animateLoading": true, + "minDynamicHeight": 4, + "maxDynamicHeight": 9000, + "dynamicHeight": "AUTO_HEIGHT", + "searchTags": [ + "typography", + "paragraph", + "label" + ], + "type": "TEXT_WIDGET", + "hideCard": false, + "isDeprecated": false, + "displayName": "Text", + "key": "7iypzmkb0n", + "iconSVG": "/static/media/icon.97c59b523e6f70ba6f40a10fc2c7c5b5.svg", + "textStyle": "HEADING", + "boxShadow": "none", + "dynamicBindingPathList": [ + { + "key": "text" + }, + { + "key": "truncateButtonColor" + }, + { + "key": "fontFamily" + }, + { + "key": "borderRadius" + } + ], + "dynamicTriggerPathList": [], + "widgetId": "hwtznp2wr7", + "renderMode": "CANVAS", + "truncateButtonColor": "{{appsmith.theme.colors.primaryColor}}", + "fontFamily": "{{appsmith.theme.fontFamily.appFont}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "isLoading": false, + "leftColumn": 16, + "rightColumn": 28, + "topRow": 0, + "bottomRow": 4, + "parentId": "h9zxkabruq" + }, + "Text2": { + "isVisible": true, + "text": "{{List1.listData.map((currentItem) => currentItem.id)}}", + "fontSize": "1rem", + "fontStyle": "BOLD", + "textAlign": "LEFT", + "textColor": "#231F20", + "widgetName": "Text2", + "shouldTruncate": false, + "overflow": "NONE", + "version": 1, + "animateLoading": true, + "minDynamicHeight": 4, + "maxDynamicHeight": 9000, + "dynamicHeight": "AUTO_HEIGHT", + "searchTags": [ + "typography", + "paragraph", + "label" + ], + "type": "TEXT_WIDGET", + "hideCard": false, + "isDeprecated": false, + "displayName": "Text", + "key": "7iypzmkb0n", + "iconSVG": "/static/media/icon.97c59b523e6f70ba6f40a10fc2c7c5b5.svg", + "textStyle": "BODY", + "boxShadow": "none", + "dynamicBindingPathList": [ + { + "key": "text" + }, + { + "key": "truncateButtonColor" + }, + { + "key": "fontFamily" + }, + { + "key": "borderRadius" + } + ], + "dynamicTriggerPathList": [], + "widgetId": "f5yqkzcdvk", + "renderMode": "CANVAS", + "truncateButtonColor": "{{appsmith.theme.colors.primaryColor}}", + "fontFamily": "{{appsmith.theme.fontFamily.appFont}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "isLoading": false, + "leftColumn": 16, + "rightColumn": 24, + "topRow": 4, + "bottomRow": 8, + "parentId": "h9zxkabruq" + } + }, + "enhancements": true, + "gridGap": 0, + "listData": [ + { + "id": "001", + "name": "Blue", + "img": "https://assets.appsmith.com/widgets/default.png" + }, + { + "id": "002", + "name": "Green", + "img": "https://assets.appsmith.com/widgets/default.png" + }, + { + "id": "003", + "name": "Red", + "img": "https://assets.appsmith.com/widgets/default.png" + } + ], + "widgetName": "List1", + "children": [ + { + "isVisible": true, + "widgetName": "Canvas1", + "version": 1, + "detachFromLayout": true, + "type": "CANVAS_WIDGET", + "hideCard": true, + "isDeprecated": false, + "displayName": "Canvas", + "key": "aon95urj2w", + "containerStyle": "none", + "canExtend": false, + "dropDisabled": true, + "openParentPropertyPane": true, + "noPad": true, + "children": [ + { + "isVisible": true, + "backgroundColor": "white", + "widgetName": "Container1", + "containerStyle": "card", + "borderColor": "#E0DEDE", + "borderWidth": "1", + "boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}", + "animateLoading": true, + "children": [ + { + "isVisible": true, + "widgetName": "Canvas2", + "version": 1, + "detachFromLayout": true, + "type": "CANVAS_WIDGET", + "hideCard": true, + "isDeprecated": false, + "displayName": "Canvas", + "key": "aon95urj2w", + "containerStyle": "none", + "canExtend": false, + "children": [ + { + "isVisible": true, + "defaultImage": "https://assets.appsmith.com/widgets/default.png", + "imageShape": "RECTANGLE", + "maxZoomLevel": 1, + "enableRotation": false, + "enableDownload": false, + "objectFit": "cover", + "image": "{{currentItem.img}}", + "widgetName": "Image1", + "version": 1, + "animateLoading": true, + "type": "IMAGE_WIDGET", + "hideCard": false, + "isDeprecated": false, + "displayName": "Image", + "key": "nqfe0za35m", + "iconSVG": "/static/media/icon.52d8fb963abcb95c79b10f1553389f22.svg", + "boxShadow": "none", + "dynamicBindingPathList": [ + { + "key": "image" + }, + { + "key": "borderRadius" + } + ], + "dynamicTriggerPathList": [], + "widgetId": "xd0fne6182", + "renderMode": "CANVAS", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "isLoading": false, + "leftColumn": 0, + "rightColumn": 16, + "topRow": 0, + "bottomRow": 8, + "parentId": "h9zxkabruq", + "logBlackList": { + "isVisible": true, + "defaultImage": true, + "imageShape": true, + "maxZoomLevel": true, + "enableRotation": true, + "enableDownload": true, + "objectFit": true, + "image": true, + "widgetName": true, + "version": true, + "animateLoading": true, + "searchTags": true, + "type": true, + "hideCard": true, + "isDeprecated": true, + "replacement": true, + "displayName": true, + "key": true, + "iconSVG": true, + "isCanvas": true, + "boxShadow": true, + "dynamicBindingPathList": true, + "dynamicTriggerPathList": true, + "minHeight": true, + "widgetId": true, + "renderMode": true, + "borderRadius": true, + "isLoading": true, + "parentColumnSpace": true, + "parentRowSpace": true, + "leftColumn": true, + "rightColumn": true, + "topRow": true, + "bottomRow": true, + "parentId": true + } + }, + { + "isVisible": true, + "text": "{{currentItem.name}}", + "fontSize": "1rem", + "fontStyle": "BOLD", + "textAlign": "LEFT", + "textColor": "#231F20", + "widgetName": "Text1", + "shouldTruncate": false, + "overflow": "NONE", + "version": 1, + "animateLoading": true, + "minDynamicHeight": 4, + "maxDynamicHeight": 9000, + "dynamicHeight": "AUTO_HEIGHT", + "searchTags": [ + "typography", + "paragraph", + "label" + ], + "type": "TEXT_WIDGET", + "hideCard": false, + "isDeprecated": false, + "displayName": "Text", + "key": "7iypzmkb0n", + "iconSVG": "/static/media/icon.97c59b523e6f70ba6f40a10fc2c7c5b5.svg", + "textStyle": "HEADING", + "boxShadow": "none", + "dynamicBindingPathList": [ + { + "key": "text" + }, + { + "key": "truncateButtonColor" + }, + { + "key": "fontFamily" + }, + { + "key": "borderRadius" + } + ], + "dynamicTriggerPathList": [], + "widgetId": "hwtznp2wr7", + "renderMode": "CANVAS", + "truncateButtonColor": "{{appsmith.theme.colors.primaryColor}}", + "fontFamily": "{{appsmith.theme.fontFamily.appFont}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "isLoading": false, + "leftColumn": 16, + "rightColumn": 28, + "topRow": 0, + "bottomRow": 5, + "parentId": "h9zxkabruq", + "logBlackList": { + "isVisible": true, + "text": true, + "fontSize": true, + "fontStyle": true, + "textAlign": true, + "textColor": true, + "widgetName": true, + "shouldTruncate": true, + "overflow": true, + "version": true, + "animateLoading": true, + "minDynamicHeight": true, + "maxDynamicHeight": true, + "dynamicHeight": true, + "searchTags": true, + "type": true, + "hideCard": true, + "isDeprecated": true, + "replacement": true, + "displayName": true, + "key": true, + "iconSVG": true, + "isCanvas": true, + "textStyle": true, + "boxShadow": true, + "dynamicBindingPathList": true, + "dynamicTriggerPathList": true, + "minHeight": true, + "widgetId": true, + "renderMode": true, + "truncateButtonColor": true, + "fontFamily": true, + "borderRadius": true, + "isLoading": true, + "parentColumnSpace": true, + "parentRowSpace": true, + "leftColumn": true, + "rightColumn": true, + "topRow": true, + "bottomRow": true, + "parentId": true + }, + "originalTopRow": 0, + "originalBottomRow": 4 + }, + { + "isVisible": true, + "text": "{{currentItem.id}}", + "fontSize": "1rem", + "fontStyle": "BOLD", + "textAlign": "LEFT", + "textColor": "#231F20", + "widgetName": "Text2", + "shouldTruncate": false, + "overflow": "NONE", + "version": 1, + "animateLoading": true, + "minDynamicHeight": 4, + "maxDynamicHeight": 9000, + "dynamicHeight": "AUTO_HEIGHT", + "searchTags": [ + "typography", + "paragraph", + "label" + ], + "type": "TEXT_WIDGET", + "hideCard": false, + "isDeprecated": false, + "displayName": "Text", + "key": "7iypzmkb0n", + "iconSVG": "/static/media/icon.97c59b523e6f70ba6f40a10fc2c7c5b5.svg", + "textStyle": "BODY", + "boxShadow": "none", + "dynamicBindingPathList": [ + { + "key": "text" + }, + { + "key": "truncateButtonColor" + }, + { + "key": "fontFamily" + }, + { + "key": "borderRadius" + } + ], + "dynamicTriggerPathList": [], + "widgetId": "f5yqkzcdvk", + "renderMode": "CANVAS", + "truncateButtonColor": "{{appsmith.theme.colors.primaryColor}}", + "fontFamily": "{{appsmith.theme.fontFamily.appFont}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "isLoading": false, + "leftColumn": 16, + "rightColumn": 24, + "topRow": 5, + "bottomRow": 12, + "parentId": "h9zxkabruq", + "logBlackList": { + "isVisible": true, + "text": true, + "fontSize": true, + "fontStyle": true, + "textAlign": true, + "textColor": true, + "widgetName": true, + "shouldTruncate": true, + "overflow": true, + "version": true, + "animateLoading": true, + "minDynamicHeight": true, + "maxDynamicHeight": true, + "dynamicHeight": true, + "searchTags": true, + "type": true, + "hideCard": true, + "isDeprecated": true, + "replacement": true, + "displayName": true, + "key": true, + "iconSVG": true, + "isCanvas": true, + "textStyle": true, + "boxShadow": true, + "dynamicBindingPathList": true, + "dynamicTriggerPathList": true, + "minHeight": true, + "widgetId": true, + "renderMode": true, + "truncateButtonColor": true, + "fontFamily": true, + "borderRadius": true, + "isLoading": true, + "parentColumnSpace": true, + "parentRowSpace": true, + "leftColumn": true, + "rightColumn": true, + "topRow": true, + "bottomRow": true, + "parentId": true + }, + "originalTopRow": 4, + "originalBottomRow": 8 + } + ], + "minHeight": 140, + "widgetId": "h9zxkabruq", + "renderMode": "CANVAS", + "isLoading": false, + "parentColumnSpace": 1, + "parentRowSpace": 1, + "leftColumn": 0, + "rightColumn": null, + "topRow": 0, + "bottomRow": 140, + "parentId": "z1hyon6hsf", + "dynamicBindingPathList": [] + } + ], + "version": 1, + "minDynamicHeight": 10, + "maxDynamicHeight": 9000, + "dynamicHeight": "FIXED", + "shouldScrollContents": true, + "searchTags": [ + "div", + "parent", + "group" + ], + "type": "CONTAINER_WIDGET", + "hideCard": false, + "isDeprecated": false, + "displayName": "Container", + "key": "bavx5ucxfq", + "iconSVG": "/static/media/icon.1977dca3370505e2db3a8e44cfd54907.svg", + "isCanvas": true, + "dragDisabled": true, + "isDeletable": false, + "disallowCopy": true, + "disablePropertyPane": true, + "disabledWidgetFeatures": [ + "dynamicHeight" + ], + "openParentPropertyPane": true, + "widgetId": "z1hyon6hsf", + "renderMode": "CANVAS", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "isLoading": false, + "leftColumn": 0, + "rightColumn": 64, + "topRow": 0, + "bottomRow": 12, + "parentId": "0f7mv88r94", + "dynamicBindingPathList": [ + { + "key": "borderRadius" + }, + { + "key": "boxShadow" + } + ] + } + ], + "minHeight": 400, + "widgetId": "0f7mv88r94", + "renderMode": "CANVAS", + "isLoading": false, + "parentColumnSpace": 1, + "parentRowSpace": 1, + "leftColumn": 0, + "rightColumn": 239.25, + "topRow": 0, + "bottomRow": 400, + "parentId": "goeipfim43", + "dynamicBindingPathList": [] + } + ], + "type": "LIST_WIDGET", + "hideCard": false, + "isDeprecated": false, + "displayName": "List", + "key": "ygfubplth9", + "iconSVG": "/static/media/icon.9925ee17dee37bf1ba7374412563a8a7.svg", + "isCanvas": true, + "widgetId": "goeipfim43", + "renderMode": "CANVAS", + "accentColor": "{{appsmith.theme.colors.primaryColor}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}", + "isLoading": false, + "parentColumnSpace": 9.96875, + "parentRowSpace": 10, + "leftColumn": 18, + "rightColumn": 42, + "topRow": 2, + "bottomRow": 42, + "parentId": "0", + "dynamicBindingPathList": [ + { + "key": "accentColor" + }, + { + "key": "borderRadius" + }, + { + "key": "boxShadow" + }, + { + "key": "template.Image1.image" + }, + { + "key": "template.Text1.text" + }, + { + "key": "template.Text2.text" + } + ], + "privateWidgets": { + "undefined": true + }, + "dynamicTriggerPathList": [] + } + ] + } +} \ No newline at end of file diff --git a/app/client/cypress/fixtures/dynamicHeightStatboxdsl.json b/app/client/cypress/fixtures/dynamicHeightStatboxdsl.json new file mode 100644 index 000000000000..d1b9422ba412 --- /dev/null +++ b/app/client/cypress/fixtures/dynamicHeightStatboxdsl.json @@ -0,0 +1,281 @@ +{ + "dsl": { + "widgetName": "MainContainer", + "backgroundColor": "none", + "rightColumn": 1280, + "snapColumns": 64, + "detachFromLayout": true, + "widgetId": "0", + "topRow": 0, + "bottomRow": 1230, + "containerStyle": "none", + "snapRows": 125, + "parentRowSpace": 1, + "type": "CANVAS_WIDGET", + "canExtend": true, + "version": 69, + "minHeight": 1240, + "parentColumnSpace": 1, + "dynamicTriggerPathList": [], + "dynamicBindingPathList": [], + "leftColumn": 0, + "children": [ + { + "labelTextSize": "0.875rem", + "boxShadow": "none", + "widgetName": "Statbox1", + "backgroundColor": "white", + "rightColumn": 21, + "dynamicHeight": "FIXED", + "widgetId": "3sii8uhhjs", + "topRow": 17, + "bottomRow": 33, + "parentRowSpace": 10, + "isVisible": true, + "type": "STATBOX_WIDGET", + "parentId": "0", + "isLoading": false, + "parentColumnSpace": 19.8125, + "leftColumn": 5, + "borderRadius": "0px", + "children": [ + { + "labelTextSize": "0.875rem", + "boxShadow": "none", + "widgetName": "Canvas1", + "rightColumn": 317, + "detachFromLayout": true, + "widgetId": "l752czyef7", + "containerStyle": "none", + "topRow": 0, + "bottomRow": 160, + "parentRowSpace": 1, + "isVisible": true, + "canExtend": false, + "type": "CANVAS_WIDGET", + "version": 1, + "parentId": "3sii8uhhjs", + "minHeight": 160, + "isLoading": false, + "parentColumnSpace": 1, + "leftColumn": 0, + "borderRadius": "0px", + "children": [ + { + "boxShadow": "none", + "widgetName": "Text1", + "dynamicPropertyPathList": [ + { + "key": "fontSize" + } + ], + "topRow": 0.5, + "bottomRow": 4.5, + "type": "TEXT_WIDGET", + "overflow": "NONE", + "fontFamily": "System Default", + "dynamicTriggerPathList": [], + "leftColumn": 1.5, + "dynamicBindingPathList": [ + { + "key": "text" + } + ], + "text": "{{MockApi.data.users[0].id}}", + "labelTextSize": "0.875rem", + "rightColumn": 37.5, + "textAlign": "LEFT", + "dynamicHeight": "FIXED", + "widgetId": "4mtayc9eas", + "isVisible": true, + "fontStyle": "BOLD", + "textColor": "#999999", + "version": 1, + "parentId": "l752czyef7", + "isLoading": false, + "borderRadius": "0px", + "maxDynamicHeight": 9000, + "fontSize": "0.75rem", + "minDynamicHeight": 4 + }, + { + "boxShadow": "none", + "widgetName": "Text2", + "dynamicPropertyPathList": [ + { + "key": "fontSize" + } + ], + "topRow": 5.5, + "bottomRow": 9.5, + "type": "TEXT_WIDGET", + "overflow": "NONE", + "fontFamily": "System Default", + "leftColumn": 1.5, + "text": "2.6 M", + "labelTextSize": "0.875rem", + "rightColumn": 37.5, + "textAlign": "LEFT", + "dynamicHeight": "FIXED", + "widgetId": "ii2tk6m48f", + "isVisible": true, + "fontStyle": "BOLD", + "textColor": "#231F20", + "version": 1, + "parentId": "l752czyef7", + "isLoading": false, + "borderRadius": "0px", + "maxDynamicHeight": 9000, + "fontSize": "1.5rem", + "minDynamicHeight": 4 + }, + { + "boxShadow": "none", + "widgetName": "Text3", + "dynamicPropertyPathList": [ + { + "key": "fontSize" + } + ], + "topRow": 10, + "bottomRow": 14, + "type": "TEXT_WIDGET", + "overflow": "NONE", + "fontFamily": "System Default", + "leftColumn": 1.5, + "text": "21% more than last month", + "labelTextSize": "0.875rem", + "rightColumn": 37.5, + "textAlign": "LEFT", + "dynamicHeight": "FIXED", + "widgetId": "ptbhksx9p1", + "isVisible": true, + "fontStyle": "BOLD", + "textColor": "#03B365", + "version": 1, + "parentId": "l752czyef7", + "isLoading": false, + "borderRadius": "0px", + "maxDynamicHeight": 9000, + "fontSize": "0.75rem", + "minDynamicHeight": 4 + }, + { + "labelTextSize": "0.875rem", + "boxShadow": "none", + "widgetName": "IconButton1", + "rightColumn": 61, + "iconName": "arrow-top-right", + "buttonColor": "#03B365", + "dynamicPropertyPathList": [ + { + "key": "borderRadius" + } + ], + "widgetId": "x35ni1hugn", + "topRow": 3, + "bottomRow": 11, + "isVisible": true, + "type": "ICON_BUTTON_WIDGET", + "version": 1, + "parentId": "l752czyef7", + "isLoading": false, + "borderRadius": "9999px", + "leftColumn": 45, + "buttonVariant": "PRIMARY", + "isDisabled": false + } + ] + } + ], + "maxDynamicHeight": 9000, + "minDynamicHeight": 4 + }, + { + "isVisible": true, + "backgroundColor": "#FFFFFF", + "widgetName": "Container1", + "containerStyle": "card", + "borderColor": "#E0DEDE", + "borderWidth": "1", + "boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}", + "animateLoading": true, + "children": [ + { + "isVisible": true, + "widgetName": "Canvas2", + "version": 1, + "detachFromLayout": true, + "type": "CANVAS_WIDGET", + "hideCard": true, + "isDeprecated": false, + "displayName": "Canvas", + "key": "vhu8oytsk7", + "containerStyle": "none", + "canExtend": false, + "children": [], + "minHeight": 580, + "widgetId": "p7wc0hnc0o", + "renderMode": "CANVAS", + "boxShadow": "none", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "accentColor": "{{appsmith.theme.colors.primaryColor}}", + "isLoading": false, + "parentColumnSpace": 1, + "parentRowSpace": 1, + "leftColumn": 0, + "rightColumn": 286.5, + "topRow": 0, + "bottomRow": 580, + "parentId": "q00fefx59g", + "dynamicBindingPathList": [ + { + "key": "borderRadius" + }, + { + "key": "accentColor" + } + ] + } + ], + "version": 1, + "minDynamicHeight": 10, + "maxDynamicHeight": 12, + "dynamicHeight": "FIXED", + "shouldScrollContents": true, + "searchTags": [ + "div", + "parent", + "group" + ], + "type": "CONTAINER_WIDGET", + "hideCard": false, + "isDeprecated": false, + "displayName": "Container", + "key": "fcexgq4024", + "iconSVG": "/static/media/icon.1977dca3370505e2db3a8e44cfd54907.svg", + "isCanvas": true, + "widgetId": "q00fefx59g", + "renderMode": "CANVAS", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "isLoading": false, + "parentColumnSpace": 11.9375, + "parentRowSpace": 10, + "leftColumn": 22, + "rightColumn": 64, + "topRow": 3, + "bottomRow": 61, + "parentId": "0", + "dynamicBindingPathList": [ + { + "key": "borderRadius" + }, + { + "key": "boxShadow" + } + ], + "dynamicTriggerPathList": [] + } + ] + } +} \ No newline at end of file diff --git a/app/client/cypress/fixtures/dynamicHeigthContainerFixedDsl.json b/app/client/cypress/fixtures/dynamicHeigthContainerFixedDsl.json new file mode 100644 index 000000000000..889f99522a57 --- /dev/null +++ b/app/client/cypress/fixtures/dynamicHeigthContainerFixedDsl.json @@ -0,0 +1,262 @@ +{ + "dsl": { + "widgetName": "MainContainer", + "backgroundColor": "none", + "rightColumn": 4896, + "snapColumns": 64, + "detachFromLayout": true, + "widgetId": "0", + "topRow": 0, + "bottomRow": 770, + "containerStyle": "none", + "snapRows": 125, + "parentRowSpace": 1, + "type": "CANVAS_WIDGET", + "canExtend": true, + "version": 69, + "minHeight": 1292, + "dynamicTriggerPathList": [], + "parentColumnSpace": 1, + "dynamicBindingPathList": [], + "leftColumn": 0, + "children": [ + { + "isVisible": true, + "backgroundColor": "#FFFFFF", + "widgetName": "Container1", + "containerStyle": "card", + "borderColor": "#E0DEDE", + "borderWidth": "1", + "boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}", + "animateLoading": true, + "children": [ + { + "isVisible": true, + "widgetName": "Canvas1", + "version": 1, + "detachFromLayout": true, + "type": "CANVAS_WIDGET", + "hideCard": true, + "isDeprecated": false, + "displayName": "Canvas", + "key": "1bw00vqtdm", + "containerStyle": "none", + "canExtend": false, + "children": [ + { + "isVisible": true, + "animateLoading": true, + "text": "Submit", + "buttonVariant": "PRIMARY", + "placement": "CENTER", + "widgetName": "Button1", + "isDisabled": false, + "isDefaultClickDisabled": true, + "disabledWhenInvalid": false, + "resetFormOnClick": false, + "recaptchaType": "V3", + "version": 1, + "searchTags": [ + "click", + "submit" + ], + "type": "BUTTON_WIDGET", + "hideCard": false, + "isDeprecated": false, + "displayName": "Button", + "key": "77f9o47k5s", + "iconSVG": "/static/media/icon.cca026338f1c8eb6df8ba03d084c2fca.svg", + "widgetId": "7dmmow3lvk", + "renderMode": "CANVAS", + "buttonColor": "{{appsmith.theme.colors.primaryColor}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "boxShadow": "none", + "isLoading": false, + "parentColumnSpace": 6.2294921875, + "parentRowSpace": 10, + "leftColumn": 4, + "rightColumn": 20, + "topRow": 2, + "bottomRow": 6, + "parentId": "61o8l6p2l0", + "dynamicBindingPathList": [ + { + "key": "buttonColor" + }, + { + "key": "borderRadius" + } + ] + }, + { + "isVisible": true, + "animateLoading": true, + "labelTextSize": "0.875rem", + "options": [ + { + "label": "Blue", + "value": "BLUE" + }, + { + "label": "Green", + "value": "GREEN" + }, + { + "label": "Red", + "value": "RED" + } + ], + "defaultSelectedValues": [ + "BLUE" + ], + "isDisabled": false, + "isInline": true, + "isRequired": false, + "labelText": "Label", + "labelPosition": "Top", + "labelAlignment": "left", + "labelWidth": 5, + "widgetName": "CheckboxGroup1", + "version": 2, + "minDynamicHeight": 4, + "maxDynamicHeight": 9000, + "dynamicHeight": "FIXED", + "type": "CHECKBOX_GROUP_WIDGET", + "hideCard": false, + "isDeprecated": false, + "displayName": "Checkbox Group", + "key": "x8dy31j5ec", + "iconSVG": "/static/media/icon.ecb3847950c4515966ef642a32758afb.svg", + "widgetId": "i2fyf6un9j", + "renderMode": "CANVAS", + "accentColor": "{{appsmith.theme.colors.primaryColor}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "isLoading": false, + "parentColumnSpace": 6.2294921875, + "parentRowSpace": 10, + "leftColumn": 31, + "rightColumn": 54, + "topRow": 6, + "bottomRow": 19, + "parentId": "61o8l6p2l0", + "dynamicBindingPathList": [ + { + "key": "accentColor" + }, + { + "key": "borderRadius" + } + ], + "dynamicTriggerPathList": [] + }, + { + "isVisible": true, + "label": "Label", + "labelPosition": "Top", + "labelAlignment": "left", + "labelTextSize": "0.875rem", + "labelWidth": 5, + "widgetName": "Input1", + "version": 2, + "defaultText": "", + "iconAlign": "left", + "autoFocus": false, + "labelStyle": "", + "resetOnSubmit": true, + "isRequired": false, + "isDisabled": false, + "animateLoading": true, + "inputType": "TEXT", + "minDynamicHeight": 4, + "maxDynamicHeight": 9000, + "dynamicHeight": "FIXED", + "searchTags": [ + "form", + "text input", + "number", + "textarea" + ], + "type": "INPUT_WIDGET_V2", + "hideCard": false, + "isDeprecated": false, + "displayName": "Input", + "key": "0okusukhfu", + "iconSVG": "/static/media/icon.9f505595da61a34f563dba82adeb06ec.svg", + "widgetId": "6nk2crlol3", + "renderMode": "CANVAS", + "accentColor": "{{appsmith.theme.colors.primaryColor}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "boxShadow": "none", + "isLoading": false, + "parentColumnSpace": 6.2294921875, + "parentRowSpace": 10, + "leftColumn": 13, + "rightColumn": 33, + "topRow": 21, + "bottomRow": 28, + "parentId": "61o8l6p2l0", + "dynamicBindingPathList": [ + { + "key": "accentColor" + }, + { + "key": "borderRadius" + } + ] + } + ], + "minHeight": 660, + "widgetId": "61o8l6p2l0", + "renderMode": "CANVAS", + "isLoading": false, + "parentColumnSpace": 1, + "parentRowSpace": 1, + "leftColumn": 0, + "rightColumn": 239.25, + "topRow": 0, + "bottomRow": 660, + "parentId": "14i1g95bi0", + "dynamicBindingPathList": [] + } + ], + "version": 1, + "minDynamicHeight": 10, + "maxDynamicHeight": 9000, + "dynamicHeight": "FIXED", + "shouldScrollContents": true, + "searchTags": [ + "div", + "parent", + "group" + ], + "type": "CONTAINER_WIDGET", + "hideCard": false, + "isDeprecated": false, + "displayName": "Container", + "key": "pjyxbucclq", + "iconSVG": "/static/media/icon.1977dca3370505e2db3a8e44cfd54907.svg", + "isCanvas": true, + "widgetId": "14i1g95bi0", + "renderMode": "CANVAS", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "isLoading": false, + "parentColumnSpace": 9.96875, + "parentRowSpace": 10, + "leftColumn": 16, + "rightColumn": 58, + "topRow": 3, + "bottomRow": 69, + "parentId": "0", + "dynamicBindingPathList": [ + { + "key": "borderRadius" + }, + { + "key": "boxShadow" + } + ], + "dynamicTriggerPathList": [] + } + ] + } +} \ No newline at end of file diff --git a/app/client/cypress/fixtures/dynamicTabWidgetdsl.json b/app/client/cypress/fixtures/dynamicTabWidgetdsl.json new file mode 100644 index 000000000000..4dde753fa7e7 --- /dev/null +++ b/app/client/cypress/fixtures/dynamicTabWidgetdsl.json @@ -0,0 +1,448 @@ +{ + "dsl": { + "widgetName": "MainContainer", + "backgroundColor": "none", + "rightColumn": 4896, + "snapColumns": 64, + "detachFromLayout": true, + "widgetId": "0", + "topRow": 0, + "bottomRow": 400, + "containerStyle": "none", + "snapRows": 125, + "parentRowSpace": 1, + "type": "CANVAS_WIDGET", + "canExtend": true, + "version": 64, + "minHeight": 1292, + "dynamicTriggerPathList": [], + "parentColumnSpace": 1, + "dynamicBindingPathList": [], + "leftColumn": 0, + "children": [ + { + "boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}", + "widgetName": "Tabs1", + "borderColor": "#E0DEDE", + "isCanvas": true, + "displayName": "Tabs", + "iconSVG": "/static/media/icon.74a6d653c8201e66f1cd367a3fba2657.svg", + "topRow": 1, + "bottomRow": 20, + "parentRowSpace": 10, + "type": "TABS_WIDGET", + "hideCard": false, + "shouldScrollContents": true, + "animateLoading": true, + "parentColumnSpace": 9.96875, + "leftColumn": 18, + "dynamicBindingPathList": [ + { + "key": "accentColor" + }, + { + "key": "borderRadius" + }, + { + "key": "boxShadow" + } + ], + "children": [ + { + "tabId": "tab1", + "boxShadow": "none", + "widgetName": "Canvas1", + "displayName": "Canvas", + "topRow": 0, + "bottomRow": 190, + "parentRowSpace": 1, + "type": "CANVAS_WIDGET", + "canExtend": true, + "hideCard": true, + "shouldScrollContents": false, + "minHeight": 190, + "parentColumnSpace": 1, + "leftColumn": 0, + "dynamicBindingPathList": [ + { + "key": "borderRadius" + }, + { + "key": "accentColor" + } + ], + "children": [ + { + "isVisible": true, + "minDynamicHeight": 4, + "maxDynamicHeight": 9000, + "dynamicHeight": "AUTO_HEIGHT", + "animateLoading": true, + "labelTextSize": "0.875rem", + "options": [ + { + "label": "Blue", + "value": "BLUE" + }, + { + "label": "Green", + "value": "GREEN" + }, + { + "label": "Red", + "value": "RED" + } + ], + "defaultSelectedValues": [ + "BLUE" + ], + "isDisabled": false, + "isInline": true, + "isRequired": false, + "labelText": "Label", + "labelPosition": "Left", + "labelAlignment": "left", + "labelWidth": 5, + "widgetName": "CheckboxGroup1", + "version": 2, + "type": "CHECKBOX_GROUP_WIDGET", + "hideCard": false, + "isDeprecated": false, + "displayName": "Checkbox Group", + "key": "2hr4hycu4v", + "iconSVG": "/static/media/icon.ecb3847950c4515966ef642a32758afb.svg", + "widgetId": "9uc345gnqg", + "renderMode": "CANVAS", + "accentColor": "{{appsmith.theme.colors.primaryColor}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "isLoading": false, + "parentColumnSpace": 3.42578125, + "parentRowSpace": 10, + "leftColumn": 13, + "rightColumn": 36, + "topRow": 9, + "bottomRow": 19, + "parentId": "zpypn0rx6a", + "dynamicBindingPathList": [ + { + "key": "accentColor" + }, + { + "key": "borderRadius" + } + ], + "dynamicTriggerPathList": [] + } + ], + "isDisabled": false, + "key": "wibrs53af5", + "isDeprecated": false, + "tabName": "Tab 1", + "rightColumn": 239.25, + "detachFromLayout": true, + "widgetId": "zpypn0rx6a", + "accentColor": "{{appsmith.theme.colors.primaryColor}}", + "isVisible": true, + "version": 1, + "parentId": "ynm15ab6f2", + "renderMode": "CANVAS", + "isLoading": false, + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}" + }, + { + "tabId": "tab2", + "boxShadow": "none", + "widgetName": "Canvas2", + "displayName": "Canvas", + "topRow": 0, + "bottomRow": 190, + "parentRowSpace": 1, + "type": "CANVAS_WIDGET", + "canExtend": true, + "hideCard": true, + "shouldScrollContents": false, + "minHeight": 190, + "parentColumnSpace": 1, + "leftColumn": 0, + "dynamicBindingPathList": [ + { + "key": "borderRadius" + }, + { + "key": "accentColor" + } + ], + "children": [ + { + "isVisible": true, + "minDynamicHeight": 4, + "maxDynamicHeight": 9000, + "dynamicHeight": "AUTO_HEIGHT", + "shouldScrollContents": true, + "borderColor": "#E0DEDE", + "borderWidth": "1", + "animateLoading": true, + "widgetName": "Form1", + "backgroundColor": "#FFFFFF", + "children": [ + { + "isVisible": true, + "widgetName": "Canvas3", + "version": 1, + "detachFromLayout": true, + "type": "CANVAS_WIDGET", + "hideCard": true, + "isDeprecated": false, + "displayName": "Canvas", + "key": "xp8804b2ms", + "containerStyle": "none", + "canExtend": false, + "children": [ + { + "isVisible": true, + "minDynamicHeight": 0, + "maxDynamicHeight": 0, + "dynamicHeight": "FIXED", + "text": "Form", + "fontSize": "1.25rem", + "fontStyle": "BOLD", + "textAlign": "LEFT", + "textColor": "#231F20", + "truncateButtonColor": "{{appsmith.theme.colors.primaryColor}}", + "widgetName": "Text1", + "shouldTruncate": false, + "overflow": "NONE", + "version": 1, + "animateLoading": true, + "searchTags": [ + "typography", + "paragraph", + "label" + ], + "type": "TEXT_WIDGET", + "hideCard": false, + "isDeprecated": false, + "displayName": "Text", + "key": "qjibufjufj", + "iconSVG": "/static/media/icon.97c59b523e6f70ba6f40a10fc2c7c5b5.svg", + "widgetId": "oltjpz4xge", + "renderMode": "CANVAS", + "fontFamily": "{{appsmith.theme.fontFamily.appFont}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "isLoading": false, + "leftColumn": 1.5, + "rightColumn": 25.5, + "topRow": 1, + "bottomRow": 5, + "parentId": "gfw87mvf4s", + "dynamicBindingPathList": [ + { + "key": "truncateButtonColor" + }, + { + "key": "fontFamily" + }, + { + "key": "borderRadius" + } + ] + }, + { + "isVisible": true, + "animateLoading": true, + "text": "Submit", + "buttonVariant": "PRIMARY", + "placement": "CENTER", + "widgetName": "Button1", + "isDisabled": false, + "isDefaultClickDisabled": true, + "disabledWhenInvalid": true, + "resetFormOnClick": true, + "recaptchaType": "V3", + "version": 1, + "searchTags": [ + "click", + "submit" + ], + "type": "BUTTON_WIDGET", + "hideCard": false, + "isDeprecated": false, + "displayName": "Button", + "key": "fkiknl12i3", + "iconSVG": "/static/media/icon.cca026338f1c8eb6df8ba03d084c2fca.svg", + "widgetId": "jdvk4o9jli", + "renderMode": "CANVAS", + "buttonColor": "{{appsmith.theme.colors.primaryColor}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "boxShadow": "none", + "isLoading": false, + "leftColumn": 46, + "rightColumn": 62, + "topRow": 33, + "bottomRow": 37, + "parentId": "gfw87mvf4s", + "dynamicBindingPathList": [ + { + "key": "buttonColor" + }, + { + "key": "borderRadius" + } + ] + }, + { + "isVisible": true, + "animateLoading": true, + "text": "Reset", + "buttonVariant": "SECONDARY", + "placement": "CENTER", + "widgetName": "Button2", + "isDisabled": false, + "isDefaultClickDisabled": true, + "disabledWhenInvalid": false, + "resetFormOnClick": true, + "recaptchaType": "V3", + "version": 1, + "searchTags": [ + "click", + "submit" + ], + "type": "BUTTON_WIDGET", + "hideCard": false, + "isDeprecated": false, + "displayName": "Button", + "key": "fkiknl12i3", + "iconSVG": "/static/media/icon.cca026338f1c8eb6df8ba03d084c2fca.svg", + "widgetId": "1u527b0c32", + "renderMode": "CANVAS", + "buttonColor": "{{appsmith.theme.colors.primaryColor}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "boxShadow": "none", + "isLoading": false, + "leftColumn": 30, + "rightColumn": 46, + "topRow": 33, + "bottomRow": 37, + "parentId": "gfw87mvf4s", + "dynamicBindingPathList": [ + { + "key": "buttonColor" + }, + { + "key": "borderRadius" + } + ] + } + ], + "minHeight": 390, + "widgetId": "gfw87mvf4s", + "renderMode": "CANVAS", + "boxShadow": "none", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "accentColor": "{{appsmith.theme.colors.primaryColor}}", + "isLoading": false, + "parentColumnSpace": 1, + "parentRowSpace": 1, + "leftColumn": 0, + "rightColumn": 100.91015625, + "topRow": 0, + "bottomRow": 390, + "parentId": "hxyieil09m", + "dynamicBindingPathList": [ + { + "key": "borderRadius" + }, + { + "key": "accentColor" + } + ] + } + ], + "searchTags": [ + "group" + ], + "type": "FORM_WIDGET", + "hideCard": false, + "isDeprecated": false, + "displayName": "Form", + "key": "23o06odb25", + "iconSVG": "/static/media/icon.ea3e08d130e59c56867ae40114c10eed.svg", + "isCanvas": true, + "widgetId": "hxyieil09m", + "renderMode": "CANVAS", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}", + "isLoading": false, + "parentColumnSpace": 4.20458984375, + "parentRowSpace": 10, + "leftColumn": 11, + "rightColumn": 35, + "topRow": 1, + "bottomRow": 40, + "parentId": "gmq0wikmsj", + "dynamicBindingPathList": [ + { + "key": "borderRadius" + }, + { + "key": "boxShadow" + } + ], + "dynamicTriggerPathList": [] + } + ], + "isDisabled": false, + "key": "wibrs53af5", + "isDeprecated": false, + "tabName": "Tab 2", + "rightColumn": 239.25, + "detachFromLayout": true, + "widgetId": "gmq0wikmsj", + "accentColor": "{{appsmith.theme.colors.primaryColor}}", + "isVisible": true, + "version": 1, + "parentId": "ynm15ab6f2", + "renderMode": "CANVAS", + "isLoading": false, + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}" + } + ], + "borderWidth": 1, + "key": "hf8p55gs4n", + "backgroundColor": "#FFFFFF", + "isDeprecated": false, + "rightColumn": 47, + "dynamicHeight": "FIXED", + "widgetId": "ynm15ab6f2", + "accentColor": "{{appsmith.theme.colors.primaryColor}}", + "defaultTab": "Tab 1", + "shouldShowTabs": true, + "tabsObj": { + "tab1": { + "label": "Tab 1", + "id": "tab1", + "widgetId": "zpypn0rx6a", + "isVisible": true, + "index": 0 + }, + "tab2": { + "label": "Tab 2", + "id": "tab2", + "widgetId": "gmq0wikmsj", + "isVisible": true, + "index": 1 + } + }, + "isVisible": true, + "version": 3, + "parentId": "0", + "renderMode": "CANVAS", + "isLoading": false, + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "maxDynamicHeight": 9000, + "minDynamicHeight": 4, + "dynamicTriggerPathList": [] + } + ] + } +} \ No newline at end of file diff --git a/app/client/cypress/fixtures/editorContextdsl.json b/app/client/cypress/fixtures/editorContextdsl.json index 6779de5f8a26..0fd32b368b9c 100644 --- a/app/client/cypress/fixtures/editorContextdsl.json +++ b/app/client/cypress/fixtures/editorContextdsl.json @@ -7,13 +7,13 @@ "detachFromLayout": true, "widgetId": "0", "topRow": 0.0, - "bottomRow": 1300.0, + "bottomRow": 1960.0, "containerStyle": "none", "snapRows": 125.0, "parentRowSpace": 1.0, "type": "CANVAS_WIDGET", "canExtend": true, - "version": 64.0, + "version": 69.0, "minHeight": 1292.0, "dynamicTriggerPathList": [], "parentColumnSpace": 1.0, @@ -228,6 +228,7 @@ "isDeprecated": false, "rightColumn": 41.0, "textAlign": "LEFT", + "dynamicHeight": "FIXED", "widgetId": "jp8t17pvg8", "isVisible": true, "fontStyle": "BOLD", @@ -237,7 +238,9 @@ "renderMode": "CANVAS", "isLoading": false, "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", - "fontSize": "1.5rem" + "maxDynamicHeight": 9000.0, + "fontSize": "1.5rem", + "minDynamicHeight": 4.0 }, { "resetFormOnClick": false, @@ -349,6 +352,7 @@ "isDeprecated": false, "rightColumn": 50.0, "detachFromLayout": true, + "dynamicHeight": "FIXED", "widgetId": "4ax70fzq06", "canOutsideClickClose": true, "canEscapeKeyClose": true, @@ -357,7 +361,461 @@ "renderMode": "CANVAS", "isLoading": false, "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", - "width": 456.0 + "maxDynamicHeight": 9000.0, + "width": 456.0, + "minDynamicHeight": 4.0 + }, + { + "boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}", + "borderColor": "#E0DEDE", + "isVisibleDownload": true, + "iconSVG": "/static/media/icon.db8a9cbd2acd22a31ea91cc37ea2a46c.svg", + "topRow": 127.0, + "isSortable": true, + "type": "TABLE_WIDGET_V2", + "inlineEditingSaveOption": "ROW_LEVEL", + "animateLoading": true, + "dynamicBindingPathList": [ + { + "key": "primaryColumns.step.computedValue" + }, + { + "key": "primaryColumns.task.computedValue" + }, + { + "key": "primaryColumns.status.computedValue" + }, + { + "key": "primaryColumns.action.computedValue" + }, + { + "key": "primaryColumns.action.buttonColor" + }, + { + "key": "primaryColumns.action.borderRadius" + }, + { + "key": "primaryColumns.action.boxShadow" + }, + { + "key": "accentColor" + }, + { + "key": "borderRadius" + }, + { + "key": "boxShadow" + }, + { + "key": "childStylesheet.button.buttonColor" + }, + { + "key": "childStylesheet.button.borderRadius" + }, + { + "key": "childStylesheet.menuButton.menuColor" + }, + { + "key": "childStylesheet.menuButton.borderRadius" + }, + { + "key": "childStylesheet.iconButton.buttonColor" + }, + { + "key": "childStylesheet.iconButton.borderRadius" + }, + { + "key": "childStylesheet.editActions.saveButtonColor" + }, + { + "key": "childStylesheet.editActions.saveBorderRadius" + }, + { + "key": "childStylesheet.editActions.discardButtonColor" + }, + { + "key": "childStylesheet.editActions.discardBorderRadius" + }, + { + "key": "primaryColumns.status.menuColor" + }, + { + "key": "primaryColumns.status.borderRadius" + }, + { + "key": "primaryColumns.status.boxShadow" + }, + { + "key": "primaryColumns.status.menuButtonLabel" + } + ], + "leftColumn": 5.0, + "delimiter": ",", + "defaultSelectedRowIndex": 0.0, + "accentColor": "{{appsmith.theme.colors.primaryColor}}", + "isVisibleFilters": true, + "isVisible": true, + "enableClientSideSearch": true, + "version": 1.0, + "totalRecordsCount": 0.0, + "isLoading": false, + "childStylesheet": { + "button": { + "buttonColor": "{{appsmith.theme.colors.primaryColor}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "boxShadow": "none" + }, + "menuButton": { + "menuColor": "{{appsmith.theme.colors.primaryColor}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "boxShadow": "none" + }, + "iconButton": { + "buttonColor": "{{appsmith.theme.colors.primaryColor}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "boxShadow": "none" + }, + "editActions": { + "saveButtonColor": "{{appsmith.theme.colors.primaryColor}}", + "saveBorderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "discardButtonColor": "{{appsmith.theme.colors.primaryColor}}", + "discardBorderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}" + } + }, + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "defaultSelectedRowIndices": [ + 0.0 + ], + "widgetName": "Table1", + "defaultPageSize": 0.0, + "columnOrder": [ + "step", + "task", + "status", + "action" + ], + "dynamicPropertyPathList": [], + "displayName": "Table", + "bottomRow": 155.0, + "columnWidthMap": { + "task": 245.0, + "step": 62.0, + "status": 75.0 + }, + "parentRowSpace": 10.0, + "hideCard": false, + "parentColumnSpace": 10.515625, + "dynamicTriggerPathList": [], + "borderWidth": "1", + "primaryColumns": { + "step": { + "index": 0.0, + "width": 150.0, + "id": "step", + "originalId": "step", + "alias": "step", + "horizontalAlignment": "LEFT", + "verticalAlignment": "CENTER", + "columnType": "text", + "textSize": "0.875rem", + "enableFilter": true, + "enableSort": true, + "isVisible": true, + "isCellVisible": true, + "isCellEditable": false, + "isDerived": false, + "label": "step", + "computedValue": "{{Table1.processedTableData.map((currentRow, currentIndex) => ( currentRow[\"step\"]))}}", + "validation": {}, + "labelColor": "#FFFFFF" + }, + "task": { + "index": 1.0, + "width": 150.0, + "id": "task", + "originalId": "task", + "alias": "task", + "horizontalAlignment": "LEFT", + "verticalAlignment": "CENTER", + "columnType": "text", + "textSize": "0.875rem", + "enableFilter": true, + "enableSort": true, + "isVisible": true, + "isCellVisible": true, + "isCellEditable": false, + "isDerived": false, + "label": "task", + "computedValue": "{{Table1.processedTableData.map((currentRow, currentIndex) => ( currentRow[\"task\"]))}}", + "validation": {}, + "labelColor": "#FFFFFF" + }, + "status": { + "index": 2.0, + "width": 150.0, + "id": "status", + "originalId": "status", + "alias": "status", + "horizontalAlignment": "LEFT", + "verticalAlignment": "CENTER", + "columnType": "menuButton", + "textSize": "0.875rem", + "enableFilter": true, + "enableSort": true, + "isVisible": true, + "isCellVisible": true, + "isCellEditable": false, + "isDerived": false, + "label": "status", + "computedValue": "{{Table1.processedTableData.map((currentRow, currentIndex) => ( currentRow[\"status\"]))}}", + "validation": {}, + "labelColor": "#FFFFFF", + "menuColor": "{{Table1.processedTableData.map((currentRow, currentIndex) => ( appsmith.theme.colors.primaryColor))}}", + "borderRadius": "{{Table1.processedTableData.map((currentRow, currentIndex) => ( appsmith.theme.borderRadius.appBorderRadius))}}", + "boxShadow": "{{Table1.processedTableData.map((currentRow, currentIndex) => ( 'none'))}}", + "customAlias": "", + "menuButtonLabel": "{{Table1.processedTableData.map((currentRow, currentIndex) => ( 'Open Menu'))}}", + "menuItems": { + "menuIteme63irwbvnd": { + "id": "menuIteme63irwbvnd", + "index": 0.0, + "label": "Menu Item 1", + "widgetId": "f6hfchc8z7", + "isDisabled": false, + "isVisible": true + } + } + }, + "action": { + "index": 3.0, + "width": 150.0, + "id": "action", + "originalId": "action", + "alias": "action", + "horizontalAlignment": "LEFT", + "verticalAlignment": "CENTER", + "columnType": "button", + "textSize": "0.875rem", + "enableFilter": true, + "enableSort": true, + "isVisible": true, + "isCellVisible": true, + "isCellEditable": false, + "isDisabled": false, + "isDerived": false, + "label": "action", + "onClick": "{{currentRow.step === '#1' ? showAlert('Done', 'success') : currentRow.step === '#2' ? navigateTo('https://docs.appsmith.com/core-concepts/connecting-to-data-sources/querying-a-database',undefined,'NEW_WINDOW') : navigateTo('https://docs.appsmith.com/core-concepts/displaying-data-read/display-data-tables',undefined,'NEW_WINDOW')}}", + "computedValue": "{{Table1.processedTableData.map((currentRow, currentIndex) => ( currentRow[\"action\"]))}}", + "validation": {}, + "labelColor": "#FFFFFF", + "buttonColor": "{{Table1.processedTableData.map((currentRow, currentIndex) => ( appsmith.theme.colors.primaryColor))}}", + "borderRadius": "{{Table1.processedTableData.map((currentRow, currentIndex) => ( appsmith.theme.borderRadius.appBorderRadius))}}", + "boxShadow": "{{Table1.processedTableData.map((currentRow, currentIndex) => ( 'none'))}}" + } + }, + "key": "eg6ib886an", + "isDeprecated": false, + "rightColumn": 39.0, + "textSize": "0.875rem", + "widgetId": "lqehhvs21b", + "tableData": [ + { + "step": "#1", + "task": "Drop a table", + "status": "✅", + "action": "" + }, + { + "step": "#2", + "task": "Create a query fetch_users with the Mock DB", + "status": "--", + "action": "" + }, + { + "step": "#3", + "task": "Bind the query using => fetch_users.data", + "status": "--", + "action": "" + } + ], + "label": "Data", + "searchKey": "", + "parentId": "0", + "renderMode": "CANVAS", + "horizontalAlignment": "LEFT", + "isVisibleSearch": true, + "isVisiblePagination": true, + "verticalAlignment": "CENTER" + }, + { + "boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}", + "widgetName": "Tabs1", + "borderColor": "#E0DEDE", + "isCanvas": true, + "displayName": "Tabs", + "iconSVG": "/static/media/icon.74a6d653c8201e66f1cd367a3fba2657.svg", + "topRow": 156.0, + "bottomRow": 188.0, + "parentRowSpace": 10.0, + "type": "TABS_WIDGET", + "hideCard": false, + "shouldScrollContents": true, + "animateLoading": true, + "parentColumnSpace": 22.78125, + "leftColumn": 33.0, + "dynamicBindingPathList": [ + { + "key": "accentColor" + }, + { + "key": "borderRadius" + }, + { + "key": "boxShadow" + } + ], + "children": [ + { + "tabId": "tab1", + "widgetName": "Canvas2", + "displayName": "Canvas", + "bottomRow": 270.0, + "topRow": 0.0, + "parentRowSpace": 1.0, + "type": "CANVAS_WIDGET", + "canExtend": true, + "hideCard": true, + "shouldScrollContents": false, + "minHeight": 270.0, + "parentColumnSpace": 1.0, + "leftColumn": 0.0, + "dynamicBindingPathList": [], + "children": [], + "isDisabled": false, + "key": "fz419vgz5h", + "isDeprecated": false, + "tabName": "Tab 1", + "rightColumn": 546.75, + "detachFromLayout": true, + "widgetId": "vu99o7ewxn", + "isVisible": true, + "version": 1.0, + "parentId": "ih1hhj512r", + "renderMode": "CANVAS", + "isLoading": false + }, + { + "tabId": "tab2", + "widgetName": "Canvas3", + "displayName": "Canvas", + "bottomRow": 270.0, + "topRow": 0.0, + "parentRowSpace": 1.0, + "type": "CANVAS_WIDGET", + "canExtend": true, + "hideCard": true, + "shouldScrollContents": false, + "minHeight": 270.0, + "parentColumnSpace": 1.0, + "leftColumn": 0.0, + "dynamicBindingPathList": [], + "children": [ + { + "resetFormOnClick": false, + "boxShadow": "none", + "widgetName": "Button4", + "buttonColor": "{{appsmith.theme.colors.primaryColor}}", + "displayName": "Button", + "iconSVG": "/static/media/icon.cca026338f1c8eb6df8ba03d084c2fca.svg", + "searchTags": [ + "click", + "submit" + ], + "topRow": 21.0, + "bottomRow": 25.0, + "parentRowSpace": 10.0, + "type": "BUTTON_WIDGET", + "hideCard": false, + "animateLoading": true, + "parentColumnSpace": 8.23046875, + "leftColumn": 17.0, + "dynamicBindingPathList": [ + { + "key": "buttonColor" + }, + { + "key": "borderRadius" + } + ], + "text": "Submit", + "isDisabled": false, + "key": "9o5rfnb50s", + "isDeprecated": false, + "rightColumn": 33.0, + "isDefaultClickDisabled": true, + "widgetId": "lz7izc77ar", + "isVisible": true, + "recaptchaType": "V3", + "version": 1.0, + "parentId": "d5apxr06ko", + "renderMode": "CANVAS", + "isLoading": false, + "disabledWhenInvalid": false, + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "buttonVariant": "PRIMARY", + "placement": "CENTER" + } + ], + "isDisabled": false, + "key": "fz419vgz5h", + "isDeprecated": false, + "tabName": "Tab 2", + "rightColumn": 546.75, + "detachFromLayout": true, + "widgetId": "d5apxr06ko", + "isVisible": true, + "version": 1.0, + "parentId": "ih1hhj512r", + "renderMode": "CANVAS", + "isLoading": false + } + ], + "borderWidth": 1.0, + "key": "ybq34680di", + "backgroundColor": "#FFFFFF", + "isDeprecated": false, + "rightColumn": 57.0, + "dynamicHeight": "AUTO_HEIGHT", + "widgetId": "ih1hhj512r", + "accentColor": "{{appsmith.theme.colors.primaryColor}}", + "defaultTab": "Tab 1", + "shouldShowTabs": true, + "tabsObj": { + "tab1": { + "label": "Tab 1", + "id": "tab1", + "widgetId": "vu99o7ewxn", + "isVisible": true, + "index": 0.0 + }, + "tab2": { + "label": "Tab 2", + "id": "tab2", + "widgetId": "d5apxr06ko", + "isVisible": true, + "index": 1.0 + } + }, + "isVisible": true, + "version": 3.0, + "parentId": "0", + "renderMode": "CANVAS", + "isLoading": false, + "originalTopRow": 156.0, + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "maxDynamicHeight": 9000.0, + "originalBottomRow": 176.0, + "minDynamicHeight": 15.0 } ] } diff --git a/app/client/cypress/fixtures/example.json b/app/client/cypress/fixtures/example.json index f343ab56edb0..8f3de355fcbb 100644 --- a/app/client/cypress/fixtures/example.json +++ b/app/client/cypress/fixtures/example.json @@ -300,6 +300,29 @@ "img": "http://www.serebii.net/pokemongo/pokemon/006.png" } ], + "MenuButtonSourceData": [ + { + "id": 1, + "email": "michael.lawson@reqres.in", + "first_name": "Michael", + "last_name": "Lawson", + "avatar": "https://reqres.in/img/faces/7-image.jpg" + }, + { + "id": 2, + "email": "lindsay.ferguson@reqres.in", + "first_name": "Lindsay", + "last_name": "Ferguson", + "avatar": "https://reqres.in/img/faces/8-image.jpg" + }, + { + "id": 3, + "email": "brock.lesnar@reqres.in", + "first_name": "Brock", + "last_name": "Lesnar", + "avatar": "https://reqres.in/img/faces/8-image.jpg" + } + ], "TableURLColumnType": [ { "image": "https://wallpaperaccess.com/full/1376499.jpg", diff --git a/app/client/cypress/fixtures/exportedApp.json b/app/client/cypress/fixtures/exportedApp.json index 8026c63d2f89..7bf9e82a2e91 100644 --- a/app/client/cypress/fixtures/exportedApp.json +++ b/app/client/cypress/fixtures/exportedApp.json @@ -1,51 +1,50 @@ { "clientSchemaVersion": 1, - "serverSchemaVersion": 2, + "serverSchemaVersion": 6, "exportedApplication": { - "name": "app2896", + "name": "app5232", "isPublic": false, + "pages": [ + { + "id": "Page1", + "isDefault": true + } + ], + "publishedPages": [ + { + "id": "Page1", + "isDefault": true + } + ], + "viewMode": false, "appIsExample": false, "unreadCommentThreads": 0, "color": "#F4FFDE", "icon": "single-person", - "slug": "app2896", + "slug": "app5232", "evaluationVersion": 2, "applicationVersion": 2, - "new": true + "isManualUpdate": false, + "deleted": false }, "datasourceList": [ { - "userPermissions": [ - "execute:datasources", - "manage:datasources", - "read:datasources" - ], - "gitSyncId": "61c2d94747cda83965fe72b5_61c5822385c0bd4ccf7d171c", "name": "mockdata", "pluginId": "postgres-plugin", - "invalids": [ - "Missing authentication details." - ], "messages": [], - "isConfigured": false, - "isValid": false, - "new": true + "isAutoGenerated": false, + "deleted": false, + "gitSyncId": "61c2d94747cda83965fe72b5_61c5822385c0bd4ccf7d171c" } ], "pageList": [ { - "userPermissions": [ - "read:pages", - "manage:pages" - ], - "gitSyncId": "61c580d685c0bd4ccf7d1716_61c580d685c0bd4ccf7d1718", "unpublishedPage": { "name": "Page1", "slug": "page1", "layouts": [ { - "id": "Page1", - "userPermissions": [], + "viewMode": false, "dsl": { "widgetName": "MainContainer", "backgroundColor": "none", @@ -60,13 +59,14 @@ "parentRowSpace": 1, "type": "CANVAS_WIDGET", "canExtend": true, - "version": 52, + "version": 65, "minHeight": 600, "parentColumnSpace": 1, "dynamicBindingPathList": [], "leftColumn": 0, "children": [ { + "boxShadow": "none", "widgetName": "Table1", "defaultPageSize": 0, "columnOrder": [ @@ -92,6 +92,9 @@ }, { "key": "primaryColumns.schema_name.computedValue" + }, + { + "key": "accentColor" } ], "leftColumn": 4, @@ -103,7 +106,7 @@ "horizontalAlignment": "LEFT", "verticalAlignment": "CENTER", "columnType": "text", - "textSize": "PARAGRAPH", + "textSize": "0.875rem", "enableFilter": true, "enableSort": true, "isVisible": true, @@ -111,15 +114,19 @@ "isCellVisible": true, "isDerived": false, "label": "schema_name", - "computedValue": "{{Table1.sanitizedTableData.map((currentRow) => ( currentRow.schema_name))}}" + "computedValue": "{{Table1.sanitizedTableData.map((currentRow) => ( currentRow.schema_name))}}", + "borderRadius": "0px", + "boxShadow": "none" } }, "delimiter": ",", "key": "5ejs55im17", "derivedColumns": {}, + "labelTextSize": "0.875rem", "rightColumn": 25, - "textSize": "PARAGRAPH", + "textSize": "0.875rem", "widgetId": "uyyp0qxfdq", + "accentColor": "{{appsmith.theme.colors.primaryColor}}", "isVisibleFilters": true, "tableData": "{{get_schema.data}}", "isVisible": true, @@ -133,6 +140,24 @@ "isLoading": false, "horizontalAlignment": "LEFT", "isVisibleSearch": true, + "childStylesheet": { + "button": { + "buttonColor": "{{appsmith.theme.colors.primaryColor}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "boxShadow": "none" + }, + "menuButton": { + "menuColor": "{{appsmith.theme.colors.primaryColor}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "boxShadow": "none" + }, + "iconButton": { + "menuColor": "{{appsmith.theme.colors.primaryColor}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "boxShadow": "none" + } + }, + "borderRadius": "0px", "isVisiblePagination": true, "verticalAlignment": "CENTER", "columnSizeMap": { @@ -142,6 +167,7 @@ } }, { + "boxShadow": "none", "widgetName": "Table2", "defaultPageSize": 0, "columnOrder": [ @@ -179,6 +205,9 @@ }, { "key": "primaryColumns.id.computedValue" + }, + { + "key": "accentColor" } ], "leftColumn": 30, @@ -190,7 +219,7 @@ "horizontalAlignment": "LEFT", "verticalAlignment": "CENTER", "columnType": "text", - "textSize": "PARAGRAPH", + "textSize": "0.875rem", "enableFilter": true, "enableSort": true, "isVisible": true, @@ -198,7 +227,9 @@ "isCellVisible": true, "isDerived": false, "label": "due", - "computedValue": "{{Table2.sanitizedTableData.map((currentRow) => ( currentRow.due))}}" + "computedValue": "{{Table2.sanitizedTableData.map((currentRow) => ( currentRow.due))}}", + "borderRadius": "0px", + "boxShadow": "none" }, "assignee": { "index": 1, @@ -207,7 +238,7 @@ "horizontalAlignment": "LEFT", "verticalAlignment": "CENTER", "columnType": "text", - "textSize": "PARAGRAPH", + "textSize": "0.875rem", "enableFilter": true, "enableSort": true, "isVisible": true, @@ -215,7 +246,9 @@ "isCellVisible": true, "isDerived": false, "label": "assignee", - "computedValue": "{{Table2.sanitizedTableData.map((currentRow) => ( currentRow.assignee))}}" + "computedValue": "{{Table2.sanitizedTableData.map((currentRow) => ( currentRow.assignee))}}", + "borderRadius": "0px", + "boxShadow": "none" }, "title": { "index": 2, @@ -224,7 +257,7 @@ "horizontalAlignment": "LEFT", "verticalAlignment": "CENTER", "columnType": "text", - "textSize": "PARAGRAPH", + "textSize": "0.875rem", "enableFilter": true, "enableSort": true, "isVisible": true, @@ -232,7 +265,9 @@ "isCellVisible": true, "isDerived": false, "label": "title", - "computedValue": "{{Table2.sanitizedTableData.map((currentRow) => ( currentRow.title))}}" + "computedValue": "{{Table2.sanitizedTableData.map((currentRow) => ( currentRow.title))}}", + "borderRadius": "0px", + "boxShadow": "none" }, "id": { "index": 4, @@ -241,7 +276,7 @@ "horizontalAlignment": "LEFT", "verticalAlignment": "CENTER", "columnType": "text", - "textSize": "PARAGRAPH", + "textSize": "0.875rem", "enableFilter": true, "enableSort": true, "isVisible": true, @@ -249,15 +284,19 @@ "isCellVisible": true, "isDerived": false, "label": "id", - "computedValue": "{{Table2.sanitizedTableData.map((currentRow) => ( currentRow.id))}}" + "computedValue": "{{Table2.sanitizedTableData.map((currentRow) => ( currentRow.id))}}", + "borderRadius": "0px", + "boxShadow": "none" } }, "delimiter": ",", "key": "5ejs55im17", "derivedColumns": {}, + "labelTextSize": "0.875rem", "rightColumn": 61, - "textSize": "PARAGRAPH", + "textSize": "0.875rem", "widgetId": "r1m4lkt7at", + "accentColor": "{{appsmith.theme.colors.primaryColor}}", "isVisibleFilters": true, "tableData": "{{mockApi.data.headers.info}}", "isVisible": true, @@ -271,6 +310,24 @@ "isLoading": false, "horizontalAlignment": "LEFT", "isVisibleSearch": true, + "childStylesheet": { + "button": { + "buttonColor": "{{appsmith.theme.colors.primaryColor}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "boxShadow": "none" + }, + "menuButton": { + "menuColor": "{{appsmith.theme.colors.primaryColor}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "boxShadow": "none" + }, + "iconButton": { + "menuColor": "{{appsmith.theme.colors.primaryColor}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "boxShadow": "none" + } + }, + "borderRadius": "0px", "isVisiblePagination": true, "verticalAlignment": "CENTER", "columnSizeMap": { @@ -281,6 +338,7 @@ } }, { + "boxShadow": "none", "widgetName": "Input1", "displayName": "Input", "iconSVG": "/static/media/icon.9f505595.svg", @@ -298,15 +356,20 @@ "dynamicBindingPathList": [ { "key": "defaultText" + }, + { + "key": "accentColor" } ], "labelStyle": "", "inputType": "TEXT", "isDisabled": false, "key": "t02w4ix9o5", + "labelTextSize": "0.875rem", "isRequired": false, "rightColumn": 38, "widgetId": "9timcor5m5", + "accentColor": "{{appsmith.theme.colors.primaryColor}}", "isVisible": true, "label": "", "allowCurrencyChange": false, @@ -314,6 +377,7 @@ "parentId": "0", "renderMode": "CANVAS", "isLoading": false, + "borderRadius": "0px", "iconAlign": "left", "defaultText": "{{JSObject1.myVar1}}" } @@ -324,6 +388,7 @@ { "id": "Page1_get_schema", "name": "get_schema", + "confirmBeforeExecute": false, "pluginType": "DB", "jsonPathKeys": [], "timeoutInMillisecond": 10000 @@ -333,24 +398,30 @@ { "id": "Page1_mockApi", "name": "mockApi", + "confirmBeforeExecute": false, "pluginType": "API", "jsonPathKeys": [], "timeoutInMillisecond": 10000 } ] ], - "new": false + "layoutOnLoadActionErrors": [], + "validOnPageLoadActions": true, + "id": "Page1", + "deleted": false, + "policies": [], + "userPermissions": [] } ], - "userPermissions": [] + "userPermissions": [], + "policies": [] }, "publishedPage": { "name": "Page1", "slug": "page1", "layouts": [ { - "id": "Page1", - "userPermissions": [], + "viewMode": false, "dsl": { "widgetName": "MainContainer", "backgroundColor": "none", @@ -372,31 +443,83 @@ "leftColumn": 0, "children": [] }, - "new": false + "validOnPageLoadActions": true, + "id": "Page1", + "deleted": false, + "policies": [], + "userPermissions": [] } ], - "userPermissions": [] + "userPermissions": [], + "policies": [] }, - "new": true + "deleted": false, + "gitSyncId": "61c580d685c0bd4ccf7d1716_61c580d685c0bd4ccf7d1718" } ], - "publishedDefaultPageName": "Page1", - "unpublishedDefaultPageName": "Page1", "actionList": [ { - "id": "Page1_mockApi", - "userPermissions": [ - "read:actions", - "execute:actions", - "manage:actions" - ], - "gitSyncId": "61c580d685c0bd4ccf7d1716_61c580e385c0bd4ccf7d171a", + "pluginType": "DB", + "pluginId": "postgres-plugin", + "unpublishedAction": { + "name": "get_schema", + "datasource": { + "pluginId": "postgres-plugin", + "messages": [], + "isAutoGenerated": false, + "id": "mockdata", + "deleted": false, + "policies": [], + "userPermissions": [] + }, + "pageId": "Page1", + "actionConfiguration": { + "timeoutInMillisecond": 10000, + "paginationType": "NONE", + "encodeParamsToggle": true, + "body": "SELECT schema_name FROM information_schema.schemata;", + "selfReferencingDataPaths": [], + "pluginSpecifiedTemplates": [ + { + "value": true + } + ] + }, + "executeOnLoad": true, + "dynamicBindingPathList": [], + "isValid": true, + "invalids": [], + "messages": [], + "jsonPathKeys": [], + "userSetOnLoad": false, + "confirmBeforeExecute": false, + "policies": [], + "userPermissions": [] + }, + "publishedAction": { + "datasource": { + "messages": [], + "isAutoGenerated": false, + "deleted": false, + "policies": [], + "userPermissions": [] + }, + "messages": [], + "userSetOnLoad": false, + "confirmBeforeExecute": false, + "policies": [], + "userPermissions": [] + }, + "id": "Page1_get_schema", + "deleted": false, + "gitSyncId": "61c580d685c0bd4ccf7d1716_61c5832685c0bd4ccf7d171e" + }, + { "pluginType": "API", "pluginId": "restapi-plugin", "unpublishedAction": { "name": "mockApi", "datasource": { - "userPermissions": [], "name": "DEFAULT_REST_DATASOURCE", "pluginId": "restapi-plugin", "datasourceConfiguration": { @@ -404,8 +527,10 @@ }, "invalids": [], "messages": [], - "isValid": true, - "new": true + "isAutoGenerated": false, + "deleted": false, + "policies": [], + "userPermissions": [] }, "pageId": "Page1", "actionConfiguration": { @@ -422,6 +547,7 @@ "queryParameters": [], "body": "", "httpMethod": "GET", + "selfReferencingDataPaths": [], "pluginSpecifiedTemplates": [ { "value": true @@ -434,43 +560,43 @@ "invalids": [], "messages": [], "jsonPathKeys": [], + "userSetOnLoad": false, "confirmBeforeExecute": false, - "userPermissions": [], - "validName": "mockApi" + "policies": [], + "userPermissions": [] }, "publishedAction": { "datasource": { - "userPermissions": [], "messages": [], - "isValid": true, - "new": true + "isAutoGenerated": false, + "deleted": false, + "policies": [], + "userPermissions": [] }, "messages": [], + "userSetOnLoad": false, "confirmBeforeExecute": false, + "policies": [], "userPermissions": [] }, - "new": false + "id": "Page1_mockApi", + "deleted": false, + "gitSyncId": "61c580d685c0bd4ccf7d1716_61c580e385c0bd4ccf7d171a" }, { - "id": "Page1_myFun1", - "userPermissions": [ - "read:actions", - "execute:actions", - "manage:actions" - ], - "gitSyncId": "61c580d685c0bd4ccf7d1716_61c58ced85c0bd4ccf7d1722", "pluginType": "JS", "pluginId": "js-plugin", "unpublishedAction": { "name": "myFun1", "fullyQualifiedName": "JSObject1.myFun1", "datasource": { - "userPermissions": [], "name": "UNUSED_DATASOURCE", "pluginId": "js-plugin", "messages": [], - "isValid": true, - "new": true + "isAutoGenerated": false, + "deleted": false, + "policies": [], + "userPermissions": [] }, "pageId": "Page1", "collectionId": "Page1_JSObject1", @@ -478,7 +604,8 @@ "timeoutInMillisecond": 10000, "paginationType": "NONE", "encodeParamsToggle": true, - "body": "() => {\n\t\t//write code here\n\t\treturn JSObject1.myVar1;\n\t}", + "body": "() => {\n return JSObject1.myVar1;\n}", + "selfReferencingDataPaths": [], "jsArguments": [], "isAsync": false }, @@ -492,100 +619,45 @@ "invalids": [], "messages": [], "jsonPathKeys": [ - "() => {\n\t\t//write code here\n\t\treturn JSObject1.myVar1;\n\t}" + "() => {\n return JSObject1.myVar1;\n}" ], + "userSetOnLoad": false, "confirmBeforeExecute": false, - "userPermissions": [], - "validName": "JSObject1.myFun1" - }, - "publishedAction": { - "datasource": { - "userPermissions": [], - "messages": [], - "isValid": true, - "new": true - }, - "messages": [], - "confirmBeforeExecute": false, + "policies": [], "userPermissions": [] }, - "new": false - }, - { - "id": "Page1_get_schema", - "userPermissions": [ - "read:actions", - "execute:actions", - "manage:actions" - ], - "gitSyncId": "61c580d685c0bd4ccf7d1716_61c5832685c0bd4ccf7d171e", - "pluginType": "DB", - "pluginId": "postgres-plugin", - "unpublishedAction": { - "name": "get_schema", - "datasource": { - "id": "mockdata", - "userPermissions": [], - "pluginId": "postgres-plugin", - "messages": [], - "isValid": true, - "new": false - }, - "pageId": "Page1", - "actionConfiguration": { - "timeoutInMillisecond": 10000, - "paginationType": "NONE", - "encodeParamsToggle": true, - "body": "SELECT schema_name FROM information_schema.schemata;", - "pluginSpecifiedTemplates": [ - { - "value": true - } - ] - }, - "executeOnLoad": true, - "dynamicBindingPathList": [], - "isValid": true, - "invalids": [], - "messages": [], - "jsonPathKeys": [], - "confirmBeforeExecute": false, - "userPermissions": [], - "validName": "get_schema" - }, "publishedAction": { "datasource": { - "userPermissions": [], "messages": [], - "isValid": true, - "new": true + "isAutoGenerated": false, + "deleted": false, + "policies": [], + "userPermissions": [] }, "messages": [], + "userSetOnLoad": false, "confirmBeforeExecute": false, + "policies": [], "userPermissions": [] }, - "new": false + "id": "Page1_JSObject1.myFun1", + "deleted": false, + "gitSyncId": "61c580d685c0bd4ccf7d1716_61c58ced85c0bd4ccf7d1722" }, { - "id": "Page1_myFun2", - "userPermissions": [ - "read:actions", - "execute:actions", - "manage:actions" - ], - "gitSyncId": "61c580d685c0bd4ccf7d1716_61c58ced85c0bd4ccf7d1724", "pluginType": "JS", "pluginId": "js-plugin", "unpublishedAction": { "name": "myFun2", "fullyQualifiedName": "JSObject1.myFun2", "datasource": { - "userPermissions": [], "name": "UNUSED_DATASOURCE", "pluginId": "js-plugin", "messages": [], - "isValid": true, - "new": true + "isAutoGenerated": false, + "deleted": false, + "policies": [], + "userPermissions": [] }, "pageId": "Page1", "collectionId": "Page1_JSObject1", @@ -593,7 +665,8 @@ "timeoutInMillisecond": 10000, "paginationType": "NONE", "encodeParamsToggle": true, - "body": "() => {\n\t\t//write code here\n\t}", + "body": "() => {}", + "selfReferencingDataPaths": [], "jsArguments": [], "isAsync": false }, @@ -607,69 +680,83 @@ "invalids": [], "messages": [], "jsonPathKeys": [ - "() => {\n\t\t//write code here\n\t}" + "() => {}" ], + "userSetOnLoad": false, "confirmBeforeExecute": false, - "userPermissions": [], - "validName": "JSObject1.myFun2" + "policies": [], + "userPermissions": [] }, "publishedAction": { "datasource": { - "userPermissions": [], "messages": [], - "isValid": true, - "new": true + "isAutoGenerated": false, + "deleted": false, + "policies": [], + "userPermissions": [] }, "messages": [], + "userSetOnLoad": false, "confirmBeforeExecute": false, + "policies": [], "userPermissions": [] }, - "new": false + "id": "Page1_JSObject1.myFun2", + "deleted": false, + "gitSyncId": "61c580d685c0bd4ccf7d1716_61c58ced85c0bd4ccf7d1724" } ], "actionCollectionList": [ { - "id": "Page1_JSObject1", - "userPermissions": [ - "read:actions", - "execute:actions", - "manage:actions" - ], - "gitSyncId": "61c580d685c0bd4ccf7d1716_61c58ced85c0bd4ccf7d1726", "unpublishedCollection": { "name": "JSObject1", "pageId": "Page1", "pluginId": "js-plugin", "pluginType": "JS", - "actionIds": [], - "archivedActionIds": [], "actions": [], "archivedActions": [], "body": "export default {\n\tmyVar1: \"Submit\",\n\tmyVar2: {},\n\tmyFun1: () => {\n\t\t//write code here\n\t\treturn this.myVar1;\n\t},\n\tmyFun2: () => {\n\t\t//write code here\n\t}\n}", "variables": [ { "name": "myVar1", - "value": "Submit" + "value": "\"Submit\"" }, { "name": "myVar2", - "value": {} + "value": "{}" } - ] + ], + "userPermissions": [] }, - "new": false + "id": "Page1_JSObject1", + "deleted": false, + "gitSyncId": "61c580d685c0bd4ccf7d1716_61c58ced85c0bd4ccf7d1726" } ], + "updatedResources": { + "actionList": [ + "JSObject1.myFun2##ENTITY_SEPARATOR##Page1", + "mockApi##ENTITY_SEPARATOR##Page1", + "JSObject1.myFun1##ENTITY_SEPARATOR##Page1", + "get_schema##ENTITY_SEPARATOR##Page1" + ], + "pageList": [ + "Page1" + ], + "actionCollectionList": [ + "JSObject1##ENTITY_SEPARATOR##Page1" + ] + }, "editModeTheme": { "name": "Classic", - "new": true, - "isSystemTheme": true + "displayName": "Classic", + "isSystemTheme": true, + "deleted": false }, "publishedTheme": { "name": "Classic", - "new": true, - "isSystemTheme": true - }, - "publishedLayoutmongoEscapedWidgets": {}, - "unpublishedLayoutmongoEscapedWidgets": {} + "displayName": "Classic", + "isSystemTheme": true, + "deleted": false + } } \ No newline at end of file diff --git a/app/client/cypress/fixtures/formResetDsl.json b/app/client/cypress/fixtures/formResetDsl.json index 0e12eb600b33..373c79a90e04 100644 --- a/app/client/cypress/fixtures/formResetDsl.json +++ b/app/client/cypress/fixtures/formResetDsl.json @@ -2,225 +2,247 @@ "dsl": { "widgetName": "MainContainer", "backgroundColor": "none", - "rightColumn": 656, + "rightColumn": 4896, "snapColumns": 64, "detachFromLayout": true, "widgetId": "0", "topRow": 0, - "bottomRow": 5120, + "bottomRow": 1060, "containerStyle": "none", - "snapRows": 128, + "snapRows": 125, "parentRowSpace": 1, "type": "CANVAS_WIDGET", "canExtend": true, - "version": 52, - "minHeight": 640, + "version": 67, + "minHeight": 1292, + "dynamicTriggerPathList": [], "parentColumnSpace": 1, "dynamicBindingPathList": [], "leftColumn": 0, "children": [ { - "widgetName": "Form1", - "backgroundColor": "white", - "rightColumn": 64, - "widgetId": "ozm6zwjk4b", - "topRow": 0, - "bottomRow": 56, - "parentRowSpace": 40, "isVisible": true, - "type": "FORM_WIDGET", - "version": 1, - "parentId": "0", - "blueprint": { - "view": [ - { - "type": "CANVAS_WIDGET", - "position": { "top": 0, "left": 0 }, - "props": { - "containerStyle": "none", - "canExtend": false, - "detachFromLayout": true, - "children": [], - "blueprint": { - "view": [ - { - "type": "TEXT_WIDGET", - "size": { "rows": 1, "cols": 12 }, - "position": { "top": 0, "left": 0 }, - "props": { - "text": "Form", - "textStyle": "HEADING" - } - }, - { - "type": "FORM_BUTTON_WIDGET", - "size": { "rows": 1, "cols": 4 }, - "position": { "top": 11, "left": 12 }, - "props": { - "text": "Submit", - "buttonStyle": "PRIMARY_BUTTON", - "disabledWhenInvalid": true, - "resetFormOnClick": true - } - }, - { - "type": "FORM_BUTTON_WIDGET", - "size": { "rows": 1, "cols": 4 }, - "position": { "top": 11, "left": 8 }, - "props": { - "text": "Reset", - "buttonStyle": "SECONDARY_BUTTON", - "disabledWhenInvalid": false, - "resetFormOnClick": true - } - } - ] - } - } - } - ] - }, - "isLoading": false, - "parentColumnSpace": 74, - "leftColumn": 0, - "dynamicBindingPathList": [], + "borderColor": "#E0DEDE", + "borderWidth": "1", + "animateLoading": true, + "widgetName": "Form1", + "backgroundColor": "#FFFFFF", "children": [ { + "isVisible": true, "widgetName": "Canvas1", - "rightColumn": 2072, + "version": 1, "detachFromLayout": true, - "widgetId": "qrqizehc5b", + "type": "CANVAS_WIDGET", + "hideCard": true, + "isDeprecated": false, + "displayName": "Canvas", + "key": "lz0d5g90t3", "containerStyle": "none", - "topRow": 0, - "bottomRow": 540, - "parentRowSpace": 1, - "isVisible": true, "canExtend": false, - "type": "CANVAS_WIDGET", - "version": 1, - "parentId": "ozm6zwjk4b", - "blueprint": { - "view": [ - { - "type": "TEXT_WIDGET", - "size": { "rows": 1, "cols": 12 }, - "position": { "top": 0, "left": 0 }, - "props": { "text": "Form", "textStyle": "HEADING" } - }, - { - "type": "FORM_BUTTON_WIDGET", - "size": { "rows": 1, "cols": 4 }, - "position": { "top": 11, "left": 12 }, - "props": { - "text": "Submit", - "buttonStyle": "PRIMARY_BUTTON", - "disabledWhenInvalid": true, - "resetFormOnClick": true - } - }, - { - "type": "FORM_BUTTON_WIDGET", - "size": { "rows": 1, "cols": 4 }, - "position": { "top": 11, "left": 8 }, - "props": { - "text": "Reset", - "buttonStyle": "SECONDARY_BUTTON", - "disabledWhenInvalid": false, - "resetFormOnClick": true - } - } - ] - }, - "minHeight": 520, - "isLoading": false, - "parentColumnSpace": 1, - "leftColumn": 0, - "dynamicBindingPathList": [], "children": [ { - "widgetName": "Text1", - "rightColumn": 48, - "textAlign": "LEFT", - "widgetId": "c481ah2q0i", - "topRow": 0, - "bottomRow": 4, "isVisible": true, - "type": "TEXT_WIDGET", + "text": "Form", + "fontSize": "1.25rem", "fontStyle": "BOLD", - "version": 1, + "textAlign": "LEFT", "textColor": "#231F20", - "parentId": "qrqizehc5b", + "widgetName": "Text1", + "shouldTruncate": false, + "overflow": "NONE", + "version": 1, + "animateLoading": true, + "searchTags": [ + "typography", + "paragraph", + "label" + ], + "type": "TEXT_WIDGET", + "hideCard": false, + "isDeprecated": false, + "displayName": "Text", + "key": "i5p29w8v0a", + "iconSVG": "/static/media/icon.97c59b523e6f70ba6f40a10fc2c7c5b5.svg", + "widgetId": "4c54s08v23", + "renderMode": "CANVAS", + "truncateButtonColor": "{{appsmith.theme.colors.primaryColor}}", + "fontFamily": "{{appsmith.theme.fontFamily.appFont}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", "isLoading": false, - "leftColumn": 0, - "dynamicBindingPathList": [], - "fontSize": "HEADING1", - "text": "Form" + "leftColumn": 1.5, + "rightColumn": 25.5, + "topRow": 1, + "bottomRow": 5, + "parentId": "muk1vy75ao", + "dynamicBindingPathList": [ + { + "key": "truncateButtonColor" + }, + { + "key": "fontFamily" + }, + { + "key": "borderRadius" + } + ] }, { - "resetFormOnClick": true, - "widgetName": "FormButton1", - "rightColumn": 64, - "isDefaultClickDisabled": true, - "buttonColor": "#03B365", - "widgetId": "zsu1y41p1e", - "topRow": 48, - "bottomRow": 52, "isVisible": true, - "type": "FORM_BUTTON_WIDGET", - "version": 1, + "animateLoading": true, + "text": "Submit", + "buttonVariant": "PRIMARY", + "placement": "CENTER", + "widgetName": "Button1", + "isDisabled": false, + "isDefaultClickDisabled": true, + "disabledWhenInvalid": true, + "resetFormOnClick": true, "recaptchaType": "V3", - "parentId": "qrqizehc5b", + "version": 1, + "searchTags": [ + "click", + "submit" + ], + "type": "BUTTON_WIDGET", + "hideCard": false, + "isDeprecated": false, + "displayName": "Button", + "key": "j7tqbmn5hc", + "iconSVG": "/static/media/icon.cca026338f1c8eb6df8ba03d084c2fca.svg", + "widgetId": "ig1u4ut3pd", + "renderMode": "CANVAS", + "buttonColor": "{{appsmith.theme.colors.primaryColor}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "boxShadow": "none", "isLoading": false, - "disabledWhenInvalid": true, - "leftColumn": 48, - "dynamicBindingPathList": [], - "buttonVariant": "PRIMARY", - "text": "Submit" + "leftColumn": 40, + "rightColumn": 51, + "topRow": 62, + "bottomRow": 66, + "parentId": "muk1vy75ao", + "dynamicBindingPathList": [ + { + "key": "buttonColor" + }, + { + "key": "borderRadius" + } + ] }, { - "resetFormOnClick": true, - "widgetName": "FormButton2", - "rightColumn": 48, - "isDefaultClickDisabled": true, - "buttonColor": "#03B365", - "widgetId": "7o0r2rp3s1", - "topRow": 48, - "bottomRow": 52, "isVisible": true, - "type": "FORM_BUTTON_WIDGET", - "version": 1, + "animateLoading": true, + "text": "Reset", + "buttonVariant": "SECONDARY", + "placement": "CENTER", + "widgetName": "Button2", + "isDisabled": false, + "isDefaultClickDisabled": true, + "disabledWhenInvalid": false, + "resetFormOnClick": true, "recaptchaType": "V3", - "parentId": "qrqizehc5b", + "version": 1, + "searchTags": [ + "click", + "submit" + ], + "type": "BUTTON_WIDGET", + "hideCard": false, + "isDeprecated": false, + "displayName": "Button", + "key": "j7tqbmn5hc", + "iconSVG": "/static/media/icon.cca026338f1c8eb6df8ba03d084c2fca.svg", + "widgetId": "2atavwomlu", + "renderMode": "CANVAS", + "buttonColor": "{{appsmith.theme.colors.primaryColor}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "boxShadow": "none", "isLoading": false, - "disabledWhenInvalid": false, - "leftColumn": 32, - "dynamicBindingPathList": [], - "buttonVariant": "PRIMARY", - "text": "Reset" + "leftColumn": 29, + "rightColumn": 38, + "topRow": 62, + "bottomRow": 66, + "parentId": "muk1vy75ao", + "dynamicBindingPathList": [ + { + "key": "buttonColor" + }, + { + "key": "borderRadius" + } + ] }, { - "widgetName": "Table1", - "columnOrder": [ - "id", - "email", - "userName", - "productName", - "orderAmount" + "isVisible": true, + "animateLoading": true, + "defaultSelectedRowIndex": 0, + "defaultSelectedRowIndices": [ + 0 ], - "isVisibleDownload": true, - "topRow": 4, - "bottomRow": 25, - "parentRowSpace": 10, - "isSortable": true, - "type": "TABLE_WIDGET", - "parentColumnSpace": 71.5, - "dynamicTriggerPathList": [], - "leftColumn": 0, + "label": "Data", + "widgetName": "Table1", + "searchKey": "", + "textSize": "0.875rem", + "horizontalAlignment": "LEFT", + "verticalAlignment": "CENTER", + "totalRecordsCount": 0, + "defaultPageSize": 0, + "dynamicPropertyPathList": [], + "borderColor": "#E0DEDE", + "borderWidth": "1", "dynamicBindingPathList": [ - { "key": "tableData" }, - { "key": "primaryColumns.id.computedValue" }, - { "key": "primaryColumns.email.computedValue" }, - { "key": "primaryColumns.userName.computedValue" }, + { + "key": "accentColor" + }, + { + "key": "borderRadius" + }, + { + "key": "boxShadow" + }, + { + "key": "childStylesheet.button.buttonColor" + }, + { + "key": "childStylesheet.button.borderRadius" + }, + { + "key": "childStylesheet.menuButton.menuColor" + }, + { + "key": "childStylesheet.menuButton.borderRadius" + }, + { + "key": "childStylesheet.iconButton.buttonColor" + }, + { + "key": "childStylesheet.iconButton.borderRadius" + }, + { + "key": "childStylesheet.editActions.saveButtonColor" + }, + { + "key": "childStylesheet.editActions.saveBorderRadius" + }, + { + "key": "childStylesheet.editActions.discardButtonColor" + }, + { + "key": "childStylesheet.editActions.discardBorderRadius" + }, + { + "key": "tableData" + }, + { + "key": "primaryColumns.id.computedValue" + }, + { + "key": "primaryColumns.email.computedValue" + }, + { + "key": "primaryColumns.userName.computedValue" + }, { "key": "primaryColumns.productName.computedValue" }, @@ -230,255 +252,473 @@ ], "primaryColumns": { "id": { + "allowCellWrapping": false, "index": 0, "width": 150, + "originalId": "id", "id": "id", + "alias": "id", "horizontalAlignment": "LEFT", "verticalAlignment": "CENTER", - "columnType": "text", - "textSize": "PARAGRAPH", - "fontStyle": "REGULAR", + "columnType": "number", + "textSize": "0.875rem", "enableFilter": true, "enableSort": true, "isVisible": true, "isDisabled": false, + "isCellEditable": false, + "isEditable": false, "isCellVisible": true, "isDerived": false, "label": "id", - "computedValue": "{{Table1.sanitizedTableData.map((currentRow) => ( currentRow.id))}}" + "isSaveVisible": true, + "isDiscardVisible": true, + "computedValue": "{{Table1.processedTableData.map((currentRow, currentIndex) => ( currentRow[\"id\"]))}}", + "validation": {} }, "email": { + "allowCellWrapping": false, "index": 1, "width": 150, + "originalId": "email", "id": "email", + "alias": "email", "horizontalAlignment": "LEFT", "verticalAlignment": "CENTER", "columnType": "text", - "textSize": "PARAGRAPH", - "fontStyle": "REGULAR", + "textSize": "0.875rem", "enableFilter": true, "enableSort": true, "isVisible": true, "isDisabled": false, + "isCellEditable": false, + "isEditable": false, "isCellVisible": true, "isDerived": false, "label": "email", - "computedValue": "{{Table1.sanitizedTableData.map((currentRow) => ( currentRow.email))}}" + "isSaveVisible": true, + "isDiscardVisible": true, + "computedValue": "{{Table1.processedTableData.map((currentRow, currentIndex) => ( currentRow[\"email\"]))}}", + "validation": {} }, "userName": { + "allowCellWrapping": false, "index": 2, "width": 150, + "originalId": "userName", "id": "userName", + "alias": "userName", "horizontalAlignment": "LEFT", "verticalAlignment": "CENTER", "columnType": "text", - "textSize": "PARAGRAPH", - "fontStyle": "REGULAR", + "textSize": "0.875rem", "enableFilter": true, "enableSort": true, "isVisible": true, "isDisabled": false, + "isCellEditable": false, + "isEditable": false, "isCellVisible": true, "isDerived": false, "label": "userName", - "computedValue": "{{Table1.sanitizedTableData.map((currentRow) => ( currentRow.userName))}}" + "isSaveVisible": true, + "isDiscardVisible": true, + "computedValue": "{{Table1.processedTableData.map((currentRow, currentIndex) => ( currentRow[\"userName\"]))}}", + "validation": {} }, "productName": { + "allowCellWrapping": false, "index": 3, "width": 150, + "originalId": "productName", "id": "productName", + "alias": "productName", "horizontalAlignment": "LEFT", "verticalAlignment": "CENTER", "columnType": "text", - "textSize": "PARAGRAPH", - "fontStyle": "REGULAR", + "textSize": "0.875rem", "enableFilter": true, "enableSort": true, "isVisible": true, "isDisabled": false, + "isCellEditable": false, + "isEditable": false, "isCellVisible": true, "isDerived": false, "label": "productName", - "computedValue": "{{Table1.sanitizedTableData.map((currentRow) => ( currentRow.productName))}}" + "isSaveVisible": true, + "isDiscardVisible": true, + "computedValue": "{{Table1.processedTableData.map((currentRow, currentIndex) => ( currentRow[\"productName\"]))}}", + "validation": {} }, "orderAmount": { + "allowCellWrapping": false, "index": 4, "width": 150, + "originalId": "orderAmount", "id": "orderAmount", + "alias": "orderAmount", "horizontalAlignment": "LEFT", "verticalAlignment": "CENTER", - "columnType": "text", - "textSize": "PARAGRAPH", - "fontStyle": "REGULAR", + "columnType": "number", + "textSize": "0.875rem", "enableFilter": true, "enableSort": true, "isVisible": true, "isDisabled": false, + "isCellEditable": false, + "isEditable": false, "isCellVisible": true, "isDerived": false, "label": "orderAmount", - "computedValue": "{{Table1.sanitizedTableData.map((currentRow) => ( currentRow.orderAmount))}}" + "isSaveVisible": true, + "isDiscardVisible": true, + "computedValue": "{{Table1.processedTableData.map((currentRow, currentIndex) => ( currentRow[\"orderAmount\"]))}}", + "validation": {} } }, - "delimiter": ",", - "migrated": true, - "derivedColumns": {}, - "rightColumn": 64, - "textSize": "PARAGRAPH", - "widgetId": "xptqefixji", - "isVisibleFilters": true, "tableData": "{{[\n {\n \"id\": 2381224,\n \"email\": \"michael.lawson@reqres.in\",\n \"userName\": \"Michael Lawson\",\n \"productName\": \"Chicken Sandwich\",\n \"orderAmount\": 4.99\n },\n {\n \"id\": 2736212,\n \"email\": \"lindsay.ferguson@reqres.in\",\n \"userName\": \"Lindsay Ferguson\",\n \"productName\": \"Tuna Salad\",\n \"orderAmount\": 9.99\n },\n {\n \"id\": 6788734,\n \"email\": \"tobias.funke@reqres.in\",\n \"userName\": \"Tobias Funke\",\n \"productName\": \"Beef steak\",\n \"orderAmount\": 19.99\n }\n]}}", - "isVisible": true, - "label": "Data", - "searchKey": "", - "fontStyle": "REGULAR", - "version": 3, - "parentId": "qrqizehc5b", - "isLoading": false, - "horizontalAlignment": "LEFT", + "columnWidthMap": { + "task": 245, + "step": 62, + "status": 75 + }, + "columnOrder": [ + "id", + "email", + "userName", + "productName", + "orderAmount" + ], + "enableClientSideSearch": true, "isVisibleSearch": true, + "isVisibleFilters": true, + "isVisibleDownload": true, "isVisiblePagination": true, - "verticalAlignment": "CENTER" - }, - { - "isRequired": true, - "widgetName": "Input1", - "rightColumn": 20, - "widgetId": "r3xvjtuhad", - "topRow": 30, - "bottomRow": 34, - "parentRowSpace": 40, - "isVisible": true, - "label": "", - "type": "INPUT_WIDGET_V2", + "isSortable": true, + "delimiter": ",", "version": 1, - "parentId": "qrqizehc5b", + "inlineEditingSaveOption": "ROW_LEVEL", + "type": "TABLE_WIDGET_V2", + "hideCard": false, + "isDeprecated": false, + "displayName": "Table", + "key": "0up8a0wm4i", + "iconSVG": "/static/media/icon.db8a9cbd2acd22a31ea91cc37ea2a46c.svg", + "widgetId": "ydkc7cm6w4", + "renderMode": "CANVAS", + "accentColor": "{{appsmith.theme.colors.primaryColor}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}", + "childStylesheet": { + "button": { + "buttonColor": "{{appsmith.theme.colors.primaryColor}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "boxShadow": "none" + }, + "menuButton": { + "menuColor": "{{appsmith.theme.colors.primaryColor}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "boxShadow": "none" + }, + "iconButton": { + "buttonColor": "{{appsmith.theme.colors.primaryColor}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "boxShadow": "none" + }, + "editActions": { + "saveButtonColor": "{{appsmith.theme.colors.primaryColor}}", + "saveBorderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "discardButtonColor": "{{appsmith.theme.colors.primaryColor}}", + "discardBorderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}" + } + }, "isLoading": false, - "parentColumnSpace": 71.5, - "dynamicTriggerPathList": [], - "leftColumn": 0, - "dynamicBindingPathList": [{ "key": "defaultText" }], - "resetOnSubmit": true, - "inputType": "TEXT", - "defaultText": "{{Table1.selectedRow.email}}" + "parentColumnSpace": 7.4755859375, + "parentRowSpace": 10, + "leftColumn": 4, + "rightColumn": 55, + "topRow": 6, + "bottomRow": 34, + "parentId": "muk1vy75ao", + "dynamicTriggerPathList": [] }, { - "widgetName": "Text2", - "rightColumn": 16, - "textAlign": "LEFT", - "widgetId": "672gf8vm2q", - "topRow": 26, - "bottomRow": 30, - "parentRowSpace": 40, "isVisible": true, - "type": "TEXT_WIDGET", - "fontStyle": "BOLD", - "version": 1, - "textColor": "#231F20", - "parentId": "qrqizehc5b", + "label": "Label", + "labelPosition": "Top", + "labelAlignment": "left", + "labelTextSize": "0.875rem", + "labelWidth": 5, + "widgetName": "Input1", + "version": 2, + "defaultText": "{{Table1.selectedRow.email}}\n", + "iconAlign": "left", + "autoFocus": false, + "labelStyle": "", + "resetOnSubmit": true, + "isRequired": false, + "isDisabled": false, + "animateLoading": true, + "inputType": "TEXT", + "searchTags": [ + "form", + "text input", + "number", + "textarea" + ], + "type": "INPUT_WIDGET_V2", + "hideCard": false, + "isDeprecated": false, + "displayName": "Input", + "key": "pm3gwntdcm", + "iconSVG": "/static/media/icon.9f505595da61a34f563dba82adeb06ec.svg", + "widgetId": "bp031y8spl", + "renderMode": "CANVAS", + "accentColor": "{{appsmith.theme.colors.primaryColor}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "boxShadow": "none", "isLoading": false, - "parentColumnSpace": 71.5, - "leftColumn": 0, - "dynamicBindingPathList": [], - "fontSize": "PARAGRAPH", - "text": "Email" + "parentColumnSpace": 9.18896484375, + "parentRowSpace": 10, + "leftColumn": 6, + "rightColumn": 26, + "topRow": 38, + "bottomRow": 45, + "parentId": "muk1vy75ao", + "dynamicBindingPathList": [ + { + "key": "accentColor" + }, + { + "key": "borderRadius" + }, + { + "key": "defaultText" + } + ], + "dynamicTriggerPathList": [] }, { - "isRequired": true, - "rightColumn": 64, - "widgetName": "Dropdown1", - "widgetId": "dwh49bulj9", - "topRow": 36, - "bottomRow": 40, - "parentRowSpace": 40, "isVisible": true, - "label": "", - "type": "MULTI_SELECT_WIDGET_V2", + "label": "Label", + "labelPosition": "Top", + "labelAlignment": "left", + "labelTextSize": "0.875rem", + "labelWidth": 5, + "widgetName": "CurrencyInput1", "version": 1, - "parentId": "qrqizehc5b", + "defaultText": "{{Table1.selectedRow.orderAmount}}", + "iconAlign": "left", + "autoFocus": false, + "labelStyle": "", + "resetOnSubmit": true, + "isRequired": false, + "isDisabled": false, + "animateLoading": true, + "allowCurrencyChange": false, + "defaultCurrencyCode": "USD", + "decimals": 0, + "searchTags": [ + "amount", + "total" + ], + "type": "CURRENCY_INPUT_WIDGET", + "hideCard": false, + "isDeprecated": false, + "displayName": "Currency Input", + "key": "x8yxtaq8v3", + "iconSVG": "/static/media/icon.f312efcb48ce4dafb08c20291635b30b.svg", + "widgetId": "s9twabojbt", + "renderMode": "CANVAS", + "accentColor": "{{appsmith.theme.colors.primaryColor}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "boxShadow": "none", "isLoading": false, - "defaultOptionValue": "", - "selectionType": "MULTI_SELECT", - "dynamicTriggerPathList": [], - "parentColumnSpace": 71.5, - "dynamicBindingPathList": [], - "leftColumn": 24, - "options": "[\n {\n \"label\": \"Option 1\",\n \"value\": \"1\"\n },\n {\n \"label\": \"Option 2\",\n \"value\": \"2\"\n },\n {\n \"label\": \"Option 3\",\n \"value\": \"3\"\n },\n {\n \"label\": \"Option 4\",\n \"value\": \"4\"\n },\n {\n \"label\": \"Option 5\",\n \"value\": \"5\"\n }\n]" + "parentColumnSpace": 9.18896484375, + "parentRowSpace": 10, + "leftColumn": 6, + "rightColumn": 26, + "topRow": 50, + "bottomRow": 57, + "parentId": "muk1vy75ao", + "dynamicBindingPathList": [ + { + "key": "accentColor" + }, + { + "key": "borderRadius" + }, + { + "key": "defaultText" + } + ], + "dynamicTriggerPathList": [] }, { + "isVisible": true, + "label": "Label", + "labelPosition": "Top", + "labelAlignment": "left", + "labelTextSize": "0.875rem", + "labelWidth": 5, "widgetName": "PhoneInput1", - "dialCode": "+1", - "displayName": "Phone Input", - "iconSVG": "/static/media/icon.ec4f5c23.svg", - "topRow": 30, - "bottomRow": 34, - "parentRowSpace": 10, + "version": 1, + "defaultText": "{{Table1.selectedRow.id}}\n", + "iconAlign": "left", "autoFocus": false, - "type": "PHONE_INPUT_WIDGET", - "hideCard": false, - "animateLoading": true, - "parentColumnSpace": 9.75, - "dynamicTriggerPathList": [], - "resetOnSubmit": true, - "leftColumn": 24, - "dynamicBindingPathList": [{ "key": "defaultText" }], - "countryCode": "US", "labelStyle": "", + "resetOnSubmit": true, + "isRequired": false, "isDisabled": false, - "key": "2en0bgew0x", - "isRequired": true, - "rightColumn": 44, - "widgetId": "kqxx5c8dug", + "animateLoading": true, + "defaultDialCode": "+1", "allowDialCodeChange": false, - "isVisible": true, - "label": "", - "version": 1, - "parentId": "qrqizehc5b", "allowFormatting": true, + "searchTags": [ + "call" + ], + "type": "PHONE_INPUT_WIDGET", + "hideCard": false, + "isDeprecated": false, + "displayName": "Phone Input", + "key": "kjhejqspa9", + "iconSVG": "/static/media/icon.108789d7165de30306435ab3c24e6cad.svg", + "widgetId": "pjvwkdpty6", "renderMode": "CANVAS", + "accentColor": "{{appsmith.theme.colors.primaryColor}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "boxShadow": "none", "isLoading": false, - "iconAlign": "left", - "defaultText": "{{Table1.selectedRow.id}}" + "parentColumnSpace": 9.18896484375, + "parentRowSpace": 10, + "leftColumn": 33, + "rightColumn": 53, + "topRow": 39, + "bottomRow": 45, + "parentId": "muk1vy75ao", + "dynamicBindingPathList": [ + { + "key": "accentColor" + }, + { + "key": "borderRadius" + }, + { + "key": "defaultText" + } + ], + "dynamicTriggerPathList": [] }, { - "widgetName": "CurrencyInput1", - "displayName": "Currency Input", - "iconSVG": "/static/media/icon.01a1e03d.svg", - "topRow": 36, - "bottomRow": 40, - "parentRowSpace": 10, - "autoFocus": false, - "type": "CURRENCY_INPUT_WIDGET", - "hideCard": false, - "animateLoading": true, - "parentColumnSpace": 9.75, - "dynamicTriggerPathList": [], - "resetOnSubmit": true, - "leftColumn": 0, - "dynamicBindingPathList": [{ "key": "defaultText" }], - "labelStyle": "", - "isDisabled": false, - "key": "8ked77j728", - "isRequired": true, - "rightColumn": 20, - "widgetId": "067zok1ef3", "isVisible": true, - "label": "", - "allowCurrencyChange": false, + "animateLoading": true, + "labelText": "Label", + "labelPosition": "Top", + "labelAlignment": "left", + "labelWidth": 5, + "labelTextSize": "0.875rem", + "options": "[\n {\n \"label\": \"Option 1\",\n \"value\": \"1\"\n },\n {\n \"label\": \"Option 2\",\n \"value\": \"2\"\n },\n {\n \"label\": \"Option 3\",\n \"value\": \"3\"\n },\n {\n \"label\": \"Option 4\",\n \"value\": \"4\"\n },\n {\n \"label\": \"Option 5\",\n \"value\": \"5\"\n }\n]", + "widgetName": "MultiSelect1", + "isFilterable": true, + "serverSideFiltering": false, + "defaultOptionValue": "{{ ((options, serverSideFiltering) => ( `[\n \n]`))(MultiSelect1.options, MultiSelect1.serverSideFiltering) }}", "version": 1, - "parentId": "qrqizehc5b", + "isRequired": false, + "isDisabled": false, + "placeholderText": "Select option(s)", + "searchTags": [ + "dropdown", + "tags" + ], + "type": "MULTI_SELECT_WIDGET_V2", + "hideCard": false, + "isDeprecated": false, + "displayName": "MultiSelect", + "key": "y1jg4jsxok", + "iconSVG": "/static/media/icon.a3495809ae48291a64404f3bb04b0e69.svg", + "widgetId": "0vdcx3nycj", "renderMode": "CANVAS", + "accentColor": "{{appsmith.theme.colors.primaryColor}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "boxShadow": "none", "isLoading": false, - "decimals": 2, - "iconAlign": "left", - "defaultText": "{{Table1.selectedRow.orderAmount}}", - "currencyCode": "USD" + "parentColumnSpace": 9.18896484375, + "parentRowSpace": 10, + "leftColumn": 33, + "rightColumn": 53, + "topRow": 47, + "bottomRow": 54, + "parentId": "muk1vy75ao", + "dynamicBindingPathList": [ + { + "key": "accentColor" + }, + { + "key": "borderRadius" + }, + { + "key": "defaultOptionValue" + } + ], + "dynamicTriggerPathList": [] + } + ], + "minHeight": 400, + "widgetId": "muk1vy75ao", + "renderMode": "CANVAS", + "boxShadow": "none", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "accentColor": "{{appsmith.theme.colors.primaryColor}}", + "isLoading": false, + "parentColumnSpace": 1, + "parentRowSpace": 1, + "leftColumn": 0, + "rightColumn": 239.25, + "topRow": 0, + "bottomRow": 680, + "parentId": "v6qsaaxfcm", + "dynamicBindingPathList": [ + { + "key": "borderRadius" + }, + { + "key": "accentColor" } ] } + ], + "searchTags": [ + "group" + ], + "type": "FORM_WIDGET", + "hideCard": false, + "isDeprecated": false, + "displayName": "Form", + "key": "c1wmb3xdrf", + "iconSVG": "/static/media/icon.ea3e08d130e59c56867ae40114c10eed.svg", + "isCanvas": true, + "widgetId": "v6qsaaxfcm", + "renderMode": "CANVAS", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}", + "isLoading": false, + "parentColumnSpace": 9.96875, + "parentRowSpace": 10, + "leftColumn": 3, + "rightColumn": 64, + "topRow": 27, + "bottomRow": 98, + "parentId": "0", + "dynamicBindingPathList": [ + { + "key": "borderRadius" + }, + { + "key": "boxShadow" + } ] } ] } } - diff --git a/app/client/cypress/fixtures/invisibleWidgetdsl.json b/app/client/cypress/fixtures/invisibleWidgetdsl.json new file mode 100644 index 000000000000..402d33c21ec4 --- /dev/null +++ b/app/client/cypress/fixtures/invisibleWidgetdsl.json @@ -0,0 +1,287 @@ +{ + "dsl": { + "widgetName": "MainContainer", + "backgroundColor": "none", + "rightColumn": 4896, + "snapColumns": 64, + "detachFromLayout": true, + "widgetId": "0", + "topRow": 0, + "bottomRow": 570, + "containerStyle": "none", + "snapRows": 125, + "parentRowSpace": 1, + "type": "CANVAS_WIDGET", + "canExtend": true, + "version": 65, + "minHeight": 1292, + "dynamicTriggerPathList": [], + "parentColumnSpace": 1, + "dynamicBindingPathList": [], + "leftColumn": 0, + "children": [ + { + "boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}", + "widgetName": "Container1", + "borderColor": "#E0DEDE", + "isCanvas": true, + "displayName": "Container", + "iconSVG": "/static/media/icon.1977dca3370505e2db3a8e44cfd54907.svg", + "searchTags": [ + "div", + "parent", + "group" + ], + "topRow": 16, + "bottomRow": 40, + "parentRowSpace": 10, + "type": "CONTAINER_WIDGET", + "hideCard": false, + "shouldScrollContents": true, + "animateLoading": true, + "parentColumnSpace": 9.96875, + "dynamicTriggerPathList": [], + "leftColumn": 8, + "dynamicBindingPathList": [ + { + "key": "borderRadius" + }, + { + "key": "boxShadow" + } + ], + "children": [ + { + "boxShadow": "none", + "widgetName": "Canvas1", + "displayName": "Canvas", + "topRow": 0, + "bottomRow": 240, + "parentRowSpace": 1, + "type": "CANVAS_WIDGET", + "canExtend": false, + "hideCard": true, + "minHeight": 240, + "parentColumnSpace": 1, + "leftColumn": 0, + "dynamicBindingPathList": [ + { + "key": "borderRadius" + }, + { + "key": "accentColor" + } + ], + "children": [ + { + "boxShadow": "none", + "widgetName": "Input1", + "dynamicPropertyPathList": [ + { + "key": "isVisible" + } + ], + "displayName": "Input", + "iconSVG": "/static/media/icon.9f505595da61a34f563dba82adeb06ec.svg", + "searchTags": [ + "form", + "text input", + "number", + "textarea" + ], + "topRow": 5, + "bottomRow": 12, + "parentRowSpace": 10, + "labelWidth": 5, + "autoFocus": false, + "type": "INPUT_WIDGET_V2", + "hideCard": false, + "animateLoading": true, + "parentColumnSpace": 3.42578125, + "dynamicTriggerPathList": [], + "resetOnSubmit": true, + "leftColumn": 18, + "dynamicBindingPathList": [ + { + "key": "accentColor" + }, + { + "key": "borderRadius" + }, + { + "key": "isVisible" + } + ], + "labelPosition": "Top", + "labelStyle": "", + "inputType": "TEXT", + "isDisabled": false, + "key": "xeqahmz2n0", + "labelTextSize": "0.875rem", + "isRequired": false, + "isDeprecated": false, + "rightColumn": 38, + "dynamicHeight": "FIXED", + "widgetId": "8bfucv7r1x", + "accentColor": "{{appsmith.theme.colors.primaryColor}}", + "isVisible": "{{!Checkbox1.isChecked}}", + "label": "Off", + "version": 2, + "parentId": "7k7dojtq6h", + "labelAlignment": "left", + "renderMode": "CANVAS", + "isLoading": false, + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "maxDynamicHeight": 9000, + "iconAlign": "left", + "defaultText": "", + "minDynamicHeight": 4 + }, + { + "boxShadow": "none", + "widgetName": "Input2", + "dynamicPropertyPathList": [ + { + "key": "isVisible" + } + ], + "displayName": "Input", + "iconSVG": "/static/media/icon.9f505595da61a34f563dba82adeb06ec.svg", + "searchTags": [ + "form", + "text input", + "number", + "textarea" + ], + "topRow": 15, + "bottomRow": 22, + "parentRowSpace": 10, + "labelWidth": 5, + "autoFocus": false, + "type": "INPUT_WIDGET_V2", + "hideCard": false, + "animateLoading": true, + "parentColumnSpace": 6.38525390625, + "dynamicTriggerPathList": [], + "resetOnSubmit": true, + "leftColumn": 16, + "dynamicBindingPathList": [ + { + "key": "accentColor" + }, + { + "key": "borderRadius" + }, + { + "key": "isVisible" + } + ], + "labelPosition": "Top", + "labelStyle": "", + "inputType": "TEXT", + "isDisabled": false, + "key": "xeqahmz2n0", + "labelTextSize": "0.875rem", + "isRequired": false, + "isDeprecated": false, + "rightColumn": 36, + "dynamicHeight": "FIXED", + "widgetId": "xiw6br6yni", + "accentColor": "{{appsmith.theme.colors.primaryColor}}", + "isVisible": "{{Checkbox1.isChecked}}", + "label": "On", + "version": 2, + "parentId": "7k7dojtq6h", + "labelAlignment": "left", + "renderMode": "CANVAS", + "isLoading": false, + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "maxDynamicHeight": 9000, + "iconAlign": "left", + "defaultText": "", + "minDynamicHeight": 4 + } + ], + "key": "j130wuw4eb", + "isDeprecated": false, + "rightColumn": 239.25, + "detachFromLayout": true, + "widgetId": "7k7dojtq6h", + "accentColor": "{{appsmith.theme.colors.primaryColor}}", + "containerStyle": "none", + "isVisible": true, + "version": 1, + "parentId": "9ti9l1m9zc", + "renderMode": "CANVAS", + "isLoading": false, + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}" + } + ], + "borderWidth": "1", + "key": "0hl3710evc", + "backgroundColor": "#FFFFFF", + "isDeprecated": false, + "rightColumn": 51, + "dynamicHeight": "AUTO_HEIGHT", + "widgetId": "9ti9l1m9zc", + "containerStyle": "card", + "isVisible": true, + "version": 1, + "parentId": "0", + "renderMode": "CANVAS", + "isLoading": false, + "originalTopRow": 16, + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "maxDynamicHeight": 9000, + "originalBottomRow": 59, + "minDynamicHeight": 4 + }, + { + "widgetName": "Checkbox1", + "displayName": "Checkbox", + "iconSVG": "/static/media/icon.aaab032b43383e4fa53ffc0ef40c90ef.svg", + "searchTags": [ + "boolean" + ], + "topRow": 45, + "bottomRow": 49, + "parentRowSpace": 10, + "type": "CHECKBOX_WIDGET", + "alignWidget": "LEFT", + "hideCard": false, + "animateLoading": true, + "parentColumnSpace": 9.96875, + "leftColumn": 22, + "dynamicBindingPathList": [ + { + "key": "accentColor" + }, + { + "key": "borderRadius" + } + ], + "labelPosition": "Left", + "isDisabled": false, + "key": "u8vvhzphvk", + "isRequired": false, + "isDeprecated": false, + "rightColumn": 34, + "dynamicHeight": "FIXED", + "widgetId": "vdfj8it9cv", + "accentColor": "{{appsmith.theme.colors.primaryColor}}", + "isVisible": true, + "label": "Label", + "version": 1, + "parentId": "0", + "renderMode": "CANVAS", + "isLoading": false, + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "defaultCheckedState": true, + "maxDynamicHeight": 9000, + "minDynamicHeight": 4, + "originalTopRow": 45, + "originalBottomRow": 49 + } + ] + } +} \ No newline at end of file diff --git a/app/client/cypress/fixtures/jsonFormDynamicHeightDsl.json b/app/client/cypress/fixtures/jsonFormDynamicHeightDsl.json new file mode 100644 index 000000000000..f91dbb6a1725 --- /dev/null +++ b/app/client/cypress/fixtures/jsonFormDynamicHeightDsl.json @@ -0,0 +1,776 @@ +{ + "dsl": { + "widgetName": "MainContainer", + "backgroundColor": "none", + "rightColumn": 4896, + "snapColumns": 64, + "detachFromLayout": true, + "widgetId": "0", + "topRow": 0, + "bottomRow": 1292, + "containerStyle": "none", + "snapRows": 125, + "parentRowSpace": 1, + "type": "CANVAS_WIDGET", + "canExtend": true, + "version": 65, + "minHeight": 1292, + "dynamicTriggerPathList": [], + "parentColumnSpace": 1, + "dynamicBindingPathList": [], + "leftColumn": 0, + "children": [ + { + "boxShadow": "none", + "widgetName": "Modal1", + "isCanvas": true, + "displayName": "Modal", + "iconSVG": "/static/media/icon.4975978e9a961fb0bfb4e38de7ecc7c5.svg", + "searchTags": [ + "dialog", + "popup", + "notification" + ], + "topRow": 0, + "bottomRow": 0, + "parentRowSpace": 1, + "type": "MODAL_WIDGET", + "hideCard": false, + "shouldScrollContents": true, + "animateLoading": true, + "parentColumnSpace": 1, + "dynamicTriggerPathList": [], + "leftColumn": 0, + "dynamicBindingPathList": [ + { + "key": "borderRadius" + } + ], + "children": [ + { + "boxShadow": "none", + "widgetName": "Canvas2", + "displayName": "Canvas", + "topRow": 0, + "bottomRow": 240, + "parentRowSpace": 1, + "type": "CANVAS_WIDGET", + "canExtend": true, + "hideCard": true, + "shouldScrollContents": false, + "minHeight": 240, + "parentColumnSpace": 1, + "leftColumn": 0, + "dynamicBindingPathList": [ + { + "key": "borderRadius" + }, + { + "key": "accentColor" + } + ], + "children": [ + { + "boxShadow": "none", + "widgetName": "IconButton1", + "onClick": "{{closeModal('Modal1')}}", + "buttonColor": "{{appsmith.theme.colors.primaryColor}}", + "displayName": "Icon Button", + "iconSVG": "/static/media/icon.1a0c634ac75f9fa6b6ae7a8df882a3ba.svg", + "searchTags": [ + "click", + "submit" + ], + "topRow": 0, + "bottomRow": 4, + "type": "ICON_BUTTON_WIDGET", + "hideCard": false, + "animateLoading": true, + "leftColumn": 58, + "dynamicBindingPathList": [ + { + "key": "buttonColor" + }, + { + "key": "borderRadius" + } + ], + "iconSize": 24, + "isDisabled": false, + "key": "oipik9z0jf", + "isDeprecated": false, + "rightColumn": 64, + "iconName": "cross", + "widgetId": "h84z4pqq7d", + "isVisible": true, + "version": 1, + "parentId": "b8h15fewxw", + "renderMode": "CANVAS", + "isLoading": false, + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "buttonVariant": "TERTIARY" + }, + { + "widgetName": "Text2", + "displayName": "Text", + "iconSVG": "/static/media/icon.97c59b523e6f70ba6f40a10fc2c7c5b5.svg", + "searchTags": [ + "typography", + "paragraph", + "label" + ], + "topRow": 1, + "bottomRow": 5, + "type": "TEXT_WIDGET", + "hideCard": false, + "animateLoading": true, + "overflow": "NONE", + "fontFamily": "{{appsmith.theme.fontFamily.appFont}}", + "leftColumn": 1, + "dynamicBindingPathList": [ + { + "key": "truncateButtonColor" + }, + { + "key": "fontFamily" + }, + { + "key": "borderRadius" + } + ], + "shouldTruncate": false, + "truncateButtonColor": "{{appsmith.theme.colors.primaryColor}}", + "text": "Modal Title", + "key": "qjy37lgbc3", + "isDeprecated": false, + "rightColumn": 41, + "dynamicHeight": "FIXED", + "textAlign": "LEFT", + "widgetId": "j98rl5e7a1", + "isVisible": true, + "fontStyle": "BOLD", + "textColor": "#231F20", + "version": 1, + "parentId": "b8h15fewxw", + "renderMode": "CANVAS", + "isLoading": false, + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "maxDynamicHeight": 9000, + "fontSize": "1.5rem", + "minDynamicHeight": 4 + }, + { + "resetFormOnClick": false, + "boxShadow": "none", + "widgetName": "Button1", + "onClick": "{{closeModal('Modal1')}}", + "buttonColor": "{{appsmith.theme.colors.primaryColor}}", + "displayName": "Button", + "iconSVG": "/static/media/icon.cca026338f1c8eb6df8ba03d084c2fca.svg", + "searchTags": [ + "click", + "submit" + ], + "topRow": 18, + "bottomRow": 22, + "type": "BUTTON_WIDGET", + "hideCard": false, + "animateLoading": true, + "leftColumn": 31, + "dynamicBindingPathList": [ + { + "key": "buttonColor" + }, + { + "key": "borderRadius" + } + ], + "text": "Close", + "isDisabled": false, + "key": "8fy9at549y", + "isDeprecated": false, + "rightColumn": 47, + "isDefaultClickDisabled": true, + "widgetId": "w3gw0z0hul", + "buttonStyle": "PRIMARY", + "isVisible": true, + "recaptchaType": "V3", + "version": 1, + "parentId": "b8h15fewxw", + "renderMode": "CANVAS", + "isLoading": false, + "disabledWhenInvalid": false, + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "buttonVariant": "SECONDARY", + "placement": "CENTER" + }, + { + "resetFormOnClick": false, + "boxShadow": "none", + "widgetName": "Button2", + "buttonColor": "{{appsmith.theme.colors.primaryColor}}", + "displayName": "Button", + "iconSVG": "/static/media/icon.cca026338f1c8eb6df8ba03d084c2fca.svg", + "searchTags": [ + "click", + "submit" + ], + "topRow": 18, + "bottomRow": 22, + "type": "BUTTON_WIDGET", + "hideCard": false, + "animateLoading": true, + "leftColumn": 47, + "dynamicBindingPathList": [ + { + "key": "buttonColor" + }, + { + "key": "borderRadius" + } + ], + "text": "Confirm", + "isDisabled": false, + "key": "8fy9at549y", + "isDeprecated": false, + "rightColumn": 63, + "isDefaultClickDisabled": true, + "widgetId": "dcky71d9uv", + "buttonStyle": "PRIMARY_BUTTON", + "isVisible": true, + "recaptchaType": "V3", + "version": 1, + "parentId": "b8h15fewxw", + "renderMode": "CANVAS", + "isLoading": false, + "disabledWhenInvalid": false, + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "buttonVariant": "PRIMARY", + "placement": "CENTER" + } + ], + "isDisabled": false, + "key": "74zfu9g19a", + "isDeprecated": false, + "rightColumn": 0, + "detachFromLayout": true, + "widgetId": "b8h15fewxw", + "accentColor": "{{appsmith.theme.colors.primaryColor}}", + "isVisible": true, + "version": 1, + "parentId": "d0r5dcpeqm", + "renderMode": "CANVAS", + "isLoading": false, + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}" + } + ], + "key": "1y0e1crldi", + "height": 240, + "isDeprecated": false, + "rightColumn": 0, + "detachFromLayout": true, + "dynamicHeight": "FIXED", + "widgetId": "d0r5dcpeqm", + "canOutsideClickClose": true, + "canEscapeKeyClose": true, + "version": 2, + "parentId": "0", + "renderMode": "CANVAS", + "isLoading": false, + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "maxDynamicHeight": 0, + "width": 456, + "minDynamicHeight": 4 + }, + { + "isVisible": true, + "minDynamicHeight": 4, + "maxDynamicHeight": 9000, + "dynamicHeight": "FIXED", + "animateLoading": true, + "backgroundColor": "#fff", + "disabledWhenInvalid": true, + "fixedFooter": true, + "schema": { + "__root_schema__": { + "children": { + "name": { + "children": {}, + "dataType": "string", + "defaultValue": "{{((sourceData, formData, fieldState) => (sourceData.name))(JSONForm1.sourceData, JSONForm1.formData, JSONForm1.fieldState)}}", + "fieldType": "Text Input", + "sourceData": "John", + "isCustomField": false, + "accessor": "name", + "identifier": "name", + "position": 0, + "originalIdentifier": "name", + "accentColor": "{{((sourceData, formData, fieldState) => (appsmith.theme.colors.primaryColor))(JSONForm1.sourceData, JSONForm1.formData, JSONForm1.fieldState)}}", + "borderRadius": "{{((sourceData, formData, fieldState) => (appsmith.theme.borderRadius.appBorderRadius))(JSONForm1.sourceData, JSONForm1.formData, JSONForm1.fieldState)}}", + "boxShadow": "none", + "iconAlign": "left", + "isDisabled": false, + "isRequired": false, + "isSpellCheck": false, + "isVisible": true, + "labelTextSize": "0.875rem", + "label": "Name" + }, + "date_of_birth": { + "children": {}, + "dataType": "string", + "defaultValue": "{{((sourceData, formData, fieldState) => (moment(sourceData.date_of_birth, \"DD/MM/YYYY\").format(\"YYYY-MM-DDTHH:mm:ss.sssZ\")))(JSONForm1.sourceData, JSONForm1.formData, JSONForm1.fieldState)}}", + "fieldType": "Datepicker", + "sourceData": "20/02/1990", + "isCustomField": false, + "accessor": "date_of_birth", + "identifier": "date_of_birth", + "position": 1, + "originalIdentifier": "date_of_birth", + "accentColor": "{{((sourceData, formData, fieldState) => (appsmith.theme.colors.primaryColor))(JSONForm1.sourceData, JSONForm1.formData, JSONForm1.fieldState)}}", + "borderRadius": "{{((sourceData, formData, fieldState) => (appsmith.theme.borderRadius.appBorderRadius))(JSONForm1.sourceData, JSONForm1.formData, JSONForm1.fieldState)}}", + "boxShadow": "none", + "closeOnSelection": false, + "convertToISO": false, + "dateFormat": "DD/MM/YYYY", + "isDisabled": false, + "isRequired": false, + "isVisible": true, + "label": "Date Of Birth", + "maxDate": "2121-12-31T18:29:00.000Z", + "minDate": "1920-12-31T18:30:00.000Z", + "shortcuts": false, + "timePrecision": "minute", + "labelTextSize": "0.875rem" + }, + "employee_id": { + "children": {}, + "dataType": "number", + "defaultValue": "{{((sourceData, formData, fieldState) => (sourceData.employee_id))(JSONForm1.sourceData, JSONForm1.formData, JSONForm1.fieldState)}}", + "fieldType": "Number Input", + "sourceData": 1001, + "isCustomField": false, + "accessor": "employee_id", + "identifier": "employee_id", + "position": 2, + "originalIdentifier": "employee_id", + "accentColor": "{{((sourceData, formData, fieldState) => (appsmith.theme.colors.primaryColor))(JSONForm1.sourceData, JSONForm1.formData, JSONForm1.fieldState)}}", + "borderRadius": "{{((sourceData, formData, fieldState) => (appsmith.theme.borderRadius.appBorderRadius))(JSONForm1.sourceData, JSONForm1.formData, JSONForm1.fieldState)}}", + "boxShadow": "none", + "iconAlign": "left", + "isDisabled": false, + "isRequired": false, + "isSpellCheck": false, + "isVisible": true, + "labelTextSize": "0.875rem", + "label": "Employee Id" + }, + "customField1": { + "children": {}, + "dataType": "string", + "fieldType": "Text Input", + "sourceData": "", + "isCustomField": true, + "accessor": "customField1", + "identifier": "customField1", + "position": 3, + "originalIdentifier": "customField1", + "accentColor": "{{((sourceData, formData, fieldState) => (appsmith.theme.colors.primaryColor))(JSONForm1.sourceData, JSONForm1.formData, JSONForm1.fieldState)}}", + "borderRadius": "{{((sourceData, formData, fieldState) => (appsmith.theme.borderRadius.appBorderRadius))(JSONForm1.sourceData, JSONForm1.formData, JSONForm1.fieldState)}}", + "boxShadow": "none", + "iconAlign": "left", + "isDisabled": false, + "isRequired": false, + "isSpellCheck": false, + "isVisible": true, + "labelTextSize": "0.875rem", + "label": "Custom Field 1" + }, + "customField2": { + "children": {}, + "dataType": "string", + "fieldType": "Text Input", + "sourceData": "", + "isCustomField": true, + "accessor": "customField2", + "identifier": "customField2", + "position": 4, + "originalIdentifier": "customField2", + "accentColor": "{{((sourceData, formData, fieldState) => (appsmith.theme.colors.primaryColor))(JSONForm1.sourceData, JSONForm1.formData, JSONForm1.fieldState)}}", + "borderRadius": "{{((sourceData, formData, fieldState) => (appsmith.theme.borderRadius.appBorderRadius))(JSONForm1.sourceData, JSONForm1.formData, JSONForm1.fieldState)}}", + "boxShadow": "none", + "iconAlign": "left", + "isDisabled": false, + "isRequired": false, + "isSpellCheck": false, + "isVisible": true, + "labelTextSize": "0.875rem", + "label": "Custom Field 2" + }, + "customField3": { + "children": {}, + "dataType": "string", + "fieldType": "Text Input", + "sourceData": "", + "isCustomField": true, + "accessor": "customField3", + "identifier": "customField3", + "position": 5, + "originalIdentifier": "customField3", + "accentColor": "{{((sourceData, formData, fieldState) => (appsmith.theme.colors.primaryColor))(JSONForm1.sourceData, JSONForm1.formData, JSONForm1.fieldState)}}", + "borderRadius": "{{((sourceData, formData, fieldState) => (appsmith.theme.borderRadius.appBorderRadius))(JSONForm1.sourceData, JSONForm1.formData, JSONForm1.fieldState)}}", + "boxShadow": "none", + "iconAlign": "left", + "isDisabled": false, + "isRequired": false, + "isSpellCheck": false, + "isVisible": true, + "labelTextSize": "0.875rem", + "label": "Custom Field 3" + }, + "customField4": { + "children": {}, + "dataType": "string", + "fieldType": "Text Input", + "sourceData": "", + "isCustomField": true, + "accessor": "customField4", + "identifier": "customField4", + "position": 6, + "originalIdentifier": "customField4", + "accentColor": "{{((sourceData, formData, fieldState) => (appsmith.theme.colors.primaryColor))(JSONForm1.sourceData, JSONForm1.formData, JSONForm1.fieldState)}}", + "borderRadius": "{{((sourceData, formData, fieldState) => (appsmith.theme.borderRadius.appBorderRadius))(JSONForm1.sourceData, JSONForm1.formData, JSONForm1.fieldState)}}", + "boxShadow": "none", + "iconAlign": "left", + "isDisabled": false, + "isRequired": false, + "isSpellCheck": false, + "isVisible": true, + "labelTextSize": "0.875rem", + "label": "Custom Field 4" + } + }, + "dataType": "object", + "defaultValue": "{{((sourceData, formData, fieldState) => (sourceData))(JSONForm1.sourceData, JSONForm1.formData, JSONForm1.fieldState)}}", + "fieldType": "Object", + "sourceData": { + "name": "John", + "date_of_birth": "20/02/1990", + "employee_id": 1001 + }, + "isCustomField": false, + "accessor": "__root_schema__", + "identifier": "__root_schema__", + "position": -1, + "originalIdentifier": "__root_schema__", + "borderRadius": "{{((sourceData, formData, fieldState) => (appsmith.theme.borderRadius.appBorderRadius))(JSONForm1.sourceData, JSONForm1.formData, JSONForm1.fieldState)}}", + "boxShadow": "none", + "cellBorderRadius": "{{((sourceData, formData, fieldState) => (appsmith.theme.borderRadius.appBorderRadius))(JSONForm1.sourceData, JSONForm1.formData, JSONForm1.fieldState)}}", + "cellBoxShadow": "none", + "isDisabled": false, + "isRequired": false, + "isVisible": true, + "labelTextSize": "0.875rem", + "label": "" + } + }, + "scrollContents": true, + "showReset": true, + "title": "Form", + "version": 1, + "borderWidth": "1", + "borderColor": "#E0DEDE", + "widgetName": "JSONForm1", + "autoGenerateForm": true, + "fieldLimitExceeded": false, + "sourceData": { + "name": "John", + "date_of_birth": "20/02/1990", + "employee_id": 1001 + }, + "submitButtonLabel": "Submit", + "resetButtonLabel": "Reset", + "type": "JSON_FORM_WIDGET", + "hideCard": false, + "isDeprecated": false, + "displayName": "JSON Form", + "key": "wrvbzxoh9c", + "iconSVG": "/static/media/icon.5b428de12db9ad6a591955ead07f86e9.svg", + "widgetId": "s10ovhzkte", + "renderMode": "CANVAS", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}", + "submitButtonStyles": { + "buttonColor": "{{appsmith.theme.colors.primaryColor}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "boxShadow": "none", + "buttonVariant": "PRIMARY" + }, + "resetButtonStyles": { + "buttonColor": "{{appsmith.theme.colors.primaryColor}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "boxShadow": "none", + "buttonVariant": "SECONDARY" + }, + "childStylesheet": { + "ARRAY": { + "accentColor": "{{appsmith.theme.colors.primaryColor}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "boxShadow": "none", + "cellBorderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "cellBoxShadow": "none" + }, + "OBJECT": { + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "boxShadow": "none", + "cellBorderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "cellBoxShadow": "none" + }, + "CHECKBOX": { + "accentColor": "{{appsmith.theme.colors.primaryColor}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}" + }, + "CURRENCY_INPUT": { + "accentColor": "{{appsmith.theme.colors.primaryColor}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "boxShadow": "none" + }, + "DATEPICKER": { + "accentColor": "{{appsmith.theme.colors.primaryColor}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "boxShadow": "none" + }, + "EMAIL_INPUT": { + "accentColor": "{{appsmith.theme.colors.primaryColor}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "boxShadow": "none" + }, + "MULTISELECT": { + "accentColor": "{{appsmith.theme.colors.primaryColor}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "boxShadow": "none" + }, + "MULTILINE_TEXT_INPUT": { + "accentColor": "{{appsmith.theme.colors.primaryColor}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "boxShadow": "none" + }, + "NUMBER_INPUT": { + "accentColor": "{{appsmith.theme.colors.primaryColor}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "boxShadow": "none" + }, + "PASSWORD_INPUT": { + "accentColor": "{{appsmith.theme.colors.primaryColor}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "boxShadow": "none" + }, + "PHONE_NUMBER_INPUT": { + "accentColor": "{{appsmith.theme.colors.primaryColor}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "boxShadow": "none" + }, + "RADIO_GROUP": { + "accentColor": "{{appsmith.theme.colors.primaryColor}}", + "boxShadow": "none" + }, + "SELECT": { + "accentColor": "{{appsmith.theme.colors.primaryColor}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "boxShadow": "none" + }, + "SWITCH": { + "accentColor": "{{appsmith.theme.colors.primaryColor}}", + "boxShadow": "none" + }, + "TEXT_INPUT": { + "accentColor": "{{appsmith.theme.colors.primaryColor}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "boxShadow": "none" + } + }, + "isLoading": false, + "parentColumnSpace": 9.96875, + "parentRowSpace": 10, + "leftColumn": 15, + "rightColumn": 40, + "topRow": 28, + "bottomRow": 78, + "parentId": "0", + "dynamicBindingPathList": [ + { + "key": "borderRadius" + }, + { + "key": "boxShadow" + }, + { + "key": "submitButtonStyles.buttonColor" + }, + { + "key": "submitButtonStyles.borderRadius" + }, + { + "key": "resetButtonStyles.buttonColor" + }, + { + "key": "resetButtonStyles.borderRadius" + }, + { + "key": "childStylesheet.ARRAY.accentColor" + }, + { + "key": "childStylesheet.ARRAY.borderRadius" + }, + { + "key": "childStylesheet.ARRAY.cellBorderRadius" + }, + { + "key": "childStylesheet.OBJECT.borderRadius" + }, + { + "key": "childStylesheet.OBJECT.cellBorderRadius" + }, + { + "key": "childStylesheet.CHECKBOX.accentColor" + }, + { + "key": "childStylesheet.CHECKBOX.borderRadius" + }, + { + "key": "childStylesheet.CURRENCY_INPUT.accentColor" + }, + { + "key": "childStylesheet.CURRENCY_INPUT.borderRadius" + }, + { + "key": "childStylesheet.DATEPICKER.accentColor" + }, + { + "key": "childStylesheet.DATEPICKER.borderRadius" + }, + { + "key": "childStylesheet.EMAIL_INPUT.accentColor" + }, + { + "key": "childStylesheet.EMAIL_INPUT.borderRadius" + }, + { + "key": "childStylesheet.MULTISELECT.accentColor" + }, + { + "key": "childStylesheet.MULTISELECT.borderRadius" + }, + { + "key": "childStylesheet.MULTILINE_TEXT_INPUT.accentColor" + }, + { + "key": "childStylesheet.MULTILINE_TEXT_INPUT.borderRadius" + }, + { + "key": "childStylesheet.NUMBER_INPUT.accentColor" + }, + { + "key": "childStylesheet.NUMBER_INPUT.borderRadius" + }, + { + "key": "childStylesheet.PASSWORD_INPUT.accentColor" + }, + { + "key": "childStylesheet.PASSWORD_INPUT.borderRadius" + }, + { + "key": "childStylesheet.PHONE_NUMBER_INPUT.accentColor" + }, + { + "key": "childStylesheet.PHONE_NUMBER_INPUT.borderRadius" + }, + { + "key": "childStylesheet.RADIO_GROUP.accentColor" + }, + { + "key": "childStylesheet.SELECT.accentColor" + }, + { + "key": "childStylesheet.SELECT.borderRadius" + }, + { + "key": "childStylesheet.SWITCH.accentColor" + }, + { + "key": "childStylesheet.TEXT_INPUT.accentColor" + }, + { + "key": "childStylesheet.TEXT_INPUT.borderRadius" + }, + { + "key": "schema.__root_schema__.children.name.defaultValue" + }, + { + "key": "schema.__root_schema__.children.name.accentColor" + }, + { + "key": "schema.__root_schema__.children.name.borderRadius" + }, + { + "key": "schema.__root_schema__.children.date_of_birth.defaultValue" + }, + { + "key": "schema.__root_schema__.children.date_of_birth.accentColor" + }, + { + "key": "schema.__root_schema__.children.date_of_birth.borderRadius" + }, + { + "key": "schema.__root_schema__.children.employee_id.defaultValue" + }, + { + "key": "schema.__root_schema__.children.employee_id.accentColor" + }, + { + "key": "schema.__root_schema__.children.employee_id.borderRadius" + }, + { + "key": "schema.__root_schema__.defaultValue" + }, + { + "key": "schema.__root_schema__.borderRadius" + }, + { + "key": "schema.__root_schema__.cellBorderRadius" + }, + { + "key": "schema.__root_schema__.children.customField1.accentColor" + }, + { + "key": "schema.__root_schema__.children.customField1.borderRadius" + }, + { + "key": "schema.__root_schema__.children.customField2.accentColor" + }, + { + "key": "schema.__root_schema__.children.customField2.borderRadius" + }, + { + "key": "schema.__root_schema__.children.customField3.accentColor" + }, + { + "key": "schema.__root_schema__.children.customField3.borderRadius" + }, + { + "key": "schema.__root_schema__.children.customField4.accentColor" + }, + { + "key": "schema.__root_schema__.children.customField4.borderRadius" + } + ], + "dynamicPropertyPathList": [ + { + "key": "schema.__root_schema__.children.date_of_birth.defaultValue" + } + ], + "dynamicTriggerPathList": [] + } + ] + } +} \ No newline at end of file diff --git a/app/client/cypress/fixtures/menuButtonDsl.json b/app/client/cypress/fixtures/menuButtonDsl.json index 1117387f6cb8..9c3fab407e3b 100644 --- a/app/client/cypress/fixtures/menuButtonDsl.json +++ b/app/client/cypress/fixtures/menuButtonDsl.json @@ -2,76 +2,97 @@ "dsl": { "widgetName": "MainContainer", "backgroundColor": "none", - "rightColumn": 909, - "snapColumns": 64, + "rightColumn": 4896.0, + "snapColumns": 64.0, "detachFromLayout": true, "widgetId": "0", - "topRow": 0, - "bottomRow": 710, + "topRow": 0.0, + "bottomRow": 790.0, "containerStyle": "none", - "snapRows": 125, - "parentRowSpace": 1, + "snapRows": 125.0, + "parentRowSpace": 1.0, "type": "CANVAS_WIDGET", "canExtend": true, - "version": 53, - "minHeight": 690, - "parentColumnSpace": 1, + "version": 66.0, + "minHeight": 1292.0, + "dynamicTriggerPathList": [], + "parentColumnSpace": 1.0, "dynamicBindingPathList": [], - "leftColumn": 0, - "children": [ - { - "isCompact": false, - "widgetName": "MenuButton1", - "displayName": "Menu Button", - "iconSVG": "/static/media/icon.0341d17d.svg", - "topRow": 5, - "bottomRow": 9, - "parentRowSpace": 10, - "type": "MENU_BUTTON_WIDGET", - "hideCard": false, - "animateLoading": true, - "parentColumnSpace": 14.015625, - "leftColumn": 3, - "isDisabled": false, - "key": "ngk48zgyuy", - "rightColumn": 19, - "menuVariant": "PRIMARY", - "widgetId": "z2o5n9g9yw", - "menuItems": { - "menuItem1": { - "label": "First Menu Item", - "id": "menuItem1", - "widgetId": "", - "isVisible": true, - "isDisabled": false, - "index": 0 - }, - "menuItem2": { - "label": "Second Menu Item", - "id": "menuItem2", - "widgetId": "", - "isVisible": true, - "isDisabled": false, - "index": 1 - }, - "menuItem3": { - "label": "Third Menu Item", - "id": "menuItem3", - "widgetId": "", - "isVisible": true, - "isDisabled": false, - "index": 2 - } + "leftColumn": 0.0, + "children": [{ + "isCompact": false, + "boxShadow": "none", + "widgetName": "MenuButton1", + "configureMenuItems": { + "label": "Configure Menu Items", + "id": "config", + "config": { + "id": "config", + "label": "", + "isVisible": true, + "isDisabled": false + } + }, + "displayName": "Menu Button", + "iconSVG": "/static/media/icon.0341d17d67020c8bfc560cc5928af2a7.svg", + "topRow": 13.0, + "bottomRow": 17.0, + "parentRowSpace": 10.0, + "type": "MENU_BUTTON_WIDGET", + "hideCard": false, + "animateLoading": true, + "parentColumnSpace": 12.5625, + "dynamicTriggerPathList": [], + "leftColumn": 14.0, + "dynamicBindingPathList": [{ + "key": "menuColor" + }, { + "key": "borderRadius" + }], + "isDisabled": false, + "sourceData": "", + "key": "ruwr6lq57j", + "sourceDataKeys": [], + "isDeprecated": false, + "rightColumn": 30.0, + "menuVariant": "PRIMARY", + "widgetId": "varnzc9ez9", + "menuItems": { + "menuItem1": { + "label": "First Menu Item", + "id": "menuItem1", + "widgetId": "", + "isVisible": true, + "isDisabled": false, + "index": 0.0 }, - "isVisible": true, - "label": "Open Menu", - "version": 1, - "parentId": "0", - "renderMode": "CANVAS", - "isLoading": false, - "menuColor": "#03B365", - "placement": "CENTER" - } - ] + "menuItem2": { + "label": "Second Menu Item", + "id": "menuItem2", + "widgetId": "", + "isVisible": true, + "isDisabled": false, + "index": 1.0 + }, + "menuItem3": { + "label": "Third Menu Item", + "id": "menuItem3", + "widgetId": "", + "isVisible": true, + "isDisabled": false, + "index": 2.0 + } + }, + "isVisible": true, + "label": "Open Menu", + "version": 1.0, + "parentId": "0", + "renderMode": "CANVAS", + "isLoading": false, + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "menuItemsSource": "STATIC", + "menuColor": "{{appsmith.theme.colors.primaryColor}}", + "placement": "CENTER" + }] } } \ No newline at end of file diff --git a/app/client/cypress/fixtures/multipleContainerdsl.json b/app/client/cypress/fixtures/multipleContainerdsl.json new file mode 100644 index 000000000000..0e6a2be59d31 --- /dev/null +++ b/app/client/cypress/fixtures/multipleContainerdsl.json @@ -0,0 +1,344 @@ +{ + "dsl": { + "widgetName": "MainContainer", + "backgroundColor": "none", + "rightColumn": 4896, + "snapColumns": 64, + "detachFromLayout": true, + "widgetId": "0", + "topRow": 0, + "bottomRow": 600, + "containerStyle": "none", + "snapRows": 125, + "parentRowSpace": 1, + "type": "CANVAS_WIDGET", + "canExtend": true, + "version": 69, + "minHeight": 1292, + "dynamicTriggerPathList": [], + "parentColumnSpace": 1, + "dynamicBindingPathList": [], + "leftColumn": 0, + "children": [ + { + "isVisible": true, + "minDynamicHeight": 4, + "maxDynamicHeight": 9000, + "dynamicHeight": "AUTO_HEIGHT", + "shouldScrollContents": true, + "backgroundColor": "#FFFFFF", + "widgetName": "Container1", + "containerStyle": "card", + "borderColor": "#E0DEDE", + "borderWidth": "1", + "boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}", + "animateLoading": true, + "children": [ + { + "isVisible": true, + "widgetName": "Canvas1", + "version": 1, + "detachFromLayout": true, + "type": "CANVAS_WIDGET", + "hideCard": true, + "isDeprecated": false, + "displayName": "Canvas", + "key": "mqfi7o9374", + "containerStyle": "none", + "canExtend": false, + "children": [ + { + "isVisible": true, + "minDynamicHeight": 4, + "maxDynamicHeight": 9000, + "dynamicHeight": "AUTO_HEIGHT", + "shouldScrollContents": true, + "backgroundColor": "#FFFFFF", + "widgetName": "Container2", + "containerStyle": "card", + "borderColor": "#E0DEDE", + "borderWidth": "1", + "boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}", + "animateLoading": true, + "children": [ + { + "isVisible": true, + "widgetName": "Canvas2", + "version": 1, + "detachFromLayout": true, + "type": "CANVAS_WIDGET", + "hideCard": true, + "isDeprecated": false, + "displayName": "Canvas", + "key": "mqfi7o9374", + "containerStyle": "none", + "canExtend": false, + "children": [ + { + "isVisible": true, + "minDynamicHeight": 4, + "maxDynamicHeight": 9000, + "dynamicHeight": "AUTO_HEIGHT", + "shouldScrollContents": true, + "backgroundColor": "#FFFFFF", + "widgetName": "Container3", + "containerStyle": "card", + "borderColor": "#E0DEDE", + "borderWidth": "1", + "boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}", + "animateLoading": true, + "children": [ + { + "isVisible": true, + "widgetName": "Canvas3", + "version": 1, + "detachFromLayout": true, + "type": "CANVAS_WIDGET", + "hideCard": true, + "isDeprecated": false, + "displayName": "Canvas", + "key": "mqfi7o9374", + "containerStyle": "none", + "canExtend": false, + "children": [ + { + "isVisible": true, + "minDynamicHeight": 4, + "maxDynamicHeight": 9000, + "dynamicHeight": "AUTO_HEIGHT", + "animateLoading": true, + "labelTextSize": "0.875rem", + "options": [ + { + "label": "Blue", + "value": "BLUE" + }, + { + "label": "Green", + "value": "GREEN" + }, + { + "label": "Red", + "value": "RED" + } + ], + "defaultSelectedValues": [ + "BLUE" + ], + "isDisabled": false, + "isInline": true, + "isRequired": false, + "labelText": "Label", + "labelPosition": "Left", + "labelAlignment": "left", + "labelWidth": 5, + "widgetName": "CheckboxGroup1", + "version": 2, + "type": "CHECKBOX_GROUP_WIDGET", + "hideCard": false, + "isDeprecated": false, + "displayName": "Checkbox Group", + "key": "hm1iv7bfuy", + "iconSVG": "/static/media/icon.ecb3847950c4515966ef642a32758afb.svg", + "widgetId": "v37fp7if60", + "renderMode": "CANVAS", + "accentColor": "{{appsmith.theme.colors.primaryColor}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "isLoading": false, + "parentColumnSpace": 2.9258722066879272, + "parentRowSpace": 10, + "leftColumn": 8, + "rightColumn": 57, + "topRow": 33, + "bottomRow": 45, + "parentId": "05i6e0t0fa", + "dynamicBindingPathList": [ + { + "key": "accentColor" + }, + { + "key": "borderRadius" + } + ], + "dynamicTriggerPathList": [] + } + ], + "minHeight": 470, + "widgetId": "05i6e0t0fa", + "renderMode": "CANVAS", + "boxShadow": "none", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "accentColor": "{{appsmith.theme.colors.primaryColor}}", + "isLoading": false, + "parentColumnSpace": 1, + "parentRowSpace": 1, + "leftColumn": 0, + "rightColumn": 47.16357421875, + "topRow": 0, + "bottomRow": 470, + "parentId": "l44iyd101d", + "dynamicBindingPathList": [ + { + "key": "borderRadius" + }, + { + "key": "accentColor" + } + ] + } + ], + "version": 1, + "searchTags": [ + "div", + "parent", + "group" + ], + "type": "CONTAINER_WIDGET", + "hideCard": false, + "isDeprecated": false, + "displayName": "Container", + "key": "ojfkbwpxhu", + "iconSVG": "/static/media/icon.1977dca3370505e2db3a8e44cfd54907.svg", + "isCanvas": true, + "widgetId": "l44iyd101d", + "renderMode": "CANVAS", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "isLoading": false, + "parentColumnSpace": 1.96514892578125, + "parentRowSpace": 10, + "leftColumn": 4, + "rightColumn": 59, + "topRow": 1, + "bottomRow": 48, + "parentId": "wor0jspl6z", + "dynamicBindingPathList": [ + { + "key": "borderRadius" + }, + { + "key": "boxShadow" + } + ], + "dynamicTriggerPathList": [] + } + ], + "minHeight": 500, + "widgetId": "wor0jspl6z", + "renderMode": "CANVAS", + "boxShadow": "none", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "accentColor": "{{appsmith.theme.colors.primaryColor}}", + "isLoading": false, + "parentColumnSpace": 1, + "parentRowSpace": 1, + "leftColumn": 0, + "rightColumn": 145.76953125, + "topRow": 0, + "bottomRow": 500, + "parentId": "429zp6cdxf", + "dynamicBindingPathList": [ + { + "key": "borderRadius" + }, + { + "key": "accentColor" + } + ] + } + ], + "version": 1, + "searchTags": [ + "div", + "parent", + "group" + ], + "type": "CONTAINER_WIDGET", + "hideCard": false, + "isDeprecated": false, + "displayName": "Container", + "key": "ojfkbwpxhu", + "iconSVG": "/static/media/icon.1977dca3370505e2db3a8e44cfd54907.svg", + "isCanvas": true, + "widgetId": "429zp6cdxf", + "renderMode": "CANVAS", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "isLoading": false, + "parentColumnSpace": 6.07373046875, + "parentRowSpace": 10, + "leftColumn": 9, + "rightColumn": 52, + "topRow": 5, + "bottomRow": 55, + "parentId": "stwsepims4", + "dynamicBindingPathList": [ + { + "key": "borderRadius" + }, + { + "key": "boxShadow" + } + ], + "dynamicTriggerPathList": [] + } + ], + "minHeight": 570, + "widgetId": "stwsepims4", + "renderMode": "CANVAS", + "boxShadow": "none", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "accentColor": "{{appsmith.theme.colors.primaryColor}}", + "isLoading": false, + "parentColumnSpace": 1, + "parentRowSpace": 1, + "leftColumn": 0, + "rightColumn": 239.25, + "topRow": 0, + "bottomRow": 570, + "parentId": "v7qrdx618b", + "dynamicBindingPathList": [ + { + "key": "borderRadius" + }, + { + "key": "accentColor" + } + ] + } + ], + "version": 1, + "searchTags": [ + "div", + "parent", + "group" + ], + "type": "CONTAINER_WIDGET", + "hideCard": false, + "isDeprecated": false, + "displayName": "Container", + "key": "ojfkbwpxhu", + "iconSVG": "/static/media/icon.1977dca3370505e2db3a8e44cfd54907.svg", + "isCanvas": true, + "widgetId": "v7qrdx618b", + "renderMode": "CANVAS", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "isLoading": false, + "parentColumnSpace": 9.96875, + "parentRowSpace": 10, + "leftColumn": 18, + "rightColumn": 59, + "topRow": 1, + "bottomRow": 58, + "parentId": "0", + "dynamicBindingPathList": [ + { + "key": "borderRadius" + }, + { + "key": "boxShadow" + } + ], + "dynamicTriggerPathList": [] + } + ] + } +} \ No newline at end of file diff --git a/app/client/cypress/fixtures/selectMultiSelectTreeSelectWidgetDsl.json b/app/client/cypress/fixtures/selectMultiSelectTreeSelectWidgetDsl.json new file mode 100644 index 000000000000..817d13f682d1 --- /dev/null +++ b/app/client/cypress/fixtures/selectMultiSelectTreeSelectWidgetDsl.json @@ -0,0 +1,236 @@ +{ + "dsl": { + "widgetName": "MainContainer", + "backgroundColor": "none", + "rightColumn": 656, + "snapColumns": 64, + "detachFromLayout": true, + "widgetId": "0", + "topRow": 0, + "bottomRow": 5016, + "containerStyle": "none", + "snapRows": 125, + "parentRowSpace": 1, + "type": "CANVAS_WIDGET", + "canExtend": true, + "version": 46, + "minHeight": 1292, + "parentColumnSpace": 1, + "dynamicBindingPathList": [], + "leftColumn": 0, + "children": [ + { + "isVisible": true, + "placeholderText": "Select option", + "labelText": "Label", + "selectionType": "SINGLE_SELECT", + "options": [ + { + "label": "Blue", + "value": "BLUE" + }, + { + "label": "Green", + "value": "GREEN" + }, + { + "label": "Red", + "value": "RED" + } + ], + "serverSideFiltering": false, + "widgetName": "Select1", + "defaultOptionValue": { + "label": "Green", + "value": "GREEN" + }, + "version": 1, + "isFilterable": false, + "isRequired": false, + "isDisabled": false, + "type": "SELECT_WIDGET", + "hideCard": false, + "displayName": "Select", + "key": "xlzcpnuumm", + "iconSVG": "/static/media/icon.bd99caba.svg", + "widgetId": "w042446bbt", + "renderMode": "CANVAS", + "isLoading": false, + "parentColumnSpace": 10.0625, + "parentRowSpace": 10, + "leftColumn": 16, + "rightColumn": 36, + "topRow": 6, + "bottomRow": 12.9, + "parentId": "0", + "onDropdownOpen": "", + "onDropdownClose": "", + "dynamicTriggerPathList": [ + { + "key": "onDropdownOpen" + }, + { + "key": "onDropdownClose" + } + ] + }, + { + "isRequired": false, + "widgetName": "MultiSelect", + "rightColumn": 32, + "widgetId": "p6qkmj8uo1", + "labelText": "Label", + "topRow": 49, + "bottomRow": 60, + "parentRowSpace": 10, + "isVisible": true, + "label": "", + "type": "MULTI_SELECT_WIDGET_V2", + "version": 1, + "parentId": "e3tq9qwta6", + "isLoading": false, + "defaultOptionValue": [ + "GREEN" + ], + "parentColumnSpace": 13.662109375, + "leftColumn": 57, + "dynamicBindingPathList": [], + "options": [ + { + "label": "Blue", + "value": "BLUE" + }, + { + "label": "Green", + "value": "GREEN" + }, + { + "label": "Red", + "value": "RED" + } + ], + "placeholderText": "select option(s)", + "isDisabled": false, + "labelTextSize": "0.875rem", + "onDropdownOpen": "", + "onDropdownClose": "", + "dynamicTriggerPathList": [ + { + "key": "onDropdownOpen" + }, + { + "key": "onDropdownClose" + } + ] + }, + { + "widgetName":"SingleSelectTree1", + "displayName":"TreeSelect", + "iconSVG":"/static/media/icon.f815ebe3.svg", + "labelText":"Label", + "topRow":58, + "bottomRow":64.8, + "parentRowSpace":10, + "type":"SINGLE_SELECT_TREE_WIDGET", + "hideCard":false, + "defaultOptionValue":"BLUE", + "parentColumnSpace":15.974999999999998, + "leftColumn":17, + "options": [ + { + "label": "Blue", + "value": "BLUE" + }, + { + "label": "Green", + "value": "GREEN" + }, + { + "label": "Red", + "value": "RED" + } + ], + "placeholderText":"select option", + "isDisabled":false, + "key":"cul8w70bzs", + "isRequired":false, + "rightColumn":33, + "widgetId":"0zloh94nd4", + "isVisible":true, + "version":1, + "expandAll":false, + "parentId":"0", + "renderMode":"CANVAS", + "isLoading":false, + "allowClear":false, + "onDropdownOpen": "", + "onDropdownClose": "", + "dynamicTriggerPathList": [ + { + "key": "onDropdownOpen" + }, + { + "key": "onDropdownClose" + } + ] + }, + { + "widgetName":"MultiSelectTree1", + "displayName":"Multi TreeSelect", + "iconSVG":"/static/media/icon.f264210c.svg", + "labelText":"Label", + "topRow":38, + "bottomRow":44.88, + "parentRowSpace":10, + "type":"MULTI_SELECT_TREE_WIDGET", + "hideCard":false, + "mode":"SHOW_ALL", + "defaultOptionValue":[ + { + "label": "Green", + "value": "GREEN" + } + ], + "parentColumnSpace":15.974999999999998, + "leftColumn":18, + "options": [ + { + "label": "Blue", + "value": "BLUE" + }, + { + "label": "Green", + "value": "GREEN" + }, + { + "label": "Red", + "value": "RED" + } + ], + "placeholderText":"select option(s)", + "isDisabled":false, + "key":"1zu067mn51", + "isRequired":false, + "rightColumn":34, + "widgetId":"zvm3vcs5gp", + "isVisible":true, + "version":1, + "expandAll":false, + "parentId":"0", + "renderMode":"CANVAS", + "isLoading":false, + "allowClear":false, + "onDropdownOpen": "", + "onDropdownClose": "", + "dynamicTriggerPathList": [ + { + "key": "onDropdownOpen" + }, + { + "key": "onDropdownClose" + } + ] + } + ] + } +} diff --git a/app/client/cypress/fixtures/testdata.json b/app/client/cypress/fixtures/testdata.json index a59034d8d824..5b01125ebad3 100644 --- a/app/client/cypress/fixtures/testdata.json +++ b/app/client/cypress/fixtures/testdata.json @@ -71,6 +71,7 @@ "input2": "(//div[@class='bp3-input-group']//input)[1]", "input3": "(//div[@class='bp3-input-group']//input)[2]", "videoUrl": "https://www.youtube.com/watch?v=S5musXykVs0", + "videoUrl2": "https://assets.appsmith.com/widgets/bird.mp4", "audioUrl": "https://assets.appsmith.com/widgets/birds_chirping.mp3", "TablePagination": [ { diff --git a/app/client/cypress/fixtures/textWidgetDynamicdsl.json b/app/client/cypress/fixtures/textWidgetDynamicdsl.json new file mode 100644 index 000000000000..81b7a20939ca --- /dev/null +++ b/app/client/cypress/fixtures/textWidgetDynamicdsl.json @@ -0,0 +1,77 @@ +{ + "dsl": { + "widgetName": "MainContainer", + "backgroundColor": "none", + "rightColumn": 4896, + "snapColumns": 64, + "detachFromLayout": true, + "widgetId": "0", + "topRow": 0, + "bottomRow": 400, + "containerStyle": "none", + "snapRows": 125, + "parentRowSpace": 1, + "type": "CANVAS_WIDGET", + "canExtend": true, + "version": 64, + "minHeight": 1292, + "dynamicTriggerPathList": [], + "parentColumnSpace": 1, + "dynamicBindingPathList": [], + "leftColumn": 0, + "children": [ + { + "isVisible": true, + "minDynamicHeight": 4, + "maxDynamicHeight": 9000, + "dynamicHeight": "AUTO_HEIGHT", + "text": "Test Auto Height", + "fontSize": "1rem", + "fontStyle": "BOLD", + "textAlign": "LEFT", + "textColor": "#231F20", + "truncateButtonColor": "{{appsmith.theme.colors.primaryColor}}", + "widgetName": "Text1", + "shouldTruncate": false, + "overflow": "NONE", + "version": 1, + "animateLoading": true, + "searchTags": [ + "typography", + "paragraph", + "label" + ], + "type": "TEXT_WIDGET", + "hideCard": false, + "isDeprecated": false, + "displayName": "Text", + "key": "t24h8uem36", + "iconSVG": "/static/media/icon.97c59b523e6f70ba6f40a10fc2c7c5b5.svg", + "widgetId": "nwkbwkxa62", + "renderMode": "CANVAS", + "fontFamily": "{{appsmith.theme.fontFamily.appFont}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "isLoading": false, + "parentColumnSpace": 9.96875, + "parentRowSpace": 10, + "leftColumn": 19, + "rightColumn": 35, + "topRow": 11, + "bottomRow": 15, + "parentId": "0", + "dynamicBindingPathList": [ + { + "key": "truncateButtonColor" + }, + { + "key": "fontFamily" + }, + { + "key": "borderRadius" + } + ], + "dynamicTriggerPathList": [] + } + ] + } +} \ No newline at end of file diff --git a/app/client/cypress/fixtures/widgetPopupDsl.json b/app/client/cypress/fixtures/widgetPopupDsl.json index 138ce99f8717..56d2d5eb6981 100644 --- a/app/client/cypress/fixtures/widgetPopupDsl.json +++ b/app/client/cypress/fixtures/widgetPopupDsl.json @@ -232,6 +232,7 @@ "rightColumn": 18, "menuVariant": "PRIMARY", "widgetId": "33f9n054tq", + "menuItemsSource": "STATIC", "menuItems": { "menuItem1": { "label": "First Menu Item", diff --git a/app/client/cypress/integration/Smoke_TestSuite/Application/EchoApiCMS_spec.js b/app/client/cypress/integration/Smoke_TestSuite/Application/EchoApiCMS_spec.js index 91218f389fad..26ee30d56c98 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/Application/EchoApiCMS_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/Application/EchoApiCMS_spec.js @@ -11,7 +11,7 @@ describe("Content Management System App", function() { cy.startRoutesForDatasource(); }); - it.only("1.Create Get echo Api call", function() { + it("1.Create Get echo Api call", function() { cy.NavigateToAPI_Panel(); cy.CreateAPI("get_data"); // creating get request using echo @@ -97,6 +97,7 @@ describe("Content Management System App", function() { .click({ force: true }); cy.xpath(appPage.pagebutton).click({ force: true }); //cy.xpath(appPage.datasourcesbutton).click({ force: true }); + cy.CheckAndUnfoldEntityItem("Queries/JS"); cy.xpath(appPage.postApi).click({ force: true }); cy.ResponseCheck("Test"); // cy.ResponseCheck("Task completed"); diff --git a/app/client/cypress/integration/Smoke_TestSuite/Application/ImportExportForkApplication_spec.js b/app/client/cypress/integration/Smoke_TestSuite/Application/ImportExportForkApplication_spec.js index 340b14011c27..0ef5c185a74e 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/Application/ImportExportForkApplication_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/Application/ImportExportForkApplication_spec.js @@ -34,7 +34,7 @@ describe("Import, Export and Fork application and validate data binding", functi const name = uuid(); appName = `app${name}`; cy.get(homePage.applicationName).click({ force: true }); - cy.get(`${homePage.applicationEditMenu} li:first-child a`).click({ + cy.get(`${homePage.applicationEditMenu} li:nth-child(3) a`).click({ force: true, }); cy.wait(2000); diff --git a/app/client/cypress/integration/Smoke_TestSuite/Application/MongoDBShoppingCart_spec.js b/app/client/cypress/integration/Smoke_TestSuite/Application/MongoDBShoppingCart_spec.js index 7f4078a2f062..cbac44b196f1 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/Application/MongoDBShoppingCart_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/Application/MongoDBShoppingCart_spec.js @@ -20,7 +20,7 @@ describe("Shopping cart App", function() { cy.get(datasource.MongoDB).click(); cy.fillMongoDatasourceForm(); cy.testSaveDatasource(); - cy.get("@createDatasource").then((httpResponse) => { + cy.get("@saveDatasource").then((httpResponse) => { datasourceName = httpResponse.response.body.data.name; }); cy.NavigateToQueryEditor(); diff --git a/app/client/cypress/integration/Smoke_TestSuite/Application/PgAdmin_spec.js b/app/client/cypress/integration/Smoke_TestSuite/Application/PgAdmin_spec.js index d63dcad9d9a3..8d0820254219 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/Application/PgAdmin_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/Application/PgAdmin_spec.js @@ -24,7 +24,7 @@ describe("PgAdmin Clone App", function() { cy.testSaveDatasource(); - cy.get("@createDatasource").then((httpResponse) => { + cy.get("@saveDatasource").then((httpResponse) => { datasourceName = httpResponse.response.body.data.name; }); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/Application/ReconnectDatasource_spec.js b/app/client/cypress/integration/Smoke_TestSuite/Application/ReconnectDatasource_spec.js index 99677255c44d..d822a980dedc 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/Application/ReconnectDatasource_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/Application/ReconnectDatasource_spec.js @@ -53,7 +53,8 @@ describe("Reconnect Datasource Modal validation while importing application", fu cy.ReconnectDatasource("Untitled Datasource"); cy.wait(1000); cy.fillPostgresDatasourceForm(); - cy.testSaveDatasource(); + cy.testDatasource(true); + cy.get(".t--save-datasource").click({ force: true }); cy.wait(2000); // cy.get(reconnectDatasourceModal.SkipToAppBtn).click({ @@ -79,7 +80,7 @@ describe("Reconnect Datasource Modal validation while importing application", fu const name = uuid(); appName = `app${name}`; cy.get(homePage.applicationName).click({ force: true }); - cy.get(`${homePage.applicationEditMenu} li:first-child a`).click({ + cy.get(`${homePage.applicationEditMenu} li:nth-child(3) a`).click({ force: true, }); cy.wait(2000); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ActionExecution/ClearStore_spec.ts b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ActionExecution/ClearStore_spec.ts index 0f2f4c6522ee..9f76e36b5ef3 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ActionExecution/ClearStore_spec.ts +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ActionExecution/ClearStore_spec.ts @@ -1,17 +1,9 @@ -import { ObjectsRegistry } from "../../../../support/Objects/Registry"; - -const { - AggregateHelper: agHelper, - DeployMode: deployMode, - EntityExplorer: ee, - JSEditor: jsEditor, - PropertyPane: propPane, -} = ObjectsRegistry; +import * as _objects from "../../../../support/Objects/ObjectsCore" describe("clearStore Action test", () => { before(() => { - ee.DragDropWidgetNVerify("buttonwidget", 100, 100); - ee.NavigateToSwitcher("explorer"); + _objects.ee.DragDropWidgetNVerify("buttonwidget", 100, 100); + _objects.ee.NavigateToSwitcher("explorer"); }); it("1. Feature 11639 : Clear all store value", function() { @@ -24,7 +16,7 @@ describe("clearStore Action test", () => { storeValue('val3', 'value 3'), ]; await Promise.all(values); - await showAlert(JSON.stringify(appsmith.store)); + await showAlert(JSON.stringify(appsmith.store)); }, clearStore: async () => { await clearStore(); @@ -33,7 +25,7 @@ describe("clearStore Action test", () => { }`; // Create js object - jsEditor.CreateJSObject(JS_OBJECT_BODY, { + _objects.jsEditor.CreateJSObject(JS_OBJECT_BODY, { paste: true, completeReplace: true, toRun: false, @@ -41,40 +33,40 @@ describe("clearStore Action test", () => { shouldCreateNewJSObj: true, }); - ee.SelectEntityByName("Button1", "Widgets"); - propPane.UpdatePropertyFieldValue("Label", ""); - propPane.TypeTextIntoField("Label", "StoreValue"); + _objects.ee.SelectEntityByName("Button1", "Widgets"); + _objects.propPane.UpdatePropertyFieldValue("Label", ""); + _objects.propPane.TypeTextIntoField("Label", "StoreValue"); cy.get("@jsObjName").then((jsObj: any) => { - propPane.SelectJSFunctionToExecute( + _objects.propPane.SelectJSFunctionToExecute( "onClick", jsObj as string, "storeValue", ); }); - ee.DragDropWidgetNVerify("buttonwidget", 100, 200); - ee.SelectEntityByName("Button2", "Widgets"); - propPane.UpdatePropertyFieldValue("Label", ""); - propPane.TypeTextIntoField("Label", "ClearStore"); + _objects.ee.DragDropWidgetNVerify("buttonwidget", 100, 200); + _objects.ee.SelectEntityByName("Button2", "Widgets"); + _objects.propPane.UpdatePropertyFieldValue("Label", ""); + _objects.propPane.TypeTextIntoField("Label", "ClearStore"); cy.get("@jsObjName").then((jsObj: any) => { - propPane.SelectJSFunctionToExecute( + _objects.propPane.SelectJSFunctionToExecute( "onClick", jsObj as string, "clearStore", ); }); - deployMode.DeployApp(); - agHelper.ClickButton("StoreValue"); - agHelper.AssertContains( + _objects.deployMode.DeployApp(); + _objects.agHelper.ClickButton("StoreValue"); + _objects.agHelper.AssertContains( JSON.stringify({ val1: "value 1", val2: "value 2", val3: "value 3", }), ); - agHelper.ClickButton("ClearStore"); - agHelper.AssertContains(JSON.stringify({})); - deployMode.NavigateBacktoEditor(); + _objects.agHelper.ClickButton("ClearStore"); + _objects.agHelper.AssertContains(JSON.stringify({})); + _objects.deployMode.NavigateBacktoEditor(); }); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ActionExecution/NavigateTo_spec.ts b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ActionExecution/NavigateTo_spec.ts index 6dd7186379bd..5a2268036cd9 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ActionExecution/NavigateTo_spec.ts +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ActionExecution/NavigateTo_spec.ts @@ -8,7 +8,6 @@ const { } = ObjectsRegistry; describe("Navigate To feature", () => { - beforeEach(() => { agHelper.RestoreLocalStorageCache(); }); @@ -29,9 +28,20 @@ describe("Navigate To feature", () => { cy.get(".t--open-dropdown-Select-Page").click(); agHelper.AssertElementLength(".bp3-menu-item", 2); cy.get(locator._dropDownValue("Page2")).click(); + cy.get("label") + .contains("Query Params") + .siblings() + .find(".CodeEditorTarget") + .then(($el) => cy.updateCodeInput($el, "{{{ test: '123' }}}")); + agHelper.ClickButton("Submit"); + cy.url().should("include", "a=b"); + cy.url().should("include", "test=123"); + ee.SelectEntityByName("Page1"); deployMode.DeployApp(); agHelper.ClickButton("Submit"); cy.get(".bp3-heading").contains("This page seems to be blank"); + cy.url().should("include", "a=b"); + cy.url().should("include", "test=123"); deployMode.NavigateBacktoEditor(); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Bind_JSObject_Postgress_Table_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Bind_JSObject_Postgress_Table_spec.js index b25080fadd80..c7896f85f135 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Bind_JSObject_Postgress_Table_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Bind_JSObject_Postgress_Table_spec.js @@ -15,7 +15,7 @@ describe("Addwidget from Query and bind with other widgets", function() { it("1. Create a query and populate response by choosing addWidget and validate in Table Widget & Bug 7413", () => { cy.addDsl(dsl); cy.createPostgresDatasource(); - cy.get("@createDatasource").then((httpResponse) => { + cy.get("@saveDatasource").then((httpResponse) => { datasourceName = httpResponse.response.body.data.name; cy.NavigateToActiveDSQueryPane(datasourceName); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Bind_TableV2_Widget_API_Derived_Column_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Bind_TableV2_Widget_API_Derived_Column_spec.js index 6ade8e782bf9..3173cb83ccaf 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Bind_TableV2_Widget_API_Derived_Column_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Bind_TableV2_Widget_API_Derived_Column_spec.js @@ -44,6 +44,7 @@ describe("Test Create Api and Bind to Table widget", function() { cy.changeColumnType("Image"); cy.closePropertyPane(); cy.SearchEntityandOpen("Table1"); + cy.backFromPropertyPanel(); cy.moveToStyleTab(); cy.get(widgetsPage.centerAlign) .first() diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Bind_Table_Widget_API_Derived_Column_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Bind_Table_Widget_API_Derived_Column_spec.js index 5ea652903d3a..7eef30f663af 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Bind_Table_Widget_API_Derived_Column_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Bind_Table_Widget_API_Derived_Column_spec.js @@ -42,7 +42,7 @@ describe("Test Create Api and Bind to Table widget", function() { it("Check Image alignment is working as expected", function() { cy.SearchEntityandOpen("Table1"); cy.editColumn("avatar"); - cy.changeColumnType("Image"); + cy.changeColumnType("Image", false); cy.closePropertyPane(); cy.SearchEntityandOpen("Table1"); cy.get(widgetsPage.centerAlign) @@ -72,6 +72,7 @@ describe("Test Create Api and Bind to Table widget", function() { it("Update table json data and check the derived column values after update", function() { cy.SearchEntityandOpen("Table1"); + cy.backFromPropertyPanel(); cy.tableColumnDataValidation("id"); cy.tableColumnDataValidation("name"); cy.tableColumnDataValidation("status"); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Widget_loading_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Widget_loading_spec.js index 9258c805b68e..3785c037197e 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Widget_loading_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Widget_loading_spec.js @@ -21,7 +21,7 @@ describe("Binding the multiple widgets and validating default data", function() cy.get(datasource.PostgreSQL).click(); cy.fillPostgresDatasourceForm(); cy.testSaveDatasource(); - cy.get("@createDatasource").then((httpResponse) => { + cy.get("@saveDatasource").then((httpResponse) => { datasourceName = httpResponse.response.body.data.name; }); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/autocomplete_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/autocomplete_spec.js index 06436faf57d4..c190c7e04ae9 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/autocomplete_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/autocomplete_spec.js @@ -7,6 +7,9 @@ describe("Dynamic input autocomplete", () => { cy.addDsl(dsl); }); it("opens autocomplete for bindings", () => { + cy.selectEntityByName("TestModal"); + cy.wait(3000); + cy.selectEntityByName("Aditya"); cy.openPropertyPane("buttonwidget"); cy.get(dynamicInputLocators.input) .first() diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/BugTests/Bug16248_spec.ts b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/BugTests/Bug16248_spec.ts deleted file mode 100644 index 81f05fb884cb..000000000000 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/BugTests/Bug16248_spec.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { ObjectsRegistry } from "../../../../support/Objects/Registry"; - -const gitSync = ObjectsRegistry.GitSync, - apiPage = ObjectsRegistry.ApiPage; - -describe("Block Shortcut Action Execution", function() { - it("Bug 16248, When GitSync modal is open, block action execution", function() { - const largeResponseApiUrl = "https://jsonplaceholder.typicode.com/users"; - const modifierKey = Cypress.platform === "darwin" ? "meta" : "ctrl"; - - apiPage.CreateAndFillApi(largeResponseApiUrl, "GitSyncTest"); - gitSync.openGitSyncModal(); - cy.get("body").type(`{${modifierKey}}{enter}`); - cy.get("@postExecute").should("not.exist"); - gitSync.closeGitSyncModal(); - cy.get("body").type(`{${modifierKey}}{enter}`); - cy.wait("@postExecute"); - }); -}); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/BugTests/Bug16702_Spec.ts b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/BugTests/Bug16702_Spec.ts index 038551c5a01a..6c3ef23871d4 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/BugTests/Bug16702_Spec.ts +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/BugTests/Bug16702_Spec.ts @@ -24,7 +24,7 @@ const GRAPHQL_RESPONSE = { }; describe("Binding Expressions should not be truncated in Url and path extraction", function() { - it("Bug 16702, Moustache+Quotes formatting goes wrong in graphql body resulting in autocomplete failure", function() { + it.skip("Bug 16702, Moustache+Quotes formatting goes wrong in graphql body resulting in autocomplete failure", function() { const jsObjectBody = `export default { limitValue: 1, offsetValue: 1, @@ -49,8 +49,14 @@ describe("Binding Expressions should not be truncated in Url and path extraction // }) //.trigger("mouseover") .dblclick() + .dblclick() .type("{{JSObject1."); - agHelper.GetNAssertElementText(locator._hints, "offsetValue", "have.text", 1); + agHelper.GetNAssertElementText( + locator._hints, + "offsetValue", + "have.text", + 1, + ); agHelper.Sleep(); agHelper.TypeText(locator._codeMirrorTextArea, "offsetValue", 1); agHelper.Sleep(2000); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/BugTests/Bug18664_spec.ts b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/BugTests/Bug18664_spec.ts new file mode 100644 index 000000000000..d74008366960 --- /dev/null +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/BugTests/Bug18664_spec.ts @@ -0,0 +1,24 @@ +import { ObjectsRegistry } from "../../../../support/Objects/Registry"; + +let dsName: any; + +const agHelper = ObjectsRegistry.AggregateHelper, + dataSources = ObjectsRegistry.DataSources; + +describe("Bug 18664: datasource unsaved changes popup shows even without changes", function() { + it("1. Create postgres datasource, save it and edit it and go back, now unsaved changes popup should not be shown", () => { + dataSources.NavigateToDSCreateNew(); + agHelper.GenerateUUID(); + cy.get("@guid").then((uid) => { + dataSources.CreatePlugIn("PostgreSQL"); + dsName = "Postgres" + uid; + agHelper.RenameWithInPane(dsName, false); + dataSources.SaveDatasource(); + cy.wait(1000); + dataSources.EditDatasource(); + agHelper.GoBack(); + agHelper.AssertElementVisible(dataSources._activeDS); + dataSources.DeleteDatasouceFromActiveTab(dsName); + }); + }); +}); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/BugTests/Bug18876_Spec.ts b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/BugTests/Bug18876_Spec.ts new file mode 100644 index 000000000000..966649b33108 --- /dev/null +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/BugTests/Bug18876_Spec.ts @@ -0,0 +1,25 @@ +import { ObjectsRegistry } from "../../../../support/Objects/Registry"; + +const apiPage = ObjectsRegistry.ApiPage, + datasource = ObjectsRegistry.DataSources; + +describe("Application crashes when saving datasource", () => { + it("ensures application does not crash when saving datasource", () => { + apiPage.CreateAndFillApi( + "https://www.jsonplaceholder.com", + "FirstAPI", + 10000, + "POST", + ); + apiPage.SelectPaneTab("Authentication"); + cy.get(apiPage._saveAsDS) + .last() + .click({ force: true }); + cy.get(".t--close-editor").click({ force: true }); + cy.get(datasource._datasourceModalSave).click(); + // ensures app does not crash and datasource is saved. + cy.contains("Edit Datasource to access authentication settings").should( + "exist", + ); + }); +}); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/BugTests/Bug9334_Spec.ts b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/BugTests/Bug9334_Spec.ts index 10b1ae88f547..2e036062def9 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/BugTests/Bug9334_Spec.ts +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/BugTests/Bug9334_Spec.ts @@ -4,14 +4,13 @@ let dsName: any; const agHelper = ObjectsRegistry.AggregateHelper, ee = ObjectsRegistry.EntityExplorer, dataSources = ObjectsRegistry.DataSources, - propPane = ObjectsRegistry.PropertyPane, - homePage = ObjectsRegistry.HomePage, locator = ObjectsRegistry.CommonLocators, - table = ObjectsRegistry.Table; + table = ObjectsRegistry.Table, + appSettings = ObjectsRegistry.AppSettings; describe("Bug 9334: The Select widget value is sent as null when user switches between the pages", function() { before(() => { - propPane.ChangeTheme("Pampas"); + appSettings.OpenPaneAndChangeTheme("Pampas"); }); it("1. Create Postgress DS", function() { diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/BugTests/DatasourceSchema_spec.ts b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/BugTests/DatasourceSchema_spec.ts new file mode 100644 index 000000000000..2fab5ecc1770 --- /dev/null +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/BugTests/DatasourceSchema_spec.ts @@ -0,0 +1,24 @@ +import { ObjectsRegistry } from "../../../../support/Objects/Registry"; + +const agHelper = ObjectsRegistry.AggregateHelper, + dataSources = ObjectsRegistry.DataSources; + +describe("Datasource form related tests", function() { + it("1. Bug - 17238 Verify datasource structure refresh on save - invalid datasource", () => { + agHelper.GenerateUUID(); + cy.get("@guid").then((uid) => { + const guid = uid; + const dataSourceName = "Postgres " + guid; + cy.get(dataSources._dsEntityItem).click(); + dataSources.NavigateToDSCreateNew(); + dataSources.CreatePlugIn("PostgreSQL"); + agHelper.RenameWithInPane(dataSourceName, false); + dataSources.FillPostgresDSForm(false, "docker", "wrongPassword"); + dataSources.verifySchema(dataSourceName, "Failed to initialize pool"); + agHelper.GetNClick(dataSources._editButton) + dataSources.updatePassword("docker"); + dataSources.verifySchema(dataSourceName, "public.", true); + dataSources.DeleteDatasouceFromWinthinDS(dataSourceName); + }); + }); +}); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/BugTests/GitBugs.ts b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/BugTests/GitBugs.ts new file mode 100644 index 000000000000..f23afc110896 --- /dev/null +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/BugTests/GitBugs.ts @@ -0,0 +1,66 @@ +import { ObjectsRegistry } from "../../../../support/Objects/Registry"; +import { WIDGET } from "../../../../locators/WidgetLocators"; + +let dataSources = ObjectsRegistry.DataSources, + gitSync = ObjectsRegistry.GitSync, + agHelper = ObjectsRegistry.AggregateHelper, + ee = ObjectsRegistry.EntityExplorer, + propPane = ObjectsRegistry.PropertyPane, + locator = ObjectsRegistry.CommonLocators, + apiPage = ObjectsRegistry.ApiPage; + +let testName: any; +describe("Git Bugs", function() { + it("1. Bug 16248, When GitSync modal is open, block shortcut action execution", function() { + const largeResponseApiUrl = "https://jsonplaceholder.typicode.com/users"; + const modifierKey = Cypress.platform === "darwin" ? "meta" : "ctrl"; + apiPage.CreateAndFillApi(largeResponseApiUrl, "GitSyncTest"); + gitSync.OpenGitSyncModal(); + cy.get("body").type(`{${modifierKey}}{enter}`); + cy.get("@postExecute").should("not.exist"); + gitSync.CloseGitSyncModal(); + cy.get("body").type(`{${modifierKey}}{enter}`); + agHelper.ValidateNetworkStatus("@postExecute"); + }); + + it("2. Bug 18665 : Creates a new Git branch, Create datasource, discard it and check current branch", function() { + gitSync.CreateNConnectToGit(); + gitSync.CreateGitBranch(); + dataSources.NavigateToDSCreateNew(); + dataSources.CreatePlugIn("PostgreSQL"); + dataSources.SaveDSFromDialog(false); + agHelper.AssertElementVisible(gitSync._branchButton); + cy.get("@gitRepoName").then((repoName) => { + testName = repoName; + }); + }); + + it("3. Bug 18376: navigateTo fails to set queryParams if the app is connected to Git", () => { + ee.AddNewPage(); + ee.DragDropWidgetNVerify(WIDGET.TEXT); + ee.SelectEntityByName("Page1", "Pages"); + ee.DragDropWidgetNVerify(WIDGET.BUTTON); + propPane.SelectPropertiesDropDown("onClick", "Navigate to"); + agHelper.Sleep(500); + propPane.SelectPropertiesDropDown("onClick", "Page2", "Page"); + agHelper.EnterActionValue("Query Params", `{{{testQP: "Yes"}}}`); + ee.SelectEntityByName("Page2", "Pages"); + ee.SelectEntityByName("Text1", "Widgets"); + propPane.UpdatePropertyFieldValue( + "Text", + "{{appsmith.URL.queryParams.testQP}}", + ); + ee.SelectEntityByName("Page1", "Pages"); + agHelper.ClickButton("Submit"); + agHelper.Sleep(500); + agHelper + .GetText(locator._textWidget) + .then(($qp) => expect($qp).to.eq("Yes")); + agHelper.ValidateURL("branch=" + testName); //Validate we are still in Git branch + agHelper.ValidateURL("testQP=Yes"); //Validate we also ve the Query Params from Page1 + }); + + after(() => { + gitSync.DeleteTestGithubRepo(testName); + }); +}); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/BugTests/Moment_Spec.ts b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/BugTests/Moment_Spec.ts index 16da68129087..27c249f2a5f4 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/BugTests/Moment_Spec.ts +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/BugTests/Moment_Spec.ts @@ -8,15 +8,15 @@ const agHelper = ObjectsRegistry.AggregateHelper, table = ObjectsRegistry.Table, locator = ObjectsRegistry.CommonLocators, deployMode = ObjectsRegistry.DeployMode, - jsEditor = ObjectsRegistry.JSEditor; + jsEditor = ObjectsRegistry.JSEditor, + appSettings = ObjectsRegistry.AppSettings; describe("Bug #14299 - The data from the query does not show up on the widget", function() { before(() => { cy.fixture("/Bugs/14299dsl").then((val: any) => { agHelper.AddDsl(val); }); - propPane.ChangeThemeColor(13, "Primary"); - propPane.ChangeThemeColor(22, "Background"); + appSettings.OpenPaneAndChangeThemeColors(13, 22); }); it("1. Create Postgress DS", function() { @@ -47,7 +47,10 @@ describe("Bug #14299 - The data from the query does not show up on the widget", ); ee.SelectEntityByName("Table1"); - propPane.UpdatePropertyFieldValue("Table Data", `{{JSObject1.runAstros.data}}`); + propPane.UpdatePropertyFieldValue( + "Table Data", + `{{JSObject1.runAstros.data}}`, + ); ee.SelectEntityByName("DatePicker1"); propPane.UpdatePropertyFieldValue( @@ -97,7 +100,7 @@ describe("Bug #14299 - The data from the query does not show up on the widget", table.NavigateToNextPage(false); table.WaitUntilTableLoad(); - table.SelectTableRow(1);//Asserting here table is available for selection + table.SelectTableRow(1); //Asserting here table is available for selection table.ReadTableRowColumnData(1, 0, 200).then(($cellData) => { expect($cellData).to.eq("286"); }); @@ -113,15 +116,20 @@ describe("Bug #14299 - The data from the query does not show up on the widget", deployMode.NavigateBacktoEditor(); agHelper.AssertContains("ran successfully"); //runAstros triggered on PageLaoad of Edit page! ee.ExpandCollapseEntity("Queries/JS"); - ee.ActionContextMenuByEntityName("getAstronauts", "Delete", "Are you sure?"); + ee.ActionContextMenuByEntityName( + "getAstronauts", + "Delete", + "Are you sure?", + ); ee.ActionContextMenuByEntityName( "JSObject1", "Delete", - "Are you sure?", true + "Are you sure?", + true, ); deployMode.DeployApp(locator._widgetInDeployed("tablewidget"), false); deployMode.NavigateBacktoEditor(); ee.ExpandCollapseEntity("Datasources"); dataSources.DeleteDatasouceFromWinthinDS(dsName, 200); //ProductLines, Employees pages are still using this ds }); -}); \ No newline at end of file +}); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DynamicHeight/DynamicHeight_Auto_Height_Limit_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DynamicHeight/DynamicHeight_Auto_Height_Limit_spec.js new file mode 100644 index 000000000000..9db436727eb9 --- /dev/null +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DynamicHeight/DynamicHeight_Auto_Height_Limit_spec.js @@ -0,0 +1,49 @@ +const dsl = require("../../../../fixtures/dynamicHeightContainerdsl.json"); +const commonlocators = require("../../../../locators/commonlocators.json"); + +describe("Dynamic Height Width validation with limits", function() { + it("Validate change in auto height with limits width for widgets and highlight section validation", function() { + cy.addDsl(dsl); + cy.wait(3000); //for dsl to settle + cy.openPropertyPane("containerwidget"); + cy.get(commonlocators.generalSectionHeight).should("be.visible"); + cy.changeLayoutHeight(commonlocators.autoHeightWithLimits); + cy.wait(3000); //for dsl to settle + //cy.checkMinDefaultValue(commonlocators.minHeight,"4") + //cy.testJsontext(commonlocators.minHeight, "5"); + //cy.get(commonlocators.overlayMin).should("be.visible"); + cy.get("[data-cy='t--auto-height-overlay-handles-min']").trigger( + "mouseover", + ); + cy.contains("Min-Height: 10 rows"); + cy.get("[data-cy='t--auto-height-overlay-handles-min']").should( + "be.visible", + ); + cy.get("[data-cy='t--auto-height-overlay-handles-min'] div") + .eq(0) + .should("have.css", "background-color", "rgb(243, 43, 139)"); + /*cy.get(commonlocators.overlayMin).should( + "have.css", + "background-color", + "rgba(243, 43, 139, 0.1)", + );*/ + cy.get("[data-cy='t--auto-height-overlay-handles-max']").trigger( + "mouseover", + ); + cy.contains("Max-Height: 12 rows"); + //cy.checkMaxDefaultValue(commonlocators.maxHeight,"40") + //cy.testJsontext(commonlocators.maxHeight, "60"); + cy.get("[data-cy='t--auto-height-overlay-handles-max']").should( + "be.visible", + ); + cy.get("[data-cy='t--auto-height-overlay-handles-max'] div") + .eq(0) + .should("have.css", "background-color", "rgb(243, 43, 139)"); + //cy.contains("Max-Height: 60 rows"); + cy.changeLayoutHeight(commonlocators.fixed); + cy.changeLayoutHeight(commonlocators.autoHeightWithLimits); + //cy.contains("Min-Height: 5 rows"); + //cy.checkMinDefaultValue(commonlocators.minHeight,"5") + // cy.checkMaxDefaultValue(commonlocators.maxHeight,"60") + }); +}); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DynamicHeight/DynamicHeight_Auto_Height_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DynamicHeight/DynamicHeight_Auto_Height_spec.js new file mode 100644 index 000000000000..efc98b80e357 --- /dev/null +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DynamicHeight/DynamicHeight_Auto_Height_spec.js @@ -0,0 +1,79 @@ +const dsl = require("../../../../fixtures/dynamicHeightContainerCheckboxdsl.json"); +const cdsl = require("../../../../fixtures/dynamicHeigthContainerFixedDsl.json"); +const commonlocators = require("../../../../locators/commonlocators.json"); +import { ObjectsRegistry } from "../../../../support/Objects/Registry"; +const agHelper = ObjectsRegistry.AggregateHelper; + +describe("Dynamic Height Width validation", function() { + afterEach(() => { + agHelper.SaveLocalStorageCache(); + }); + + beforeEach(() => { + agHelper.RestoreLocalStorageCache(); + }); + it("Validate change with auto height width for widgets", function() { + cy.addDsl(dsl); + cy.wait(3000); //for dsl to settle + cy.openPropertyPane("containerwidget"); + //cy.changeLayoutHeight(commonlocators.autoHeight); + cy.openPropertyPane("checkboxgroupwidget"); + //cy.changeLayoutHeight(commonlocators.autoHeight); + cy.get(".t--widget-containerwidget") + .invoke("css", "height") + .then((height) => { + cy.get(".t--widget-checkboxgroupwidget") + .invoke("css", "height") + .then((checkboxheight) => { + cy.get(commonlocators.addOption).click(); + cy.wait(200); + cy.wait("@updateLayout").should( + "have.nested.property", + "response.body.responseMeta.status", + 200, + ); + cy.wait(3000); + cy.get(".t--widget-checkboxgroupwidget") + .invoke("css", "height") + .then((newcheckboxheight) => { + expect(checkboxheight).to.not.equal(newcheckboxheight); + }); + }); + cy.wait(2000); + cy.get(".t--widget-containerwidget") + .invoke("css", "height") + .then((newheight) => { + expect(height).to.not.equal(newheight); + }); + }); + }); + + it("Validate container with auto height and child widgets with fixed height", function() { + cy.addDsl(cdsl); + cy.wait(3000); //for dsl to settle + //cy.openPropertyPane("containerwidget"); + //cy.changeLayoutHeight(commonlocators.autoHeight); + cy.openPropertyPane("checkboxgroupwidget"); + cy.get(commonlocators.generalSectionHeight) + .scrollIntoView() + .should("be.visible"); + cy.changeLayoutHeight(commonlocators.autoHeight); + cy.openPropertyPane("inputwidgetv2"); + cy.get(commonlocators.generalSectionHeight) + .scrollIntoView() + .should("be.visible"); + cy.changeLayoutHeight(commonlocators.autoHeight); + cy.get(".t--widget-containerwidget") + .invoke("css", "height") + .then((height) => { + cy.openPropertyPane("containerwidget"); + cy.changeLayoutHeight(commonlocators.autoHeight); + cy.wait(4000); + cy.get(".t--widget-containerwidget") + .invoke("css", "height") + .then((newheight) => { + expect(height).to.not.equal(newheight); + }); + }); + }); +}); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DynamicHeight/DynamicHeight_CanvasHeight_resize_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DynamicHeight/DynamicHeight_CanvasHeight_resize_spec.js new file mode 100644 index 000000000000..e1684a2378bd --- /dev/null +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DynamicHeight/DynamicHeight_CanvasHeight_resize_spec.js @@ -0,0 +1,78 @@ +const dsl = require("../../../../fixtures/dynamicHeightCanvasResizeDsl.json"); +const commonlocators = require("../../../../locators/commonlocators.json"); +import { ObjectsRegistry } from "../../../../support/Objects/Registry"; +const agHelper = ObjectsRegistry.AggregateHelper; + +describe("Dynamic Height Width validation with multiple containers and text widget", function() { + afterEach(() => { + agHelper.SaveLocalStorageCache(); + }); + + beforeEach(() => { + agHelper.RestoreLocalStorageCache(); + }); + it("Validate change with auto height width for widgets", function() { + const textMsg = + "Dynamic panel validation for text widget wrt height Dynamic panel validation for text widget wrt height Dynamic panel validation for text widget wrt height"; + cy.addDsl(dsl); + cy.wait(3000); //for dsl to settle + cy.get(".t--widget-containerwidget") + .eq(0) + .invoke("css", "height") + .then((oheight) => { + cy.get(".t--widget-textwidget") + .invoke("css", "height") + .then((tnewheight) => { + cy.openPropertyPane("textwidget"); + cy.get(".t--widget-textwidget") + .invoke("css", "height") + .then((theight) => { + //Changing the text label + cy.testCodeMirror(textMsg); + cy.moveToStyleTab(); + cy.ChangeTextStyle( + this.data.TextHeading, + commonlocators.headingTextStyle, + textMsg, + ); + cy.wait("@updateLayout"); + cy.get(".t--widget-textwidget") + .invoke("css", "height") + .then((tnewheight) => { + expect(theight).to.not.equal(tnewheight); + cy.get(".t--widget-containerwidget") + .eq(0) + .invoke("css", "height") + .then((newcheight) => { + expect(oheight).to.not.equal(newcheight); + cy.moveToContentTab(); + const modifierKey = + Cypress.platform === "darwin" ? "meta" : "ctrl"; + cy.get(".CodeMirror textarea") + .first() + .focus() + .type(`{${modifierKey}}a`) + .then(($cm) => { + if ($cm.val() !== "") { + cy.get(".CodeMirror textarea") + .first() + .clear({ + force: true, + }); + } + }); + cy.wait("@updateLayout"); + cy.wait(4000); + cy.get(".t--widget-containerwidget") + .eq(0) + .invoke("css", "height") + .then((updatedcheight) => { + expect(oheight).to.equal(updatedcheight); + }); + }); + }); + }); + }); + }); + }); +}); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DynamicHeight/DynamicHeight_Container_Scroll_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DynamicHeight/DynamicHeight_Container_Scroll_spec.js new file mode 100644 index 000000000000..5a3d589db909 --- /dev/null +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DynamicHeight/DynamicHeight_Container_Scroll_spec.js @@ -0,0 +1,16 @@ +const dsl = require("../../../../fixtures/dynamicHeightContainerScrolldsl.json"); +const commonlocators = require("../../../../locators/commonlocators.json"); + +describe("Dynamic Height Width validation", function() { + it("Validate change with auto height width for widgets", function() { + cy.addDsl(dsl); + cy.wait(3000); //for dsl to settle + cy.openPropertyPane("containerwidget"); + cy.get(".t--widget-textwidget").trigger("mouseover", { force: true }); // Scroll 'sidebar' to its bottom + cy.openPropertyPane("textwidget"); + //cy.PublishtheApp(); + //cy.wait(5000); + //cy.get(".t--widget-containerwidget").trigger("mouseover",{force:true}) // Scroll 'sidebar' to its bottom + cy.wait(5000); + }); +}); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DynamicHeight/DynamicHeight_Container_collapse_undo_redoSpec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DynamicHeight/DynamicHeight_Container_collapse_undo_redoSpec.js new file mode 100644 index 000000000000..4fe9e47dd0f3 --- /dev/null +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DynamicHeight/DynamicHeight_Container_collapse_undo_redoSpec.js @@ -0,0 +1,32 @@ +const dsl = require("../../../../fixtures/DynamicHeightDefaultHeightdsl.json"); +const commonlocators = require("../../../../locators/commonlocators.json"); + +describe("Dynamic Height Width validation", function() { + it("Validate change with auto height width for widgets", function() { + const modifierKey = Cypress.platform === "darwin" ? "meta" : "ctrl"; + cy.addDsl(dsl); + cy.wait(3000); //for dsl to settle + cy.openPropertyPane("containerwidget"); + cy.get(".t--widget-containerwidget") + .invoke("css", "height") + .then((height) => { + cy.openPropertyPane("buttonwidget"); + cy.get("body").type("{del}", { force: true }); + cy.wait(2000); + cy.get(".t--widget-containerwidget") + .invoke("css", "height") + .then((newheight) => { + expect(height).to.not.equal(newheight); + expect(newheight).to.equal("100px"); + cy.get("body").type(`{${modifierKey}}z`); + cy.wait(2000); + cy.get(".t--widget-containerwidget") + .invoke("css", "height") + .then((oheight) => { + expect(oheight).to.equal(height); + expect(oheight).to.not.equal(newheight); + }); + }); + }); + }); +}); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DynamicHeight/DynamicHeight_Form_With_SwitchGroup_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DynamicHeight/DynamicHeight_Form_With_SwitchGroup_spec.js new file mode 100644 index 000000000000..1d98ca2d3076 --- /dev/null +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DynamicHeight/DynamicHeight_Form_With_SwitchGroup_spec.js @@ -0,0 +1,95 @@ +const dsl = require("../../../../fixtures/dynamicHeightFormSwitchdsl.json"); +const commonlocators = require("../../../../locators/commonlocators.json"); + +describe("Dynamic Height Width validation", function() { + it("Validate change with auto height width for Form/Switch", function() { + cy.addDsl(dsl); + cy.wait(3000); //for dsl to settle + cy.openPropertyPane("formwidget"); + cy.get(".t--widget-formwidget") + .invoke("css", "height") + .then((formheight) => { + cy.changeLayoutHeight(commonlocators.autoHeight); + cy.openPropertyPane("switchgroupwidget"); + cy.changeLayoutHeight(commonlocators.autoHeight); + cy.get(".t--widget-switchgroupwidget") + .invoke("css", "height") + .then((switchheight) => { + cy.get(".t--widget-formwidget") + .invoke("css", "height") + .then((newformheight) => { + //expect(formheight).to.not.equal(newformheight) + cy.updateCodeInput( + ".t--property-control-options", + `[ + { + "label": "Blue", + "value": "BLUE" + }, + { + "label": "Green", + "value": "GREEN" + }, + { + "label": "Red", + "value": "RED" + }, + { + "label": "Yellow", + "value": "YELLOW" + }, + { + "label": "Purple", + "value": "PURPLE" + }, + { + "label": "Pink", + "value": "PINK" + }, + { + "label": "Black", + "value": "BLACK" + }, + { + "label": "Grey", + "value": "GREY" + }, + { + "label": "Orange", + "value": "ORANGE" + }, + { + "label": "Cream", + "value": "CREAM" + } + ]`, + ); + cy.get(".t--widget-switchgroupwidget") + .invoke("css", "height") + .then((newswitchheight) => { + cy.get(".t--widget-formwidget") + .invoke("css", "height") + .then((updatedformheight) => { + expect(newformheight).to.not.equal(updatedformheight); + expect(switchheight).to.not.equal(newswitchheight); + }); + }); + }); + }); + }); + cy.get(".t--draggable-switchgroupwidget .bp3-control-indicator") + .first() + .click({ force: true }); + cy.wait(3000); + cy.get(".t--modal-widget").should("have.length", 1); + cy.get(".t--widget-propertypane-toggle") + .first() + .click({ force: true }); + //cy.changeLayoutHeight(commonlocators.autoHeightWithLimits); + //cy.checkMinDefaultValue(commonlocators.minHeight,"4") + //cy.checkMaxDefaultValue(commonlocators.maxHeight,"24") + cy.changeLayoutHeight(commonlocators.autoHeight); + cy.wait(3000); + cy.get("button:contains('Close')").click({ force: true }); + }); +}); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DynamicHeight/DynamicHeight_JsonForm_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DynamicHeight/DynamicHeight_JsonForm_spec.js new file mode 100644 index 000000000000..9f9abe55730c --- /dev/null +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DynamicHeight/DynamicHeight_JsonForm_spec.js @@ -0,0 +1,62 @@ +const dsl = require("../../../../fixtures/jsonFormDynamicHeightDsl.json"); +const commonlocators = require("../../../../locators/commonlocators.json"); + +describe("Dynamic Height Width validation", function() { + it("Validate change with auto height width for JsonForm", function() { + cy.addDsl(dsl); + cy.wait(3000); //for dsl to settle + cy.openPropertyPane("jsonformwidget"); + cy.get(".t--widget-jsonformwidget") + .invoke("css", "height") + .then((formheight) => { + cy.changeLayoutHeight(commonlocators.autoHeight); + cy.wait(5000); + cy.get(".t--widget-jsonformwidget") + .invoke("css", "height") + .then((newformheight) => { + expect(formheight).to.not.equal(newformheight); + cy.get(".t--show-column-btn") + .eq(0) + .click({ force: true }); + cy.get(".t--show-column-btn") + .eq(1) + .click({ force: true }); + cy.get(".t--show-column-btn") + .eq(2) + .click({ force: true }); + // cy.get("[data-cy='t--resizable-handle-TOP']") + // .within(($el) => { + // cy.window().then((win) => { + // const after = win.getComputedStyle($el[0], "::after"); + // expect(after).not.to.exist + // }); + // }); + // cy.get("[data-cy='t--resizable-handle-BOTTOM']").should("not.exist"); + cy.changeLayoutHeight(commonlocators.fixed); + cy.wait(5000); + cy.get(".t--widget-jsonformwidget") + .invoke("css", "height") + .then((updatedformheight) => { + expect(newformheight).to.not.equal(updatedformheight); + cy.get(".t--show-column-btn") + .eq(2) + .click({ force: true }); + cy.get(".t--show-column-btn") + .eq(1) + .click({ force: true }); + // cy.get("[data-cy='t--resizable-handle-TOP']").should("exist"); + // cy.get("[data-cy='t--resizable-handle-BOTTOM']").should("exist"); + cy.changeLayoutHeight(commonlocators.autoHeight); + cy.wait(5000); + cy.get(".t--widget-jsonformwidget") + .invoke("css", "height") + .then((newupdatedformheight) => { + expect(updatedformheight).to.not.equal( + newupdatedformheight, + ); + }); + }); + }); + }); + }); +}); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DynamicHeight/DynamicHeight_List_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DynamicHeight/DynamicHeight_List_spec.js new file mode 100644 index 000000000000..86a85351ab0f --- /dev/null +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DynamicHeight/DynamicHeight_List_spec.js @@ -0,0 +1,38 @@ +const dsl = require("../../../../fixtures/dynamicHeightListDsl.json"); +const commonlocators = require("../../../../locators/commonlocators.json"); +import { ObjectsRegistry } from "../../../../support/Objects/Registry"; +const agHelper = ObjectsRegistry.AggregateHelper; + +describe("Dynamic Height Width validation", function() { + afterEach(() => { + agHelper.SaveLocalStorageCache(); + }); + + beforeEach(() => { + agHelper.RestoreLocalStorageCache(); + }); + it("Validate change with auto height width for widgets", function() { + const textMsg = "Dynamic panel validation for text widget wrt height"; + cy.addDsl(dsl); + cy.wait(3000); //for dsl to settle + cy.openPropertyPane("listwidget"); + cy.get(".t--widget-listwidget") + .invoke("css", "height") + .then((lheight) => { + cy.get(commonlocators.generalSectionHeight).should("not.exist"); + cy.openPropertyPaneWithIndex("textwidget", 0); + cy.get(commonlocators.generalSectionHeight).should("be.visible"); + cy.changeLayoutHeightWithoutWait(commonlocators.autoHeight); + cy.testCodeMirror(textMsg); + cy.openPropertyPaneWithIndex("textwidget", 1); + cy.get(commonlocators.generalSectionHeight).should("be.visible"); + cy.changeLayoutHeightWithoutWait(commonlocators.autoHeight); + cy.testCodeMirror(textMsg); + cy.get(".t--widget-listwidget") + .invoke("css", "height") + .then((newheight) => { + expect(lheight).to.equal(newheight); + }); + }); + }); +}); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DynamicHeight/DynamicHeight_Multiple_Container_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DynamicHeight/DynamicHeight_Multiple_Container_spec.js new file mode 100644 index 000000000000..e62feac13b27 --- /dev/null +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DynamicHeight/DynamicHeight_Multiple_Container_spec.js @@ -0,0 +1,71 @@ +const dsl = require("../../../../fixtures/multipleContainerdsl.json"); +const commonlocators = require("../../../../locators/commonlocators.json"); + +describe("Dynamic Height Width validation for multiple container", function() { + before(() => { + cy.addDsl(dsl); + }); + it("Validate change in auto height width with multiple containers", function() { + cy.wait(3000); //for dsl to settle + cy.openPropertyPaneWithIndex("containerwidget", 0); + cy.changeLayoutHeight(commonlocators.fixed); + cy.changeLayoutHeight(commonlocators.autoHeight); + cy.openPropertyPaneWithIndex("containerwidget", 1); + cy.changeLayoutHeight(commonlocators.fixed); + cy.changeLayoutHeight(commonlocators.autoHeight); + cy.openPropertyPane("checkboxgroupwidget"); + cy.changeLayoutHeight(commonlocators.fixed); + cy.changeLayoutHeight(commonlocators.autoHeight); + cy.wait(2000); + cy.get(".t--widget-containerwidget") + .eq(0) + .invoke("css", "height") + .then((oheight) => { + cy.get(".t--widget-containerwidget") + .eq(1) + .invoke("css", "height") + .then((mheight) => { + cy.get(".t--widget-containerwidget") + .eq(2) + .invoke("css", "height") + .then((iheight) => { + cy.get(".t--widget-checkboxgroupwidget") + .invoke("css", "height") + .then((checkboxheight) => { + cy.get(commonlocators.addOption).click({ force: true }); + cy.wait("@updateLayout").should( + "have.nested.property", + "response.body.responseMeta.status", + 200, + ); + cy.wait(3000); + cy.get(".t--widget-checkboxgroupwidget") + .invoke("css", "height") + .then((newcheckboxheight) => { + expect(checkboxheight).to.not.equal(newcheckboxheight); + }); + }); + cy.wait(2000); + cy.get(".t--widget-containerwidget") + .eq(0) + .invoke("css", "height") + .then((onewheight) => { + expect(oheight).to.not.equal(onewheight); + }); + cy.get(".t--widget-containerwidget") + .eq(1) + .invoke("css", "height") + .then((mnewheight) => { + expect(mheight).to.not.equal(mnewheight); + }); + cy.get(".t--widget-containerwidget") + .eq(2) + .invoke("css", "height") + .then((inewheight) => { + expect(iheight).to.not.equal(inewheight); + }); + }); + }); + }); + }); +}); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DynamicHeight/DynamicHeight_Tab_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DynamicHeight/DynamicHeight_Tab_spec.js new file mode 100644 index 000000000000..2c0e65897218 --- /dev/null +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DynamicHeight/DynamicHeight_Tab_spec.js @@ -0,0 +1,120 @@ +const dsl = require("../../../../fixtures/dynamicTabWidgetdsl.json"); +const commonlocators = require("../../../../locators/commonlocators.json"); +const publish = require("../../../../locators/publishWidgetspage.json"); + +describe("Dynamic Height Width validation for Tab widget", function() { + before(() => { + cy.addDsl(dsl); + }); + + function validateHeight() { + cy.wait(5000); + cy.get(".t--tabid-tab1").click({ force: true }); + cy.wait(3000); + cy.get(".t--widget-tabswidget") + .invoke("css", "height") + .then((theight) => { + cy.get(".t--tabid-tab2").click({ force: true }); + cy.wait(3000); + cy.wait("@updateLayout").should( + "have.nested.property", + "response.body.responseMeta.status", + 200, + ); + //cy.get(".t--draggable-checkboxwidget .bp3-control-indicator").click({ force: true }) + cy.get(".t--widget-tabswidget") + .invoke("css", "height") + .then((tnewheight) => { + expect(theight).to.not.equal(tnewheight); + }); + }); + } + it("Tab widget validation of height with dynamic height feature with publish mode", function() { + //changing the Text Name and verifying + cy.wait(3000); + cy.openPropertyPane("tabswidget"); + cy.changeLayoutHeightWithoutWait(commonlocators.autoHeight); + cy.get(".t--tabid-tab1").click({ force: true }); + validateHeight(); + cy.PublishtheApp(); + validateHeight(); + cy.get(publish.backToEditor).click(); + cy.get(".t--switch-preview-mode-toggle").should("be.visible"); + cy.get(".t--switch-preview-mode-toggle").click({ force: true }); + cy.wait(5000); + cy.get(".t--tabid-tab1").click({ force: true }); + cy.wait(3000); + cy.get(".t--widget-tabswidget") + .invoke("css", "height") + .then((theight) => { + cy.get(".t--tabid-tab2").click({ force: true }); + cy.wait(3000); + //cy.get(".t--draggable-checkboxwidget .bp3-control-indicator").click({ force: true }) + cy.get(".t--widget-tabswidget") + .invoke("css", "height") + .then((tnewheight) => { + expect(theight).to.not.equal(tnewheight); + }); + }); + }); + + it("Tab widget validation of height with preview mode", function() { + cy.get(".t--switch-comment-mode-off").should("be.visible"); + cy.get(".t--switch-comment-mode-off").click({ force: true }); + cy.wait(3000); + cy.openPropertyPane("tabswidget"); + cy.changeLayoutHeight(commonlocators.fixed); + cy.get(".t--tabid-tab1").click({ force: true }); + cy.wait(3000); + cy.get(".t--widget-tabswidget") + .invoke("css", "height") + .then((theight) => { + cy.get(".t--tabid-tab2").click({ force: true }); + cy.wait(3000); + //cy.get(".t--draggable-checkboxwidget .bp3-control-indicator").click({ force: true }) + cy.get(".t--widget-tabswidget") + .invoke("css", "height") + .then((tnewheight) => { + expect(theight).to.equal(tnewheight); + cy.get(commonlocators.showTabsControl).click({ force: true }); + cy.wait("@updateLayout").should( + "have.nested.property", + "response.body.responseMeta.status", + 200, + ); + cy.get(".t--widget-tabswidget") + .invoke("css", "height") + .then((upheight) => { + expect(tnewheight).to.equal(upheight); + cy.get(".t--tabid-tab1").should("not.exist"); + cy.get(".t--tabid-tab2").should("not.exist"); + }); + }); + }); + }); + + it("Tab widget validation of height with reload", function() { + cy.wait(3000); + cy.openPropertyPane("tabswidget"); + cy.get(commonlocators.generalSectionHeight).should("be.visible"); + cy.get(commonlocators.showTabsControl).click({ force: true }); + cy.changeLayoutHeight(commonlocators.autoHeight); + cy.wait(3000); + cy.get(".t--tabid-tab1").click({ force: true }); + cy.wait(5000); + cy.get(".t--widget-tabswidget") + .invoke("css", "height") + .then((theight) => { + cy.get(".t--tabid-tab2").click({ force: true }); + cy.changeLayoutHeight(commonlocators.fixed); + cy.wait(3000); + cy.reload(); + cy.openPropertyPane("tabswidget"); + cy.get(".t--widget-tabswidget") + .invoke("css", "height") + .then((tnewheight) => { + expect(theight).to.not.equal(tnewheight); + }); + }); + }); +}); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DynamicHeight/DynamicHeight_Text_Widget_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DynamicHeight/DynamicHeight_Text_Widget_spec.js new file mode 100644 index 000000000000..362b00465042 --- /dev/null +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DynamicHeight/DynamicHeight_Text_Widget_spec.js @@ -0,0 +1,42 @@ +const dsl = require("../../../../fixtures/textWidgetDynamicdsl.json"); +const commonlocators = require("../../../../locators/commonlocators.json"); + +describe("Dynamic Height Width validation for text widget", function() { + before(() => { + cy.addDsl(dsl); + }); + it("Text widget validation of height with dynamic height feature", function() { + const textMsg = "Dynamic panel validation for text widget wrt height"; + //changing the Text Name and verifying + cy.openPropertyPane("textwidget"); + cy.get(commonlocators.generalSectionHeight).should("be.visible"); + cy.changeLayoutHeightWithoutWait(commonlocators.autoHeight); + cy.get(".t--widget-textwidget") + .invoke("css", "height") + .then((theight) => { + //Changing the text label + cy.testCodeMirror(textMsg); + cy.moveToStyleTab(); + cy.ChangeTextStyle( + this.data.TextHeading, + commonlocators.headingTextStyle, + textMsg, + ); + cy.wait("@updateLayout"); + cy.get(".t--widget-textwidget") + .invoke("css", "height") + .then((tnewheight) => { + expect(theight).to.not.equal(tnewheight); + }); + cy.PublishtheApp(); + cy.get(commonlocators.headingTextStyle) + .should("have.text", textMsg) + .should("have.css", "font-size", "16px"); + cy.get(".t--widget-textwidget") + .invoke("css", "height") + .then((tnewheight) => { + expect(theight).to.not.equal(tnewheight); + }); + }); + }); +}); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DynamicHeight/DynamicHeight_Text_With_Different_Size_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DynamicHeight/DynamicHeight_Text_With_Different_Size_spec.js new file mode 100644 index 000000000000..40e5278db284 --- /dev/null +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DynamicHeight/DynamicHeight_Text_With_Different_Size_spec.js @@ -0,0 +1,138 @@ +const dsl = require("../../../../fixtures/alignmentWithDynamicHeightDsl.json"); +const commonlocators = require("../../../../locators/commonlocators.json"); + +describe("Dynamic Height Width validation", function() { + function validateCssProperties(property) { + cy.get("button:contains('Small')").click({ force: true }); + cy.wait(3000); + cy.selectEntityByName("Text1"); + cy.get(".t--widget-textwidget") + .eq(0) + .invoke("css", property) + .then((firstText) => { + cy.selectEntityByName("Text2"); + cy.get(".t--widget-textwidget") + .eq(1) + .invoke("css", property) + .then((secondText) => { + cy.selectEntityByName("Text3"); + cy.get(".t--widget-textwidget") + .eq(2) + .invoke("css", property) + .then((thirdText) => { + cy.selectEntityByName("Text4"); + cy.get(".t--widget-textwidget") + .eq(3) + .invoke("css", property) + .then((fourthText) => { + cy.get("button:contains('Large')").click({ force: true }); + cy.selectEntityByName("Text1"); + cy.get(".t--widget-textwidget") + .eq(0) + .invoke("css", property) + .then((largefirstText) => { + cy.selectEntityByName("Text2"); + cy.get(".t--widget-textwidget") + .eq(1) + .invoke("css", property) + .then((largesecondText) => { + cy.selectEntityByName("Text3"); + cy.get(".t--widget-textwidget") + .eq(2) + .invoke("css", property) + .then((largethirdText) => { + cy.selectEntityByName("Text4"); + cy.get(".t--widget-textwidget") + .eq(3) + .invoke("css", property) + .then((largefourthText) => { + if (property == "left") { + expect(firstText).to.equal( + largefirstText, + ); + expect(secondText).to.equal( + largesecondText, + ); + expect(thirdText).to.equal( + largethirdText, + ); + expect(fourthText).to.equal( + largefourthText, + ); + } else { + expect(firstText).to.not.equal( + largefirstText, + ); + expect(secondText).to.not.equal( + largesecondText, + ); + expect(thirdText).to.not.equal( + largethirdText, + ); + expect(fourthText).to.not.equal( + largefourthText, + ); + } + cy.get("button:contains('Small')").click({ + force: true, + }); + cy.wait(3000); + cy.selectEntityByName("Text1"); + cy.get(".t--widget-textwidget") + .eq(0) + .invoke("css", property) + .then((updatelargefirstText) => { + cy.selectEntityByName("Text2"); + cy.get(".t--widget-textwidget") + .eq(1) + .invoke("css", property) + .then((updatelargesecondText) => { + cy.selectEntityByName("Text3"); + cy.get(".t--widget-textwidget") + .eq(2) + .invoke("css", property) + .then((updatelargethirdText) => { + cy.selectEntityByName("Text4"); + cy.get(".t--widget-textwidget") + .eq(3) + .invoke("css", property) + .then( + (updatelargefourthText) => { + //expect(firstText).to.equal(updatelargefirstText); + expect( + secondText, + ).to.equal( + updatelargesecondText, + ); + expect( + thirdText, + ).to.equal( + updatelargethirdText, + ); + expect( + fourthText, + ).to.equal( + updatelargefourthText, + ); + }, + ); + }); + }); + }); + }); + }); + }); + }); + }); + }); + }); + }); + } + it("Validate change with auto height width for text widgets", function() { + cy.addDsl(dsl); + cy.wait(30000); //for dsl to settled + validateCssProperties("height"); + //validateCssProperties("top"); + validateCssProperties("left"); + }); +}); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DynamicHeight/DynamicHeight_Visibility_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DynamicHeight/DynamicHeight_Visibility_spec.js new file mode 100644 index 000000000000..17c2fac37cdd --- /dev/null +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DynamicHeight/DynamicHeight_Visibility_spec.js @@ -0,0 +1,52 @@ +const commonlocators = require("../../../../locators/commonlocators.json"); +const dsl = require("../../../../fixtures/invisibleWidgetdsl.json"); + +describe("Dynamic Height Width validation for Visibility", function() { + before(() => { + cy.addDsl(dsl); + }); + it("Validating visbility/invisiblity of widget with dynamic height feature", function() { + //changing the Text Name and verifying + cy.wait(3000); + cy.openPropertyPane("containerwidget"); + cy.changeLayoutHeightWithoutWait(commonlocators.autoHeight); + cy.openPropertyPaneWithIndex("inputwidgetv2", 0); + cy.changeLayoutHeightWithoutWait(commonlocators.autoHeight); + cy.openPropertyPaneWithIndex("inputwidgetv2", 1); + cy.changeLayoutHeightWithoutWait(commonlocators.autoHeight); + cy.get(".t--widget-containerwidget") + .invoke("css", "height") + .then((theight) => { + cy.get(commonlocators.checkboxIndicator).click({ force: true }); + cy.get(".t--widget-containerwidget") + .invoke("css", "height") + .then((tnewheight) => { + expect(theight).to.equal(tnewheight); + cy.get("label:Contains('On')").should("not.be.enabled"); + }); + }); + cy.PublishtheApp(); + cy.get(".t--widget-containerwidget") + .invoke("css", "height") + .then((theight) => { + cy.get(".bp3-control-indicator").click({ force: true }); + cy.wait(2000); + cy.get(".t--widget-containerwidget") + .invoke("css", "height") + .then((tnewheight) => { + expect(theight).to.not.equal(tnewheight); + cy.get("label:Contains('On')").should("not.exist"); + cy.get("label:Contains('Off')").should("be.visible"); + cy.get(".bp3-control-indicator").click({ force: true }); + cy.wait(2000); + cy.get(".t--widget-containerwidget") + .invoke("css", "height") + .then((tonheight) => { + expect(tonheight).to.not.equal(tnewheight); + cy.get("label:Contains('Off')").should("not.exist"); + cy.get("label:Contains('On')").should("be.visible"); + }); + }); + }); + }); +}); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_API_Pane_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_API_Pane_spec.js index 7ec702dc81d0..b02808b1901d 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_API_Pane_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_API_Pane_spec.js @@ -9,7 +9,7 @@ let ee = ObjectsRegistry.EntityExplorer, homePage = ObjectsRegistry.HomePage; describe("Entity explorer API pane related testcases", function() { - it("Empty Message validation for Widgets/API/Queries", function() { + it("1. Empty Message validation for Widgets/API/Queries", function() { homePage.NavigateToHome(); homePage.CreateNewWorkspace("EmptyMsgCheck"); homePage.CreateAppInWorkspace("EmptyMsgCheck"); @@ -32,7 +32,7 @@ describe("Entity explorer API pane related testcases", function() { agHelper.AssertElementVisible(locator._visibleTextDiv("NEW DATASOURCE")); }); - it("Move to page / edit API name /properties validation", function() { + it("2. Move to page / edit API name /properties validation", function() { cy.NavigateToAPI_Panel(); cy.CreateAPI("FirstAPI"); cy.log("Creation of FirstAPI Action successful"); @@ -45,7 +45,7 @@ describe("Entity explorer API pane related testcases", function() { testdata.Get, ); cy.ResponseStatusCheck(testdata.successStatusCode); - cy.CheckAndUnfoldEntityItem("Queries/JS"); + ee.ExpandCollapseEntity("Queries/JS"); ee.ActionContextMenuByEntityName("FirstAPI", "Show Bindings"); cy.get(apiwidget.propertyList).then(function($lis) { expect($lis).to.have.length(5); @@ -59,9 +59,8 @@ describe("Entity explorer API pane related testcases", function() { .contains(testdata.Get) .should("be.visible"); cy.Createpage(pageid); - cy.get(".t--entity-name") - .contains("Page1") - .click(); + ee.SelectEntityByName("Page1"); + ee.ExpandCollapseEntity("Queries/JS"); ee.ActionContextMenuByEntityName("FirstAPI", "Edit Name"); cy.EditApiNameFromExplorer("SecondAPI"); cy.xpath(apiwidget.popover) @@ -70,9 +69,8 @@ describe("Entity explorer API pane related testcases", function() { .invoke("show") .click({ force: true }); ee.ActionContextMenuByEntityName("SecondAPI", "Move to page", pageid); - cy.get(".t--entity-name") - .contains("SecondAPI") - .should("exist"); + cy.wait(500); + ee.AssertEntityPresenceInExplorer("SecondAPI"); /*To be enabled once the bug is fixed cy.get(apiwidget.propertyList).then(function($lis) { expect($lis).to.have.length(3); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_CopyQuery_RenameDatasource_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_CopyQuery_RenameDatasource_spec.js index 23263d55378e..266e428348fe 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_CopyQuery_RenameDatasource_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_CopyQuery_RenameDatasource_spec.js @@ -22,16 +22,13 @@ describe("Entity explorer tests related to copy query", function() { it("1. Create a query with dataSource in explorer, Create new Page", function() { cy.Createpage(pageid); - cy.get(".t--entity-name") - .contains("Page1") - .click({ force: true }); - cy.wait(2000); + ee.SelectEntityByName("Page1"); cy.NavigateToDatasourceEditor(); cy.get(datasource.PostgreSQL).click(); cy.fillPostgresDatasourceForm(); cy.testSaveDatasource(); - cy.get("@createDatasource").then((httpResponse) => { + cy.get("@saveDatasource").then((httpResponse) => { datasourceName = httpResponse.response.body.data.name; cy.CheckAndUnfoldEntityItem("Datasources"); cy.NavigateToActiveDSQueryPane(datasourceName); @@ -51,10 +48,9 @@ describe("Entity explorer tests related to copy query", function() { cy.EvaluateCurrentValue("select * from users"); cy.get(".t--action-name-edit-field").click({ force: true }); - cy.get("@createDatasource").then((httpResponse) => { + cy.get("@saveDatasource").then((httpResponse) => { datasourceName = httpResponse.response.body.data.name; - - cy.CheckAndUnfoldEntityItem("Queries/JS"); + ee.ExpandCollapseEntity("Queries/JS"); ee.ActionContextMenuByEntityName("Query1", "Show Bindings"); cy.get(apiwidget.propertyList).then(function($lis) { expect($lis).to.have.length(5); @@ -68,14 +64,10 @@ describe("Entity explorer tests related to copy query", function() { }); it("2. Copy query in explorer to new page & verify Bindings are copied too", function() { - cy.get(".t--entity-name") - .contains("Page1") - .click({ force: true }); + ee.SelectEntityByName("Query1", "Queries/JS"); ee.ActionContextMenuByEntityName("Query1", "Copy to page", pageid); - cy.CheckAndUnfoldEntityItem("Queries/JS"); - cy.get(".t--entity-name") - .contains("Query1") - .click({ force: true }); + ee.ExpandCollapseEntity("Queries/JS"); + ee.SelectEntityByName("Query1"); cy.runQuery(); ee.ActionContextMenuByEntityName("Query1", "Show Bindings"); cy.get(apiwidget.propertyList).then(function($lis) { @@ -88,17 +80,16 @@ describe("Entity explorer tests related to copy query", function() { }); it("3. Rename datasource in explorer, Delete query and try to Delete datasource", function() { - cy.get(".t--entity-name") - .contains("Page1") - .click({ force: true }); - cy.wait(2000); + ee.SelectEntityByName("Page1"); cy.generateUUID().then((uid) => { updatedName = uid; cy.log("complete uid :" + updatedName); updatedName = uid.replace(/-/g, "_").slice(1, 15); cy.log("sliced id :" + updatedName); - cy.CheckAndUnfoldEntityItem("Queries/JS"); - cy.EditEntityNameByDoubleClick(datasourceName, updatedName); + ee.ExpandCollapseEntity("Queries/JS"); + ee.ExpandCollapseEntity("Datasources"); + ee.RenameEntityFromExplorer(datasourceName, updatedName); + //cy.EditEntityNameByDoubleClick(datasourceName, updatedName); cy.wait(1000); ee.ActionContextMenuByEntityName(updatedName, "Delete", "Are you sure?"); cy.wait(1000); @@ -109,9 +100,7 @@ describe("Entity explorer tests related to copy query", function() { 409, ); }); - cy.get(".t--entity-name") - .contains("Query1") - .click(); + ee.SelectEntityByName("Query1", "Queries/JS"); ee.ActionContextMenuByEntityName("Query1", "Delete", "Are you sure?"); }); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_Datasource_Structure_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_Datasource_Structure_spec.js index 1592f00a9019..438dc83a3948 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_Datasource_Structure_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_Datasource_Structure_spec.js @@ -13,7 +13,7 @@ describe("Entity explorer datasource structure", function() { //cy.ClearSearch(); cy.startRoutesForDatasource(); cy.createPostgresDatasource(); - cy.get("@createDatasource").then((httpResponse) => { + cy.get("@saveDatasource").then((httpResponse) => { datasourceName = httpResponse.response.body.data.name; }); }); @@ -32,10 +32,9 @@ describe("Entity explorer datasource structure", function() { .should("have.value", "MyQuery") .blur(); cy.WaitAutoSave(); - cy.CheckAndUnfoldEntityItem("Datasources"); - cy.get(".t--entity-name") - .contains(datasourceName) - .click({ force: true }); + ee.ExpandCollapseEntity("Datasources"); + ee.ActionContextMenuByEntityName(datasourceName, "Refresh"); + cy.wait(2000); //for the tables to open cy.wait("@getDatasourceStructure").should( "have.nested.property", "response.body.responseMeta.status", diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_DragAndDropWidget_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_DragAndDropWidget_spec.js index 4dd800f29f71..d95033b6ecd0 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_DragAndDropWidget_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_DragAndDropWidget_spec.js @@ -37,7 +37,6 @@ describe("Entity explorer Drag and Drop widgets testcases", function() { * @param{toggleButton Css} Assert to be checked */ cy.moveToContentTab(); - cy.togglebar(commonlocators.scrollView); cy.get(formWidgetsPage.formD) .scrollTo("bottom") .should("be.visible"); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_Widgets_Copy_Paste_Delete_Undo_Keyboard_Event_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_Widgets_Copy_Paste_Delete_Undo_Keyboard_Event_spec.js index 77d6193bb349..fe23293c3527 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_Widgets_Copy_Paste_Delete_Undo_Keyboard_Event_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_Widgets_Copy_Paste_Delete_Undo_Keyboard_Event_spec.js @@ -10,7 +10,7 @@ before(() => { }); describe("Test Suite to validate copy/delete/undo functionalites", function() { - it("Drag and drop form widget and validate copy widget via toast message", function() { + it.only("Drag and drop form widget and validate copy widget via toast message", function() { const modifierKey = Cypress.platform === "darwin" ? "meta" : "ctrl"; cy.openPropertyPane("formwidget"); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ExplorerTests/Pages_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ExplorerTests/Pages_spec.js index 7226f2568426..ba57ef94f556 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ExplorerTests/Pages_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ExplorerTests/Pages_spec.js @@ -2,6 +2,10 @@ const pages = require("../../../../locators/Pages.json"); const explorerLocators = require("../../../../locators/explorerlocators.json"); const apiwidget = require("../../../../locators/apiWidgetslocator.json"); +const locators = { + errorPageTitle: ".t--error-page-title", +}; + describe("Pages", function() { let veryLongPageName = `abcdefghijklmnopqrstuvwxyz1234`; let apiName = "someApi"; @@ -59,8 +63,8 @@ describe("Pages", function() { it("Checks if 404 is showing correct route", () => { cy.visit("/route-that-does-not-exist"); - cy.get(".bold-text").should(($x) => { - expect($x).contain("Page not found"); + cy.get(locators.errorPageTitle).should(($x) => { + expect($x).contain(Cypress.env("MESSAGES").PAGE_NOT_FOUND()); }); }); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ExplorerTests/Scrolling_Spec.ts b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ExplorerTests/Scrolling_Spec.ts index 48c0a0943c02..f7d4c8328f9a 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ExplorerTests/Scrolling_Spec.ts +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ExplorerTests/Scrolling_Spec.ts @@ -24,8 +24,9 @@ describe("Entity explorer context menu should hide on scrolling", function() { mockDBNameMovies = $createdMock.response?.body.data.name; dataSources.CreateQuery(mockDBNameMovies); }); + ee.ExpandCollapseEntity("Users"); + ee.ExpandCollapseEntity("Movies"); ee.ExpandCollapseEntity("public.users"); - ee.ExpandCollapseEntity("movies"); agHelper.GetNClick(locator._createNew); agHelper.AssertElementVisible(ee._createNewPopup); agHelper.ScrollTo(ee._entityExplorerWrapper, "bottom"); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/FormNativeToRawTests/Mongo_spec.ts b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/FormNativeToRawTests/Mongo_spec.ts index 5e81319601ba..0e9ddef7a6ba 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/FormNativeToRawTests/Mongo_spec.ts +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/FormNativeToRawTests/Mongo_spec.ts @@ -6,7 +6,7 @@ const agHelper = ObjectsRegistry.AggregateHelper, describe("Mongo Form to Native conversion works", () => { beforeEach(() => { - dataSources.startRoutesForDatasource(); + dataSources.StartDataSourceRoutes(); }); it("Form to Native conversion works.", () => { diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Git/GitDiscardChange/DiscardChanges_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Git/GitDiscardChange/DiscardChanges_spec.js index 40b28cd8139b..7b65758e9de8 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Git/GitDiscardChange/DiscardChanges_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Git/GitDiscardChange/DiscardChanges_spec.js @@ -1,8 +1,12 @@ +import { ObjectsRegistry } from "../../../../../support/Objects/Registry"; + const datasource = require("../../../../../locators/DatasourcesEditor.json"); const queryLocators = require("../../../../../locators/QueryEditor.json"); const dynamicInputLocators = require("../../../../../locators/DynamicInput.json"); const explorer = require("../../../../../locators/explorerlocators.json"); +let dataSources = ObjectsRegistry.DataSources; + describe("Git discard changes:", function() { let datasourceName; let repoName; @@ -20,7 +24,10 @@ describe("Git discard changes:", function() { cy.testSaveDatasource(); - cy.get("@createDatasource").then((httpResponse) => { + // go back to active ds list + dataSources.NavigateToActiveTab(); + + cy.get("@saveDatasource").then((httpResponse) => { datasourceName = httpResponse.response.body.data.name; cy.get(datasource.datasourceCard) diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Git/GitImport/GitImport_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Git/GitImport/GitImport_spec.js index 0c84b377def3..fa13652707a8 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Git/GitImport/GitImport_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Git/GitImport/GitImport_spec.js @@ -9,18 +9,18 @@ const datasourceEditor = require("../../../../../locators/DatasourcesEditor.json const jsObject = "JSObject1"; const newBranch = "feat/temp"; const mainBranch = "master"; -let repoName; +let repoName, newWorkspaceName; -describe("Git import flow", function() { +describe("Git import flow ", function() { before(() => { cy.NavigateToHome(); cy.createWorkspace(); cy.wait("@createWorkspace").then((interception) => { - const newWorkspaceName = interception.response.body.data.name; + newWorkspaceName = interception.response.body.data.name; cy.CreateAppForWorkspace(newWorkspaceName, newWorkspaceName); }); }); - it("1. Import an app from JSON with Postgres, MySQL, Mongo db", () => { + it("1. Import an app from JSON with Postgres, MySQL, Mongo db & then connect it to Git", () => { cy.NavigateToHome(); cy.get(homePage.optionsIcon) .first() @@ -39,19 +39,22 @@ describe("Git import flow", function() { cy.wait(1000); cy.fillPostgresDatasourceForm(); cy.get(datasourceEditor.sectionAuthentication).click(); - cy.testSaveDatasource(); + cy.testDatasource(true); + cy.get(".t--save-datasource").click({ force: true }); cy.wait(1000); cy.ReconnectDatasource("TEDMySQL"); cy.wait(500); cy.fillMySQLDatasourceForm(); cy.get(datasourceEditor.sectionAuthentication).click(); - cy.testSaveDatasource(); + cy.testDatasource(true); + cy.get(".t--save-datasource").click({ force: true }); cy.wait(1000); cy.ReconnectDatasource("TEDMongo"); cy.wait(1000); cy.fillMongoDatasourceForm(); cy.get(datasourceEditor.sectionAuthentication).click(); - cy.testSaveDatasource(); + cy.testDatasource(true); + cy.get(".t--save-datasource").click({ force: true }); cy.wait(2000); /*cy.get(homePage.toastMessage).should( "contain", @@ -68,8 +71,10 @@ describe("Git import flow", function() { cy.connectToGitRepo(repoName); }); }); + cy.wait(5000); // for git connection to settle! }); - it("2. Import an app from Git and reconnect Postgres, MySQL and Mongo db ", () => { + + it("2. Import the previous app connected to Git and reconnect Postgres, MySQL and Mongo db ", () => { cy.NavigateToHome(); cy.createWorkspace(); cy.wait("@createWorkspace").then((interception) => { @@ -85,31 +90,34 @@ describe("Git import flow", function() { .next() .click(); cy.importAppFromGit(repoName); - cy.wait(100); + cy.wait(5000); cy.get(reconnectDatasourceModal.Modal).should("be.visible"); cy.ReconnectDatasource("TEDPostgres"); cy.wait(500); cy.fillPostgresDatasourceForm(); cy.get(datasourceEditor.sectionAuthentication).click(); - cy.testSaveDatasource(); + cy.testDatasource(true); + cy.get(".t--save-datasource").click({ force: true }); cy.wait(500); cy.ReconnectDatasource("TEDMySQL"); cy.wait(500); cy.fillMySQLDatasourceForm(); cy.get(datasourceEditor.sectionAuthentication).click(); - cy.testSaveDatasource(); + cy.testDatasource(true); + cy.get(".t--save-datasource").click({ force: true }); cy.wait(500); cy.ReconnectDatasource("TEDMongo"); cy.wait(500); cy.fillMongoDatasourceForm(); cy.get(datasourceEditor.sectionAuthentication).click(); - cy.testSaveDatasource(); + cy.testDatasource(true); + cy.get(".t--save-datasource").click({ force: true }); cy.wait(2000); cy.get(reconnectDatasourceModal.ImportSuccessModal).should("be.visible"); cy.get(reconnectDatasourceModal.ImportSuccessModalCloseBtn).click({ force: true, }); - cy.wait(1000); + cy.wait(4000); //for git connection to settle /* cy.get(homePage.toastMessage).should( "contain", "Application imported successfully", @@ -119,6 +127,7 @@ describe("Git import flow", function() { cy.wait(1000); }); }); + it("3. Verfiy imported app should have all the data binding visible in view and edit mode", () => { // verify postgres data binded to table cy.get(".tbody") @@ -133,7 +142,9 @@ describe("Git import flow", function() { // verify js object binded to input widget cy.xpath("//input[@value='Success']").should("be.visible"); }); - it("4. Create a new branch, clone page and validate data on that branch in view and edit mode", () => { + + // skipping this due to open bug #18776 + it.skip("4. Create a new branch, clone page and validate data on that branch in view and edit mode", () => { cy.createGitBranch(newBranch); cy.get(".tbody") .first() @@ -202,7 +213,9 @@ describe("Git import flow", function() { cy.get(commonlocators.backToEditor).click(); cy.wait(2000); }); - it("5. Switch to master and verify data in edit and view mode", () => { + + // skipping this due to open bug #18776 + it.skip("5. Switch to master and verify data in edit and view mode", () => { cy.switchGitBranch("master"); cy.wait(2000); // validate data binding in edit and deploy mode @@ -224,7 +237,9 @@ describe("Git import flow", function() { cy.get(commonlocators.backToEditor).click(); cy.wait(2000); }); - it("6. Add widget to master, merge then checkout to child branch and verify data", () => { + + // skipping this due to open bug #18776 + it.skip("6. Add widget to master, merge then checkout to child branch and verify data", () => { cy.get(explorer.widgetSwitchId).click(); cy.wait(2000); // wait for transition cy.dragAndDropToCanvas("buttonwidget", { x: 300, y: 600 }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Git/GitSync/Deploy_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Git/GitSync/Deploy_spec.ts similarity index 79% rename from app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Git/GitSync/Deploy_spec.js rename to app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Git/GitSync/Deploy_spec.ts index 4a03c6c123fc..ecd84ff43f2a 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Git/GitSync/Deploy_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Git/GitSync/Deploy_spec.ts @@ -1,6 +1,9 @@ import gitSyncLocators from "../../../../../locators/gitSyncLocators"; import homePage from "../../../../../locators/HomePage"; -const commonLocators = require("../../../../../locators/commonlocators.json"); +import { ObjectsRegistry } from "../../../../../support/Objects/Registry"; + +const agHelper = ObjectsRegistry.AggregateHelper, + commonLocators = ObjectsRegistry.CommonLocators; let repoName; describe("Git sync modal: deploy tab", function() { @@ -49,9 +52,9 @@ describe("Git sync modal: deploy tab", function() { }); it("post connection app name deploy menu", function() { - cy.get(homePage.applicationName).click(); - cy.get(commonLocators.appNameDeployMenu).click(); - cy.get(commonLocators.appNameDeployMenuPublish).click(); + // deploy + agHelper.GetNClick(commonLocators._publishButton); + cy.get(gitSyncLocators.gitSyncModal); cy.get(gitSyncLocators.gitSyncModalDeployTab).should( "have.class", @@ -67,13 +70,12 @@ describe("Git sync modal: deploy tab", function() { cy.get(gitSyncLocators.closeGitSyncModal).click(); - cy.get(homePage.applicationName).click(); - cy.get(commonLocators.appNameDeployMenu).click(); - cy.get(commonLocators.appNameDeployMenuCurrentVersion).click(); + // current deployed version + agHelper.GetNClick(homePage.deployPopupOptionTrigger); + agHelper.AssertElementExist(homePage.currentDeployedPreviewBtn); - cy.get(homePage.applicationName).click(); - cy.get(commonLocators.appNameDeployMenu).click(); - cy.get(commonLocators.appNameDeployMenuConnectToGit).should("not.exist"); + // connect to git + agHelper.AssertElementAbsence(homePage.connectToGitBtn); }); after(() => { diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Git/GitSync/GitSyncedApps_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Git/GitSync/GitSyncedApps_spec.js index a7381142341a..79d39477d415 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Git/GitSync/GitSyncedApps_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Git/GitSync/GitSyncedApps_spec.js @@ -60,7 +60,7 @@ describe("Git sync apps", function() { cy.wait("@saveDatasource").should( "have.nested.property", "response.body.responseMeta.status", - 200, + 201, ); cy.wait("@getDatasourceStructure").should( @@ -187,12 +187,10 @@ describe("Git sync apps", function() { }); cy.wait(2000); // clone the page from page settings - cy.xpath("//span[contains(@class,'entity-right-icon')]").click({ - force: true, + cy.get(`.t--entity-item:contains(${newPage})`).within(() => { + cy.get(".t--context-menu").click({ force: true }); }); - cy.xpath("(//button[@type='button'])") - .eq(9) - .click(); + cy.selectAction("Clone"); cy.wait("@clonePage").should( "have.nested.property", "response.body.responseMeta.status", @@ -322,7 +320,7 @@ describe("Git sync apps", function() { .click({ force: true }); ee.ActionContextMenuByEntityName("JSObject1", "Move to page", "Child_Page"); cy.wait(2000); - cy.get(explorer.addWidget).click(); + cy.get(explorer.addWidget).click({ force: true }); // bind input widgets to the jsObject and query response cy.dragAndDropToCanvas("inputwidgetv2", { x: 300, y: 300 }); cy.get(".t--widget-inputwidgetv2").should("exist"); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Git/GitSync/Merge_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Git/GitSync/Merge_spec.js index c32cbb42f812..5b518830b195 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Git/GitSync/Merge_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Git/GitSync/Merge_spec.js @@ -32,6 +32,7 @@ describe("Git sync modal: merge tab", function() { .should("eq", "true"); cy.get(gitSyncLocators.mergeButton).should("be.disabled"); + cy.wait(3000); cy.get(gitSyncLocators.mergeBranchDropdownDestination).click(); cy.get(commonLocators.dropdownmenu) .contains(mainBranch) diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Git/GitSync/PreconnectionAppNameDeployMenu_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Git/GitSync/PreconnectionAppNameDeployMenu_spec.ts similarity index 67% rename from app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Git/GitSync/PreconnectionAppNameDeployMenu_spec.js rename to app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Git/GitSync/PreconnectionAppNameDeployMenu_spec.ts index ba05dd405843..bbe09ef59c33 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Git/GitSync/PreconnectionAppNameDeployMenu_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Git/GitSync/PreconnectionAppNameDeployMenu_spec.ts @@ -1,7 +1,10 @@ import homePage from "../../../../../locators/HomePage"; -const commonLocators = require("../../../../../locators/commonlocators.json"); +import { ObjectsRegistry } from "../../../../../support/Objects/Registry"; import gitSyncLocators from "../../../../../locators/gitSyncLocators"; +const agHelper = ObjectsRegistry.AggregateHelper, + commonLocators = ObjectsRegistry.CommonLocators; + describe("Pre git connection spec:", function() { it("deploy menu at the application dropdown menu", () => { // create new app @@ -20,18 +23,17 @@ describe("Pre git connection spec:", function() { }); }); - cy.get(homePage.applicationName).click(); - cy.get(commonLocators.appNameDeployMenu).click(); - cy.get(commonLocators.appNameDeployMenuPublish).click(); + // deploy + agHelper.GetNClick(commonLocators._publishButton); cy.wait("@publishApp"); - cy.get(homePage.applicationName).click(); - cy.get(commonLocators.appNameDeployMenu).click(); - cy.get(commonLocators.appNameDeployMenuCurrentVersion).click(); + // current deployed version + agHelper.GetNClick(homePage.deployPopupOptionTrigger); + agHelper.AssertElementExist(homePage.currentDeployedPreviewBtn); + + // connect to git + agHelper.GetNClick(homePage.connectToGitBtn); - cy.get(homePage.applicationName).click(); - cy.get(commonLocators.appNameDeployMenu).click(); - cy.get(commonLocators.appNameDeployMenuConnectToGit).click(); cy.get(gitSyncLocators.gitSyncModal); cy.contains("Git Connection") .parent() diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Git/GitWithTheming/GitWithTheming_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Git/GitWithTheming/GitWithTheming_spec.js index 38c72bab6e0b..c5b03b027056 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Git/GitWithTheming/GitWithTheming_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Git/GitWithTheming/GitWithTheming_spec.js @@ -1,4 +1,7 @@ +import { ObjectsRegistry } from "../../../../../support/Objects/Registry"; + const commonlocators = require("../../../../../locators/commonlocators.json"); +const appSettings = ObjectsRegistry.AppSettings; describe("Git with Theming:", function() { const backgroudColorMaster = "rgb(85, 61, 233)"; @@ -29,6 +32,8 @@ describe("Git with Theming:", function() { }); }); it("Bug #13860 Theming is not getting applied on view mode when the app is connected to Git", function() { + appSettings.OpenAppSettings(); + appSettings.GoToThemeSettings(); // apply theme on master branch and deploy cy.get(commonlocators.changeThemeBtn).click({ force: true }); @@ -45,6 +50,7 @@ describe("Git with Theming:", function() { .then((text) => { cy.get(commonlocators.toastmsg).contains(`Theme ${text} Applied`); }); + appSettings.ClosePane(); // drag a widget and assert theme is applied cy.dragAndDropToCanvas("buttonwidget", { x: 300, y: 700 }); //cy.get('.t--draggable-buttonwidget').closest("div").should('have.css' , 'background-color', backgroudColorChildBranch) @@ -59,6 +65,8 @@ describe("Git with Theming:", function() { cy.wait(1000); cy.get("body").click(300, 300); // change theme on tempBranch + appSettings.OpenAppSettings(); + appSettings.GoToThemeSettings(); cy.get(commonlocators.changeThemeBtn).click({ force: true }); // select a theme @@ -75,6 +83,7 @@ describe("Git with Theming:", function() { .then((text) => { cy.get(commonlocators.toastmsg).contains(`Theme ${text} Applied`); }); + appSettings.ClosePane(); cy.xpath("(//button[@type='button'])").should( "have.css", diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/IDE/Canvas_Context_Bug_Fixes.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/IDE/Canvas_Context_Bug_Fixes.js new file mode 100644 index 000000000000..c4137779c4d1 --- /dev/null +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/IDE/Canvas_Context_Bug_Fixes.js @@ -0,0 +1,38 @@ +const dsl = require("../../../../fixtures/Bugs/CheckboxGroupInListWidgetDsl.json"); + +describe("Canvas context Property Pane", function() { + it("Bug Fix: Unable to delete checkbox child when it is inside list widget #18191", () => { + cy.addDsl(dsl); + cy.openPropertyPane("checkboxgroupwidget"); + //check number of options + cy.get(".t--property-control-options > div:nth-child(2) > div").should( + "have.length", + 3, + ); + //click on delete button + cy.get( + ".t--property-control-options > div:nth-child(2) > div:nth-child(2) > button", + ).click(); + + //verify deletion + cy.get(".t--property-control-options > div:nth-child(2) > div").should( + "have.length", + 2, + ); + }); + + it("Bug Fix: widget explorer should automatically open on widget selection", () => { + cy.reload(); + cy.CheckAndUnfoldEntityItem("Widgets"); + //check it was originally not expanded + cy.get(`[data-guided-tour-id="explorer-entity-Image1"]`).should( + "not.exist", + ); + + cy.get(".t--widget-imagewidget") + .eq(0) + .click(); + //check if the entities are not expanded + cy.get(`[data-guided-tour-id="explorer-entity-Image1"]`).should("exist"); + }); +}); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/IDE/Canvas_Context_Property_Pane_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/IDE/Canvas_Context_Property_Pane_spec.js index 466b44b9c5af..e909284450a3 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/IDE/Canvas_Context_Property_Pane_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/IDE/Canvas_Context_Property_Pane_spec.js @@ -33,6 +33,7 @@ describe("Canvas context Property Pane", function() { () => { cy.assertSoftFocusOnPropertyPane(propertyControlSelector); }, + "Button1", ); }); @@ -46,6 +47,7 @@ describe("Canvas context Property Pane", function() { () => { cy.get(propertyControlSelector).should("be.focused"); }, + "Button1", ); }); @@ -58,6 +60,7 @@ describe("Canvas context Property Pane", function() { () => { cy.get(propertyControlSelector).should("be.focused"); }, + "Button1", ); }); @@ -75,6 +78,7 @@ describe("Canvas context Property Pane", function() { () => { cy.get(propertyControlVerifySelector).should("be.focused"); }, + "Button1", ); }); @@ -88,6 +92,7 @@ describe("Canvas context Property Pane", function() { () => { cy.get(propertyControlVerifySelector).should("be.focused"); }, + "Button1", true, ); }); @@ -101,6 +106,7 @@ describe("Canvas context Property Pane", function() { () => { cy.get(propertyControlSelector).should("be.focused"); }, + "Button1", true, ); }); @@ -120,6 +126,7 @@ describe("Canvas context Property Pane", function() { () => { verifyPropertyPaneSectionState(propertySectionState); }, + "Button1", ); }); @@ -141,8 +148,189 @@ describe("Canvas context Property Pane", function() { () => { verifyPropertyPaneSectionState(propertySectionState); }, + "Button1", ); }); + + it("9. Layered PropertyPane - Code Editor should have focus while switching between widgets, pages and Editor Panes", function() { + const propertyControlSelector = ".t--property-control-computedvalue"; + verifyPropertyPaneContext( + () => { + cy.editColumn("step"); + cy.focusCodeInput(propertyControlSelector); + }, + () => { + cy.assertSoftFocusOnPropertyPane(propertyControlSelector); + }, + "Table1", + ); + + cy.get(".t--property-pane-back-btn").click(); + cy.get(".t--property-pane-title").should("contain", "Table1"); + }); + + it("10. Layered PropertyPane - Toggle Property controls should have focus while switching between widgets, pages and Editor Panes", function() { + const propertyControlSelector = `.t--property-control-cellwrapping input[type="checkbox"]`; + verifyPropertyPaneContext( + () => { + cy.editColumn("step"); + cy.get(propertyControlSelector).click({ force: true }); + }, + () => { + cy.get(propertyControlSelector).should("be.focused"); + }, + "Table1", + ); + + cy.get(".t--property-pane-back-btn").click(); + cy.get(".t--property-pane-title").should("contain", "Table1"); + }); + + it("11. Layered PropertyPane - Property Sections should retain state while switching between widgets, pages and Editor Panes", function() { + const propertySectionState = { + data: false, + general: true, + }; + + verifyPropertyPaneContext( + () => { + cy.editColumn("step"); + setPropertyPaneSectionState(propertySectionState); + }, + () => { + cy.wait(500); + verifyPropertyPaneSectionState(propertySectionState); + }, + "Table1", + ); + + cy.get(".t--property-pane-back-btn").click(); + cy.get(".t--property-pane-title").should("contain", "Table1"); + }); + + it("12. Layered PropertyPane - Property Tabs and Sections should retain state while switching between widgets, pages and Editor Panes", function() { + const propertySectionState = { + textformatting: true, + color: false, + }; + + verifyPropertyPaneContext( + () => { + cy.editColumn("step"); + cy.get(`.tab-title:contains("STYLE")`) + .eq(0) + .click(); + setPropertyPaneSectionState(propertySectionState); + }, + () => { + verifyPropertyPaneSectionState(propertySectionState); + }, + "Table1", + ); + + cy.get(".t--property-pane-back-btn").click(); + cy.get(".t--property-pane-title").should("contain", "Table1"); + }); + + it("13. Multi Layered PropertyPane - Code Editor should have focus while switching between widgets, pages and Editor Panes", function() { + const propertyControlSelector = ".t--property-control-text"; + verifyPropertyPaneContext( + () => { + cy.editColumn("status"); + cy.editColumn("menuIteme63irwbvnd", false); + cy.focusCodeInput(propertyControlSelector); + }, + () => { + cy.assertSoftFocusOnPropertyPane(propertyControlSelector); + }, + "Table1", + ); + + cy.get(".t--property-pane-back-btn").click(); + cy.get(".t--property-pane-title").should("contain", "status"); + + cy.wait(500); + cy.get(".t--property-pane-back-btn").click(); + cy.get(".t--property-pane-title").should("contain", "Table1"); + }); + + it("14. Multi Layered PropertyPane - Toggle Property controls should have focus while switching between widgets, pages and Editor Panes", function() { + const propertyControlSelector = `.t--property-control-visible input[type="checkbox"]`; + verifyPropertyPaneContext( + () => { + cy.editColumn("status"); + cy.editColumn("menuIteme63irwbvnd", false); + cy.get(propertyControlSelector).click({ force: true }); + }, + () => { + cy.get(propertyControlSelector).should("be.focused"); + }, + "Table1", + ); + + cy.get(".t--property-pane-back-btn").click(); + cy.get(".t--property-pane-title").should("contain", "status"); + + cy.wait(500); + cy.get(".t--property-pane-back-btn").click(); + cy.get(".t--property-pane-title").should("contain", "Table1"); + }); + + it("15. Multi Layered PropertyPane - Property Sections should retain state while switching between widgets, pages and Editor Panes", function() { + const propertySectionState = { + basic: false, + general: true, + }; + + verifyPropertyPaneContext( + () => { + cy.editColumn("status"); + cy.editColumn("menuIteme63irwbvnd", false); + setPropertyPaneSectionState(propertySectionState); + }, + () => { + cy.wait(500); + verifyPropertyPaneSectionState(propertySectionState); + }, + "Table1", + ); + + cy.get(".t--property-pane-back-btn").click(); + cy.get(".t--property-pane-title").should("contain", "status"); + + cy.wait(500); + cy.get(".t--property-pane-back-btn").click(); + cy.get(".t--property-pane-title").should("contain", "Table1"); + }); + + it("16. Multi Layered PropertyPane - Property Tabs and Sections should retain state while switching between widgets, pages and Editor Panes", function() { + const propertySectionState = { + icon: true, + color: false, + }; + + verifyPropertyPaneContext( + () => { + cy.editColumn("status"); + cy.editColumn("menuIteme63irwbvnd", false); + cy.get(`.tab-title:contains("STYLE")`) + .eq(0) + .click(); + setPropertyPaneSectionState(propertySectionState); + }, + () => { + verifyPropertyPaneSectionState(propertySectionState); + }, + "Table1", + ); + + cy.get(".t--property-pane-back-btn").click(); + cy.get(".t--property-pane-title").should("contain", "status"); + + cy.wait(500); + cy.get(".t--property-pane-back-btn").click(); + cy.get(".t--property-pane-title").should("contain", "Table1"); + }); }); const propertySectionClass = (section) => @@ -181,13 +369,14 @@ function verifyPropertyPaneSectionState(propertySectionState) { function verifyPropertyPaneContext( focusCallback, assertCallback, + widgetName, isStyleTab = false, ) { //select Button1 widget in page1 - ee.SelectEntityByName("Button1", "Widgets"); + ee.SelectEntityByName(widgetName, "Widgets"); //verify the Button1 is selected in page1 - cy.get(".t--property-pane-title").should("contain", "Button1"); + cy.get(".t--property-pane-title").should("contain", widgetName); if (isStyleTab) { cy.get(`.tab-title:contains("STYLE")`) @@ -203,7 +392,7 @@ function verifyPropertyPaneContext( cy.get(".t--property-pane-title").should("contain", "Camera1"); //Switch back to Button1 widget - ee.SelectEntityByName("Button1", "Widgets"); + ee.SelectEntityByName(widgetName, "Widgets"); cy.wait(500); //assert Callback diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/IDE/Canvas_Context_Selected_Widgets_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/IDE/Canvas_Context_Selected_Widgets_spec.js index 496c1a25b111..ed5618e4190d 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/IDE/Canvas_Context_Selected_Widgets_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/IDE/Canvas_Context_Selected_Widgets_spec.js @@ -219,4 +219,52 @@ describe("Canvas context widget selection", function() { cy.get(".t--modal-widget").should("have.length", 1); cy.get(".t--property-pane-title").should("contain", "Text1"); }); + + it.skip("9. Widget inside non default tab in tab widget should be selected and the given tab should be open while switching back and forth between pages", function() { + //switch to tab 2 and select widget a button inside tab 2 in page1 + cy.get(".t--tabid-tab2").click({ force: true }); + cy.SearchEntityandOpen("Button4", "Widgets"); + + //verify the tab 2 is open and Button 4 is selected in page1 + cy.get(".is-selected").should("contain", "Tab 2"); + cy.get(".t--property-pane-title").should("contain", "Button4"); + + //switch to page2 + ee.SelectEntityByName(page2, "Pages"); + + //select widget in page2 + cy.SearchEntityandOpen("Text1", "Widgets"); + + //verify the widget is selected in page2 + cy.get(`div[data-testid='t--selected']`).should("have.length", 1); + + //switch to page1 + ee.SelectEntityByName(page1, "Pages"); + + //verify the tab 2 is open and Button 4 is selected in page1 + cy.get(".is-selected").should("contain", "Tab 2"); + cy.get(".t--property-pane-title").should("contain", "Button4"); + }); + + it.skip("10. Widget inside non default tab in tab widget should be selected and the given tab should be open while switching back to page from API pane", function() { + //switch to tab 2 and select widget a button inside tab 2 in page1 + cy.get(".t--tabid-tab2").click({ force: true }); + cy.SearchEntityandOpen("Button4", "Widgets"); + + //verify the tab 2 is open and Button 4 is selected in page1 + cy.get(".is-selected").should("contain", "Tab 2"); + cy.get(".t--property-pane-title").should("contain", "Button4"); + + //navigate to API1 + ee.SelectEntityByName(api1, "Queries/JS"); + cy.wait(500); + + //navigate back to page1 + cy.get(".t--close-editor").click(); + cy.wait(500); + + //verify the tab 2 is open and Button 4 is selected in page1 + cy.get(".is-selected").should("contain", "Tab 2"); + cy.get(".t--property-pane-title").should("contain", "Button4"); + }); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/IDE/Command_Click_Navigation_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/IDE/Command_Click_Navigation_spec.js new file mode 100644 index 000000000000..1477a1cfe57b --- /dev/null +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/IDE/Command_Click_Navigation_spec.js @@ -0,0 +1,109 @@ +import reconnectDatasourceModal from "../../../../locators/ReconnectLocators"; +import { ObjectsRegistry } from "../../../../support/Objects/Registry"; +import { PROPERTY_SELECTOR } from "../../../../locators/WidgetLocators"; + +const homePage = ObjectsRegistry.HomePage; +const agHelper = ObjectsRegistry.AggregateHelper; +const commonLocators = ObjectsRegistry.CommonLocators; + +const NAVIGATION_ATTRIBUTE = "data-navigate-to"; + +describe("1. CommandClickNavigation", function() { + it("1. Import the test application", () => { + homePage.NavigateToHome(); + cy.intercept("GET", "/api/v1/users/features", { + fixture: "featureFlags.json", + }).as("featureFlags"); + cy.reload(); + homePage.ImportApp("ContextSwitching.json"); + cy.wait("@importNewApplication").then((interception) => { + agHelper.Sleep(); + const { isPartialImport } = interception.response.body.data; + if (isPartialImport) { + // should reconnect modal + cy.get(reconnectDatasourceModal.SkipToAppBtn).click({ + force: true, + }); + cy.wait(2000); + } else { + homePage.AssertImportToast(); + } + }); + }); + + it("2. Assert link and and style", () => { + cy.CheckAndUnfoldEntityItem("Queries/JS"); + + cy.SearchEntityandOpen("Text1"); + cy.updateCodeInput(".t--property-control-text", "{{ Graphql_Query.data }}"); + + cy.get(`[${NAVIGATION_ATTRIBUTE}="Graphql_Query"]`) + .should("have.length", 1) + .should("have.text", "Graphql_Query") + .realHover() + .should("have.css", "cursor", "text"); + + // TODO how to hover with cmd or ctrl to assert pointer? + }); + + it("3. Assert navigation only when cmd or ctrl is pressed", () => { + cy.get(`[${NAVIGATION_ATTRIBUTE}="Graphql_Query"]`).click(); + + cy.url().should("not.contain", "/api/"); + + cy.get(`[${NAVIGATION_ATTRIBUTE}="Graphql_Query"]`).click({ + ctrlKey: true, + }); + + cy.url().should("contain", "/api/"); + }); + + it("4. Assert working on url field", () => { + cy.updateCodeInput( + ".t--dataSourceField", + "https://www.test.com/{{ SQL_Query.data }}", + ); + + cy.get(`[${NAVIGATION_ATTRIBUTE}="SQL_Query"]`) + .should("have.length", 1) + .click({ cmdKey: true }); + + cy.url().should("contain", "/queries/"); + }); + + it("5. Will open modals", () => { + cy.updateCodeInput( + ".t--actionConfiguration\\.body", + "SELECT * from {{ Button3.text }}", + ); + cy.get(`[${NAVIGATION_ATTRIBUTE}="Button3"]`) + .should("have.length", 1) + .click({ cmdKey: true }); + + cy.url().should("not.contain", "/queries/"); + }); + + it("6. Will close modals", () => { + cy.updateCodeInput( + `${commonLocators._propertyControl}text`, + "{{ Image1.image }}", + ); + + cy.get(`[${NAVIGATION_ATTRIBUTE}="Image1"]`) + .should("have.length", 1) + .click({ cmdKey: true }); + }); + + it.skip("7. Will work with string arguments in framework functions", () => { + cy.get(PROPERTY_SELECTOR.onClick) + .find(".t--js-toggle") + .click(); + cy.updateCodeInput( + PROPERTY_SELECTOR.onClick, + "{{ resetWidget('Input1') }}", + ); + cy.get(`[${NAVIGATION_ATTRIBUTE}="Input1"]`) + .should("have.length", 1) + .click({ cmdKey: true }); + }); +}); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/IDE/MaintainContext&Focus_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/IDE/MaintainContext&Focus_spec.js index f176a6752246..f23cd92c6c96 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/IDE/MaintainContext&Focus_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/IDE/MaintainContext&Focus_spec.js @@ -4,6 +4,9 @@ import { ObjectsRegistry } from "../../../../support/Objects/Registry"; const homePage = ObjectsRegistry.HomePage; const agHelper = ObjectsRegistry.AggregateHelper; +const dataSources = ObjectsRegistry.DataSources; +const ee = ObjectsRegistry.EntityExplorer; +const apiPage = ObjectsRegistry.ApiPage; describe("MaintainContext&Focus", function() { it("1. Import the test application", () => { @@ -135,4 +138,61 @@ describe("MaintainContext&Focus", function() { cy.SearchEntityandOpen("JSObject2"); cy.assertCursorOnCodeInput(".js-editor", { ch: 2, line: 2 }); }); + + it("7. Check if selected tab on right tab persists", () => { + ee.SelectEntityByName("Rest_Api_1", "Queries/JS"); + apiPage.SelectRightPaneTab("connections"); + ee.SelectEntityByName("SQL_Query"); + ee.SelectEntityByName("Rest_Api_1"); + apiPage.AssertRightPaneSelectedTab("connections"); + }); + + it("8. Check if the URL is persisted while switching pages", () => { + cy.Createpage("Page2"); + + ee.SelectEntityByName("Page1", "Pages"); + ee.SelectEntityByName("Rest_Api_1", "Queries/JS"); + + ee.SelectEntityByName("Page2", "Pages"); + cy.dragAndDropToCanvas("textwidget", { x: 300, y: 200 }); + + ee.SelectEntityByName("Page1", "Pages"); + cy.get(".t--nameOfApi .bp3-editable-text-content").should( + "contain", + "Rest_Api_1", + ); + }); + it("9. Datasource edit mode has to be maintained", () => { + ee.SelectEntityByName("Appsmith", "Datasources"); + dataSources.EditDatasource(); + dataSources.SaveDSFromDialog(false); + ee.SelectEntityByName("Github", "Datasources"); + dataSources.AssertViewMode(); + ee.SelectEntityByName("Appsmith", "Datasources"); + dataSources.AssertEditMode(); + }); + + it("10. Datasource collapse state has to be maintained", () => { + // Create datasource 1 + dataSources.SaveDSFromDialog(false); + dataSources.NavigateToDSCreateNew(); + dataSources.CreatePlugIn("PostgreSQL"); + agHelper.RenameWithInPane("Postgres1", false); + // Expand section with index 1 + dataSources.ExpandSection(1); + // Create and switch to datasource 2 + dataSources.SaveDSFromDialog(true); + dataSources.NavigateToDSCreateNew(); + dataSources.CreatePlugIn("MongoDB"); + agHelper.RenameWithInPane("Mongo1", false); + // Validate if section with index 1 is collapsed + dataSources.AssertSectionCollapseState(1, false); + // Switch back to datasource 1 + dataSources.SaveDSFromDialog(false); + dataSources.CreateNewQueryInDS("Postgres1"); + ee.SelectEntityByName("Postgres1"); + dataSources.EditDatasource(); + // Validate if section with index 1 is expanded + dataSources.AssertSectionCollapseState(1, false); + }); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Linting/BasicLint_spec.ts b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Linting/BasicLint_spec.ts index e679e96f4e3b..1c9c44820c12 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Linting/BasicLint_spec.ts +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Linting/BasicLint_spec.ts @@ -271,9 +271,10 @@ describe("Linting", () => { apiPage.CreateAndFillApi("https://jsonplaceholder.typicode.com/"); createMySQLDatasourceQuery(); - + agHelper.RefreshPage();//Since this seems failing a bit clickButtonAndAssertLintError(false); }); + it("8. Doesn't show lint errors for supported web apis", () => { const JS_OBJECT_WITH_WEB_API = `export default { myFun1: () => { diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Onboarding/GuidedTour_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Onboarding/GuidedTour_spec.js index 3d50f31bba98..1d7c13f3ea1d 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Onboarding/GuidedTour_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Onboarding/GuidedTour_spec.js @@ -13,7 +13,7 @@ describe("Guided Tour", function() { cy.get(onboardingLocators.welcomeTourBtn).should("be.visible"); }); - it("Guided Tour", function() { + it("1. Guided Tour", function() { // Start guided tour cy.get(commonlocators.homeIcon).click({ force: true }); cy.get(guidedTourLocators.welcomeTour).click(); @@ -31,9 +31,10 @@ describe("Guided Tour", function() { cy.testJsontext("tabledata", "{{getCustomers.data}}"); cy.get(guidedTourLocators.successButton).click(); cy.get(guidedTourLocators.infoButton).click(); - // Renaming widgets - cy.wait("@updateWidgetName"); - // Step 4: Add binding to the defaulText property of NameInput + // Renaming widgets // Commending below wait due to flakiness + //cy.wait("@updateWidgetName"); + // Step 4: Add binding to the defaultText property of NameInput + cy.wait(1000); cy.get(guidedTourLocators.hintButton).click(); cy.testJsontext("defaultvalue", "{{CustomersTable.selectedRow.name}}"); cy.get(guidedTourLocators.successButton).click(); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/OtherUIFeatures/ExportApplication_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/OtherUIFeatures/ExportApplication_spec.js index f1cf88dabf6c..4e001dccb3bd 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/OtherUIFeatures/ExportApplication_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/OtherUIFeatures/ExportApplication_spec.js @@ -65,7 +65,6 @@ describe("Export application as a JSON file", function() { cy.get(homePage.shareApp).click({ force: true }); // cy.shareApp(Cypress.env("TESTUSERNAME1"), homePage.adminRole); HomePage.InviteUserToWorkspaceFromApp( - workspaceId, Cypress.env("TESTUSERNAME1"), "Administrator", ); @@ -118,7 +117,6 @@ describe("Export application as a JSON file", function() { cy.get("h2").contains("Drag and drop a widget here"); cy.get(homePage.shareApp).click({ force: true }); HomePage.InviteUserToWorkspaceFromApp( - workspaceId, Cypress.env("TESTUSERNAME1"), "Developer", ); @@ -173,7 +171,6 @@ describe("Export application as a JSON file", function() { cy.get(homePage.shareApp).click({ force: true }); HomePage.InviteUserToWorkspaceFromApp( - workspaceId, Cypress.env("TESTUSERNAME1"), "App Viewer", ); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/OtherUIFeatures/GlobalSearch_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/OtherUIFeatures/GlobalSearch_spec.js index 0c646c2445b1..444e1aef0b9c 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/OtherUIFeatures/GlobalSearch_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/OtherUIFeatures/GlobalSearch_spec.js @@ -4,7 +4,6 @@ const dsl = require("../../../../fixtures/MultipleWidgetDsl.json"); const globalSearchLocators = require("../../../../locators/GlobalSearch.json"); const datasourceHomeLocators = require("../../../../locators/apiWidgetslocator.json"); const datasourceLocators = require("../../../../locators/DatasourcesEditor.json"); -const appPage = require("../../../../locators/PgAdminlocators.json"); describe("GlobalSearch", function() { before(() => { @@ -87,7 +86,7 @@ describe("GlobalSearch", function() { it("4. navigatesToDatasourceHavingAQuery", () => { cy.createPostgresDatasource(); - cy.get("@createDatasource").then((httpResponse) => { + cy.get("@saveDatasource").then((httpResponse) => { const expectedDatasource = httpResponse.response.body.data; cy.NavigateToActiveDSQueryPane(expectedDatasource.name); @@ -147,17 +146,15 @@ describe("GlobalSearch", function() { cy.get(globalSearchLocators.createNew).click({ force: true }); cy.get(globalSearchLocators.blankDatasource).click({ force: true }); cy.get(datasourceHomeLocators.createAuthApiDatasource).click(); - cy.wait("@createDatasource").should( - "have.nested.property", - "response.body.responseMeta.status", - 201, - ); cy.get(datasourceLocators.datasourceTitleLocator).click(); cy.get(`${datasourceLocators.datasourceTitleLocator} input`) .clear() .type("omnibarApiDatasource", { force: true }) .blur(); + cy.fillAuthenticatedAPIForm(); + cy.saveDatasource(); + cy.get(globalSearchLocators.createNew).click({ force: true }); cy.contains( globalSearchLocators.fileOperation, @@ -169,25 +166,19 @@ describe("GlobalSearch", function() { .then((title) => expect(title).includes("Api")); }); + // since now datasource will only be saved once user clicks on save button explicitly, + // updated test so that when user clicks on google sheet and searches for the same datasource, no + // results found will be shown it("8. navigatesToGoogleSheetsQuery does not break again: Bug 15012", () => { cy.createGoogleSheetsDatasource(); cy.renameDatasource("XYZ"); cy.wait(4000); - cy.get(appPage.dropdownChevronLeft).click(); cy.get(commonlocators.globalSearchTrigger).click({ force: true }); // eslint-disable-next-line cypress/no-unnecessary-waiting cy.wait(1000); // modal open transition should be deterministic cy.get(commonlocators.globalSearchInput).type("XYZ"); - cy.get("body").type("{enter}"); - - cy.get(".t--save-datasource") - .contains("Save and Authorize") - .should("be.visible"); - - cy.deleteDatasource("XYZ"); - // this should be called at the end of the last test case in this spec file. - cy.NavigateToHome(); + cy.get(".no-data-title").should("be.visible"); }); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/OtherUIFeatures/Omnibar_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/OtherUIFeatures/Omnibar_spec.js index 2c7c6f0ba4f6..9727df576e1b 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/OtherUIFeatures/Omnibar_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/OtherUIFeatures/Omnibar_spec.js @@ -166,14 +166,17 @@ describe("Omnibar functionality test cases", () => { // verify recently opened items with their subtext i.e page name cy.xpath(omnibar.recentlyopenItem) .eq(0) - .should("have.text", "Button1") - .next() .should("have.text", "Page1"); cy.xpath(omnibar.recentlyopenItem) .eq(1) .should("have.text", "Audio1") .next() .should("have.text", "Page1"); + cy.xpath(omnibar.recentlyopenItem) + .eq(2) + .should("have.text", "Button1") + .next() + .should("have.text", "Page1"); cy.xpath(omnibar.recentlyopenItem) .eq(3) .should("have.text", "Omnibar2") @@ -197,8 +200,8 @@ describe("Omnibar functionality test cases", () => { .click() .wait(2000); cy.url().should( - "eq", - "https://docs.appsmith.com/core-concepts/connecting-to-data-sources/", + "contain", + "https://docs.appsmith.com/core-concepts/connecting-to-data-sources", ); // => true cy.go(-1); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/OtherUIFeatures/PreviewMode_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/OtherUIFeatures/PreviewMode_spec.js index dced5ce447e3..ff4ee09730b7 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/OtherUIFeatures/PreviewMode_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/OtherUIFeatures/PreviewMode_spec.js @@ -17,6 +17,7 @@ describe("Preview mode functionality", function() { it("checks if widgets can be selected or not", function() { // in preview mode, entity explorer and property pane are not visible + // Also, draggable and resizable components are not available. const selector = `.t--draggable-buttonwidget`; cy.wait(500); cy.get(selector) diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/OtherUIFeatures/Replay_Editor_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/OtherUIFeatures/Replay_Editor_spec.js index 109ba1e72983..becdf4456665 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/OtherUIFeatures/Replay_Editor_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/OtherUIFeatures/Replay_Editor_spec.js @@ -9,7 +9,7 @@ describe("Undo/Redo functionality", function() { const modifierKey = Cypress.platform === "darwin" ? "meta" : "ctrl"; let postgresDatasourceName; - it("Checks undo/redo in datasource forms", () => { + it("1. Checks undo/redo in datasource forms", () => { cy.NavigateToDatasourceEditor(); cy.get(datasource.PostgreSQL).click(); cy.generateUUID().then((uid) => { @@ -46,7 +46,7 @@ describe("Undo/Redo functionality", function() { cy.get(datasourceEditor.saveBtn).click({ force: true }); }); - it("Checks undo/redo for Api pane", function() { + it("2. Checks undo/redo for Api pane", function() { cy.NavigateToAPI_Panel(); cy.log("Navigation to API Panel screen successful"); cy.CreateAPI("FirstAPI"); @@ -85,7 +85,7 @@ describe("Undo/Redo functionality", function() { ); }); - it("Checks undo/redo in query editor", () => { + it("3. Checks undo/redo in query editor", () => { cy.NavigateToActiveDSQueryPane(postgresDatasourceName); cy.get(queryLocators.templateMenu).click(); cy.get(".CodeMirror textarea") @@ -127,7 +127,7 @@ describe("Undo/Redo functionality", function() { cy.get(".CodeMirror-code").should("not.have.text", "{{FirstAPI}}"); }); - it("Checks undo/redo in JS Objects", () => { + it("4. Checks undo/redo in JS Objects", () => { cy.NavigateToJSEditor(); cy.wait(1000); cy.get(".CodeMirror textarea") @@ -152,7 +152,8 @@ describe("Undo/Redo functionality", function() { // cy.get(".function-name").should("not.contain.text", "test"); }); - it("Checks undo/redo for Authenticated APIs", () => { + //Skipping this since its failing in CI + it.skip("5. Checks undo/redo for Authenticated APIs", () => { cy.NavigateToAPI_Panel(); cy.get(apiwidget.createAuthApiDatasource).click({ force: true }); cy.wait(2000); @@ -161,6 +162,7 @@ describe("Undo/Redo functionality", function() { cy.get("body").click(0, 0); cy.get("body").type(`{${modifierKey}}z`); cy.get("body").type(`{${modifierKey}}z`); + cy.wait(2000); cy.get("input[name='url']").should("have.value", ""); cy.get("input[name='headers[0].key']").should("have.value", ""); cy.get("body").type(`{${modifierKey}}{shift}z`); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/OtherUIFeatures/Resize_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/OtherUIFeatures/Resize_spec.js index 1f969e2c1323..7e93e8a29683 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/OtherUIFeatures/Resize_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/OtherUIFeatures/Resize_spec.js @@ -16,7 +16,7 @@ describe("Canvas Resize", function() { cy.get(commonlocators.dropTarget).should( "have.css", "height", - `${dsl.bottomRow}px`, + `${dsl.minHeight}px`, ); }); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/PropertyPane/PropertyPane_Search_spec.ts b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/PropertyPane/PropertyPane_Search_spec.ts new file mode 100644 index 000000000000..515c126dfb0b --- /dev/null +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/PropertyPane/PropertyPane_Search_spec.ts @@ -0,0 +1,187 @@ +import { ObjectsRegistry } from "../../../../support/Objects/Registry"; + +const agHelper = ObjectsRegistry.AggregateHelper, + ee = ObjectsRegistry.EntityExplorer, + propPane = ObjectsRegistry.PropertyPane; + +describe("Property Pane Search", function() { + before(() => { + cy.fixture("swtchTableV2Dsl").then((val: any) => { + agHelper.AddDsl(val); + }); + }); + + it("1. Verify if the search Input is getting focused when a widget is selected", function() { + ee.SelectEntityByName("Table1", "Widgets"); + + // Initially the search input will only be soft focused + // We need to press Enter to properly focus it + agHelper.AssertElementFocus(propPane._propertyPaneSearchInputWrapper); + agHelper.PressEnter(); + agHelper.AssertElementFocus(propPane._propertyPaneSearchInput); + + // Pressing Escape should soft focus the search input + agHelper.PressEscape(); + agHelper.AssertElementFocus(propPane._propertyPaneSearchInputWrapper); + + // Opening a panel should focus the search input + propPane.OpenTableColumnSettings("name"); + agHelper.AssertElementFocus(propPane._propertyPaneSearchInputWrapper); + + // Opening some other widget and then going back to the initial widget should soft focus the search input that is inside a panel + ee.SelectEntityByName("Switch1", "Widgets"); + ee.SelectEntityByName("Table1", "Widgets"); + agHelper.AssertElementFocus(propPane._propertyPaneSearchInputWrapper); + + // Going out of the panel should soft focus the search input + propPane.NavigateBackToPropertyPane(); + agHelper.AssertElementFocus(propPane._propertyPaneSearchInputWrapper); + }); + + it("2. Search for Properties", function() { + // Search for a property inside content tab + propPane.Search("visible"); + propPane.AssertIfPropertyOrSectionExists("general", "CONTENT", "visible"); + + // Search for a property inside style tab + propPane.Search("text color"); + propPane.AssertIfPropertyOrSectionExists("color", "STYLE", "textcolor"); + + // search for a camel case property + propPane.Search("on row selected"); + propPane.AssertIfPropertyOrSectionExists( + "rowselection", + "CONTENT", + "onrowselected", + ); + + // search for another variation of camel case property + propPane.Search("onSort"); + propPane.AssertIfPropertyOrSectionExists("sorting", "CONTENT", "onsort"); + }); + + it("3. Search for Sections", function() { + // Search for a section inside content tab + propPane.Search("general"); + propPane.AssertIfPropertyOrSectionExists("general", "CONTENT"); + + // Search for a section inside style tab + propPane.Search("text formaTTing"); + propPane.AssertIfPropertyOrSectionExists("textformatting", "STYLE"); + + // Clear the search input for the next test + propPane.Search(""); + }); + + it("4. Search for Properties inside a panel", function() { + propPane.OpenTableColumnSettings("name"); + + // Search for a property inside content tab + propPane.Search("Visible"); + propPane.AssertIfPropertyOrSectionExists("general", "CONTENT", "visible"); + + // Search for a property inside style tab + propPane.Search("text Color"); + propPane.AssertIfPropertyOrSectionExists("color", "STYLE", "textcolor"); + }); + + it("5. Search for Sections inside a panel", function() { + // Search for a section inside content tab + propPane.Search("DATA"); + propPane.AssertIfPropertyOrSectionExists("data", "CONTENT"); + + // Search for a section inside style tab + propPane.Search("color"); + propPane.AssertIfPropertyOrSectionExists("color", "STYLE"); + }); + + it("6. Search for gibberish and verify if empty results message is shown", function() { + // Searching Gibberish inside a panel + propPane.Search("pigglywiggly"); + agHelper.AssertElementExist(propPane._propertyPaneEmptySearchResult); + + // Searching Gibberish inside main property panel + propPane.NavigateBackToPropertyPane(); + propPane.Search("pigglywiggly"); + agHelper.AssertElementExist(propPane._propertyPaneEmptySearchResult); + }); + + it("7. Verify behaviour with Dynamically hidden properties inside search results", function() { + // Search for a Section with Dynamically hidden properties + propPane.Search("pagination"); + propPane.AssertIfPropertyOrSectionExists("pagination", "CONTENT"); + // Do the operation so that the dymnamic property is visible + propPane.ToggleOnOrOff("Server Side Pagination", "On"); + // Verify if the property is visible + propPane.AssertIfPropertyOrSectionExists( + "pagination", + "CONTENT", + "onpagechange", + ); + + // Do the operation so that the dymnamic property is hidden again + propPane.ToggleOnOrOff("Server Side Pagination", "Off"); + // Verify whether the property is hidden + agHelper.AssertElementAbsence(".t--property-control-onpagechange"); + }); + + it("8. Verify the search works even if the section is collapsed initially", function() { + ee.SelectEntityByName("Switch1", "Widgets"); + // Collapse All the sections both in CONTENT and STYLE tabs + propPane.ToggleSection("label"); + propPane.ToggleSection("general"); + propPane.ToggleSection("events"); + propPane.moveToStyleTab(); + propPane.ToggleSection("labelstyles"); + propPane.ToggleSection("color"); + + // Search for sections & properties + propPane.Search("events"); + propPane.AssertIfPropertyOrSectionExists("events", "CONTENT"); + + propPane.Search("visible"); + propPane.AssertIfPropertyOrSectionExists("events", "CONTENT", "visible"); + + propPane.Search("color"); + propPane.AssertIfPropertyOrSectionExists("color", "STYLE"); + + propPane.Search("emphasis"); + propPane.AssertIfPropertyOrSectionExists( + "labelstyles", + "STYLE", + "emphasis", + ); + }); + + it("9. Verify the search input clears when another widget is selected", function() { + propPane.Search("visible"); + propPane.AssertSearchInputValue("visible"); + + ee.SelectEntityByName("Table1", "Widgets"); + propPane.AssertSearchInputValue(""); + }); + + // Ensuring a bug won't come back + it("10. Verify searching for properties inside the same section one after the other works", function() { + // Search for a property + propPane.Search("onsort"); + propPane.AssertIfPropertyOrSectionExists("sorting", "CONTENT", "onsort"); + + // Search for another property in the same section + propPane.Search("column sorting"); + propPane.AssertIfPropertyOrSectionExists( + "sorting", + "CONTENT", + "columnsorting", + ); + + // Search for the same section name and verify all the properties under it are visible + propPane.Search("sorting"); + propPane.AssertIfPropertyOrSectionExists("sorting", "CONTENT", "onsort"); + propPane.AssertIfPropertyOrSectionExists( + "sorting", + "CONTENT", + "columnsorting", + ); + }); +}); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/SettingsPane/GeneralSettings_spec.ts b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/SettingsPane/GeneralSettings_spec.ts new file mode 100644 index 000000000000..e5d6a7632f14 --- /dev/null +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/SettingsPane/GeneralSettings_spec.ts @@ -0,0 +1,43 @@ +import * as _ from "../../../../support/Objects/ObjectsCore"; + +let guid: string; +describe("General Settings", () => { + before(() => { + _.agHelper.GenerateUUID(); + cy.get("@guid").then((uid: any) => { + guid = uid; + }); + }); + + it("1. App name change updates URL", () => { + _.appSettings.OpenAppSettings(); + _.appSettings.GoToGeneralSettings(); + _.generalSettings.UpdateAppNameAndVerifyUrl(true, guid); + _.homePage.GetAppName().then((appName) => { + _.deployMode.DeployApp(); + _.appSettings.CheckUrl(appName as string, "Page1", undefined, false); + _.deployMode.NavigateBacktoEditor(); + }); + }); + + it("2. Handles app icon change", () => { + _.appSettings.OpenAppSettings(); + _.appSettings.GoToGeneralSettings(); + _.generalSettings.UpdateAppIcon(); + _.appSettings.ClosePane(); + }); + + it("3. App name allows special and accented character", () => { + _.appSettings.OpenAppSettings(); + _.appSettings.GoToGeneralSettings(); + _.generalSettings.UpdateAppNameAndVerifyUrl(true, guid + "!@#œ™¡", guid); + _.appSettings.ClosePane(); + }); + + it("4. Veirfy App name doesn't allow empty", () => { + _.appSettings.OpenAppSettings(); + _.appSettings.GoToGeneralSettings(); + _.generalSettings.AssertAppErrorMessage("", "App name cannot be empty"); + _.appSettings.ClosePane(); + }); +}); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/SettingsPane/PageSettings_spec.ts b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/SettingsPane/PageSettings_spec.ts new file mode 100644 index 000000000000..2ca83710af4d --- /dev/null +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/SettingsPane/PageSettings_spec.ts @@ -0,0 +1,101 @@ +import * as _ from "../../../../support/Objects/ObjectsCore"; + +describe("Page Settings", () => { + it("1. Page name change updates URL", () => { + _.appSettings.OpenAppSettings(); + _.appSettings.GoToPageSettings("Page1"); + _.pageSettings.UpdatePageNameAndVerifyUrl("Page2", undefined, false); + _.homePage.GetAppName().then((appName) => { + _.deployMode.DeployApp(); + _.appSettings.CheckUrl(appName as string, "Page2", undefined, false); + _.deployMode.NavigateBacktoEditor(); + }); + _.agHelper.Sleep(); + }); + + it("2. Custom slug change updates URL", () => { + _.appSettings.OpenAppSettings(); + _.appSettings.GoToPageSettings("Page2"); + _.pageSettings.UpdateCustomSlugAndVerifyUrl("custom"); + _.homePage.GetAppName().then((appName) => { + _.deployMode.DeployApp(); + _.appSettings.CheckUrl(appName as string, "Page2", "custom", false); + _.deployMode.NavigateBacktoEditor(); + }); + _.agHelper.Sleep(); + }); + + it("3. Check SetAsHome page setting", () => { + _.ee.AddNewPage(); + _.appSettings.OpenAppSettings(); + _.appSettings.GoToPageSettings("Page3"); + _.pageSettings.ToggleHomePage(); + _.pageSettings.AssertHomePage("Page3"); + }); + + it("4. Check SetPageNavigation settings", () => { + _.agHelper.GetNClick(_.locators._previewModeToggle); + _.agHelper.AssertElementExist(_.locators._deployedPage); + _.agHelper.GetNClick(_.locators._editModeToggle); + _.appSettings.OpenAppSettings(); + _.appSettings.GoToPageSettings("Page2"); + _.pageSettings.TogglePageNavigation(); + _.agHelper.GetNClick(_.locators._previewModeToggle); + _.agHelper.AssertElementAbsence(_.locators._deployedPage); + _.agHelper.GetNClick(_.locators._editModeToggle); + }); + + it("5. Page name allows accented character", () => { + _.appSettings.OpenAppSettings(); + _.appSettings.GoToPageSettings("Page3"); + _.pageSettings.UpdatePageNameAndVerifyUrl("Page3œßð", "Page3"); + _.appSettings.ClosePane(); + }); + + it("6. Page name doesn't allow special character", () => { + _.appSettings.OpenAppSettings(); + _.appSettings.GoToPageSettings("Page3"); + _.pageSettings.UpdatePageNameAndVerifyTextValue("Page3!@#", "Page3 "); + _.appSettings.ClosePane(); + }); + + it("7. Page name doesn't allow empty", () => { + _.appSettings.OpenAppSettings(); + _.appSettings.GoToPageSettings("Page3"); + _.pageSettings.AssertPageErrorMessage( + "", + "Page name cannot be empty", + ); + _.appSettings.ClosePane(); + }); + + it("8. Bug #18698 : Page name doesn't allow duplicate name", () => { + _.appSettings.OpenAppSettings(); + _.appSettings.GoToPageSettings("Page3"); + _.pageSettings.AssertPageErrorMessage( + "Page2", + "Page2 is already being used.", + ); + _.appSettings.ClosePane(); + }); + + it("9. Page name doesn't allow keywords", () => { + _.appSettings.OpenAppSettings(); + _.appSettings.GoToPageSettings("Page3"); + _.pageSettings.AssertPageErrorMessage( + "appsmith", + "appsmith is already being used.", + ); + _.appSettings.ClosePane(); + }); + + it("10. Custom slug doesn't allow special/accented characters", () => { + _.appSettings.OpenAppSettings(); + _.appSettings.GoToPageSettings("Page2"); + _.pageSettings.UpdateCustomSlugAndVerifyTextValue( + "custom-slug!@#œßð", + "custom-slug", + ); + _.appSettings.ClosePane(); + }); +}); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Templates/Fork_Template_Existing_app_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Templates/Fork_Template_Existing_app_spec.js index 075e30866b6f..e5193df2a939 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Templates/Fork_Template_Existing_app_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Templates/Fork_Template_Existing_app_spec.js @@ -4,8 +4,9 @@ const publish = require("../../../../locators/publishWidgetspage.json"); describe("Fork a template to the current app from new page popover", () => { it("1. Fork template from page section", () => { + cy.wait(5000); cy.AddPageFromTemplate(); - cy.wait(3000); + cy.wait(5000); cy.get(template.templateDialogBox).should("be.visible"); cy.wait(4000); cy.xpath( @@ -25,7 +26,7 @@ describe("Fork a template to the current app from new page popover", () => { it("2. Add selected page of template from page section", () => { cy.AddPageFromTemplate(); - cy.wait(3000); + cy.wait(5000); cy.get(template.templateDialogBox).should("be.visible"); cy.wait(4000); cy.xpath("//div[text()='Customer Support Dashboard']").click(); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Templates/Fork_Template_To_App_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Templates/Fork_Template_To_App_spec.js index 43f83dd5ac33..8b58a2429808 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Templates/Fork_Template_To_App_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Templates/Fork_Template_To_App_spec.js @@ -15,6 +15,7 @@ describe("Fork a template to the current app", () => { }); it("1. Fork a template to the current app", () => { + cy.wait(5000); cy.get(template.startFromTemplateCard).click(); cy.wait("@fetchTemplate").should( "have.nested.property", diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ThemingTests/Basic_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ThemingTests/Basic_spec.js index e4520f6fb22e..0bb41e2769a0 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ThemingTests/Basic_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ThemingTests/Basic_spec.js @@ -5,7 +5,8 @@ const publish = require("../../../../locators/publishWidgetspage.json"); const dsl = require("../../../../fixtures/replay.json"); import { ObjectsRegistry } from "../../../../support/Objects/Registry"; -let ee = ObjectsRegistry.EntityExplorer; +const ee = ObjectsRegistry.EntityExplorer, + appSettings = ObjectsRegistry.AppSettings; describe("App Theming funtionality", function() { before(() => { @@ -25,6 +26,8 @@ describe("App Theming funtionality", function() { themesSection(sectionName, themeName) + "/following-sibling::button"; it("1. Checks if theme can be changed to one of the existing themes", function() { + appSettings.OpenAppSettings(); + appSettings.GoToThemeSettings(); cy.get(commonlocators.changeThemeBtn).click({ force: true }); // select a theme @@ -57,6 +60,8 @@ describe("App Theming funtionality", function() { it("2. Checks if theme can be edited", function() { cy.get(commonlocators.selectThemeBackBtn).click({ force: true }); + appSettings.ClosePane(); + // drop a button widget and click on body cy.get(explorer.widgetSwitchId).click(); cy.dragAndDropToCanvas("buttonwidget", { x: 200, y: 200 }); //iconbuttonwidget @@ -65,6 +70,9 @@ describe("App Theming funtionality", function() { .first(0) .trigger("click", { force: true }); + appSettings.OpenAppSettings(); + appSettings.GoToThemeSettings(); + //Click the back button //Commenting below since expanded by default //cy.get(commonlocators.selectThemeBackBtn).click({ force: true }); @@ -200,6 +208,7 @@ describe("App Theming funtionality", function() { cy.wait(200); cy.get(commonlocators.toastMsg).contains("Theme testtheme Saved"); + appSettings.ClosePane(); }); it("4. Verify Save Theme after changing all properties & widgets conform to the selected theme", () => { @@ -210,6 +219,8 @@ describe("App Theming funtionality", function() { .first(0) .trigger("click", { force: true }); + appSettings.OpenAppSettings(); + appSettings.GoToThemeSettings(); //#region Change Font & verify widgets: // cy.contains("Font") // .click({ force: true }) @@ -275,7 +286,7 @@ describe("App Theming funtionality", function() { cy.get(widgetsPage.colorPickerV2Popover) .click({ force: true }) .click(); - cy.get(widgetsPage.colorPickerV2Color) + cy.get(widgetsPage.colorPickerV2TailwindColor) .eq(23) .then(($elem) => { cy.get($elem).click({ force: true }); @@ -444,7 +455,7 @@ describe("App Theming funtionality", function() { // cy.wait(200); cy.xpath(themesDeletebtn("Your Themes", "testtheme")) - .click() + .click({ force: true }) .wait(200); cy.contains( "Do you really want to delete this theme? This process cannot be undone.", @@ -456,14 +467,14 @@ describe("App Theming funtionality", function() { //Click on Delete theme trash icon & cancel it cy.xpath(themesDeletebtn("Your Themes", "testtheme")) - .click() + .click({ force: true }) .wait(200); cy.xpath("//span[text()='Cancel']/parent::a").click(); cy.get(commonlocators.toastMsg).should("not.exist"); //Click on Delete theme trash icon & delete it cy.xpath(themesDeletebtn("Your Themes", "testtheme")) - .click() + .click({ force: true }) .wait(200); cy.contains("Delete").click({ force: true }); @@ -786,8 +797,8 @@ describe("App Theming funtionality", function() { cy.get(widgetsPage.colorPickerV2Popover) .click({ force: true }) .click(); - cy.get(widgetsPage.colorPickerV2Color) - .eq(35) + cy.get(widgetsPage.colorPickerV2TailwindColor) + .eq(33) .then(($elem) => { cy.get($elem).click({ force: true }); cy.get(widgetsPage.widgetBtn) @@ -998,6 +1009,9 @@ describe("App Theming funtionality", function() { .first(0) .trigger("click", { force: true }); + appSettings.OpenAppSettings(); + appSettings.GoToThemeSettings(); + cy.get(commonlocators.changeThemeBtn).click({ force: true }); //Changing to one of featured themes & then changing individual widget properties @@ -1015,8 +1029,8 @@ describe("App Theming funtionality", function() { cy.get(widgetsPage.colorPickerV2Popover) .click({ force: true }) .click(); - cy.get(widgetsPage.colorPickerV2Color) - .eq(17) + cy.get(widgetsPage.colorPickerV2TailwindColor) + .eq(13) .then(($elem) => { cy.get($elem).click({ force: true }); cy.get(widgetsPage.widgetBtn) diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ThemingTests/ThemeReset_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ThemingTests/ThemeReset_spec.js index a607c11b7168..dc5a8f3eace1 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ThemingTests/ThemeReset_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ThemingTests/ThemeReset_spec.js @@ -1,6 +1,9 @@ const widgetsPage = require("../../../../locators/Widgets.json"); const explorer = require("../../../../locators/explorerlocators.json"); const commonlocators = require("../../../../locators/commonlocators.json"); +import { ObjectsRegistry } from "../../../../support/Objects/Registry"; + +const appSettings = ObjectsRegistry.AppSettings; describe("Theme validation usecases", function() { it("Drag and drop button widget, change value and check reset flow", function() { @@ -21,6 +24,8 @@ describe("Theme validation usecases", function() { // click on canvas to see the theming pane cy.get("#canvas-selection-0").click({ force: true }); + appSettings.OpenAppSettings(); + appSettings.GoToThemeSettings(); // reset theme cy.contains("Theme Properties") .closest("div") @@ -40,5 +45,6 @@ describe("Theme validation usecases", function() { backgroudColor, ); }); + appSettings.ClosePane(); }); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ThemingTests/Theme_Default_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ThemingTests/Theme_Default_spec.js index 69f273f1a1cd..763b1c5adee8 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ThemingTests/Theme_Default_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ThemingTests/Theme_Default_spec.js @@ -1,14 +1,14 @@ -const testdata = require("../../../../fixtures/testdata.json"); -const apiwidget = require("../../../../locators/apiWidgetslocator.json"); +import { ObjectsRegistry } from "../../../../support/Objects/Registry"; + const widgetsPage = require("../../../../locators/Widgets.json"); const explorer = require("../../../../locators/explorerlocators.json"); const commonlocators = require("../../../../locators/commonlocators.json"); const formWidgetsPage = require("../../../../locators/FormWidgets.json"); -const publish = require("../../../../locators/publishWidgetspage.json"); const themelocator = require("../../../../locators/ThemeLocators.json"); +const appSettings = ObjectsRegistry.AppSettings; + let themeBackgroudColor; -let themeFont; describe("Theme validation for default data", function() { it("Drag and drop form widget and validate Default color/font/shadow/border and list of font validation", function() { @@ -28,6 +28,9 @@ describe("Theme validation for default data", function() { cy.wait(3000); cy.get(themelocator.canvas).click({ force: true }); cy.wait(2000); + + appSettings.OpenAppSettings(); + appSettings.GoToThemeSettings(); //Border validation //cy.contains("Border").click({ force: true }); cy.get(themelocator.border).should("have.length", "3"); @@ -52,7 +55,7 @@ describe("Theme validation for default data", function() { cy.wait(250); cy.get(themelocator.fontsSelected) - .eq(0) + .eq(10) .should("have.text", "Nunito Sans"); }); cy.contains("Font").click({ force: true }); @@ -64,6 +67,7 @@ describe("Theme validation for default data", function() { cy.validateColor(0, "#553DE9"); cy.colorMouseover(1, "Background Color"); cy.validateColor(1, "#F8FAFC"); + appSettings.ClosePane(); }); it("Validate Default Theme change across application", function() { @@ -85,6 +89,8 @@ describe("Theme validation for default data", function() { .should("have.css", "background-color") .and("eq", "rgb(21, 128, 61)"); cy.get("#canvas-selection-0").click({ force: true }); + appSettings.OpenAppSettings(); + appSettings.GoToThemeSettings(); //Change the Theme cy.get(commonlocators.changeThemeBtn).click({ force: true }); cy.get(".cursor-pointer:contains('Applied Theme')").click({ force: true }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ThemingTests/Theme_FormWidget_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ThemingTests/Theme_FormWidget_spec.js index 0c9b2f7049d0..e41b1f177225 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ThemingTests/Theme_FormWidget_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ThemingTests/Theme_FormWidget_spec.js @@ -1,9 +1,13 @@ +import { ObjectsRegistry } from "../../../../support/Objects/Registry"; + const widgetsPage = require("../../../../locators/Widgets.json"); const explorer = require("../../../../locators/explorerlocators.json"); const commonlocators = require("../../../../locators/commonlocators.json"); const formWidgetsPage = require("../../../../locators/FormWidgets.json"); const themelocator = require("../../../../locators/ThemeLocators.json"); +const appSettings = ObjectsRegistry.AppSettings; + let themeBackgroudColor; let themeFont; @@ -26,6 +30,8 @@ describe("Theme validation usecases", function() { cy.get(themelocator.canvas).click({ force: true }); cy.wait(2000); + appSettings.OpenAppSettings(); + appSettings.GoToThemeSettings(); //Border validation //cy.contains("Border").click({ force: true }); cy.get(themelocator.border).should("have.length", "3"); @@ -69,7 +75,7 @@ describe("Theme validation usecases", function() { }); cy.get(themelocator.fontsSelected) - .eq(0) + .eq(10) .should("have.text", "Nunito Sans"); cy.get(".ads-dropdown-options-wrapper div") @@ -131,6 +137,7 @@ describe("Theme validation usecases", function() { cy.get(themelocator.inputColor).should("have.value", "Black"); cy.wait(2000); cy.contains("Color").click({ force: true }); + appSettings.ClosePane(); }); it("2. Publish the App and validate Font across the app", function() { @@ -177,6 +184,9 @@ describe("Theme validation usecases", function() { .should("have.css", "background-color") .and("eq", "rgb(21, 128, 61)"); cy.get("#canvas-selection-0").click({ force: true }); + + appSettings.OpenAppSettings(); + appSettings.GoToThemeSettings(); //Change the Theme cy.get(commonlocators.changeThemeBtn).click({ force: true }); cy.get(themelocator.currentTheme).click({ force: true }); @@ -190,6 +200,7 @@ describe("Theme validation usecases", function() { .then((selectedBackgroudColor) => { expect(CurrentBackgroudColor).to.equal(selectedBackgroudColor); themeBackgroudColor = CurrentBackgroudColor; + appSettings.ClosePane(); }); }); }); @@ -236,6 +247,8 @@ describe("Theme validation usecases", function() { cy.get("#canvas-selection-0").click({ force: true }); + appSettings.OpenAppSettings(); + appSettings.GoToThemeSettings(); //Change the Theme cy.get(commonlocators.changeThemeBtn).click({ force: true }); // select a theme @@ -283,6 +296,7 @@ describe("Theme validation usecases", function() { .then((selectedBackgroudColor) => { expect(CurrentBackgroudColor).to.equal(selectedBackgroudColor); themeBackgroudColor = CurrentBackgroudColor; + appSettings.ClosePane(); }); }); cy.get(formWidgetsPage.formD).click(); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ThemingTests/Theme_MultiSelectWidget_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ThemingTests/Theme_MultiSelectWidget_spec.js index f7551b2c28f8..6552a77c278c 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ThemingTests/Theme_MultiSelectWidget_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ThemingTests/Theme_MultiSelectWidget_spec.js @@ -7,15 +7,18 @@ let themeBackgroudColor; let themeFont; let themeColour; let propPane = ObjectsRegistry.PropertyPane, - ee = ObjectsRegistry.EntityExplorer; + ee = ObjectsRegistry.EntityExplorer, + appSettings = ObjectsRegistry.AppSettings; describe("Theme validation usecase for multi-select widget", function() { - it("Drag and drop multi-select widget and validate Default font and list of font validation + Bug 15007", function() { + it("1. Drag and drop multi-select widget and validate Default font and list of font validation + Bug 15007", function() { //cy.reload(); // To remove the rename tooltip ee.DragDropWidgetNVerify("multiselectwidgetv2", 300, 80); cy.get(themelocator.canvas).click({ force: true }); cy.wait(2000); + appSettings.OpenAppSettings(); + appSettings.GoToThemeSettings(); //Border validation //cy.contains("Border").click({ force: true }); cy.get(themelocator.border).should("have.length", "3"); @@ -60,7 +63,7 @@ describe("Theme validation usecase for multi-select widget", function() { }); cy.get(themelocator.fontsSelected) - .eq(0) + .eq(10) .should("have.text", "Nunito Sans"); cy.get(".ads-dropdown-options-wrapper div") @@ -94,9 +97,10 @@ describe("Theme validation usecase for multi-select widget", function() { cy.get(themelocator.inputColor).should("have.value", "brown"); cy.wait(1000); cy.contains("Color").click({ force: true }); + appSettings.ClosePane(); }); - it.skip("Publish the App and validate Font across the app + Bug 15007", function() { + it.skip("2. Publish the App and validate Font across the app + Bug 15007", function() { //Skipping due to mentioned bug cy.PublishtheApp(); cy.get(".rc-select-selection-item > .rc-select-selection-item-content") @@ -118,8 +122,10 @@ describe("Theme validation usecase for multi-select widget", function() { cy.goToEditFromPublish(); }); - it("Validate current theme feature", function() { + it("3. Validate current theme feature", function() { cy.get("#canvas-selection-0").click({ force: true }); + appSettings.OpenAppSettings(); + appSettings.GoToThemeSettings(); //Change the Theme cy.get(commonlocators.changeThemeBtn).click({ force: true }); cy.get(themelocator.currentTheme).click({ force: true }); @@ -134,11 +140,12 @@ describe("Theme validation usecase for multi-select widget", function() { expect("rgba(0, 0, 0, 0)").to.equal(selectedBackgroudColor); themeBackgroudColor = CurrentBackgroudColor; themeColour = selectedBackgroudColor; + appSettings.ClosePane(); }); }); }); - it("Publish the App and validate change of Theme across the app in publish mode", function() { + it("4. Publish the App and validate change of Theme across the app in publish mode", function() { cy.PublishtheApp(); cy.get(".rc-select-selection-item > .rc-select-selection-item-content") .first() diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/VisualTests/AppPageLayout_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/VisualTests/AppPageLayout_spec.js index 24c6a35dbcbd..97c1e77ef443 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/VisualTests/AppPageLayout_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/VisualTests/AppPageLayout_spec.js @@ -53,8 +53,7 @@ describe("Visual regression tests", () => { cy.get(homePage.signOutIcon).click(); cy.wait(500); // validating all the fields on login page - cy.get(homePage.headerAppSmithLogo).should("be.visible"); - cy.xpath("//h1").should("have.text", "Sign in to your account"); + cy.xpath("//h1").should("have.text", "Sign in"); cy.get(".bp3-label") .first() .should("have.text", "Email "); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/VisualTests/WidgetsLayout_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/VisualTests/WidgetsLayout_spec.js index dd58a820b59b..3729deac8819 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/VisualTests/WidgetsLayout_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/VisualTests/WidgetsLayout_spec.js @@ -1,7 +1,8 @@ describe("Visual regression tests", () => { // for any changes in UI, update the screenshot in snapshot folder, to do so: // 1. Delete the required screenshot which you want to update - // 2. Run test in headless mode with any browser (to maintain same resolution in CI) + // 2. Run test in headless mode with chrome (to maintain same resolution in CI) + // command: "npx cypress run --spec cypress/integration/Smoke_TestSuite/ClientSideTests/VisualTests/WidgetsLayout_spec.js --browser chrome" // 3. New screenshot will be generated in the snapshot folder it("Verify SwitchGroup inline enable/disbale", () => { @@ -18,7 +19,7 @@ describe("Visual regression tests", () => { //Unchecking & verify snap cy.get(".t--property-control-inline input") .uncheck({ force: true }) - .wait(200) + .wait(2000) .should("not.be.checked"); cy.get("[data-testid=switchgroup-container]").matchImageSnapshot( "inlineDisabled", @@ -27,7 +28,7 @@ describe("Visual regression tests", () => { //Checking again & verify snap cy.get(".t--property-control-inline input") .check({ force: true }) - .wait(200) + .wait(2000) .should("be.checked"); cy.get("[data-testid=switchgroup-container]").matchImageSnapshot( @@ -37,7 +38,7 @@ describe("Visual regression tests", () => { //Unchecking again & verify snap cy.get(".t--property-control-inline input") .uncheck({ force: true }) - .wait(200) + .wait(2000) .should("not.be.checked"); // taking screenshot of app home page in edit mode cy.get("[data-testid=switchgroup-container]").matchImageSnapshot( diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Audio/audio_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Audio/audio_spec.js index bb6a5ca78235..d0c466a86620 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Audio/audio_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Audio/audio_spec.js @@ -52,4 +52,34 @@ describe("Audio Widget Functionality", function() { 200, ); }); + + it("Checks if audio widget is reset on button click", function() { + cy.dragAndDropToCanvas("buttonwidget", { x: 300, y: 300 }); + cy.openPropertyPane("buttonwidget"); + cy.widgetText( + "Button1", + widgetsPage.buttonWidget, + commonlocators.buttonInner, + ); + cy.get(commonlocators.onClick).click(); + cy.selectResetWidget(); + cy.selectWidgetForReset("Audio1"); + + cy.dragAndDropToCanvas("textwidget", { x: 300, y: 500 }); + cy.openPropertyPane("textwidget"); + cy.updateCodeInput(".t--property-control-text", `{{Audio1.playState}}`); + + cy.openPropertyPane("audiowidget"); + cy.get(widgetsPage.autoPlay).click({ force: true }); + // Wait time added, allowing a second to pass between playing and pausing the widget, before it is reset to zero + cy.wait(1000); + cy.get(widgetsPage.autoPlay).click({ force: true }); + cy.get(widgetsPage.widgetBtn).click({ force: true }); + cy.wait(1000); + cy.get(`${widgetsPage.audioWidget} audio`).then(($audio) => { + const audio = $audio.get(0); + expect(audio.currentTime).to.equal(0); + }); + cy.get(".t--widget-textwidget").should("contain", "NOT_STARTED"); + }); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Button/ButtonLintErrorValidation_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Button/ButtonLintErrorValidation_spec.js index c96a1a01cbfb..efd12627be6c 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Button/ButtonLintErrorValidation_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Button/ButtonLintErrorValidation_spec.js @@ -52,12 +52,6 @@ describe("Linting warning validation with button widget", function() { .should("be.visible") .click({ force: true }); - cy.get(commonlocators.debugErrorMsg) - .eq(0) - .contains("ReferenceError: Nodata is not defined"); - - cy.get(commonlocators.debugErrorMsg) - .eq(2) - .contains("ReferenceError: lintError is not defined"); + cy.get(commonlocators.debugErrorMsg).should("have.length", 3); }); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Button/Button_onClickAction_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Button/Button_onClickAction_spec.js index 9d5b83b767c2..72036e7ba8de 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Button/Button_onClickAction_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Button/Button_onClickAction_spec.js @@ -69,11 +69,6 @@ describe("Button Widget Functionality", function() { .should("have.value", postgresDatasourceName) .blur(); - cy.wait("@saveDatasource").should( - "have.nested.property", - "response.body.responseMeta.status", - 200, - ); cy.fillPostgresDatasourceForm(); cy.saveDatasource(); cy.NavigateToActiveDSQueryPane(postgresDatasourceName); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Datepicker/DatePickerV2Updated_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Datepicker/DatePickerV2Updated_spec.js new file mode 100644 index 000000000000..daa5c2b16f3b --- /dev/null +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Datepicker/DatePickerV2Updated_spec.js @@ -0,0 +1,27 @@ +const dsl = require("../../../../../fixtures/datePickerV2Updated_dsl.json"); +const widgetsPage = require("../../../../../locators/Widgets.json"); +import { ObjectsRegistry } from "../../../../../support/Objects/Registry"; + +let agHelper = ObjectsRegistry.AggregateHelper; + +describe("DatePicker Widget Property pane tests with js bindings", function() { + beforeEach(() => { + agHelper.RestoreLocalStorageCache(); + }); + + afterEach(() => { + agHelper.SaveLocalStorageCache(); + }); + + before(() => { + cy.addDsl(dsl); + }); + + it("1. Datepicker tooltip renders if tooltip prop is not empty", () => { + cy.openPropertyPane("datepickerwidget2"); + // enter tooltip in property pan + cy.get(widgetsPage.inputTooltipControl).type("Helpful text for tooltip !"); + // tooltip help icon shows + cy.get(".datepicker-tooltip").should("be.visible"); + }); +}); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Datepicker/DatePickerV2_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Datepicker/DatePickerV2_spec.js index f2ce9eef0e1d..e43f1042427d 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Datepicker/DatePickerV2_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Datepicker/DatePickerV2_spec.js @@ -1,6 +1,9 @@ const formWidgetsPage = require("../../../../../locators/FormWidgets.json"); const dsl = require("../../../../../fixtures/datePicker2dsl.json"); const datedsl = require("../../../../../fixtures/datePickerdsl.json"); +const widgetsPage = require("../../../../../locators/Widgets.json"); +const publishPage = require("../../../../../locators/publishWidgetspage.json"); + import { ObjectsRegistry } from "../../../../../support/Objects/Registry"; let agHelper = ObjectsRegistry.AggregateHelper; @@ -202,5 +205,24 @@ describe("DatePicker Widget Property pane tests with js bindings", function() { cy.PublishtheApp(); // eslint-disable-next-line cypress/no-unnecessary-waiting cy.wait(10000); + cy.get(publishPage.backToEditor).click({ force: true }); + }); +}); + +describe("DatePicker Widget Property tests onFocus and onBlur", function() { + it("onBlur and onFocus should be triggered from the datePicker widget", () => { + cy.Createpage("New Page"); + cy.dragAndDropToCanvas("datepickerwidget2", { x: 300, y: 600 }); + cy.openPropertyPane("datepickerwidget2"); + + cy.get(widgetsPage.toggleOnFocus).click({ force: true }); + cy.testJsontext("onfocus", "{{showAlert('Focused','success')}}"); + cy.get(widgetsPage.toggleOnBlur).click({ force: true }); + cy.testJsontext("onblur", "{{showAlert('Blurred','success')}}"); + + cy.get(widgetsPage.datepickerInput).click({ force: true }); + cy.validateToastMessage("Focused"); + agHelper.PressEscape(); + cy.validateToastMessage("Blurred"); }); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Datepicker/DatePicker_With_Switch_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Datepicker/DatePicker_With_Switch_spec.js index 40159cd4b5b5..323b80c27840 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Datepicker/DatePicker_With_Switch_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Datepicker/DatePicker_With_Switch_spec.js @@ -29,7 +29,9 @@ describe("Switch Widget within Form widget Functionality", function() { cy.setDate(1, "ddd MMM DD YYYY"); const nextDay = dayjs().format("DD/MM/YYYY"); cy.log(nextDay); - cy.get(widgetsPage.actionSelect).click({ force: true }); + cy.get( + commonlocators.onDateSelectedField + " " + widgetsPage.actionSelect, + ).click({ force: true }); cy.get(commonlocators.chooseAction) .children() .contains("Reset widget") diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Dropdown/Dropdown_onOptionChange_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Dropdown/Dropdown_onOptionChange_spec.js index 30ea04bce7c7..39e872a27924 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Dropdown/Dropdown_onOptionChange_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Dropdown/Dropdown_onOptionChange_spec.js @@ -84,11 +84,11 @@ describe("Dropdown Widget Functionality", function() { .should("have.value", postgresDatasourceName) .blur(); - cy.wait("@saveDatasource").should( - "have.nested.property", - "response.body.responseMeta.status", - 200, - ); + // cy.wait("@saveDatasource").should( + // "have.nested.property", + // "response.body.responseMeta.status", + // 201, + // ); cy.fillPostgresDatasourceForm(); cy.saveDatasource(); cy.NavigateToActiveDSQueryPane(postgresDatasourceName); @@ -174,7 +174,7 @@ describe("Dropdown Widget Functionality", function() { // Open property pane cy.SearchEntityandOpen("Dropdown1"); // Dropdown On Option Change - cy.addEvent("Option Changed"); + cy.addEvent("Option Changed", ".t--property-control-onoptionchange"); cy.PublishtheApp(); // Change the Option cy.get(formWidgetsPage.selectWidget) diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Filepicker/FilePickerV2_Widget_Reskinning_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Filepicker/FilePickerV2_Widget_Reskinning_spec.js index b56cdbeea0d8..b4294bbd8fa9 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Filepicker/FilePickerV2_Widget_Reskinning_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Filepicker/FilePickerV2_Widget_Reskinning_spec.js @@ -1,7 +1,11 @@ +import { ObjectsRegistry } from "../../../../../support/Objects/Registry"; + const commonlocators = require("../../../../../locators/commonlocators.json"); const themeLocator = require("../../../../../locators/ThemeLocators.json"); const dsl = require("../../../../../fixtures/filePickerV2WidgetReskinDsl.json"); +const appSettings = ObjectsRegistry.AppSettings; + describe("Checkbox Widget Functionality", function() { before(() => { cy.addDsl(dsl); @@ -11,9 +15,12 @@ describe("Checkbox Widget Functionality", function() { // Click on canvas to get global theme settings cy.get(commonlocators.canvas).click({ force: true }); + appSettings.OpenAppSettings(); + appSettings.GoToThemeSettings(); cy.get(commonlocators.themeAppBorderRadiusBtn) .last() .click(); + appSettings.ClosePane(); cy.get(commonlocators.filepickerv2).click(); @@ -54,9 +61,12 @@ describe("Checkbox Widget Functionality", function() { // Change the theme border radius to M and check if the remove file icon's border radius is 4px; cy.get(commonlocators.canvas).click({ force: true }); + appSettings.OpenAppSettings(); + appSettings.GoToThemeSettings(); cy.get(commonlocators.themeAppBorderRadiusBtn) .eq(1) .click(); + appSettings.ClosePane(); cy.get(commonlocators.filepickerv2).click(); @@ -75,6 +85,8 @@ describe("Checkbox Widget Functionality", function() { // Change the global theme primary color cy.get(commonlocators.canvas).click({ force: true }); cy.wait(300); + appSettings.OpenAppSettings(); + appSettings.GoToThemeSettings(); cy.get(themeLocator.inputColor).click({ force: true }); cy.get(".t--colorpicker-v2-color") @@ -82,6 +94,7 @@ describe("Checkbox Widget Functionality", function() { .click({ force: true }) .then(($elem) => { const primaryColor = $elem.css("background-color"); + appSettings.ClosePane(); cy.get(commonlocators.filepickerv2).click(); cy.get(".uppy-StatusBar-actionBtn--upload").should( "have.css", @@ -105,10 +118,13 @@ describe("Checkbox Widget Functionality", function() { cy.get(".uppy-Dashboard-close").click({ force: true }); cy.get(commonlocators.canvas).click({ force: true }); cy.wait(300); + appSettings.OpenAppSettings(); + appSettings.GoToThemeSettings(); cy.get(themeLocator.fontsSelected).click({ force: true }); cy.contains("Roboto").click({ force: true }); + appSettings.ClosePane(); cy.get(commonlocators.filepickerv2).click(); cy.get(".uppy-DashboardContent-back").should( diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Filepicker/FilePickerV2_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Filepicker/FilePickerV2_spec.js index 7b75c20f25e0..1dbabcd9d73d 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Filepicker/FilePickerV2_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Filepicker/FilePickerV2_spec.js @@ -1,6 +1,8 @@ const explorer = require("../../../../../locators/explorerlocators.json"); const commonlocators = require("../../../../../locators/commonlocators.json"); const widgetsPage = require("../../../../../locators/Widgets.json"); +import { ObjectsRegistry } from "../../../../../support/Objects/Registry"; +let ee = ObjectsRegistry.EntityExplorer; const widgetName = "filepickerwidgetv2"; @@ -14,7 +16,7 @@ describe("File picker widget v2", () => { cy.updateCodeInput(".t--property-control-text", `{{FilePicker1.isDirty}}`); }); - it("Check isDirty meta property", function() { + it("2. Check isDirty meta property", function() { // Check if initial value of isDirty is false cy.get(".t--widget-textwidget").should("contain", "false"); // Upload a new file @@ -29,7 +31,7 @@ describe("File picker widget v2", () => { cy.get(".t--widget-textwidget").should("contain", "true"); }); - it("Check if the uploaded data does not reset when back from query page", () => { + it("3. Check if the uploaded data does not reset when back from query page", () => { cy.openPropertyPane("textwidget"); cy.updateCodeInput( ".t--property-control-text", @@ -56,12 +58,13 @@ describe("File picker widget v2", () => { cy.get(".t--widget-textwidget").should("contain", "testFile.mov"); }); - it("Check if the uploaded file is removed on click of cancel button", () => { + it("4. Check if the uploaded file is removed on click of cancel button", () => { cy.get(widgetsPage.filepickerwidgetv2).click(); cy.get(widgetsPage.filepickerwidgetv2CancelBtn).click(); cy.get(widgetsPage.filepickerwidgetv2).should("contain", "Select Files"); cy.get(widgetsPage.filepickerwidgetv2CloseModalBtn).click(); cy.get(widgetsPage.explorerSwitchId).click(); + ee.ExpandCollapseEntity("Queries/JS"); cy.get(".t--entity-item:contains(Api1)").click(); cy.get("[class*='t--actionConfiguration']") .eq(0) diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Form/FormReset_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Form/FormReset_spec.js index 9941b6f67330..9aa0124fdc56 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Form/FormReset_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Form/FormReset_spec.js @@ -12,20 +12,18 @@ describe("Form reset functionality", function() { .eq(2) .click() .should("have.class", "selected-row"); - // Select three options - cy.get(widgets.multiSelectWidget).click({ force: true }); - cy.get(widgets.multiSelectWidget).type("Option"); + cy.wait(2000); + cy.get(".rc-select-selection-overflow").click({ force: true }); cy.dropdownMultiSelectDynamic("Option 1"); cy.dropdownMultiSelectDynamic("Option 2"); cy.dropdownMultiSelectDynamic("Option 3"); // Verify input should include the name "lindsay.ferguson@reqres.in" - cy.get(widgets.inputWidget + " " + "input") + cy.get(".text-input-wrapper input") + .eq(0) .invoke("attr", "value") .should("contain", "lindsay.ferguson@reqres.in"); // Reset the form - cy.get(widgets.formButtonWidget) - .contains("Reset") - .click({ force: true }); + cy.get("button:contains('Reset')").click({ force: true }); // eslint-disable-next-line cypress/no-unnecessary-waiting cy.wait(500); // verify table should not have selected row @@ -39,32 +37,27 @@ describe("Form reset functionality", function() { }, ); // Verify input should not include "lindsay.ferguson@reqres.in" - cy.get(widgets.inputWidget + " " + "input") + cy.get(".text-input-wrapper input") + .eq(0) .invoke("attr", "value") .should("not.contain", "lindsay.ferguson@reqres.in"); // input widgets should not be in error state - cy.get(widgets.inputWidget + " " + "input").should( - "not.have.css", - "border-color", - "rgb(242, 43, 43)", - ); + cy.get(".text-input-wrapper input") + .eq(0) + .should("not.have.css", "border-color", "rgb(242, 43, 43)"); - cy.get(widgets.currencyInputWidget + " " + "input").should( - "not.have.css", - "border-color", - "rgb(242, 43, 43)", - ); + cy.get(".text-input-wrapper input") + .eq(0) + .should("not.have.css", "border-color", "rgb(242, 43, 43)"); - cy.get(widgets.phoneInputWidget + " " + "input").should( - "not.have.css", - "border-color", - "rgb(242, 43, 43)", - ); + cy.get(".text-input-wrapper input") + .eq(1) + .should("not.have.css", "border-color", "rgb(242, 43, 43)"); // Earlier select widget used to remain in error state which wasn't an expected behavior after reset // now even select widget will not show error after reset. - cy.get(`.rc-select-selector`).should( + cy.get(`.rc-select-selection-overflow`).should( "not.have.css", "border-color", "rgb(242, 43, 43)", diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Input/Input_MaxChar_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Input/Input_MaxChar_spec.js index 6440f8339563..175fa58ea19c 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Input/Input_MaxChar_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Input/Input_MaxChar_spec.js @@ -18,7 +18,9 @@ describe("Input Widget Max Char Functionality", function() { it("Text Input maxChar shows error if defaultText longer", () => { cy.get(widgetsPage.innertext).click(); cy.get(".bp3-popover-content").should(($x) => { - expect($x).contain("Default text length must be less than 5 characters"); + expect($x).contain( + "Default text length must be less than or equal to 5 characters", + ); }); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Input/Input_OnFocus_OnBlur_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Input/Input_OnFocus_OnBlur_spec.js new file mode 100644 index 000000000000..ef4bace82c1a --- /dev/null +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Input/Input_OnFocus_OnBlur_spec.js @@ -0,0 +1,63 @@ +const widgetsPage = require("../../../../../locators/Widgets.json"); +import { ObjectsRegistry } from "../../../../../support/Objects/Registry"; + +const inputWidgetName = "inputwidgetv2"; +const widgetInput = widgetsPage.inputWidget + " " + "input"; + +const phoneInputWidgetName = "phoneinputwidget"; +const phoneInputWidget = widgetsPage.phoneInputWidget + " " + "input"; + +const currencyInputWidgetName = "currencyinputwidget"; +const currencyInputWidget = widgetsPage.currencyInputWidget + " " + "input"; + +const agHelper = ObjectsRegistry.AggregateHelper; + +describe("Input Widget Property tests onFocus and onBlur", function() { + it("1. onBlur and onFocus should be triggered from the input widget", () => { + cy.dragAndDropToCanvas(inputWidgetName, { x: 300, y: 200 }); + cy.openPropertyPane(inputWidgetName); + + cy.get(widgetsPage.toggleOnFocus).click({ force: true }); + cy.testJsontext("onfocus", "{{showAlert('Focused','success')}}"); + cy.get(widgetsPage.toggleOnBlur).click({ force: true }); + cy.testJsontext("onblur", "{{showAlert('Blurred','success')}}"); + + cy.get(widgetInput).click({ force: true }); + cy.validateToastMessage("Focused"); + agHelper.PressEscape(); + cy.openPropertyPane(inputWidgetName); + cy.validateToastMessage("Blurred"); + }); + + it("2. onBlur and onFocus should be triggered from the phone input widget", () => { + cy.dragAndDropToCanvas(phoneInputWidgetName, { x: 300, y: 400 }); + cy.openPropertyPane(phoneInputWidgetName); + + cy.get(widgetsPage.toggleOnFocus).click({ force: true }); + cy.testJsontext("onfocus", "{{showAlert('Focused','success')}}"); + cy.get(widgetsPage.toggleOnBlur).click({ force: true }); + cy.testJsontext("onblur", "{{showAlert('Blurred','success')}}"); + + cy.get(phoneInputWidget).click({ force: true }); + cy.validateToastMessage("Focused"); + agHelper.PressEscape(); + cy.openPropertyPane(phoneInputWidgetName); + cy.validateToastMessage("Blurred"); + }); + + it("3. onBlur and onFocus should be triggered from the currency input widget", () => { + cy.dragAndDropToCanvas(currencyInputWidgetName, { x: 300, y: 600 }); + cy.openPropertyPane(currencyInputWidgetName); + + cy.get(widgetsPage.toggleOnFocus).click({ force: true }); + cy.testJsontext("onfocus", "{{showAlert('Focused','success')}}"); + cy.get(widgetsPage.toggleOnBlur).click({ force: true }); + cy.testJsontext("onblur", "{{showAlert('Blurred','success')}}"); + + cy.get(currencyInputWidget).click({ force: true }); + cy.validateToastMessage("Focused"); + agHelper.PressEscape(); + cy.openPropertyPane(currencyInputWidgetName); + cy.validateToastMessage("Blurred"); + }); +}); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/JSONForm/JSONForm_ArrayField_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/JSONForm/JSONForm_ArrayField_spec.js index 79d2d9510b88..fd04ef954910 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/JSONForm/JSONForm_ArrayField_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/JSONForm/JSONForm_ArrayField_spec.js @@ -213,8 +213,8 @@ describe("JSON Form Widget Array Field", () => { cy.openPropertyPane("jsonformwidget"); cy.openFieldConfiguration("education") - .openFieldConfiguration("__array_item__") - .openFieldConfiguration("college"); + .openFieldConfiguration("__array_item__", false) + .openFieldConfiguration("college", false); // Modify default text of eductation -> college field cy.testJsontext("defaultvalue", collegeFieldDefaultValue); @@ -247,14 +247,14 @@ describe("JSON Form Widget Array Field", () => { cy.openPropertyPane("jsonformwidget"); cy.openFieldConfiguration("education"); - cy.openFieldConfiguration("__array_item__"); + cy.openFieldConfiguration("__array_item__", false); // Add new custom field cy.get(".t--property-control-fieldconfiguration .t--add-column-btn").click({ force: true, }); - cy.openFieldConfiguration("customField1"); + cy.openFieldConfiguration("customField1", false); cy.selectDropdownValue( commonlocators.jsonFormFieldType, /^Phone Number Input/, @@ -285,14 +285,14 @@ describe("JSON Form Widget Array Field", () => { cy.openPropertyPane("jsonformwidget"); cy.openFieldConfiguration("education"); - cy.openFieldConfiguration("__array_item__"); + cy.openFieldConfiguration("__array_item__", false); // Add new custom field cy.get(".t--property-control-fieldconfiguration .t--add-column-btn").click({ force: true, }); - cy.openFieldConfiguration("customField1"); + cy.openFieldConfiguration("customField1", false); cy.selectDropdownValue(commonlocators.jsonFormFieldType, /^Currency Input/); // Enable Allow Country Code Change diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/JSONForm/JSONForm_CustomField_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/JSONForm/JSONForm_CustomField_spec.js index 8664471a2d6d..4daecedd8db5 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/JSONForm/JSONForm_CustomField_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/JSONForm/JSONForm_CustomField_spec.js @@ -46,6 +46,7 @@ describe("JSON Form Widget Custom Field", () => { }; cy.openPropertyPane("jsonformwidget"); + cy.backFromPropertyPanel(); cy.testJsontext("sourcedata", JSON.stringify(sourceData)); cy.wait(500); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/JSONForm/JSONForm_FieldProperties_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/JSONForm/JSONForm_FieldProperties_spec.js index 5c331428b74b..6a776e0d47cb 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/JSONForm/JSONForm_FieldProperties_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/JSONForm/JSONForm_FieldProperties_spec.js @@ -38,7 +38,9 @@ describe("Text Field Property Control", () => { cy.testJsontext("maxchars", 5); cy.get(`${fieldPrefix}-name input`).click(); cy.get(".bp3-popover-content").should(($x) => { - expect($x).contain("Default text length must be less than 5 characters"); + expect($x).contain( + "Default text length must be less than or equal to 5 characters", + ); }); cy.testJsontext("maxchars", ""); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/JSONForm/JSONForm_FormBindings_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/JSONForm/JSONForm_FormBindings_spec.js index a806bc218955..832afab75999 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/JSONForm/JSONForm_FormBindings_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/JSONForm/JSONForm_FormBindings_spec.js @@ -239,15 +239,15 @@ describe("JSON Form Widget Form Bindings", () => { .wait(500); // migrant.visible -> false - cy.openFieldConfiguration("migrant"); + cy.openFieldConfiguration("migrant", false); cy.togglebarDisable(`${propertyControlPrefix}-visible input`); cy.get(backBtn) .click({ force: true }) .wait(500); // address.street.required -> true - cy.openFieldConfiguration("address"); - cy.openFieldConfiguration("street"); + cy.openFieldConfiguration("address", false); + cy.openFieldConfiguration("street", false); cy.togglebar(`${propertyControlPrefix}-required input`); cy.get(backBtn) .click({ force: true }) @@ -259,13 +259,13 @@ describe("JSON Form Widget Form Bindings", () => { // education.college.required -> true // education.year.visible -> false cy.openFieldConfiguration("education"); - cy.openFieldConfiguration("__array_item__"); - cy.openFieldConfiguration("college"); + cy.openFieldConfiguration("__array_item__", false); + cy.openFieldConfiguration("college", false); cy.togglebar(`${propertyControlPrefix}-required input`); cy.get(backBtn) .click({ force: true }) .wait(500); - cy.openFieldConfiguration("year"); + cy.openFieldConfiguration("year", false); cy.togglebarDisable(`${propertyControlPrefix}-visible input`); cy.get(backBtn) .click({ force: true }) @@ -373,9 +373,9 @@ describe("JSON Form Widget Form Bindings", () => { .wait(500); // Change accessor education -> college to education -> graduatingCollege - cy.openFieldConfiguration("education"); - cy.openFieldConfiguration("__array_item__"); - cy.openFieldConfiguration("college"); + cy.openFieldConfiguration("education", false); + cy.openFieldConfiguration("__array_item__", false); + cy.openFieldConfiguration("college", false); cy.testJsontext("propertyname", "graduatingCollege"); cy.wait(5000); @@ -411,8 +411,8 @@ describe("JSON Form Widget Form Bindings", () => { // Change accessor education -> college to education -> graduatingCollege cy.openFieldConfiguration("education"); - cy.openFieldConfiguration("__array_item__"); - cy.openFieldConfiguration("college"); + cy.openFieldConfiguration("__array_item__", false); + cy.openFieldConfiguration("college", false); cy.testJsontext("propertyname", "graduatingCollege"); cy.wait(5000); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/JSONForm/JSONForm_HiddenFields_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/JSONForm/JSONForm_HiddenFields_spec.js index 1be037bda098..1c7ff5408e2c 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/JSONForm/JSONForm_HiddenFields_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/JSONForm/JSONForm_HiddenFields_spec.js @@ -62,6 +62,7 @@ function changeFieldType(fieldName, fieldType) { function addCustomField(fieldType) { cy.openPropertyPane("jsonformwidget"); + cy.backFromPropertyPanel(); // Add new field cy.get(commonlocators.jsonFormAddNewCustomFieldBtn).click({ @@ -83,7 +84,7 @@ describe("JSON Form Hidden fields", () => { cy.testJsontext("text", "{{JSON.stringify(JSONForm1.formData)}}"); }); - it("can hide Array Field", () => { + it("1. can hide Array Field", () => { cy.openPropertyPane("jsonformwidget"); cy.openFieldConfiguration("education"); hideAndVerifyProperties("education", [ @@ -94,18 +95,18 @@ describe("JSON Form Hidden fields", () => { ]); }); - it("can hide Array Field's inner fields", () => { + it("2. can hide Array Field's inner fields", () => { cy.openPropertyPane("jsonformwidget"); cy.openFieldConfiguration("education"); - cy.openFieldConfiguration("__array_item__"); - cy.openFieldConfiguration("college"); + cy.openFieldConfiguration("__array_item__", false); + cy.openFieldConfiguration("college", false); hideAndVerifyProperties("education-0--college", "MIT", (formData) => { return formData.education[0].college; }); }); - it("can hide Checkbox Field", () => { + it("3. can hide Checkbox Field", () => { // Add new custom field addCustomField("Checkbox"); @@ -114,7 +115,7 @@ describe("JSON Form Hidden fields", () => { removeCustomField(); }); - it("can hide Currency Field", () => { + it("4. can hide Currency Field", () => { const defaultValue = 1000; // Add new custom field addCustomField("Currency Input"); @@ -123,28 +124,28 @@ describe("JSON Form Hidden fields", () => { removeCustomField(); }); - it("can hide Date Field", () => { + it("5. can hide Date Field", () => { cy.openPropertyPane("jsonformwidget"); cy.openFieldConfiguration("dob"); hideAndVerifyProperties("dob", "10/12/1992"); }); - it("can hide Input Field", () => { + it("6. can hide Input Field", () => { cy.openPropertyPane("jsonformwidget"); cy.openFieldConfiguration("name"); hideAndVerifyProperties("name", "John"); }); - it("can hide Multiselect Field", () => { + it("7. can hide Multiselect Field", () => { cy.openPropertyPane("jsonformwidget"); cy.openFieldConfiguration("hobbies"); hideAndVerifyProperties("hobbies", ["travelling", "swimming"]); }); - it("can hide Object Field", () => { + it("8. can hide Object Field", () => { cy.openPropertyPane("jsonformwidget"); cy.openFieldConfiguration("address"); @@ -154,7 +155,7 @@ describe("JSON Form Hidden fields", () => { }); }); - it("can hide Phone Number Input Field", () => { + it("9. can hide Phone Number Input Field", () => { const defaultValue = "1000"; // Add new custom field addCustomField("Phone Number Input"); @@ -166,7 +167,7 @@ describe("JSON Form Hidden fields", () => { removeCustomField(); }); - it("can hide Radio Group Field", () => { + it("10. can hide Radio Group Field", () => { const defaultValue = "Y"; // Add new custom field addCustomField("Phone Number Input"); @@ -178,7 +179,7 @@ describe("JSON Form Hidden fields", () => { removeCustomField(); }); - it("can hide Select Field", () => { + it("11. can hide Select Field", () => { const defaultValue = "BLUE"; // Add new custom field addCustomField(/^Select/); @@ -190,7 +191,7 @@ describe("JSON Form Hidden fields", () => { removeCustomField(); }); - it("can hide Switch Field", () => { + it("12. can hide Switch Field", () => { // Add new custom field addCustomField("Switch"); @@ -199,7 +200,7 @@ describe("JSON Form Hidden fields", () => { removeCustomField(); }); - it("hides fields on first load", () => { + it("13. hides fields on first load", () => { cy.openPropertyPane("jsonformwidget"); // hide education field @@ -214,6 +215,7 @@ describe("JSON Form Hidden fields", () => { // publish the app cy.PublishtheApp(); + cy.wait(1000); // Check if name is hidden cy.get(`${fieldPrefix}-name`).should("not.exist"); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/JSONForm/JSONForm_NestedField_Select_Multiselect.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/JSONForm/JSONForm_NestedField_Select_Multiselect.js index 01f84946a477..47d7b5b0048f 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/JSONForm/JSONForm_NestedField_Select_Multiselect.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/JSONForm/JSONForm_NestedField_Select_Multiselect.js @@ -33,14 +33,14 @@ describe("JSONForm select field", () => { cy.openPropertyPane("jsonformwidget"); cy.testJsontext("sourcedata", JSON.stringify(schema)); cy.openFieldConfiguration("object"); - cy.openFieldConfiguration("select"); + cy.openFieldConfiguration("select", false); cy.selectDropdownValue(commonlocators.jsonFormFieldType, /^Select$/); cy.closePropertyPane(); cy.openPropertyPane("jsonformwidget"); cy.openFieldConfiguration("array"); - cy.openFieldConfiguration("__array_item__"); - cy.openFieldConfiguration("select"); + cy.openFieldConfiguration("__array_item__", false); + cy.openFieldConfiguration("select", false); cy.selectDropdownValue(commonlocators.jsonFormFieldType, /^Select$/); }); @@ -93,14 +93,14 @@ describe("JSONForm select field", () => { cy.openPropertyPane("jsonformwidget"); cy.testJsontext("sourcedata", JSON.stringify(schema)); cy.openFieldConfiguration("object"); - cy.openFieldConfiguration("multiselect"); + cy.openFieldConfiguration("multiselect", false); cy.selectDropdownValue(commonlocators.jsonFormFieldType, /^Multiselect$/); cy.closePropertyPane(); cy.openPropertyPane("jsonformwidget"); cy.openFieldConfiguration("array"); - cy.openFieldConfiguration("__array_item__"); - cy.openFieldConfiguration("multiselect"); + cy.openFieldConfiguration("__array_item__", false); + cy.openFieldConfiguration("multiselect", false); cy.selectDropdownValue(commonlocators.jsonFormFieldType, /^Multiselect$/); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/JSONForm/JSONForm_UnicodeKeys_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/JSONForm/JSONForm_UnicodeKeys_spec.js index af6fd530e3d0..fcf0c8bdbe44 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/JSONForm/JSONForm_UnicodeKeys_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/JSONForm/JSONForm_UnicodeKeys_spec.js @@ -219,7 +219,7 @@ describe("JSON Form Widget Unicode keys", () => { // open field суроға -> شارع cy.openFieldConfiguration("xn__80a1afdk69b"); - cy.openFieldConfiguration("xn__mgbuhw"); + cy.openFieldConfiguration("xn__mgbuhw", false); cy.testJsontext("propertyname", "شارع1 شارع"); cy.get(backBtn) .click({ force: true }) @@ -236,8 +236,8 @@ describe("JSON Form Widget Unicode keys", () => { // open field การศึกษา -> array item -> କଲେଜ cy.openFieldConfiguration("xn__12ca5huag4ce3a"); - cy.openFieldConfiguration("__array_item__"); - cy.openFieldConfiguration("xn__ohco9d4d"); + cy.openFieldConfiguration("__array_item__", false); + cy.openFieldConfiguration("xn__ohco9d4d", false); cy.testJsontext("propertyname", "ସ୍ନାତକ କଲେଜ"); cy.wait(5000); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/List/ListWidgetLintErrorValidation.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/List/ListWidgetLintErrorValidation.js index 2a10590cf0d0..ae54d4bb027c 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/List/ListWidgetLintErrorValidation.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/List/ListWidgetLintErrorValidation.js @@ -52,12 +52,6 @@ describe("Linting warning validation with list widget", function() { .should("be.visible") .click({ force: true }); - cy.get(commonlocators.debugErrorMsg) - .eq(0) - .contains("ReferenceError: ERROR is not defined"); - - cy.get(commonlocators.debugErrorMsg) - .eq(1) - .contains("SyntaxError: Unexpected identifier"); + cy.get(commonlocators.debugErrorMsg).should("have.length", 6); }); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Modal_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Modal_spec.js index 3c855f74fe97..49330c3267d8 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Modal_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Modal_spec.js @@ -120,6 +120,8 @@ describe("Modal Widget Functionality", function() { //paste cy.get("body").type(`{${modifierKey}}v`); + ee.ExpandCollapseEntity("Widgets", true); + //verify that the two modal widget should have pasted on the main canvas cy.get('.bp3-collapse-body > [step="0"]') .eq(1) diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Multiselect/MultiSelect4_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Multiselect/MultiSelect4_spec.js index d4c601e02a72..f6b261e7b3e7 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Multiselect/MultiSelect4_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Multiselect/MultiSelect4_spec.js @@ -16,7 +16,7 @@ describe("MultiSelect Widget Functionality", function() { beforeEach(() => { cy.wait(3000); }); - it("Add new multiselect widget", () => { + it("1. Add new multiselect widget", () => { cy.get(explorer.addWidget).click(); cy.dragAndDropToCanvas("multiselectwidgetv2", { x: 300, y: 300 }); cy.get(".t--widget-multiselectwidgetv2").should("exist"); @@ -43,7 +43,7 @@ describe("MultiSelect Widget Functionality", function() { ); }); - it("Copy and paste multiselect widget", () => { + it("2. Copy and paste multiselect widget", () => { cy.openPropertyPane("multiselectwidgetv2"); const modifierKey = Cypress.platform === "darwin" ? "meta" : "ctrl"; //copy and paste @@ -74,4 +74,12 @@ describe("MultiSelect Widget Functionality", function() { } catch (error) {} }); }); + + it("3. Select tooltip renders if tooltip prop is not empty", () => { + cy.openPropertyPane("multiselectwidgetv2"); + // enter tooltip in property pan + cy.get(widgetsPage.inputTooltipControl).type("Helpful text for tooltip !"); + // tooltip help icon shows + cy.get(".multiselect-tooltip").should("be.visible"); + }); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Multiselect/Multi_Select_Tree_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Multiselect/Multi_Select_Tree_spec.js index 1b95c5c663dd..82ffbe51e952 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Multiselect/Multi_Select_Tree_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Multiselect/Multi_Select_Tree_spec.js @@ -3,6 +3,7 @@ const formWidgetsPage = require("../../../../../locators/FormWidgets.json"); const publish = require("../../../../../locators/publishWidgetspage.json"); const commonlocators = require("../../../../../locators/commonlocators.json"); const explorer = require("../../../../../locators/explorerlocators.json"); +const widgetsPage = require("../../../../../locators/Widgets.json"); describe("MultiSelectTree Widget Functionality", function() { before(() => { @@ -119,6 +120,16 @@ describe("MultiSelectTree Widget Functionality", function() { "No Results Found", ); }); + + it("8. Select tooltip renders if tooltip prop is not empty", () => { + cy.openPropertyPane("multiselecttreewidget"); + // enter tooltip in property pan + cy.get(widgetsPage.inputTooltipControl).type("Helpful text for tooltip !"); + // tooltip help icon shows + cy.get(".multitree-select-tooltip") + .scrollIntoView() + .should("be.visible"); + }); }); afterEach(() => { // put your clean up code if any diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Others/Divider_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Others/Divider_spec.js index e5da1208c6b8..b25485bc5df8 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Others/Divider_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Others/Divider_spec.js @@ -8,7 +8,7 @@ describe("Divider Widget Functionality", function() { it("Add new Divider", () => { cy.get(explorer.addWidget).click(); - cy.dragAndDropToCanvas("dividerwidget", { x: 300, y: 300 }); + cy.dragAndDropToCanvas("dividerwidget", { x: 320, y: 300 }); cy.get(".t--divider-widget").should("exist"); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Others/MenuButton_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Others/MenuButton_spec.js index 3d51cf53bf5f..f42f7770fd00 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Others/MenuButton_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Others/MenuButton_spec.js @@ -1,5 +1,7 @@ const dsl = require("../../../../../fixtures/menuButtonDsl.json"); const formWidgetsPage = require("../../../../../locators/FormWidgets.json"); +const commonlocators = require("../../../../../locators/commonlocators.json"); +const { modifierKey } = require("../../../../../support/Constants"); describe("Menu Button Widget Functionality", () => { before(() => { @@ -76,7 +78,7 @@ describe("Menu Button Widget Functionality", () => { .contains("Third Menu Item"); // Undo - cy.get("body").type("{ctrl+z}"); + cy.get("body").type(`{${modifierKey}}+z`); // Check first menu item cy.get(".bp3-menu-item") .eq(0) @@ -102,4 +104,115 @@ describe("Menu Button Widget Functionality", () => { // Navigate Back cy.get(".t--property-pane-back-btn").click(); }); + + it("3. MenuButton widget functionality to add dynamic menu items", function() { + cy.openPropertyPane("menubuttonwidget"); + cy.moveToContentTab(); + + // Select menu items source as Dynamic + cy.get(`${commonlocators.menuButtonMenuItemsSource} .t--button-tab-DYNAMIC`) + .last() + .click({ + force: true, + }); + + cy.wait(200); + + // Add sample source data + cy.testJsontext( + "sourcedata", + JSON.stringify(this.data.MenuButtonSourceData), + ); + + // Open configure array item panel + cy.get(commonlocators.menuButtonConfigureArrayItems).click({ + force: true, + }); + + // Update label binding + cy.testJsontext("label", `{{currentItem.first_name}}`); + cy.wait(1000); + + cy.closePropertyPane(); + + // Check if a total of 3 menu items have been added + cy.get(`${formWidgetsPage.menuButtonWidget} button`).click({ + force: true, + }); + cy.wait(500); + cy.get(".bp3-menu-item") + .eq(0) + .contains("Michael"); + cy.get(".bp3-menu-item") + .eq(1) + .contains("Lindsay"); + cy.get(".bp3-menu-item") + .eq(2) + .contains("Brock"); + + cy.closePropertyPane(); + }); + + it("4. Disable one dynamic item using {{currentItem}} binding", function() { + cy.openPropertyPane("menubuttonwidget"); + cy.moveToContentTab(); + + // Open configure array item panel + cy.get(commonlocators.menuButtonConfigureArrayItems).click({ + force: true, + }); + + // Update disabled JS binding + cy.get(commonlocators.Disablejs) + .find(".t--js-toggle") + .first() + .click({ force: true }); + cy.testJsontext("disabled", `{{currentItem.first_name === "Lindsay"}}`); + cy.wait(1000); + + // Check if the 2nd item is disabled + cy.get(`${formWidgetsPage.menuButtonWidget} button`).click({ + force: true, + }); + cy.wait(500); + cy.get(".bp3-menu-item") + .eq(1) + .should("have.class", "bp3-disabled"); + + cy.closePropertyPane(); + }); + + it("5. Apply background color to dynamic items using {{currentItem}} binding", function() { + cy.openPropertyPane("menubuttonwidget"); + cy.moveToContentTab(); + + // Open configure array item panel + cy.get(commonlocators.menuButtonConfigureArrayItems).click({ + force: true, + }); + cy.moveToStyleTab(); + + // Update disabled JS binding + cy.get(".t--property-control-backgroundcolor .t--js-toggle").click(); + cy.updateCodeInput( + ".t--property-control-backgroundcolor", + `{{currentItem.first_name === "Michael" ? "rgb(255, 165, 0)" : "rgb(0, 128, 0)"}}`, + ); + cy.wait(1000); + + cy.get(`${formWidgetsPage.menuButtonWidget} button`).click({ + force: true, + }); + cy.wait(500); + + // Check if the 1st item has orange background color + cy.get(".bp3-menu-item") + .eq(0) + .should("have.css", "background-color", "rgb(255, 165, 0)"); + + // Check if the 3rd item has green background color + cy.get(".bp3-menu-item") + .eq(2) + .should("have.css", "background-color", "rgb(0, 128, 0)"); + }); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Others/StatBox_DragAndDrop_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Others/StatBox_DragAndDrop_spec.js new file mode 100644 index 000000000000..2d1a962af717 --- /dev/null +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Others/StatBox_DragAndDrop_spec.js @@ -0,0 +1,28 @@ +const dsl = require("../../../../../fixtures/dynamicHeightStatboxdsl.json"); +const explorer = require("../../../../../locators/explorerlocators.json"); +const data = require("../../../../../fixtures/example.json"); +const widgetsPage = require("../../../../../locators/Widgets.json"); +import { ObjectsRegistry } from "../../../../../support/Objects/Registry"; +const agHelper = ObjectsRegistry.AggregateHelper; + +describe("Statbox Widget Functionality", function() { + afterEach(() => { + agHelper.SaveLocalStorageCache(); + }); + + beforeEach(() => { + agHelper.RestoreLocalStorageCache(); + cy.addDsl(dsl); + }); + + it("Verify Statbox can be placed inside another widget", () => { + cy.get(explorer.addWidget).click(); + // placing statbox widget inside container widget + cy.dragAndDropToWidget("statboxwidget", "containerwidget", { + x: 100, + y: 100, + }); + cy.openPropertyPaneWithIndex("statboxwidget", 1); + cy.openPropertyPaneWithIndex("statboxwidget", 0); + }); +}); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Others/Statbox_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Others/Statbox_spec.js index 879a282d55c7..39317f7b1456 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Others/Statbox_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Others/Statbox_spec.js @@ -1,4 +1,5 @@ const dsl = require("../../../../../fixtures/StatboxDsl.json"); +const dsl1 = require("../../../../../fixtures/dynamicHeightStatboxdsl.json"); const explorer = require("../../../../../locators/explorerlocators.json"); const data = require("../../../../../fixtures/example.json"); const widgetsPage = require("../../../../../locators/Widgets.json"); @@ -85,14 +86,4 @@ describe("Statbox Widget Functionality", function() { parseSpecialCharSequences: false, }); }); - - it("5. Verify Statbox can be placed inside another widget", () => { - cy.get(explorer.addWidget).click(); - // placing statbox widget inside container widget - cy.dragAndDropToCanvas("containerwidget", { x: 500, y: 300 }); - cy.dragAndDropToWidget("statboxwidget", "containerwidget", { - x: 100, - y: 100, - }); - }); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Others/Video_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Others/Video_spec.js index b7e456bcd0b4..6f72ae64365a 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Others/Video_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Others/Video_spec.js @@ -73,6 +73,37 @@ describe("Video Widget Functionality", function() { */ }); + it("Checks if video widget is reset on button click", function() { + cy.testCodeMirror(testdata.videoUrl2); + cy.dragAndDropToCanvas("buttonwidget", { x: 300, y: 300 }); + cy.openPropertyPane("buttonwidget"); + cy.widgetText( + "Button1", + widgetsPage.buttonWidget, + commonlocators.buttonInner, + ); + cy.get(commonlocators.onClick).click(); + cy.selectResetWidget(); + cy.selectWidgetForReset("Video1"); + + cy.dragAndDropToCanvas("textwidget", { x: 300, y: 500 }); + cy.openPropertyPane("textwidget"); + cy.updateCodeInput(".t--property-control-text", `{{Video1.playState}}`); + + cy.openPropertyPane("videowidget"); + cy.get(widgetsPage.autoPlay).click({ force: true }); + // Wait time added, allowing a second to pass between playing and pausing the widget, before it is reset to zero + cy.wait(1000); + cy.get(widgetsPage.autoPlay).click({ force: true }); + cy.get(widgetsPage.widgetBtn).click({ force: true }); + cy.wait(1000); + cy.get(`${widgetsPage.videoWidget} video`).then(($video) => { + const video = $video.get(0); + expect(video.currentTime).to.equal(0); + }); + cy.get(".t--widget-textwidget").should("contain", "NOT_STARTED"); + }); + afterEach(() => { // put your clean up code if any }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/RTE/RichTextEditor_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/RTE/RichTextEditor_spec.js index 3b1cd8eede46..699c5627c6bc 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/RTE/RichTextEditor_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/RTE/RichTextEditor_spec.js @@ -41,7 +41,7 @@ describe("RichTextEditor Widget Functionality", function() { cy.openPropertyPane("richtexteditorwidget"); }); - it("RichTextEditor-Edit Text area with HTML body functionality", function() { + it("1. RichTextEditor-Edit Text area with HTML body functionality", function() { //changing the Text Name cy.widgetText( this.data.RichTextEditorName, @@ -66,7 +66,7 @@ describe("RichTextEditor Widget Functionality", function() { ); }); - it("RichTextEditor-Enable Validation", function() { + it("2. RichTextEditor-Enable Validation", function() { //Uncheck the Disabled checkbox cy.UncheckWidgetProperties(formWidgetsPage.disableJs); cy.validateEnableWidget( @@ -81,7 +81,7 @@ describe("RichTextEditor Widget Functionality", function() { ); }); - it("RichTextEditor-Disable Validation", function() { + it("3. RichTextEditor-Disable Validation", function() { //Check the Disabled checkbox cy.CheckWidgetProperties(formWidgetsPage.disableJs); cy.validateDisableWidget( @@ -96,21 +96,21 @@ describe("RichTextEditor Widget Functionality", function() { ); }); - it("RichTextEditor-check Visible field validation", function() { + it("4. RichTextEditor-check Visible field validation", function() { // Uncheck the visible checkbox cy.UncheckWidgetProperties(commonlocators.visibleCheckbox); cy.PublishtheApp(); cy.get(publishPage.richTextEditorWidget).should("not.exist"); }); - it("RichTextEditor-uncheck Visible field validation", function() { + it("5. RichTextEditor-uncheck Visible field validation", function() { // Check the visible checkbox cy.CheckWidgetProperties(commonlocators.visibleCheckbox); cy.PublishtheApp(); cy.get(publishPage.richTextEditorWidget).should("be.visible"); }); - it("RichTextEditor-check Hide toolbar field validation", function() { + it("6. RichTextEditor-check Hide toolbar field validation", function() { // Check the Hide toolbar checkbox cy.CheckWidgetProperties(commonlocators.hideToolbarCheckbox); cy.validateToolbarHidden( @@ -124,7 +124,7 @@ describe("RichTextEditor Widget Functionality", function() { ); }); - it("RichTextEditor-uncheck Hide toolbar field validation", function() { + it("7. RichTextEditor-uncheck Hide toolbar field validation", function() { // Uncheck the Hide toolbar checkbox cy.UncheckWidgetProperties(commonlocators.hideToolbarCheckbox); cy.validateToolbarVisible( @@ -138,7 +138,7 @@ describe("RichTextEditor Widget Functionality", function() { ); }); - it("Reset RichTextEditor", function() { + it("8. Reset RichTextEditor", function() { // Enable the widget cy.UncheckWidgetProperties(formWidgetsPage.disableJs); @@ -159,7 +159,7 @@ describe("RichTextEditor Widget Functionality", function() { ); }); - it("Check isDirty meta property", function() { + it("9. Check isDirty meta property", function() { cy.openPropertyPane("textwidget"); cy.updateCodeInput( ".t--property-control-text", @@ -194,7 +194,7 @@ describe("RichTextEditor Widget Functionality", function() { cy.get(".t--widget-textwidget").should("contain", "false"); }); - it("Check if the binding is getting removed from the text and the RTE widget", function() { + it("10. Check if the binding is getting removed from the text and the RTE widget", function() { cy.openPropertyPane("textwidget"); cy.updateCodeInput(".t--property-control-text", `{{RichtextEditor.text}}`); // Change defaultText of the RTE @@ -213,7 +213,7 @@ describe("RichTextEditor Widget Functionality", function() { cy.get(".t--widget-textwidget").should("contain", ""); }); - it("Check if text does not re-appear when cut, inside the RTE widget", function() { + it("11. Check if text does not re-appear when cut, inside the RTE widget", function() { cy.window().then((win) => { const tinyMceId = "rte-6h8j08u7ea"; @@ -233,7 +233,7 @@ describe("RichTextEditor Widget Functionality", function() { }); }); - it.only("Check if the cursor position is at the end for the RTE widget", function() { + it("12. Check if the cursor position is at the end for the RTE widget", function() { const tinyMceId = "rte-6h8j08u7ea"; const testString = "Test Content"; const testStringLen = testString.length; @@ -252,7 +252,7 @@ describe("RichTextEditor Widget Functionality", function() { cy.get(".t--button-tab-html").click({ force: true }); }); - it("Check if different font size texts are supported inside the RTE widget", function() { + it("13. Check if different font size texts are supported inside the RTE widget", function() { const tinyMceId = "rte-6h8j08u7ea"; const testString = "Test Content"; @@ -274,6 +274,18 @@ describe("RichTextEditor Widget Functionality", function() { }); }); + it("14. Check if button for Underline exists within the Toolbar of RTE widget", () => { + cy.get('[aria-label="Underline"]').should("exist"); + }); + + it("15. Check if button for Background Color is rendered only once within the Toolbar of RTE widget", () => { + cy.get('[aria-label="Background color"]').should("have.length", 1); + }); + + it("16. Check if button for Text Color is rendered only once within the Toolbar of RTE widget", () => { + cy.get('[aria-label="Text color"]').should("have.length", 1); + }); + afterEach(() => { cy.goToEditFromPublish(); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Select/Select_TreeSelect_MultiSelect_OnFocus_OnBlur_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Select/Select_TreeSelect_MultiSelect_OnFocus_OnBlur_spec.js new file mode 100644 index 000000000000..bc88c24efc27 --- /dev/null +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Select/Select_TreeSelect_MultiSelect_OnFocus_OnBlur_spec.js @@ -0,0 +1,97 @@ +const widgetsPage = require("../../../../../locators/Widgets.json"); +const dsl = require("../../../../../fixtures/selectMultiSelectTreeSelectWidgetDsl.json"); +const formWidgetsPage = require("../../../../../locators/FormWidgets.json"); + +describe("Select, MultiSelect, Tree Select and Multi Tree Select Widget Property tests onFocus and onBlur", function() { + before(() => { + cy.addDsl(dsl); + }); + + it("1. onDropdownOpen and onDropdownClose should be triggered from the select widget", () => { + cy.openPropertyPane("selectwidget"); + + cy.get(widgetsPage.toggleOnDropdownOpen).click({ force: true }); + cy.testJsontext( + "ondropdownopen", + "{{showAlert('Select1 dropdown opened', 'success')}}", + ); + cy.get(widgetsPage.toggleOnDropdownClose).click({ force: true }); + cy.testJsontext( + "ondropdownclose", + "{{showAlert('Select1 dropdown closed', 'success')}}", + ); + + cy.get(formWidgetsPage.selectButton).click({ force: true }); + cy.validateToastMessage("Select1 dropdown opened"); + cy.get(formWidgetsPage.selectButton).click({ force: true }); + cy.validateToastMessage("Select1 dropdown closed"); + }); + + it("2. onDropdownOpen and onDropdownClose should be triggered from the multiselect widget", () => { + cy.openPropertyPane("multiselectwidgetv2"); + + cy.get(widgetsPage.toggleOnDropdownOpen).click({ force: true }); + cy.testJsontext( + "ondropdownopen", + "{{showAlert('MultiSelect1 dropdown opened', 'success')}}", + ); + cy.get(widgetsPage.toggleOnDropdownClose).click({ force: true }); + cy.testJsontext( + "ondropdownclose", + "{{showAlert('MultiSelect1 dropdown closed', 'success')}}", + ); + + cy.get(formWidgetsPage.multiSelect).click({ force: true }); + cy.validateToastMessage("MultiSelect1 dropdown opened"); + cy.get(formWidgetsPage.multiSelect).click({ force: true }); + cy.validateToastMessage("MultiSelect1 dropdown closed"); + }); + + it("3. onDropdownOpen and onDropdownClose should be triggered from the treeselect widget", () => { + cy.openPropertyPane("singleselecttreewidget"); + + cy.get(widgetsPage.toggleOnDropdownOpen).click({ force: true }); + cy.testJsontext( + "ondropdownopen", + "{{showAlert('TreeSelect1 dropdown opened', 'success')}}", + ); + cy.get(widgetsPage.toggleOnDropdownClose).click({ force: true }); + cy.testJsontext( + "ondropdownclose", + "{{showAlert('TreeSelect1 dropdown closed', 'success')}}", + ); + + cy.get(formWidgetsPage.treeSelect) + .last() + .click({ force: true }); + cy.validateToastMessage("TreeSelect1 dropdown opened"); + cy.get(formWidgetsPage.treeSelect) + .last() + .click({ force: true }); + cy.validateToastMessage("TreeSelect1 dropdown closed"); + }); + + it("4. onDropdownOpen and onDropdownClose should be triggered from the multitreeselect widget", () => { + cy.openPropertyPane("multiselecttreewidget"); + + cy.get(widgetsPage.toggleOnDropdownOpen).click({ force: true }); + cy.testJsontext( + "ondropdownopen", + "{{showAlert('MultiTreeSelect1 dropdown opened', 'success')}}", + ); + cy.get(widgetsPage.toggleOnDropdownClose).click({ force: true }); + cy.testJsontext( + "ondropdownclose", + "{{showAlert('MultiTreeSelect1 dropdown closed', 'success')}}", + ); + + cy.get(formWidgetsPage.multiTreeSelect) + .first() + .click({ force: true }); + cy.validateToastMessage("MultiTreeSelect1 dropdown opened"); + cy.get(formWidgetsPage.multiTreeSelect) + .first() + .click({ force: true }); + cy.validateToastMessage("MultiTreeSelect1 dropdown closed"); + }); +}); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Select/Select_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Select/Select_spec.js index 86182197921a..c814044bcfd2 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Select/Select_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Select/Select_spec.js @@ -2,6 +2,7 @@ const explorer = require("../../../../../locators/explorerlocators.json"); const commonlocators = require("../../../../../locators/commonlocators.json"); const formWidgetsPage = require("../../../../../locators/FormWidgets.json"); const widgetLocators = require("../../../../../locators/Widgets.json"); +const widgetsPage = require("../../../../../locators/Widgets.json"); describe("Select widget", () => { it("1. Drag and drop Select/Text widgets", () => { @@ -79,4 +80,12 @@ describe("Select widget", () => { .invoke("val") .should("not.be.empty"); }); + + it("5. Select tooltip renders if tooltip prop is not empty", () => { + cy.openPropertyPane("selectwidget"); + // enter tooltip in property pan + cy.get(widgetsPage.inputTooltipControl).type("Helpful text for tooltip !"); + // tooltip help icon shows + cy.get(".select-tooltip").should("be.visible"); + }); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Select/Single_Select_Tree_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Select/Single_Select_Tree_spec.js index e783da1c7872..2428d22563ff 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Select/Single_Select_Tree_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Select/Single_Select_Tree_spec.js @@ -2,6 +2,7 @@ const dsl = require("../../../../../fixtures/TreeSelectDsl.json"); const formWidgetsPage = require("../../../../../locators/FormWidgets.json"); const publish = require("../../../../../locators/publishWidgetspage.json"); const commonlocators = require("../../../../../locators/commonlocators.json"); +const widgetsPage = require("../../../../../locators/Widgets.json"); describe("Single Select Widget Functionality", function() { before(() => { @@ -133,6 +134,14 @@ describe("Single Select Widget Functionality", function() { "select option", ); }); + + it("9. Select tooltip renders if tooltip prop is not empty", () => { + cy.openPropertyPane("singleselecttreewidget"); + // enter tooltip in property pan + cy.get(widgetsPage.inputTooltipControl).type("Helpful text for tooltip !"); + // tooltip help icon shows + cy.get(".tree-select-tooltip").should("be.visible"); + }); }); afterEach(() => { // put your clean up code if any diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Sliders/NumberSlider_spec.ts b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Sliders/NumberSlider_spec.ts index a4a30f611700..07c8863ec3d0 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Sliders/NumberSlider_spec.ts +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Sliders/NumberSlider_spec.ts @@ -85,13 +85,13 @@ describe("Number Slider spec", () => { propPane.UpdatePropertyFieldValue("Default Value", "-10"); agHelper.VerifyEvaluatedErrorMessage( - "This value must be greater than min value", + "This value must be greater than or equal to the min value", ); propPane.UpdatePropertyFieldValue("Default Value", "110"); agHelper.VerifyEvaluatedErrorMessage( - "This value must be less than max value", + "This value must be less than or equal to the max value", ); propPane.UpdatePropertyFieldValue("Default Value", "asd"); @@ -119,7 +119,10 @@ describe("Number Slider spec", () => { agHelper .GetElement(locator._sliderThumb) .focus() - .type("{rightArrow}"); + .type("{rightArrow}") + .wait(500); + + agHelper.Sleep(2000); //for the changes to reflect in text widget // Assert the Text widget has value 20 agHelper.GetText(getWidgetSelector(WIDGET.TEXT)).then(($label) => { @@ -133,8 +136,7 @@ describe("Number Slider spec", () => { .type("{leftArrow}") .type("{leftArrow}"); - agHelper.Sleep(200); - + agHelper.Sleep(2000); //for the changes to reflect in text widget // Assert the Text widget has value 0 agHelper.GetText(getWidgetSelector(WIDGET.TEXT)).then(($label) => { expect($label).to.eq("0"); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Sliders/RangeSlider_spec.ts b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Sliders/RangeSlider_spec.ts index a10e97624deb..ef1adaf62fce 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Sliders/RangeSlider_spec.ts +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Sliders/RangeSlider_spec.ts @@ -39,8 +39,6 @@ describe("Range Slider spec", () => { agHelper.VerifyEvaluatedErrorMessage("This value must be a number"); propPane.UpdatePropertyFieldValue("Min. Value", "0"); - - // agHelper.VerifyEvaluatedValue("0"); }); it("2. Validates Max. Value", () => { @@ -59,8 +57,6 @@ describe("Range Slider spec", () => { agHelper.VerifyEvaluatedErrorMessage("This value must be a number"); propPane.UpdatePropertyFieldValue("Max. Value", "100"); - - // agHelper.VerifyEvaluatedValue("100"); }); it("3. Validates Step Size", () => { @@ -79,8 +75,6 @@ describe("Range Slider spec", () => { agHelper.VerifyEvaluatedErrorMessage("This value must be a number"); propPane.UpdatePropertyFieldValue("Step Size", "1"); - - // agHelper.VerifyEvaluatedValue("1"); }); it("4. Validates Min Range", () => { @@ -101,15 +95,13 @@ describe("Range Slider spec", () => { agHelper.VerifyEvaluatedErrorMessage("This value must be less than 100"); propPane.UpdatePropertyFieldValue("Min. Range", "10"); - - // agHelper.VerifyEvaluatedValue("10"); }); it("5. Validates Default Start Value", () => { propPane.UpdatePropertyFieldValue("Default Start Value", "-100"); agHelper.VerifyEvaluatedErrorMessage( - "This value must be greater than min value", + "This value must be greater than or equal to the min value", ); propPane.UpdatePropertyFieldValue("Default Start Value", "110"); @@ -123,8 +115,6 @@ describe("Range Slider spec", () => { agHelper.VerifyEvaluatedErrorMessage("This value must be a number"); propPane.UpdatePropertyFieldValue("Default Start Value", "10"); - - // agHelper.VerifyEvaluatedValue("10"); }); it("6. Validates Default End Value", () => { @@ -137,7 +127,7 @@ describe("Range Slider spec", () => { propPane.UpdatePropertyFieldValue("Default End Value", "110"); agHelper.VerifyEvaluatedErrorMessage( - "This value must be less than max value", + "This value must be less than or equal to the max value", ); propPane.UpdatePropertyFieldValue("Default End Value", "asd"); @@ -145,8 +135,6 @@ describe("Range Slider spec", () => { agHelper.VerifyEvaluatedErrorMessage("This value must be a number"); propPane.UpdatePropertyFieldValue("Default End Value", "100"); - - // agHelper.VerifyEvaluatedValue("100"); }); it("7. Change Step Size and check if binding value changes", () => { diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Tab/Tab_OnEvent_Navigation_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Tab/Tab_OnEvent_Navigation_spec.js new file mode 100644 index 000000000000..65e3c2738012 --- /dev/null +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Tab/Tab_OnEvent_Navigation_spec.js @@ -0,0 +1,37 @@ +const LayoutPage = require("../../../../../locators/Layout.json"); +const widgetsPage = require("../../../../../locators/Widgets.json"); +const dsl = require("../../../../../fixtures/tabsWidgetReset.json"); +const publishPage = require("../../../../../locators/publishWidgetspage.json"); + +describe("Tabs widget on change of selection navigation usecases", function() { + before(() => { + cy.addDsl(dsl); + }); + + it("1.On change of tab selection Navigate to a URL", function() { + cy.openPropertyPane("tabswidget"); + cy.get(".code-highlight") + .children() + .contains("No action") + .last() + .click({ force: true }) + .selectOnClickOption("Navigate to"); + cy.wait(5000); + cy.get("#switcher--url").click({ force: true }); + cy.testCodeMirrorWithIndex("www.appsmith.com", 1); + cy.wait(5000); + }); + + it("2.Publish the app and validate the navigation change on tab selection.", function() { + cy.PublishtheApp(); + cy.wait(5000); + cy.get(".t--page-switch-tab:contains('Tab 3')").click( + { force: true }, + { multiple: true }, + ); + cy.url().should("include", "appsmith"); + cy.go("back"); + cy.get(".t--page-switch-tab:contains('Tab 3')").should("be.visible"); + //cy.get(publishPage.backToEditor).click({ force: true }); + }); +}); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV1/Table_Button_Icon_validation_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV1/Table_Button_Icon_validation_spec.js index a0522358ee98..3551b37a737f 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV1/Table_Button_Icon_validation_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV1/Table_Button_Icon_validation_spec.js @@ -30,10 +30,10 @@ describe("Table Widget property pane feature validation", function() { cy.editColumn("id"); cy.get(widgetsPage.tableBtn).should("not.exist"); // Changing column data type to "Button" - cy.changeColumnType("Button"); + cy.changeColumnType("Button", false); // Changing the computed value (data) to "orderAmount" cy.updateComputedValue(testdata.currentRowOrderAmt); - cy.changeColumnType("Button"); + cy.changeColumnType("Button", false); cy.get(widgetsPage.buttonColor) .click({ force: true }) .clear() @@ -51,7 +51,7 @@ describe("Table Widget property pane feature validation", function() { cy.get(commonlocators.editPropBackButton).click({ force: true }); cy.editColumn("id"); // Change Column type to icon Button - cy.changeColumnType("Icon Button"); + cy.changeColumnType("Icon Button", false); // Select Icon from Icon Control cy.get(".t--property-control-icon .bp3-icon-caret-down").click({ force: true, diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV1/Table_Derived_Column_Data_validation_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV1/Table_Derived_Column_Data_validation_spec.js index b42e3bae3445..2fbe3fd86413 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV1/Table_Derived_Column_Data_validation_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV1/Table_Derived_Column_Data_validation_spec.js @@ -76,6 +76,7 @@ describe("Test Create Api and Bind to Table widget", function() { it("Update table json data and check the column names updated", function() { // Open table propert pane cy.SearchEntityandOpen("Table1"); + cy.backFromPropertyPanel(); // Change the table data cy.testJsontext("tabledata", JSON.stringify(this.data.TableInputUpdate)); cy.wait("@updateLayout"); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV1/Table_GeneralProperty_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV1/Table_GeneralProperty_spec.js index 200ee1bb8918..a6f38188ead3 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV1/Table_GeneralProperty_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV1/Table_GeneralProperty_spec.js @@ -159,7 +159,7 @@ describe("Table Widget property pane feature validation", function() { // Open email property pane cy.editColumn("email"); // Change column type to url - cy.changeColumnType("URL"); + cy.changeColumnType("URL", false); //Check all the occurance cy.get(".link-text").should("have.length", "3"); /* diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV1/Table_PropertyPane_IconName_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV1/Table_PropertyPane_IconName_spec.js index 70bc1af8c3c2..ac532bfb4e0d 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV1/Table_PropertyPane_IconName_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV1/Table_PropertyPane_IconName_spec.js @@ -11,14 +11,14 @@ describe("Table Widget property pane feature validation", function() { cy.addColumn("CustomColumn"); cy.editColumn("customColumn1"); - cy.changeColumnType("Menu Button"); + cy.changeColumnType("Menu Button", false); cy.wait(400); cy.get(commonlocators.selectedIcon).should("have.text", "(none)"); cy.getTableDataSelector("1", "5").then((selector) => { cy.get(selector + " button span.bp3-icon").should("not.exist"); }); - cy.changeColumnType("Icon Button"); + cy.changeColumnType("Icon Button", false); cy.wait(400); cy.get(commonlocators.selectedIcon).should("have.text", "add"); cy.getTableDataSelector("1", "5").then((selector) => { @@ -28,7 +28,7 @@ describe("Table Widget property pane feature validation", function() { .and("equal", "add"); }); - cy.changeColumnType("Menu Button"); + cy.changeColumnType("Menu Button", false); cy.wait(500); cy.get(commonlocators.selectedIcon).should("have.text", "(none)"); cy.getTableDataSelector("1", "5").then((selector) => { diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV1/Table_PropertyPane_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV1/Table_PropertyPane_spec.js index 4117740c2378..af1614f689e3 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV1/Table_PropertyPane_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV1/Table_PropertyPane_spec.js @@ -110,7 +110,7 @@ describe("Table Widget property pane feature validation", function() { }); // Changing Column data type from "Plain text" to "Number" - cy.changeColumnType("Number"); + cy.changeColumnType("Number", false); cy.readTabledataPublish("1", "4").then((tabData) => { cy.log(tabData); expect(tabData).to.not.equal("lindsay.ferguson@reqres.in"); @@ -124,7 +124,7 @@ describe("Table Widget property pane feature validation", function() { }); // Changing Column data type from "Number" to "Date" - cy.changeColumnType("Date"); + cy.changeColumnType("Date", false); // orderAmout to "Moment Date" cy.updateComputedValue(testdata.momentDate); cy.readTabledataPublish("1", "1").then((tabData) => { @@ -134,7 +134,7 @@ describe("Table Widget property pane feature validation", function() { // Changing Column data type from "URL" to "Video" /* const videoVal = 'https://youtu.be/Sc-m3ceZyfk'; - cy.changeColumnType("Video"); + cy.changeColumnType("Video", false); // "Moement "date" to "Video" cy.updateComputedValue(videoVal); // cy.testJson text("computedvalue", videoVal, ) @@ -147,7 +147,7 @@ describe("Table Widget property pane feature validation", function() { const imageVal = "https://images.pexels.com/photos/736230/pexels-photo-736230.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500"; - cy.changeColumnType("Image"); + cy.changeColumnType("Image", false); // "Moement "date" to "Image" cy.updateComputedValue(imageVal); // Verifying the href of the image added. @@ -156,7 +156,7 @@ describe("Table Widget property pane feature validation", function() { }); // change column data type to "icon button" - cy.changeColumnType("Icon Button"); + cy.changeColumnType("Icon Button", false); cy.wait(400); cy.get(commonlocators.selectedIcon).should("have.text", "add"); @@ -166,7 +166,7 @@ describe("Table Widget property pane feature validation", function() { // Changing Column data type from "Date" to "URl" cy.readTabledataPublish("1", "1").then((actualEmail) => { - cy.changeColumnType("URL"); + cy.changeColumnType("URL", false); // "Image" to "url" cy.updateComputedValue(testdata.currentRowEmail); cy.readTabledataPublish("1", "0").then((tabData2) => { @@ -273,6 +273,7 @@ describe("Table Widget property pane feature validation", function() { it("12. Verify default search text", function() { // Open property pane cy.openPropertyPane("tablewidget"); + cy.backFromPropertyPanel(); // Chage deat search text value to "data" cy.testJsontext("defaultsearchtext", "data"); cy.PublishtheApp(); @@ -284,6 +285,7 @@ describe("Table Widget property pane feature validation", function() { it("13. Verify default selected row", function() { // Open property pane cy.openPropertyPane("tablewidget"); + cy.backFromPropertyPanel(); cy.testJsontext("defaultsearchtext", ""); // Change default selected row value to 1 cy.get(widgetsPage.defaultSelectedRowField).type("1"); @@ -307,7 +309,7 @@ describe("Table Widget property pane feature validation", function() { // cy.tableColumnDataValidation("customColumn2"); //To be updated later // cy.editColumn("customColumn2"); - // cy.changeColumnType("Button"); + // cy.changeColumnType("Button", false); // // default selected opts // cy.get(commonlocators.tableButtonVariant + " span[type='p1']").should( // "have.text", diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV1/Table_Widget_Add_button_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV1/Table_Widget_Add_button_spec.js index cd2fe107da57..45469f57117c 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV1/Table_Widget_Add_button_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV1/Table_Widget_Add_button_spec.js @@ -14,7 +14,7 @@ describe("Table Widget property pane feature validation", function() { cy.editColumn("id"); cy.get(widgetsPage.tableBtn).should("not.exist"); // Changing column data type to "Button" - cy.changeColumnType("Button"); + cy.changeColumnType("Button", false); // Changing the computed value (data) to "orderAmount" cy.updateComputedValue(testdata.currentRowOrderAmt); // Selecting button action to show message @@ -85,7 +85,7 @@ describe("Table Widget property pane feature validation", function() { .children() .contains("Plain Text") .click(); - cy.changeColumnType("Button"); */ + cy.changeColumnType("Button", false); */ const color1 = "rgb(255, 0, 0)"; cy.get(widgetsPage.buttonColor) .click({ force: true }) @@ -128,7 +128,7 @@ describe("Table Widget property pane feature validation", function() { //Open New Custom Column cy.editColumn("customColumn1"); // Change Column type to icon Button - cy.changeColumnType("Icon Button"); + cy.changeColumnType("Icon Button", false); // Select Icon from Icon Control cy.get(".t--property-control-icon .bp3-icon-caret-down").click({ force: true, @@ -167,7 +167,7 @@ describe("Table Widget property pane feature validation", function() { //Open New Custom Column cy.editColumn("customColumn1"); // Change Column type to icon Button - cy.changeColumnType("Menu Button"); + cy.changeColumnType("Menu Button", false); //Changing the text on the Menu Button cy.testJsontext("label", "Menu button"); // Select Icon from Icon Control @@ -205,15 +205,18 @@ describe("Table Widget property pane feature validation", function() { cy.get(widgetsPage.tableBtn).should("have.css", "background-color", color2); // Add a Menu item 1 - cy.get(".t--add-menu-item-btn").click({ - force: true, - }); + cy.get(".t--add-menu-item-btn") + .click({ + force: true, + }) + .wait(500); // Edit a Menu item cy.get(".t--property-pane-section-menuitems .t--edit-column-btn") .first() .click({ force: true, }); + cy.wait(1000); // update menu item background color cy.get(widgetsPage.backgroundcolorPickerNew) .type("#03b365", { diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV1/Table_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV1/Table_spec.js index abbc54709186..8a81e4094360 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV1/Table_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV1/Table_spec.js @@ -48,7 +48,7 @@ describe("Table Widget Functionality", function() { it("Table Widget Functionality To Show a Base64 Image", function() { cy.openPropertyPane("tablewidget"); cy.editColumn("image"); - cy.changeColumnType("Image"); + cy.changeColumnType("Image", false); cy.isSelectRow(1); const index = 1; diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV2/Add_new_row_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV2/Add_new_row_spec.js index abc1712f0985..d52ae3ebaef1 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV2/Add_new_row_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV2/Add_new_row_spec.js @@ -16,7 +16,7 @@ describe("Table widget Add new row feature's", () => { cy.addDsl(dsl); }); - it("should test that allow Add new row property is present", () => { + it("1.1. should test that allow Add new row property is present", () => { cy.openPropertyPane("tablewidgetv2"); cy.get(".t--property-control-allowaddingarow").should("exist"); cy.get( @@ -24,7 +24,7 @@ describe("Table widget Add new row feature's", () => { ).should("exist"); }); - it("should test that Add new row link appears on the UI when the allow add new row property is enabled", () => { + it("1.2. should test that Add new row link appears on the UI when the allow add new row property is enabled", () => { cy.get(".t--add-new-row").should("not.exist"); propPane.ToggleOnOrOff("Allow adding a row", "On"); cy.get(".t--add-new-row").should("exist"); @@ -32,7 +32,7 @@ describe("Table widget Add new row feature's", () => { cy.get(".t--add-new-row").should("not.exist"); }); - it("should test that onSave, onDiscard and default row are showing up only when the allow add new property is enabled", () => { + it("1.3. should test that onSave, onDiscard and default row are showing up only when the allow add new property is enabled", () => { cy.get(".t--property-control-onsave").should("not.exist"); cy.get(".t--property-control-ondiscard").should("not.exist"); cy.get(".t--property-control-defaultvalues").should("not.exist"); @@ -42,14 +42,14 @@ describe("Table widget Add new row feature's", () => { cy.get(".t--property-control-defaultvalues").should("exist"); }); - it("should test that add new row link is disabled during the inline editing flow", () => { + it("1.4. should test that add new row link is disabled during the inline editing flow", () => { cy.get(".t--add-new-row.disabled").should("not.exist"); cy.makeColumnEditable("step"); cy.editTableCell(0, 0); cy.get(".t--add-new-row.disabled").should("exist"); }); - it("should test that clicking on add new row link adds an empty row at the top of the table", () => { + it("1.5. should test that clicking on add new row link adds an empty row at the top of the table", () => { cy.openPropertyPane("tablewidgetv2"); cy.get(".tableWrap .new-row").should("not.exist"); cy.get(".t--add-new-row").click(); @@ -57,7 +57,7 @@ describe("Table widget Add new row feature's", () => { cy.get(".t--discard-new-row").click({ force: true }); }); - it("should test that new row is getting populated with the default row property value", () => { + it("1.6. should test that new row is getting populated with the default row property value", () => { cy.updateCodeInput( ".t--property-control-defaultvalues", "{{{step: 'newStepCell'}}}", @@ -70,7 +70,7 @@ describe("Table widget Add new row feature's", () => { cy.get(".t--discard-new-row").click({ force: true }); }); - it("should test that inline editing, row selection, pagination, search, filters are actions cannot be performed while in add new row feature", () => { + it("1.7. should test that inline editing, row selection, pagination, search, filters are actions cannot be performed while in add new row feature", () => { cy.get(".t--widget-tablewidgetv2 .t--search-input").should("exist"); cy.get(".t--widget-tablewidgetv2 .t--table-filter-toggle-btn").should( "exist", @@ -111,7 +111,7 @@ describe("Table widget Add new row feature's", () => { cy.get(".t--discard-new-row").click({ force: true }); }); - it("should test that only editable column cells are in editmode in the new row", () => { + it("1.8. should test that only editable column cells are in editmode in the new row", () => { cy.get(".t--add-new-row").click(); cy.get( `[data-colindex=0][data-rowindex=0] .t--inlined-cell-editor`, @@ -136,7 +136,7 @@ describe("Table widget Add new row feature's", () => { ).should("not.exist"); }); - it("should test that newRow property holds the entered data", () => { + it("1.9. should test that newRow property holds the entered data", () => { cy.makeColumnEditable("step"); cy.makeColumnEditable("task"); cy.enterTableCellValue(0, 0, "22"); @@ -150,7 +150,7 @@ describe("Table widget Add new row feature's", () => { ); }); - it("should test that non data (iconBitton, button, menubutton) column cells are not showing up", () => { + it("1.10. should test that non data (iconBitton, button, menubutton) column cells are not showing up", () => { cy.openPropertyPane("tablewidgetv2"); cy.editColumn("step"); ["Button", "Menu Button", "Icon Button"].forEach((columnType) => { @@ -174,7 +174,7 @@ describe("Table widget Add new row feature's", () => { cy.addDsl(dsl); }); - it("should test that validation is working for a new row cell", () => { + it("2.1. should test that validation is working for a new row cell", () => { cy.openPropertyPane("tablewidgetv2"); propPane.ToggleOnOrOff("Allow adding a row", "On"); cy.get(".t--add-new-row").click(); @@ -262,7 +262,7 @@ describe("Table widget Add new row feature's", () => { cy.get(".t--discard-new-row").click({ force: true }); }); - it("should test that validation variable isNewRow is working", () => { + it("2.2. should test that validation variable isNewRow is working", () => { propPane.UpdatePropertyFieldValue( "Valid", "{{isNewRow ? (editedValue === 1) : (editedValue === 2)}}", @@ -290,7 +290,7 @@ describe("Table widget Add new row feature's", () => { cy.get(".t--discard-new-row").click({ force: true }); }); - it("should test that validation is working for more than one add new row cell at a time", () => { + it("2.3. should test that validation is working for more than one add new row cell at a time", () => { propPane.UpdatePropertyFieldValue("Valid", "{{editedValue === 1}}"); cy.get(".t--property-pane-back-btn").click(); cy.wait(500); @@ -306,7 +306,7 @@ describe("Table widget Add new row feature's", () => { cy.get(`.t--inlined-cell-editor-has-error`).should("have.length", 2); }); - it("should test that validation error message only appears when a cell is in focus", () => { + it("2.4. should test that validation error message only appears when a cell is in focus", () => { cy.get(".error-tooltip .bp3-popover-content").should("not.exist"); cy.get(`[data-colindex=1][data-rowindex=0] input`).focus(); cy.get(".error-tooltip .bp3-popover-content").should("have.length", 1); @@ -316,7 +316,7 @@ describe("Table widget Add new row feature's", () => { cy.get(".error-tooltip .bp3-popover-content").should("have.length", 1); }); - it("should test that save button is disabled when there is an error", () => { + it("2.5. should test that save button is disabled when there is an error", () => { cy.get(".t--save-new-row").should("be.disabled"); cy.get(`.t--inlined-cell-editor-has-error`).should("have.length", 2); cy.enterTableCellValue(0, 0, "1"); @@ -335,7 +335,7 @@ describe("Table widget Add new row feature's", () => { cy.addDsl(dsl); }); - it("should test that discard button is undoing the add new feature", () => { + it("3.1. should test that discard button is undoing the add new feature", () => { cy.openPropertyPane("tablewidgetv2"); propPane.ToggleOnOrOff("Allow adding a row", "On"); cy.get(".tableWrap .new-row").should("not.exist"); @@ -344,7 +344,7 @@ describe("Table widget Add new row feature's", () => { cy.get(".t--discard-new-row").click({ force: true }); }); - it("should test that discard events is triggered when user clicks on the discard button", () => { + it("3.2. should test that discard events is triggered when user clicks on the discard button", () => { cy.get( ".t--property-control-ondiscard .t--open-dropdown-Select-Action", ).click({ force: true }); @@ -354,16 +354,11 @@ describe("Table widget Add new row feature's", () => { cy.get(".tableWrap .new-row").should("exist"); cy.get(".t--discard-new-row").click({ force: true }); cy.get(widgetsPage.toastAction).should("be.visible"); - cy.get(widgetsPage.toastActionText) - .last() - .invoke("text") - .then((text) => { - expect(text).to.equal("discarded!!"); - }); + agHelper.AssertContains("discarded!!"); cy.get(".tableWrap .new-row").should("not.exist"); }); - it("should test that save event is triggered when user clicks on the save button", () => { + it("3.3. should test that save event is triggered when user clicks on the save button", () => { cy.get( ".t--property-control-onsave .t--open-dropdown-Select-Action", ).click({ force: true }); @@ -373,12 +368,7 @@ describe("Table widget Add new row feature's", () => { cy.get(".tableWrap .new-row").should("exist"); cy.get(".t--save-new-row").click({ force: true }); cy.get(widgetsPage.toastAction).should("be.visible"); - cy.get(widgetsPage.toastActionText) - .last() - .invoke("text") - .then((text) => { - expect(text).to.equal("saved!!"); - }); + agHelper.AssertContains("saved!!"); cy.get(".tableWrap .new-row").should("not.exist"); }); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV2/Custom_column_alias_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV2/Custom_column_alias_spec.js index bb2d564a4f61..9676030f158d 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV2/Custom_column_alias_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV2/Custom_column_alias_spec.js @@ -56,6 +56,7 @@ describe("Custom column alias functionality", () => { cy.openPropertyPane("textwidget"); propPane.UpdatePropertyFieldValue("Text", "{{Table1.triggeredRow}}"); cy.openPropertyPane("tablewidgetv2"); + cy.backFromPropertyPanel(); cy.get(widgetsPage.addColumn).scrollIntoView(); cy.get(widgetsPage.addColumn).click({ force: true }); cy.wait(500); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV2/Image_resize_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV2/Image_resize_spec.js index 9089fdb75e0b..527a108bb64d 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV2/Image_resize_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV2/Image_resize_spec.js @@ -35,6 +35,7 @@ describe("Table Widget Image Resize feature validation", function() { it("2. Verify image size with cell wrapping turned on", function() { cy.openPropertyPane("tablewidgetv2"); cy.editColumn("title"); + cy.moveToContentTab(); cy.get(".t--property-control-cellwrapping .bp3-switch").click(); cy.closePropertyPane(); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV2/Inline_editing_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV2/Inline_editing_spec.js index 39906e06f6d9..4eca0074825a 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV2/Inline_editing_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV2/Inline_editing_spec.js @@ -2,6 +2,7 @@ const dsl = require("../../../../../fixtures/Table/InlineEditingDSL.json"); const commonlocators = require("../../../../../locators/commonlocators.json"); const widgetsPage = require("../../../../../locators/Widgets.json"); import { ObjectsRegistry } from "../../../../../support/Objects/Registry"; +import { PROPERTY_SELECTOR } from "../../../../../locators/WidgetLocators"; const agHelper = ObjectsRegistry.AggregateHelper; describe("Table widget inline editing functionality", () => { @@ -711,4 +712,104 @@ describe("Table widget inline editing functionality", () => { "[data-colindex='0'][data-rowindex='0'] .t--inlined-cell-editor", ).should("not.have.css", "height", "34px"); }); + + it("26. should check if updatedRowIndex is getting updated for single row update mode", () => { + cy.dragAndDropToCanvas("textwidget", { x: 400, y: 400 }); + cy.get(".t--widget-textwidget").should("exist"); + cy.updateCodeInput( + ".t--property-control-text", + `{{Table1.updatedRowIndex}}`, + ); + + cy.dragAndDropToCanvas("buttonwidget", { x: 300, y: 300 }); + cy.get(".t--widget-buttonwidget").should("exist"); + cy.get(PROPERTY_SELECTOR.onClick) + .find(".t--js-toggle") + .click(); + cy.updateCodeInput(".t--property-control-label", "Reset"); + cy.updateCodeInput( + PROPERTY_SELECTOR.onClick, + `{{resetWidget("Table1",true)}}`, + ); + + // case 1: check if updatedRowIndex has -1 as the default value: + cy.get(commonlocators.textWidgetContainer).should("contain.text", -1); + + cy.openPropertyPane("tablewidgetv2"); + + cy.makeColumnEditable("step"); + cy.wait(1000); + + // case 2: check if updatedRowIndex is 0, when cell at row 0 is updated. + cy.editTableCell(0, 0); + cy.enterTableCellValue(0, 0, "#12").type("{enter}"); + cy.get(commonlocators.textWidgetContainer).should("contain.text", 0); + + // case 3: check if updatedRowIndex is -1 when changes are discarded. + cy.discardTableRow(4, 0); + cy.get(commonlocators.textWidgetContainer).should("contain.text", -1); + + // case 4: check if the updateRowIndex is -1 when widget is reset + cy.editTableCell(0, 1); + cy.enterTableCellValue(0, 1, "#13").type("{enter}"); + cy.contains("Reset").click({ force: true }); + cy.get(commonlocators.textWidgetContainer).should("contain.text", -1); + + // case 5: check if the updatedRowIndex changes to -1 when the table data changes. + cy.wait(1000); + cy.editTableCell(0, 2); + cy.enterTableCellValue(0, 2, "#14").type("{enter}"); + cy.openPropertyPane("tablewidgetv2"); + cy.get(widgetsPage.tabedataField).type("{backspace}"); + cy.wait(300); + cy.get(commonlocators.textWidgetContainer).should("contain.text", -1); + }); + + it.only("27. should check if updatedRowIndex is getting updated for multi row update mode", () => { + cy.dragAndDropToCanvas("textwidget", { x: 400, y: 400 }); + cy.get(".t--widget-textwidget").should("exist"); + cy.updateCodeInput( + ".t--property-control-text", + `{{Table1.updatedRowIndex}}`, + ); + + cy.dragAndDropToCanvas("buttonwidget", { x: 300, y: 300 }); + cy.get(".t--widget-buttonwidget").should("exist"); + cy.get(PROPERTY_SELECTOR.onClick) + .find(".t--js-toggle") + .click(); + cy.updateCodeInput(".t--property-control-label", "Reset"); + cy.updateCodeInput( + PROPERTY_SELECTOR.onClick, + `{{resetWidget("Table1",true)}}`, + ); + + cy.openPropertyPane("tablewidgetv2"); + + cy.makeColumnEditable("step"); + cy.get(".t--button-tab-CUSTOM").click({ force: true }); + cy.wait(1000); + + // case 1: check if updatedRowIndex is 0, when cell at row 0 is updated. + cy.editTableCell(0, 0); + cy.enterTableCellValue(0, 0, "#12").type("{enter}"); + cy.get(commonlocators.textWidgetContainer).should("contain.text", 0); + + // case 2: check if the updateRowIndex is -1 when widget is reset + cy.editTableCell(0, 1); + cy.enterTableCellValue(0, 1, "#13").type("{enter}"); + cy.get(commonlocators.textWidgetContainer).should("contain.text", 1); + cy.contains("Reset").click({ force: true }); + cy.get(commonlocators.textWidgetContainer).should("contain.text", -1); + + // case 3: check if the updatedRowIndex changes to -1 when the table data changes. + cy.wait(1000); + cy.editTableCell(0, 2); + cy.enterTableCellValue(0, 2, "#14").type("{enter}"); + cy.get(commonlocators.textWidgetContainer).should("contain.text", 2); + cy.openPropertyPane("tablewidgetv2"); + cy.get(widgetsPage.tabedataField).type("{backspace}"); + cy.wait(300); + cy.get(commonlocators.textWidgetContainer).should("contain.text", -1); + }); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV2/TableV2_Derived_Column_Data_validation_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV2/TableV2_Derived_Column_Data_validation_spec.js index 764855e5f8d6..e53a45bfe36f 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV2/TableV2_Derived_Column_Data_validation_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV2/TableV2_Derived_Column_Data_validation_spec.js @@ -67,6 +67,7 @@ describe("Test Create Api and Bind to Table widget", function() { it("4. Edit column name and validate test for computed value based on column type selected", function() { // opoen customColumn1 property pane cy.editColumn("customColumn1"); + cy.moveToContentTab(); // Enter Apil 1st user email data into customColumn1 cy.readTableV2dataPublish("1", "9").then((tabData) => { const tabValue = tabData; @@ -82,6 +83,7 @@ describe("Test Create Api and Bind to Table widget", function() { it("5. Update table json data and check the column names updated", function() { // Open table propert pane cy.SearchEntityandOpen("Table1"); + cy.backFromPropertyPanel(); // Change the table data cy.testJsontext("tabledata", JSON.stringify(this.data.TableInputUpdate)); cy.wait("@updateLayout"); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV2/TableV2_PropertyPane_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV2/TableV2_PropertyPane_spec.js index 2fbe37ca8e87..25528c8a8fcc 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV2/TableV2_PropertyPane_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV2/TableV2_PropertyPane_spec.js @@ -344,10 +344,47 @@ describe("Table Widget V2 property pane feature validation", function() { cy.openPropertyPane("tablewidgetv2"); cy.moveToContentTab(); // Chage deat search text value to "data" + cy.backFromPropertyPanel(); cy.testJsontext("defaultsearchtext", "data"); cy.PublishtheApp(); // Verify the deaullt search text cy.get(widgetsPage.searchField).should("have.value", "data"); cy.get(publish.backToEditor).click(); }); + + it("13. Verify custom column property name changes with change in column name ([FEATURE]: #17142)", function() { + // Open property pane + cy.openPropertyPane("tablewidgetv2"); + cy.moveToContentTab(); + cy.addColumnV2("customColumn18"); + cy.editColumn("customColumn1"); + cy.get(".t--property-control-propertyname pre span span").should( + "have.text", + "customColumn18", + ); + cy.editColName("customColumn00"); + cy.get(".t--property-control-propertyname pre span span").should( + "have.text", + "customColumn00", + ); + cy.get(".t--property-pane-back-btn").click(); + cy.get('[data-rbd-draggable-id="customColumn1"] input').should( + "have.value", + "customColumn00", + ); + cy.get("[data-rbd-draggable-id='customColumn1'] input[type='text']").clear({ + force: true, + }); + cy.get("[data-rbd-draggable-id='customColumn1'] input[type='text']").type( + "customColumn99", + { + force: true, + }, + ); + cy.editColumn("customColumn1"); + cy.get(".t--property-control-propertyname pre span span").should( + "have.text", + "customColumn99", + ); + }); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV2/TableV2_Widget_Add_button_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV2/TableV2_Widget_Add_button_spec.js index c1d6cd9bf150..edcd315045e7 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV2/TableV2_Widget_Add_button_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV2/TableV2_Widget_Add_button_spec.js @@ -259,6 +259,7 @@ describe("Table Widget V2 property pane feature validation", function() { .click({ force: true, }); + cy.wait(500); cy.moveToStyleTab(); // update menu item background color cy.get(widgetsPage.backgroundcolorPickerNew) @@ -287,6 +288,8 @@ describe("Table Widget V2 property pane feature validation", function() { force: true, }); cy.wait(1000); + cy.moveToContentTab(); + cy.wait(500); cy.get(".t--property-control-disabled label.bp3-switch.unchecked").click({ force: true, }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV2/TableV2_Widget_Copy_Paste_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV2/TableV2_Widget_Copy_Paste_spec.js index f2850c8f496f..c3e0d8fb193d 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV2/TableV2_Widget_Copy_Paste_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV2/TableV2_Widget_Copy_Paste_spec.js @@ -44,7 +44,7 @@ describe("Test Suite to validate copy/paste table Widget V2", function() { cy.hoverAndClickParticularIndex(1); cy.selectAction("Show Bindings"); cy.get(apiwidget.propertyList).then(function($lis) { - expect($lis).to.have.length(19); + expect($lis).to.have.length(20); expect($lis.eq(0)).to.contain("{{Table1Copy.selectedRow}}"); expect($lis.eq(1)).to.contain("{{Table1Copy.selectedRows}}"); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV2/TableV2_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV2/TableV2_spec.js index 0f98154a16de..bf0ed0cb1d8a 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV2/TableV2_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/TableV2/TableV2_spec.js @@ -3,6 +3,10 @@ const widgetsPage = require("../../../../../locators/Widgets.json"); const commonlocators = require("../../../../../locators/commonlocators.json"); const publish = require("../../../../../locators/publishWidgetspage.json"); const dsl = require("../../../../../fixtures/tableV2WidgetDsl.json"); +import { ObjectsRegistry } from "../../../../../support/Objects/Registry"; + +const table = ObjectsRegistry.TableV2; +const PropPane = ObjectsRegistry.PropertyPane; describe("Table Widget V2 Functionality", function() { before(() => { @@ -116,10 +120,132 @@ describe("Table Widget V2 Functionality", function() { const tabValue = tabData; expect(tabValue).not.to.be.equal("Tobias Funke"); }); + cy.get(publish.backToEditor).click({ + force: true, + }); + }); + + it("5. Verify that table filter dropdown only includes filterable columns", () => { + cy.openPropertyPane("tablewidgetv2"); + cy.wait(500); + PropPane.UpdatePropertyFieldValue("Table Data", `{{[{step: 1, task: 1}]}}`); + cy.get( + ".t--property-control-allowfiltering .bp3-control-indicator", + ).click(); + cy.editColumn("step"); + cy.get(".t--table-filter-toggle-btn").click(); + + [ + { + columnType: "URL", + expected: "contain", + }, + { + columnType: "Number", + expected: "contain", + }, + { + columnType: "Date", + expected: "contain", + }, + { + columnType: "Image", + expected: "not.contain", + }, + { + columnType: "Video", + expected: "not.contain", + }, + { + columnType: "Button", + expected: "not.contain", + }, + { + columnType: "Menu Button", + expected: "not.contain", + }, + { + columnType: "Icon Button", + expected: "not.contain", + }, + { + columnType: "Plain Text", + expected: "contain", + }, + { + columnType: "Checkbox", + expected: "contain", + }, + { + columnType: "Switch", + expected: "contain", + }, + ].forEach((data) => { + cy.get(commonlocators.changeColType) + .last() + .click(); + cy.get(".t--dropdown-option") + .children() + .contains(data.columnType) + .click(); + cy.wait("@updateLayout"); + cy.get(".t--table-filter-columns-dropdown").click(); + cy.get(".t--dropdown-option").should(data.expected, "step"); + }); + + cy.get(".t--property-pane-back-btn").click(); + cy.makeColumnEditable("step"); + cy.get(".t--button-tab-ROW_LEVEL").click(); + cy.get(".t--table-filter-columns-dropdown").click(); + cy.get(".t--dropdown-option").should("not.contain", "Save / Discard"); + }); + + it("6. Verify that table filter is retained when the tableData scehma doesn't change", () => { + cy.openPropertyPane("tablewidgetv2"); + PropPane.UpdatePropertyFieldValue( + "Table Data", + `{{[{number: "1", work: "test"}, {number: "2", work: "celebrate!"}]}}`, + ); + table.OpenNFilterTable("number", "contains", "2"); + cy.get(".t--table-filter-toggle-btn").should("have.text", "Filters (1)"); + cy.readTableV2data(0, 1).then((val) => { + expect(val).to.equal("2"); + }); + PropPane.UpdatePropertyFieldValue( + "Table Data", + `{{[{number: "1.1", work: "test"}, {number: "2", work: "celebrate!"}]}}`, + ); + cy.get(".t--table-filter-toggle-btn").should("have.text", "Filters (1)"); + cy.readTableV2data(0, 1).then((val) => { + expect(val).to.equal("2"); + }); + cy.get(".t--close-filter-btn").click({ force: true }); + PropPane.UpdatePropertyFieldValue( + "Table Data", + `{{[{number: "1.1", task: "test"}, {number: "2", task: "celebrate!"}]}}`, + ); + cy.get(".t--table-filter-toggle-btn").should("have.text", "Filters"); + cy.readTableV2data(0, 1).then((val) => { + expect(val).to.equal("1.1"); + }); + table.OpenNFilterTable("number", "contains", "2"); + cy.get(".t--table-filter-toggle-btn").should("have.text", "Filters (1)"); + cy.readTableV2data(0, 1).then((val) => { + expect(val).to.equal("2"); + }); + cy.get(".t--close-filter-btn").click({ force: true }); + PropPane.UpdatePropertyFieldValue( + "Table Data", + `{{[{number: "1", task: "test"}, {number: "2", task: "celebrate!"}]}}`, + ); + cy.get(".t--table-filter-toggle-btn").should("have.text", "Filters (1)"); + cy.readTableV2data(0, 1).then((val) => { + expect(val).to.equal("2"); + }); }); - it("5. should check that adding cyclic dependency in the table doesn't crash the app", () => { - cy.get(publish.backToEditor).click(); + it("7. should check that adding cyclic dependency in the table doesn't crash the app", () => { + //cy.get(publish.backToEditor).click(); cy.openPropertyPane("tablewidgetv2"); cy.updateCodeInput(".t--property-control-defaultselectedrow", `{{Table1}}`); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Text/TextWidget_LintErrorValidation_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Text/TextWidget_LintErrorValidation_spec.js index be7604831551..a3287e60deef 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Text/TextWidget_LintErrorValidation_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Text/TextWidget_LintErrorValidation_spec.js @@ -53,12 +53,6 @@ describe("Linting warning validation with text widget", function() { .should("be.visible") .click({ force: true }); - cy.get(commonlocators.debugErrorMsg) - .eq(0) - .contains("ReferenceError: error is not defined"); - - cy.get(commonlocators.debugErrorMsg) - .eq(1) - .contains("ReferenceError: lintErrror is not defined"); + cy.get(commonlocators.debugErrorMsg).should("have.length", 3); }); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Text/Text_new_feature_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Text/Text_new_feature_spec.js index 9aebab157672..d19a4eee9848 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Text/Text_new_feature_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Widgets/Text/Text_new_feature_spec.js @@ -12,6 +12,15 @@ describe("Text Widget color/font/alignment Functionality", function() { cy.openPropertyPane("textwidget"); }); it("Test to validate parsing link", function() { + // Add link to text widget + cy.testCodeMirror("app.appsmith.com"); + // check if it's a link when no http or https is passed, + cy.get(`${commonlocators.headingTextStyle} a`).should( + "have.attr", + "href", + "http://app.appsmith.com", + ); + // Add link to text widget cy.testCodeMirror("https://app.appsmith.com"); // check if it's parsed as link diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Workspace/DeleteWorkspace_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Workspace/DeleteWorkspace_spec.js index 94ef1bbfc7db..09e33256f688 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Workspace/DeleteWorkspace_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Workspace/DeleteWorkspace_spec.js @@ -1,8 +1,7 @@ /// import { ObjectsRegistry } from "../../../../support/Objects/Registry"; import homePage from "../../../../locators/HomePage"; -let HomePage = ObjectsRegistry.HomePage, - agHelper = ObjectsRegistry.AggregateHelper; +let HomePage = ObjectsRegistry.HomePage; describe("Delete workspace test spec", function() { let newWorkspaceName; diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Workspace/LeaveWorkspaceTest_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Workspace/LeaveWorkspaceTest_spec.js index e123477cd9a7..59a83c633df1 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Workspace/LeaveWorkspaceTest_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Workspace/LeaveWorkspaceTest_spec.js @@ -1,11 +1,8 @@ /// import { ObjectsRegistry } from "../../../../support/Objects/Registry"; -import homePage from "../../../../locators/HomePage"; -let HomePage = ObjectsRegistry.HomePage, - agHelper = ObjectsRegistry.AggregateHelper; +let HomePage = ObjectsRegistry.HomePage; describe("Leave workspace test spec", function() { - let newWorkspaceId; let newWorkspaceName; it("leave workspace menu is visible validation", function() { @@ -13,7 +10,6 @@ describe("Leave workspace test spec", function() { cy.createWorkspace(); cy.wait("@createWorkspace").then((interception) => { newWorkspaceName = interception.response.body.data.name; - newWorkspaceId = interception.response.body.data.name; cy.visit("/applications"); cy.openWorkspaceOptionsPopup(newWorkspaceName); cy.contains("Leave Workspace"); @@ -25,7 +21,6 @@ describe("Leave workspace test spec", function() { cy.createWorkspace(); cy.wait("@createWorkspace").then((interception) => { newWorkspaceName = interception.response.body.data.name; - newWorkspaceId = interception.response.body.data.name; cy.visit("/applications"); cy.openWorkspaceOptionsPopup(newWorkspaceName); cy.contains("Leave Workspace").click(); @@ -37,12 +32,11 @@ describe("Leave workspace test spec", function() { }); }); - it("Non admin users can only access leave workspace popup menu validation", function() { + it("Bug 17235 & 17987 - Non admin users can only access leave workspace popup menu validation", function() { cy.visit("/applications"); cy.createWorkspace(); cy.wait("@createWorkspace").then((interception) => { newWorkspaceName = interception.response.body.data.name; - newWorkspaceId = interception.response.body.data.name; cy.visit("/applications"); HomePage.InviteUserToWorkspace( newWorkspaceName, diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Workspace/LoginFromUIApp_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Workspace/LoginFromUIApp_spec.js index 46ec4e60acac..dba86f95455a 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Workspace/LoginFromUIApp_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Workspace/LoginFromUIApp_spec.js @@ -38,9 +38,5 @@ describe("Login from UI and check the functionality", function() { cy.get(homePage.signOutIcon).click(); // eslint-disable-next-line cypress/no-unnecessary-waiting cy.wait(500); - cy.get(homePage.headerAppSmithLogo).click(); - // eslint-disable-next-line cypress/no-unnecessary-waiting - cy.wait(500); - cy.url().should("include", "user/login"); }); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Workspace/MemberRoles_Spec.ts b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Workspace/MemberRoles_Spec.ts index dd1ff73e59e0..138342b75430 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Workspace/MemberRoles_Spec.ts +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Workspace/MemberRoles_Spec.ts @@ -1,7 +1,7 @@ import { ObjectsRegistry } from "../../../../support/Objects/Registry"; import HomePage from "../../../../locators/HomePage"; let workspaceId: any, appid: any; -let agHelper = ObjectsRegistry.AggregateHelper, +const agHelper = ObjectsRegistry.AggregateHelper, homePage = ObjectsRegistry.HomePage; describe("Create new workspace and invite user & validate all roles", () => { @@ -48,8 +48,9 @@ describe("Create new workspace and invite user & validate all roles", () => { cy.wait(2000); cy.xpath(HomePage.selectRole).click(); cy.get(".t--dropdown-option") + // .should("have.length", Cypress.env("Edition") === 1 ? 1 : 2) .should("have.length", 1) - .and("contain.text", `App Viewer - ${workspaceId}`); + .and("contain.text", `App Viewer`); cy.get(HomePage.closeBtn).click(); homePage.LaunchAppFromAppHover(); homePage.LogOutviaAPI(); @@ -86,12 +87,9 @@ describe("Create new workspace and invite user & validate all roles", () => { cy.wait(2000); cy.xpath(HomePage.selectRole).click(); cy.get(".t--dropdown-option") + // .should("have.length", Cypress.env("Edition") === 0 ? 2 : 3) .should("have.length", 2) - .and( - "contain.text", - `App Viewer - ${workspaceId}`, - `Developer - ${workspaceId}`, - ); + .and("contain.text", `App Viewer`, `Developer`); cy.get(HomePage.closeBtn).click(); homePage.LogOutviaAPI(); }); @@ -134,16 +132,10 @@ describe("Create new workspace and invite user & validate all roles", () => { cy.wait(2000); cy.xpath(HomePage.selectRole).click(); cy.get(".t--dropdown-option") + // .should("have.length", Cypress.env("Edition") === 0 ? 3 : 4) .should("have.length", 3) - .should( - "contain.text", - `App Viewer - ${workspaceId}`, - `Developer - ${workspaceId}`, - ); - cy.get(".t--dropdown-option").should( - "contain.text", - `Administrator - ${workspaceId}`, - ); + .should("contain.text", `App Viewer`, `Developer`); + cy.get(".t--dropdown-option").should("contain.text", `Administrator`); cy.get(HomePage.closeBtn).click(); homePage.LogOutviaAPI(); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Workspace/ShareAppTests_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Workspace/ShareAppTests_spec.js index 84f336c9ccde..3ad1f49ce566 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Workspace/ShareAppTests_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Workspace/ShareAppTests_spec.js @@ -3,9 +3,7 @@ import homePage from "../../../../locators/HomePage"; const publish = require("../../../../locators/publishWidgetspage.json"); import { ObjectsRegistry } from "../../../../support/Objects/Registry"; -const commonlocators = require("../../../../locators/commonlocators.json"); -let HomePage = ObjectsRegistry.HomePage, - agHelper = ObjectsRegistry.AggregateHelper; +let HomePage = ObjectsRegistry.HomePage; describe("Create new workspace and share with a user", function() { let workspaceId; @@ -33,7 +31,6 @@ describe("Create new workspace and share with a user", function() { cy.get("h2").contains("Drag and drop a widget here"); cy.get(homePage.shareApp).click({ force: true }); HomePage.InviteUserToWorkspaceFromApp( - workspaceId, Cypress.env("TESTUSERNAME1"), "App Viewer", ); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/ApiTests/API_Styles_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/ApiTests/API_Styles_spec.js index be5315713c43..e631f3766186 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/ApiTests/API_Styles_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/ApiTests/API_Styles_spec.js @@ -1,9 +1,9 @@ import ApiEditor from "../../../../locators/ApiEditor"; import DynamicInput from "../../../../locators/DynamicInput"; import HomePage from "../../../../locators/HomePage"; -const pages = require("../../../../locators/Pages.json"); -const datasourcesEditor = require("../../../../locators/DatasourcesEditor.json"); const commonLocators = require("../../../../locators/commonlocators.json"); +import { ObjectsRegistry } from "../../../../support/Objects/Registry"; +let ee = ObjectsRegistry.EntityExplorer; describe("Validate API Panel CSS Styles", function() { const backgroundColorGray200 = "rgb(231, 231, 231)"; @@ -83,6 +83,7 @@ describe("Validate API Panel CSS Styles", function() { //Create two datasource for testing binding prompt background-color cy.createNewAuthApiDatasource(appName1); cy.createNewAuthApiDatasource(appName2); + ee.ExpandCollapseEntity("Queries/JS"); cy.get(commonLocators.entityName) .contains("test_styles") .click(); @@ -108,6 +109,7 @@ describe("Validate API Panel CSS Styles", function() { .contains("test_styles") .should("not.exist"); //Delete two datasources + ee.ExpandCollapseEntity("Datasources"); cy.deleteDatasource(appName1); cy.deleteDatasource(appName2); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Datasources/ArangoDataSourceStub_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Datasources/ArangoDataSourceStub_spec.js index a915ab5de458..56add028f621 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Datasources/ArangoDataSourceStub_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Datasources/ArangoDataSourceStub_spec.js @@ -1,13 +1,9 @@ const datasource = require("../../../../locators/DatasourcesEditor.json"); -const datasourceEditor = require("../../../../locators/DatasourcesEditor.json"); - import { ObjectsRegistry } from "../../../../support/Objects/Registry"; let agHelper = ObjectsRegistry.AggregateHelper, dataSources = ObjectsRegistry.DataSources; -let datasourceName; - describe("Arango datasource test cases", function() { beforeEach(() => { cy.startRoutesForDatasource(); @@ -18,9 +14,6 @@ describe("Arango datasource test cases", function() { dataSources.CreatePlugIn("ArangoDB"); agHelper.RenameWithInPane("ArangoWithnoTrailing", false); cy.fillArangoDBDatasourceForm(); - cy.get("@createDatasource").then((httpResponse) => { - datasourceName = httpResponse.response.body.data.name; - }); cy.intercept("POST", "/api/v1/datasources/test", { fixture: "testAction.json", }).as("testDatasource"); @@ -37,12 +30,10 @@ describe("Arango datasource test cases", function() { fixture: "testAction.json", }).as("testDatasource"); cy.testSaveDatasource(false); - //dataSources.DeleteDatasouceFromActiveTab("ArangoWithTrailing"); }); it("3. Create a new query from the datasource editor", function() { - // cy.get(datasource.createQuery).click(); - cy.get(`${datasourceEditor.datasourceCard} ${datasource.createQuery}`) + cy.get(datasource.createQuery) .last() .click(); cy.wait("@createNewApi").should( @@ -60,6 +51,6 @@ describe("Arango datasource test cases", function() { agHelper .GetText(dataSources._databaseName, "val") .then(($dbName) => expect($dbName).to.eq("_system")); - dataSources.DeleteDSDirectly(); + dataSources.SaveDSFromDialog(false); }); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Datasources/AuthenticatedApiDatasource_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Datasources/AuthenticatedApiDatasource_spec.js index abbe2eebe566..0988cc9bc930 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Datasources/AuthenticatedApiDatasource_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Datasources/AuthenticatedApiDatasource_spec.js @@ -1,6 +1,10 @@ const apiwidget = require("../../../../locators/apiWidgetslocator.json"); const datasourceFormData = require("../../../../fixtures/datasources.json"); const datasourceEditor = require("../../../../locators/DatasourcesEditor.json"); +const testdata = require("../../../../fixtures/testdata.json"); + +import { ObjectsRegistry } from "../../../../support/Objects/Registry"; +let dataSources = ObjectsRegistry.DataSources; describe("Authenticated API Datasource", function() { const URL = datasourceFormData["authenticatedApiUrl"]; @@ -10,11 +14,6 @@ describe("Authenticated API Datasource", function() { it("1. Bug: 12045 - No Blank screen diplay after New Authentication API datasource creation", function() { cy.NavigateToAPI_Panel(); cy.get(apiwidget.createAuthApiDatasource).click(); - cy.wait("@createDatasource").should( - "have.nested.property", - "response.body.responseMeta.status", - 201, - ); cy.renameDatasource("FakeAuthenticatedApi"); cy.fillAuthenticatedAPIForm(); cy.saveDatasource(); @@ -24,7 +23,7 @@ describe("Authenticated API Datasource", function() { it("2. Bug: 12045 - No Blank screen diplay after editing/opening existing Authentication API datasource", function() { cy.xpath("//span[text()='EDIT']/parent::a").click(); cy.get(datasourceEditor.url).type("/users"); - cy.saveDatasource(); + cy.get(".t--save-datasource").click({ force: true }); cy.contains(URL + "/users"); cy.deleteDatasource("FakeAuthenticatedApi"); }); @@ -32,11 +31,6 @@ describe("Authenticated API Datasource", function() { it("3. Bug: 14181 -Make sure the datasource view mode page does not contain labels with no value.", function() { cy.NavigateToAPI_Panel(); cy.get(apiwidget.createAuthApiDatasource).click(); - cy.wait("@createDatasource").should( - "have.nested.property", - "response.body.responseMeta.status", - 201, - ); cy.renameDatasource("FakeAuthenticatedApi"); cy.fillAuthenticatedAPIForm(); cy.saveDatasource(); @@ -44,4 +38,28 @@ describe("Authenticated API Datasource", function() { cy.contains(queryParams).should("not.exist"); cy.deleteDatasource("FakeAuthenticatedApi"); }); + + it("4. Bug: 18051 - Save and Authorise should return to datasource page in view mode and not new datasource page", () => { + cy.NavigateToAPI_Panel(); + cy.get(apiwidget.createAuthApiDatasource).click(); + cy.generateUUID().then((uuid) => { + cy.renameDatasource(uuid); + cy.fillAuthenticatedAPIForm(); + cy.addOAuth2AuthorizationCodeDetails( + testdata.accessTokenUrl, + testdata.clientID, + testdata.clientSecret, + testdata.authorizationURL, + ); + dataSources.AuthAPISaveAndAuthorize(); + cy.xpath('//input[@name="email"]').type("Test@email.com"); + cy.xpath('//input[@name="email"]').type("Test"); + cy.xpath("//input[@name='password']").type("Test@123"); + cy.xpath("//input[@id='login-submit']").click(); + cy.wait(2000); + cy.reload(); + cy.get(".t--edit-datasource").should("be.visible"); + dataSources.DeleteDatasouceFromActiveTab(uuid); + }); + }); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Datasources/DatasourceForm_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Datasources/DatasourceForm_spec.js index bed8c80f1bd5..fde1ed4a38f5 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Datasources/DatasourceForm_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Datasources/DatasourceForm_spec.js @@ -1,14 +1,17 @@ const testdata = require("../../../../fixtures/testdata.json"); import { ObjectsRegistry } from "../../../../support/Objects/Registry"; -let agHelper = ObjectsRegistry.AggregateHelper; +let agHelper = ObjectsRegistry.AggregateHelper, + dataSource = ObjectsRegistry.DataSources, + locator = ObjectsRegistry.CommonLocators, + ee = ObjectsRegistry.EntityExplorer; describe("Datasource form related tests", function() { beforeEach(() => { cy.startRoutesForDatasource(); }); - it("1. Check whether the delete button has the right color", function() { + it("1. Check whether the number of key value pairs is equal to number of delete buttons", function() { cy.NavigateToAPI_Panel(); cy.CreateAPI(); //Not giving name to enable for cypress re-attempt cy.enterDatasourceAndPath(testdata.baseUrl, testdata.methods); @@ -16,26 +19,35 @@ describe("Datasource form related tests", function() { cy.get(".t--store-as-datasource") .trigger("click") .wait(1000); - agHelper.ValidateToastMessage("datasource created"); //verifying there is no error toast, Bug 14566 + + agHelper.AssertElementAbsence( + locator._specificToast("Duplicate key error"), + ); //verifying there is no error toast, Bug 14566 cy.get(".t--add-field") .first() .click(); - cy.get(".t--delete-field").should("attr", "color", "#A3B3BF"); + + // Two array pairs for headers key,value should have 2 delete buttons as per new uqi designs, so the first header can also be deleted : Bug #14804 + cy.get(".t--headers-array .t--delete-field") + .children() + .should("have.length", 2); }); it("2. Check if save button is disabled", function() { cy.get(".t--save-datasource").should("not.be.disabled"); + dataSource.SaveDSFromDialog(); }); it("3. Check if saved api as a datasource does not fail on cloning", function() { cy.NavigateToAPI_Panel(); + ee.ExpandCollapseEntity("Queries/JS"); cy.get(".t--entity-name") .contains("Api") .trigger("mouseover"); cy.hoverAndClickParticularIndex(1); cy.get('.single-select:contains("Copy to page")').click(); cy.get('.single-select:contains("Page1")').click({ force: true }); - cy.validateToastMessage("action copied to page Page1 successfully"); + agHelper.AssertContains("action copied to page Page1 successfully"); }); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Datasources/ElasticSearchDatasource_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Datasources/ElasticSearchDatasource_spec.js index 99619ac302d3..b1faffde4069 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Datasources/ElasticSearchDatasource_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Datasources/ElasticSearchDatasource_spec.js @@ -1,6 +1,8 @@ const datasource = require("../../../../locators/DatasourcesEditor.json"); +import { ObjectsRegistry } from "../../../../support/Objects/Registry"; let elasticSearchName; +let dataSource = ObjectsRegistry.DataSources; describe("Elastic search datasource tests", function() { beforeEach(() => { @@ -12,7 +14,6 @@ describe("Elastic search datasource tests", function() { cy.get(datasource.ElasticSearch).trigger("click", { force: true }); cy.generateUUID().then((uid) => { elasticSearchName = uid; - cy.get(".t--edit-datasource-name").click(); cy.get(".t--edit-datasource-name input") .clear() @@ -20,14 +21,11 @@ describe("Elastic search datasource tests", function() { .should("have.value", elasticSearchName) .blur(); }); - cy.wait("@saveDatasource").should( - "have.nested.property", - "response.body.responseMeta.status", - 200, - ); cy.fillElasticDatasourceForm(); //once we have test values for elastic search we can test and save the datasources. // cy.testSaveDatasource(); + + dataSource.SaveDSFromDialog(false); }); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Datasources/GoogleSheetsStub_spec.ts b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Datasources/GoogleSheetsStub_spec.ts index 5234475712b8..67287eadb54a 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Datasources/GoogleSheetsStub_spec.ts +++ b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Datasources/GoogleSheetsStub_spec.ts @@ -12,8 +12,8 @@ describe("Google Sheets datasource test cases", function() { "Read, Edit and Create Files", "Read, Edit, Create and Delete Files", ]); - dataSources.DeleteDSDirectly(); - }); + dataSources.SaveDSFromDialog(false); + }); function VerifyFunctionDropdown(scopeOptions: string[]) { agHelper.GetNClick(dataSources._gsScopeDropdown); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Datasources/MongoDatasource_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Datasources/MongoDatasource_spec.js index a850dd88ce8c..d124e21a1c8d 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Datasources/MongoDatasource_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Datasources/MongoDatasource_spec.js @@ -5,14 +5,14 @@ describe("Create, test, save then delete a mongo datasource", function() { cy.startRoutesForDatasource(); }); - it("Create, test, save then delete a mongo datasource", function() { + it("1. Create, test, save then delete a mongo datasource", function() { cy.NavigateToDatasourceEditor(); cy.get(datasource.MongoDB).click(); cy.fillMongoDatasourceForm(); cy.testSaveDeleteDatasource(); }); - it("Create with trailing white spaces in host address and database name, test, save then delete a mongo datasource", function() { + it("2. Create with trailing white spaces in host address and database name, test, save then delete a mongo datasource", function() { cy.NavigateToDatasourceEditor(); cy.get(datasource.MongoDB).click(); cy.fillMongoDatasourceForm(true); //fills form with trailing white spaces diff --git a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Datasources/MsSQLDataSourceStub_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Datasources/MsSQLDataSourceStub_spec.js index 912d2e261e2f..cbc7807f9f81 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Datasources/MsSQLDataSourceStub_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Datasources/MsSQLDataSourceStub_spec.js @@ -1,6 +1,7 @@ const datasource = require("../../../../locators/DatasourcesEditor.json"); -const datasourceEditor = require("../../../../locators/DatasourcesEditor.json"); +import { ObjectsRegistry } from "../../../../support/Objects/Registry"; +let dataSource = ObjectsRegistry.DataSources; let datasourceName; describe("MsSQL datasource test cases", function() { @@ -15,33 +16,31 @@ describe("MsSQL datasource test cases", function() { cy.generateUUID().then((UUID) => { datasourceName = `MsSQL MOCKDS ${UUID}`; cy.renameDatasource(datasourceName); + cy.intercept("POST", "/api/v1/datasources/test", { + fixture: "testAction.json", + }).as("testDatasource"); + cy.testSaveDatasource(false); + dataSource.DeleteDatasouceFromActiveTab(datasourceName); }); - - cy.get("@createDatasource").then((httpResponse) => { - datasourceName = httpResponse.response.body.data.name; - }); - cy.intercept("POST", "/api/v1/datasources/test", { - fixture: "testAction.json", - }).as("testDatasource"); - cy.testSaveDatasource(false); }); it("2. Create with trailing white spaces in host address and database name, test, save then delete a MsSQL datasource", function() { cy.NavigateToDatasourceEditor(); cy.get(datasource.MsSQL).click(); cy.fillMsSQLDatasourceForm(true); - cy.get("@createDatasource").then((httpResponse) => { - datasourceName = httpResponse.response.body.data.name; - }); cy.intercept("POST", "/api/v1/datasources/test", { fixture: "testAction.json", }).as("testDatasource"); cy.testSaveDatasource(false); + cy.get("@saveDatasource").then((httpResponse) => { + datasourceName = JSON.stringify( + httpResponse.response.body.data.name, + ).replace(/['"]+/g, ""); + }); }); it("3. Create a new query from the datasource editor", function() { - // cy.get(datasource.createQuery).click(); - cy.get(`${datasourceEditor.datasourceCard} ${datasource.createQuery}`) + cy.get(datasource.createQuery) .last() .click(); cy.wait("@createNewApi").should( @@ -49,9 +48,7 @@ describe("MsSQL datasource test cases", function() { "response.body.responseMeta.status", 201, ); - cy.deleteQueryUsingContext(); - cy.deleteDatasource(datasourceName); }); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Datasources/MySQLDataSourceStub_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Datasources/MySQLDataSourceStub_spec.js index ef116ea37eac..605922128329 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Datasources/MySQLDataSourceStub_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Datasources/MySQLDataSourceStub_spec.js @@ -1,6 +1,6 @@ const datasource = require("../../../../locators/DatasourcesEditor.json"); -const queryEditor = require("../../../../locators/QueryEditor.json"); -const datasourceEditor = require("../../../../locators/DatasourcesEditor.json"); +import { ObjectsRegistry } from "../../../../support/Objects/Registry"; +let dataSource = ObjectsRegistry.DataSources; let datasourceName; @@ -16,33 +16,31 @@ describe("MySQL datasource test cases", function() { cy.generateUUID().then((UUID) => { datasourceName = `MySQL MOCKDS ${UUID}`; cy.renameDatasource(datasourceName); + cy.intercept("POST", "/api/v1/datasources/test", { + fixture: "testAction.json", + }).as("testDatasource"); + cy.testSaveDatasource(false); + dataSource.DeleteDatasouceFromActiveTab(datasourceName); }); - - cy.get("@createDatasource").then((httpResponse) => { - datasourceName = httpResponse.response.body.data.name; - }); - cy.intercept("POST", "/api/v1/datasources/test", { - fixture: "testAction.json", - }).as("testDatasource"); - cy.testSaveDatasource(false); }); it("2. Create with trailing white spaces in host address and database name, test, save then delete a MySQL datasource", function() { cy.NavigateToDatasourceEditor(); cy.get(datasource.MySQL).click(); cy.fillMySQLDatasourceForm(true); - cy.get("@createDatasource").then((httpResponse) => { - datasourceName = httpResponse.response.body.data.name; - }); cy.intercept("POST", "/api/v1/datasources/test", { fixture: "testAction.json", }).as("testDatasource"); cy.testSaveDatasource(false); + cy.get("@saveDatasource").then((httpResponse) => { + datasourceName = JSON.stringify( + httpResponse.response.body.data.name, + ).replace(/['"]+/g, ""); + }); }); it("3. Create a new query from the datasource editor", function() { - // cy.get(datasource.createQuery).click(); - cy.get(`${datasourceEditor.datasourceCard} ${datasource.createQuery}`) + cy.get(datasource.createQuery) .last() .click(); cy.wait("@createNewApi").should( @@ -50,9 +48,7 @@ describe("MySQL datasource test cases", function() { "response.body.responseMeta.status", 201, ); - cy.deleteQueryUsingContext(); - cy.deleteDatasource(datasourceName); }); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Datasources/MySQL_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Datasources/MySQL_spec.js index 876d02396206..0904b14ba2b3 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Datasources/MySQL_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Datasources/MySQL_spec.js @@ -1,7 +1,7 @@ const datasource = require("../../../../locators/DatasourcesEditor.json"); -const queryEditor = require("../../../../locators/QueryEditor.json"); -const datasourceEditor = require("../../../../locators/DatasourcesEditor.json"); +import { ObjectsRegistry } from "../../../../support/Objects/Registry"; +let dataSource = ObjectsRegistry.DataSources; let datasourceName; describe("MySQL datasource test cases", function() { @@ -13,25 +13,28 @@ describe("MySQL datasource test cases", function() { cy.NavigateToDatasourceEditor(); cy.get(datasource.MySQL).click(); cy.fillMySQLDatasourceForm(); - cy.get("@createDatasource").then((httpResponse) => { - datasourceName = httpResponse.response.body.data.name; + cy.generateUUID().then((UUID) => { + datasourceName = `MySQL MOCKDS ${UUID}`; + cy.renameDatasource(datasourceName); + cy.testSaveDatasource(); + dataSource.DeleteDatasouceFromActiveTab(datasourceName); }); - cy.testSaveDatasource(); }); it("2. Create with trailing white spaces in host address and database name, test, save then delete a MySQL datasource", function() { cy.NavigateToDatasourceEditor(); cy.get(datasource.MySQL).click(); cy.fillMySQLDatasourceForm(true); - cy.get("@createDatasource").then((httpResponse) => { - datasourceName = httpResponse.response.body.data.name; + cy.generateUUID().then((UUID) => { + datasourceName = `MySQL MOCKDS ${UUID}`; + cy.renameDatasource(datasourceName); }); cy.testSaveDatasource(); }); it("3. Create a new query from the datasource editor", function() { // cy.get(datasource.createQuery).click(); - cy.get(`${datasourceEditor.datasourceCard} ${datasource.createQuery}`) + cy.get(datasource.createQuery) .last() .click(); cy.wait("@createNewApi").should( @@ -39,9 +42,7 @@ describe("MySQL datasource test cases", function() { "response.body.responseMeta.status", 201, ); - cy.deleteQueryUsingContext(); - cy.deleteDatasource(datasourceName); }); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Datasources/PostgresDatasource_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Datasources/PostgresDatasource_spec.js index f938e16ade3f..dd8ffe9555d8 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Datasources/PostgresDatasource_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Datasources/PostgresDatasource_spec.js @@ -1,7 +1,7 @@ const datasource = require("../../../../locators/DatasourcesEditor.json"); -const queryEditor = require("../../../../locators/QueryEditor.json"); -const datasourceEditor = require("../../../../locators/DatasourcesEditor.json"); +import { ObjectsRegistry } from "../../../../support/Objects/Registry"; +let dataSource = ObjectsRegistry.DataSources; let datasourceName; describe("Postgres datasource test cases", function() { @@ -13,25 +13,29 @@ describe("Postgres datasource test cases", function() { cy.NavigateToDatasourceEditor(); cy.get(datasource.PostgreSQL).click(); cy.fillPostgresDatasourceForm(); - cy.get("@createDatasource").then((httpResponse) => { - datasourceName = httpResponse.response.body.data.name; - }); cy.testSaveDatasource(); + cy.get("@saveDatasource").then((httpResponse) => { + datasourceName = JSON.stringify(httpResponse.response.body.data.name); + dataSource.DeleteDatasouceFromActiveTab( + datasourceName.replace(/['"]+/g, ""), + ); + }); }); it("2. Create with trailing white spaces in host address and database name, test, save then delete a postgres datasource", function() { cy.NavigateToDatasourceEditor(); cy.get(datasource.PostgreSQL).click(); cy.fillPostgresDatasourceForm(true); - cy.get("@createDatasource").then((httpResponse) => { - datasourceName = httpResponse.response.body.data.name; - }); cy.testSaveDatasource(); + cy.get("@saveDatasource").then((httpResponse) => { + datasourceName = JSON.stringify( + httpResponse.response.body.data.name, + ).replace(/['"]+/g, ""); + }); }); it("3. Create a new query from the datasource editor", function() { - // cy.get(datasource.createQuery).click(); - cy.get(`${datasourceEditor.datasourceCard} ${datasource.createQuery}`) + cy.get(datasource.createQuery) .last() .click(); cy.wait("@createNewApi").should( @@ -39,9 +43,7 @@ describe("Postgres datasource test cases", function() { "response.body.responseMeta.status", 201, ); - cy.deleteQueryUsingContext(); - cy.deleteDatasource(datasourceName); }); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Datasources/RedshiftDataSourceStub_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Datasources/RedshiftDataSourceStub_spec.js index 7643ed7094d9..ff16f190206c 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Datasources/RedshiftDataSourceStub_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Datasources/RedshiftDataSourceStub_spec.js @@ -1,7 +1,4 @@ const datasource = require("../../../../locators/DatasourcesEditor.json"); -const queryEditor = require("../../../../locators/QueryEditor.json"); -const datasourceEditor = require("../../../../locators/DatasourcesEditor.json"); - let datasourceName; describe("Redshift datasource test cases", function() { @@ -17,10 +14,6 @@ describe("Redshift datasource test cases", function() { datasourceName = `Redshift MOCKDS ${UUID}`; cy.renameDatasource(datasourceName); }); - - cy.get("@createDatasource").then((httpResponse) => { - datasourceName = httpResponse.response.body.data.name; - }); cy.intercept("POST", "/api/v1/datasources/test", { fixture: "testAction.json", }).as("testDatasource"); @@ -31,18 +24,19 @@ describe("Redshift datasource test cases", function() { cy.NavigateToDatasourceEditor(); cy.get(datasource.Redshift).click(); cy.fillRedshiftDatasourceForm(true); - cy.get("@createDatasource").then((httpResponse) => { - datasourceName = httpResponse.response.body.data.name; + cy.generateUUID().then((UUID) => { + datasourceName = `Redshift MOCKDS ${UUID}`; + cy.renameDatasource(datasourceName); }); cy.intercept("POST", "/api/v1/datasources/test", { fixture: "testAction.json", }).as("testDatasource"); cy.testSaveDatasource(false); + cy.deleteDatasource(datasourceName); }); it("3. Create a new query from the datasource editor", function() { - // cy.get(datasource.createQuery).click(); - cy.get(`${datasourceEditor.datasourceCard} ${datasource.createQuery}`) + cy.get(datasource.createQuery) .last() .click(); cy.wait("@createNewApi").should( @@ -50,9 +44,7 @@ describe("Redshift datasource test cases", function() { "response.body.responseMeta.status", 201, ); - cy.deleteQueryUsingContext(); - cy.deleteDatasource(datasourceName); }); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Datasources/RestApiDatasource_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Datasources/RestApiDatasource_spec.js index 7cfa392c258a..0e23c523b0e9 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Datasources/RestApiDatasource_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Datasources/RestApiDatasource_spec.js @@ -1,7 +1,8 @@ const testdata = require("../../../../fixtures/testdata.json"); import { ObjectsRegistry } from "../../../../support/Objects/Registry"; -let agHelper = ObjectsRegistry.AggregateHelper; +let agHelper = ObjectsRegistry.AggregateHelper, + locator = ObjectsRegistry.CommonLocators; describe("Create a rest datasource", function() { beforeEach(() => { @@ -16,10 +17,12 @@ describe("Create a rest datasource", function() { cy.get(".t--store-as-datasource") .trigger("click") .wait(1000); - agHelper.ValidateToastMessage("datasource created"); //verifying there is no error toast, Bug 14566 + agHelper.AssertElementAbsence( + locator._specificToast("Duplicate key error"), + ); //verifying there is no error toast, Bug 14566 cy.testSelfSignedCertificateSettingsInREST(false); cy.saveDatasource(); - cy.contains(".datasource-highlight", "https://mock-api.appsmith.com"); + cy.contains(".datasource-highlight", "https://mock-api.appsmith.com"); //failing here since Save as Datasource is broken cy.SaveAndRunAPI(); }); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Datasources/RestApiOAuth2Validation_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Datasources/RestApiOAuth2Validation_spec.js index 27f30d5f04b1..52d43ccdcb89 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Datasources/RestApiOAuth2Validation_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Datasources/RestApiOAuth2Validation_spec.js @@ -5,13 +5,14 @@ import { ObjectsRegistry } from "../../../../support/Objects/Registry"; let agHelper = ObjectsRegistry.AggregateHelper, apiPage = ObjectsRegistry.ApiPage, - ee = ObjectsRegistry.EntityExplorer; + ee = ObjectsRegistry.EntityExplorer, + datasources = ObjectsRegistry.DataSources; describe("Datasource form OAuth2 client credentials related tests", function() { it("1. Create an API with app url and save as Datasource for Client Credentials test", function() { apiPage.CreateAndFillApi(testdata.appUrl, "TestOAuth"); agHelper.GetNClick(apiPage._saveAsDS); - agHelper.ValidateToastMessage("datasource created"); //verifying there is no error toast, Bug 14566 + // agHelper.ValidateToastMessage("datasource created"); //verifying there is no error toast, Bug 14566 }); it("2. Add Oauth details to datasource and save", function() { @@ -22,6 +23,11 @@ describe("Datasource form OAuth2 client credentials related tests", function() { testdata.clientSecret, testdata.oauth2Scopes, ); + + // since we are moving to different, it will show unsaved changes dialog + // save datasource and then proceed + datasources.SaveDatasource(); + ee.SelectEntityByName("TestOAuth", "Queries/JS"); agHelper.ActionContextMenuWithInPane("Delete", "Are you sure?"); }); @@ -29,7 +35,7 @@ describe("Datasource form OAuth2 client credentials related tests", function() { it("3. Create an API with app url and save as Datasource for Authorization code details test", function() { apiPage.CreateAndFillApi(testdata.appUrl, "TestOAuth"); agHelper.GetNClick(apiPage._saveAsDS); - agHelper.ValidateToastMessage("datasource created"); //verifying there is no error toast, Bug 14566 + // agHelper.ValidateToastMessage("datasource created"); //verifying there is no error toast, Bug 14566 }); it("4. Add Oauth details to datasource and save", function() { diff --git a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Datatypes/MySQL_Spec.ts b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Datatypes/MySQL_Spec.ts index aaef244cf4a3..081919d03e5e 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Datatypes/MySQL_Spec.ts +++ b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Datatypes/MySQL_Spec.ts @@ -5,17 +5,17 @@ let dsName: any, query: string; const agHelper = ObjectsRegistry.AggregateHelper, ee = ObjectsRegistry.EntityExplorer, dataSources = ObjectsRegistry.DataSources, - propPane = ObjectsRegistry.PropertyPane, table = ObjectsRegistry.Table, locator = ObjectsRegistry.CommonLocators, - deployMode = ObjectsRegistry.DeployMode; + deployMode = ObjectsRegistry.DeployMode, + appSettings = ObjectsRegistry.AppSettings; describe("MySQL Datatype tests", function() { before(() => { cy.fixture("Datatypes/mySQLdsl").then((val: any) => { agHelper.AddDsl(val); }); - propPane.ChangeTheme("Moon"); + appSettings.OpenPaneAndChangeTheme("Moon"); }); it("1. Create Mysql DS", function() { @@ -35,6 +35,7 @@ describe("MySQL Datatype tests", function() { dataSources.EnterQuery(query); dataSources.RunQuery(); + ee.ExpandCollapseEntity("Datasources"); ee.ActionContextMenuByEntityName(dsName, "Refresh"); agHelper.AssertElementVisible( ee._entityNameInExplorer(inputData.tableName), @@ -88,10 +89,10 @@ describe("MySQL Datatype tests", function() { inputData.result.forEach((res_array, i) => { res_array.forEach((value, j) => { table.ReadTableRowColumnData(j, i, 0).then(($cellData) => { - if(i === inputData.result.length-1){ - let obj = JSON.parse($cellData) + if (i === inputData.result.length - 1) { + const obj = JSON.parse($cellData); expect(JSON.stringify(obj)).to.eq(JSON.stringify(value)); - }else{ + } else { expect($cellData).to.eq(value); } }); @@ -137,14 +138,11 @@ describe("MySQL Datatype tests", function() { it("9. Verify Deletion of the datasource after all created queries are Deleted", () => { dataSources.DeleteDatasouceFromWinthinDS(dsName, 409); //Since all queries exists ee.ExpandCollapseEntity("Queries/JS"); - [ - "createTable", - "dropTable", - "insertRecord", - "selectRecords", - ].forEach((type) => { - ee.ActionContextMenuByEntityName(type, "Delete", "Are you sure?"); - }); + ["createTable", "dropTable", "insertRecord", "selectRecords"].forEach( + (type) => { + ee.ActionContextMenuByEntityName(type, "Delete", "Are you sure?"); + }, + ); deployMode.DeployApp(); deployMode.NavigateBacktoEditor(); ee.ExpandCollapseEntity("Queries/JS"); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Datatypes/MySQL_false_Spec.ts b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Datatypes/MySQL_false_Spec.ts index 1cb430433df0..d26fe6a270f9 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Datatypes/MySQL_false_Spec.ts +++ b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Datatypes/MySQL_false_Spec.ts @@ -28,6 +28,7 @@ describe("MySQL Datatype tests", function() { dataSources.EnterQuery(query); dataSources.RunQuery(); + ee.ExpandCollapseEntity("Datasources"); ee.ActionContextMenuByEntityName(dsName, "Refresh"); agHelper.AssertElementVisible( ee._entityNameInExplorer(inputData.tableName), diff --git a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/GenerateCRUD/Mongo_Spec.ts b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/GenerateCRUD/Mongo_Spec.ts index 8fe19437c4e1..452566f72462 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/GenerateCRUD/Mongo_Spec.ts +++ b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/GenerateCRUD/Mongo_Spec.ts @@ -2,14 +2,13 @@ import { ObjectsRegistry } from "../../../../support/Objects/Registry"; let dsName: any; -let agHelper = ObjectsRegistry.AggregateHelper, +const agHelper = ObjectsRegistry.AggregateHelper, ee = ObjectsRegistry.EntityExplorer, locator = ObjectsRegistry.CommonLocators, - homePage = ObjectsRegistry.HomePage, dataSources = ObjectsRegistry.DataSources, deployMode = ObjectsRegistry.DeployMode, table = ObjectsRegistry.Table, - propPane = ObjectsRegistry.PropertyPane; + appSettings = ObjectsRegistry.AppSettings; describe("Validate Mongo CRUD with JSON Form", () => { before(() => { @@ -24,7 +23,8 @@ describe("Validate Mongo CRUD with JSON Form", () => { }); it("1. Create DS & then Add new Page and generate CRUD template using created datasource", () => { - propPane.ChangeTheme("Water Lily"); + appSettings.OpenPaneAndChangeTheme("Water Lily"); + dataSources.CreateDataSource("Mongo"); cy.get("@dsName").then(($dsName) => { dsName = $dsName; @@ -88,7 +88,7 @@ describe("Validate Mongo CRUD with JSON Form", () => { ) { agHelper.GetNClick(dataSources._generatePageBtn); agHelper.ValidateNetworkStatus("@replaceLayoutWithCRUDPage", 201); - agHelper.AssertContains("Successfully generated a page");// Commenting this since FindQuery failure appears sometimes + agHelper.AssertContains("Successfully generated a page"); // Commenting this since FindQuery failure appears sometimes agHelper.ValidateNetworkStatus("@getActions", 200); agHelper.ValidateNetworkStatus("@postExecute", 200); agHelper.ValidateNetworkStatus("@updateLayout", 200); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/GenerateCRUD/MySQL2_Spec.ts b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/GenerateCRUD/MySQL2_Spec.ts index b68b47dadb6f..162a051ff94b 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/GenerateCRUD/MySQL2_Spec.ts +++ b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/GenerateCRUD/MySQL2_Spec.ts @@ -64,6 +64,7 @@ describe("Validate MySQL Generate CRUD with JSON Form", () => { dataSources.RunQueryNVerifyResponseViews(); agHelper.ActionContextMenuWithInPane("Delete"); + ee.ExpandCollapseEntity("Datasources"); ee.ExpandCollapseEntity(dsName); ee.ActionContextMenuByEntityName(dsName, "Refresh"); agHelper.AssertElementVisible(ee._entityNameInExplorer("Stores")); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/GenerateCRUD/Postgres1_Spec.ts b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/GenerateCRUD/Postgres1_Spec.ts index 0eb5d2aca224..9f1d88243228 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/GenerateCRUD/Postgres1_Spec.ts +++ b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/GenerateCRUD/Postgres1_Spec.ts @@ -8,8 +8,8 @@ const agHelper = ObjectsRegistry.AggregateHelper, table = ObjectsRegistry.Table, homePage = ObjectsRegistry.HomePage, dataSources = ObjectsRegistry.DataSources, - propPane = ObjectsRegistry.PropertyPane, - deployMode = ObjectsRegistry.DeployMode; + deployMode = ObjectsRegistry.DeployMode, + appSettings = ObjectsRegistry.AppSettings; describe("Validate Postgres Generate CRUD with JSON Form", () => { it("1. Create DS & then Add new Page and generate CRUD template using created datasource", () => { @@ -77,7 +77,7 @@ describe("Validate Postgres Generate CRUD with JSON Form", () => { cy.get("@dsName").then(($dsName) => { dsName = $dsName; }); - propPane.ChangeTheme("Sunrise"); + appSettings.OpenPaneAndChangeTheme("Sunrise"); }); it("3. Generate CRUD page from datasource present in ACTIVE section", function() { diff --git a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/GenerateCRUD/S3_Spec.js b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/GenerateCRUD/S3_Spec.js index 63e0a215505b..c8e2c0454f2d 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/GenerateCRUD/S3_Spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/GenerateCRUD/S3_Spec.js @@ -155,10 +155,10 @@ describe("Generate New CRUD Page Inside from entity explorer", function() { //Save source cy.get(".t--save-datasource").click(); - cy.wait("@createDatasource"); + cy.wait("@saveDatasource"); //Verify page after save clicked - // cy.get("@createDatasource").then((httpResponse) => { + // cy.get("@saveDatasource").then((httpResponse) => { // datasourceName = httpResponse.response.body.data.name; // }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/JsFunctionExecution/Fetch_Spec.ts b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/JsFunctionExecution/Fetch_Spec.ts new file mode 100644 index 000000000000..2c245781363a --- /dev/null +++ b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/JsFunctionExecution/Fetch_Spec.ts @@ -0,0 +1,91 @@ +import { ObjectsRegistry } from "../../../../support/Objects/Registry"; +const jsEditor = ObjectsRegistry.JSEditor; +const agHelper = ObjectsRegistry.AggregateHelper; +const explorerHelper = ObjectsRegistry.EntityExplorer; +const propertyPaneHelper = ObjectsRegistry.PropertyPane; +const aggregateHelper = ObjectsRegistry.AggregateHelper; + +describe("Tests fetch calls", () => { + it("1. Ensures that cookies are not passed with fetch calls", function() { + jsEditor.CreateJSObject( + `export default { + myVar1: [], + myVar2: {}, + myFun1: async (x = "default") => { + return fetch("/api/v1/users/me", { credentials: 'include' }).then(res => res.json()).then(function(res) { + showAlert(res.data.username); + }) + }, + myFun2: async function() { + const req = new Request("/api/v1/users/me", { credentials: 'include' }); + const res = await fetch(req); + const jsonRes = await res.json(); + showAlert(jsonRes.data.username); + } + }`, + { + paste: true, + completeReplace: true, + toRun: false, + shouldCreateNewJSObj: true, + prettify: true, + }, + ); + agHelper.Sleep(2000); + jsEditor.RunJSObj(); + agHelper.AssertContains("anonymousUser", "exist"); + + jsEditor.SelectFunctionDropdown("myFun2"); + jsEditor.RunJSObj(); + agHelper.AssertContains("anonymousUser", "exist"); + }); + it("2. Tests if fetch works with setTimeout", function() { + jsEditor.CreateJSObject( + `export default { + myVar1: [], + myVar2: {}, + delay: (fn, x = 1000) => { + setTimeout(fn, x); + }, + api: async function() { + const req = new Request("/api/v1/users/me", { credentials: 'include' }); + const res = await fetch(req); + const jsonRes = await res.json(); + showAlert(jsonRes.data.username); + }, + invoker() { + this.delay(this.api, 3000); + } + }`, + { + paste: true, + completeReplace: true, + toRun: false, + shouldCreateNewJSObj: true, + prettify: true, + }, + ); + agHelper.Sleep(2000); + jsEditor.SelectFunctionDropdown("invoker"); + jsEditor.RunJSObj(); + agHelper.Sleep(3000); + agHelper.AssertContains("anonymousUser", "exist"); + }); + + it("3. Tests if fetch works with store value", function() { + explorerHelper.NavigateToSwitcher("widgets"); + explorerHelper.DragDropWidgetNVerify("buttonwidget", 500, 200); + explorerHelper.SelectEntityByName("Button1"); + propertyPaneHelper.TypeTextIntoField("Label", "getUserID"); + propertyPaneHelper.EnterJSContext( + "onClick", + `{{fetch('https://jsonplaceholder.typicode.com/todos/1') + .then(res => res.json()) + .then(json => storeValue('userId', json.userId)) + .then(() => showAlert("UserId: " + appsmith.store.userId))}}`, + ); + aggregateHelper.Sleep(2000); + aggregateHelper.ClickButton("getUserID"); + agHelper.AssertContains("UserId: 1", "exist"); + }); +}); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/OnLoadTests/APIOnLoad_Spec.ts b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/OnLoadTests/APIOnLoad_Spec.ts index 529f79407390..396071920175 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/OnLoadTests/APIOnLoad_Spec.ts +++ b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/OnLoadTests/APIOnLoad_Spec.ts @@ -38,6 +38,7 @@ describe("JSObjects OnLoad Actions tests", function() { "PageLoadApi2", ); apiPage.ToggleOnPageLoadRun(true); + ee.ExpandCollapseEntity("Widgets") ee.ExpandCollapseEntity("Container3"); ee.SelectEntityByName("Table1"); propPane.UpdatePropertyFieldValue( @@ -49,6 +50,7 @@ describe("JSObjects OnLoad Actions tests", function() { }); after(() => { + ee.ExpandCollapseEntity("Queries/JS"); ee.ActionContextMenuByEntityName("PageLoadApi", "Delete", "Are you sure?"); ee.ActionContextMenuByEntityName("PageLoadApi2", "Delete", "Are you sure?"); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/OnLoadTests/JSOnLoad1_Spec.ts b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/OnLoadTests/JSOnLoad1_Spec.ts index 490469d779e2..2bedbcf5b4fa 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/OnLoadTests/JSOnLoad1_Spec.ts +++ b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/OnLoadTests/JSOnLoad1_Spec.ts @@ -24,9 +24,6 @@ describe("JSObjects OnLoad Actions tests", function() { cy.fixture("tablev1NewDsl").then((val: any) => { agHelper.AddDsl(val); }); - }); - - it("1. Create Postgress DS & the query", function() { ee.NavigateToSwitcher("explorer"); dataSources.CreateDataSource("Postgres"); cy.get("@dsName").then(($dsName) => { @@ -34,7 +31,7 @@ describe("JSObjects OnLoad Actions tests", function() { }); }); - it("2. Tc 54, 55 - Verify User enables only 'Before Function calling' & OnPage Load is Automatically enable after mapping done on JSOBject", function() { + it("1. Tc 54, 55 - Verify User enables only 'Before Function calling' & OnPage Load is Automatically enable after mapping done on JSOBject", function() { jsEditor.CreateJSObject( `export default { getEmployee: async () => { @@ -81,7 +78,7 @@ describe("JSObjects OnLoad Actions tests", function() { deployMode.NavigateBacktoEditor(); }); - it("3. Tc 54, 55 - Verify OnPage Load - auto enabled from above case for JSOBject", function() { + it("2. Tc 54, 55 - Verify OnPage Load - auto enabled from above case for JSOBject", function() { agHelper.AssertElementVisible(jsEditor._dialog("Confirmation Dialog")); agHelper.AssertElementVisible( jsEditor._dialogBody((jsName as string) + ".getEmployee"), @@ -93,16 +90,51 @@ describe("JSObjects OnLoad Actions tests", function() { jsEditor.VerifyAsyncFuncSettings("getEmployee", true, true); }); + it("3. Tc 56 - Verify OnPage Load - Enabled & Before Function calling Enabled for JSOBject & User clicks No & then Yes in Confirmation dialog", function() { + deployMode.DeployApp();//Adding this check since GetEmployee failure toast is always coming & making product flaky + //agHelper.WaitUntilAllToastsDisappear(); + agHelper.AssertElementVisible(jsEditor._dialog("Confirmation Dialog")); + agHelper.AssertElementVisible( + jsEditor._dialogBody((jsName as string) + ".getEmployee"), + ); + agHelper.ClickButton("No"); + agHelper.AssertContains(`${jsName + ".getEmployee"} was cancelled`); + table.WaitForTableEmpty(); + agHelper.WaitUntilAllToastsDisappear(); + + agHelper.RefreshPage(); + agHelper.AssertElementVisible(jsEditor._dialog("Confirmation Dialog")); + agHelper.AssertElementVisible( + jsEditor._dialogBody((jsName as string) + ".getEmployee"), + ); + agHelper.ClickButton("Yes"); + agHelper.AssertElementAbsence(locator._toastMsg); + // agHelper.ValidateNetworkExecutionSuccess("@postExecute"); + table.ReadTableRowColumnData(0, 0).then((cellData) => { + expect(cellData).to.be.equal("2"); + }); + deployMode.NavigateBacktoEditor(); + agHelper.AssertElementVisible(jsEditor._dialog("Confirmation Dialog")); + agHelper.AssertElementVisible( + jsEditor._dialogBody((jsName as string) + ".getEmployee"), + ); + agHelper.ClickButton("Yes"); + agHelper.ValidateToastMessage("getEmployee ran successfully"); //Verify this toast comes in EDIT page only + }); + + //Skipping due to - "tableData":"ERROR: invalid input syntax for type smallint: "{}"" it.skip("4. Tc 53 - Verify OnPage Load - Enabled & Disabling - Before Function calling for JSOBject", function() { ee.SelectEntityByName(jsName as string, "Queries/JS"); jsEditor.EnableDisableAsyncFuncSettings("getEmployee", true, false); + //jsEditor.RunJSObj(); //Even running JS functin before delpoying does not help + //agHelper.Sleep(2000); deployMode.DeployApp(); agHelper.AssertElementAbsence(jsEditor._dialog("Confirmation Dialog")); agHelper.AssertElementAbsence( jsEditor._dialogBody((jsName as string) + ".getEmployee"), ); // assert that on view mode, we don't get "successful run" toast message for onpageload actions - agHelper.AssertElementAbsence(locator._specificToast("ran successfully")); + agHelper.AssertElementAbsence(locator._specificToast("ran successfully")); //failed toast is appearing hence skipping agHelper.ValidateNetworkExecutionSuccess("@postExecute"); table.ReadTableRowColumnData(0, 0).then((cellData) => { expect(cellData).to.be.equal("2"); @@ -117,63 +149,33 @@ describe("JSObjects OnLoad Actions tests", function() { agHelper.WaitUntilToastDisappear('The action "GetEmployee" has failed'); deployMode.NavigateBacktoEditor(); agHelper.WaitUntilToastDisappear('The action "GetEmployee" has failed'); - ee.ExpandCollapseEntity("Queries/JS"); - ee.SelectEntityByName(jsName as string); - jsEditor.EnableDisableAsyncFuncSettings("getEmployee", true, true); - }); - - it.skip("6. Tc 55 - Verify OnPage Load - Enabling & Before Function calling Enabling for JSOBject", function() { // ee.ExpandCollapseEntity("Queries/JS"); // ee.SelectEntityByName(jsName as string); // jsEditor.EnableDisableAsyncFuncSettings("getEmployee", true, true); - deployMode.DeployApp(locator._widgetInDeployed("tablewidget"), false); - agHelper.Sleep(6000); //incase toast appears - agHelper.AssertElementVisible(jsEditor._dialog("Confirmation Dialog")); - agHelper.AssertElementVisible( - jsEditor._dialogBody((jsName as string) + ".getEmployee"), - ); - agHelper.ClickButton("Yes"); - agHelper.AssertElementAbsence(locator._toastMsg); - table.ReadTableRowColumnData(0, 0, 2000).then((cellData) => { - expect(cellData).to.be.equal("2"); - }); - //agHelper.ValidateNetworkExecutionSuccess("@postExecute"); - deployMode.NavigateBacktoEditor(); - agHelper.AssertElementVisible(jsEditor._dialog("Confirmation Dialog")); - agHelper.AssertElementVisible( - jsEditor._dialogBody((jsName as string) + ".getEmployee"), - ); - agHelper.ClickButton("Yes"); - agHelper.ValidateToastMessage("getEmployee ran successfully"); //Verify this toast comes in EDIT page only + // agHelper.GetNClick(jsEditor._runButton); + // agHelper.ClickButton("Yes"); }); - it("7. Tc 56 - Verify OnPage Load - Enabled & Before Function calling Enabled for JSOBject & User clicks No & then Yes in Confirmation dialog", function() { - deployMode.DeployApp(); - agHelper.AssertElementVisible(jsEditor._dialog("Confirmation Dialog")); - agHelper.AssertElementVisible( - jsEditor._dialogBody((jsName as string) + ".getEmployee"), - ); - agHelper.ClickButton("No"); - agHelper.AssertContains(`${jsName + ".getEmployee"} was cancelled`); - table.WaitForTableEmpty(); - agHelper.RefreshPage(); - agHelper.AssertElementVisible(jsEditor._dialog("Confirmation Dialog")); - agHelper.AssertElementVisible( - jsEditor._dialogBody((jsName as string) + ".getEmployee"), - ); - agHelper.ClickButton("Yes"); - agHelper.AssertElementAbsence(locator._toastMsg); - // agHelper.ValidateNetworkExecutionSuccess("@postExecute"); - table.ReadTableRowColumnData(0, 0).then((cellData) => { - expect(cellData).to.be.equal("2"); - }); - deployMode.NavigateBacktoEditor(); - agHelper.AssertElementVisible(jsEditor._dialog("Confirmation Dialog")); - agHelper.AssertElementVisible( - jsEditor._dialogBody((jsName as string) + ".getEmployee"), - ); - agHelper.ClickButton("Yes"); - agHelper.ValidateToastMessage("getEmployee ran successfully"); //Verify this toast comes in EDIT page only + it("6. Tc 55 - Verify OnPage Load - Enabling & Before Function calling Enabling for JSOBject & deleting testdata", function() { + // deployMode.DeployApp(locator._widgetInDeployed("tablewidget"), false); + // agHelper.WaitUntilAllToastsDisappear(); //incase toast appears, GetEmployee failure toast is appearing + // agHelper.AssertElementVisible(jsEditor._dialog("Confirmation Dialog")); + // agHelper.AssertElementVisible( + // jsEditor._dialogBody((jsName as string) + ".getEmployee"), + // ); + // agHelper.ClickButton("Yes"); + // agHelper.AssertElementAbsence(locator._toastMsg); + // table.ReadTableRowColumnData(0, 0, 2000).then((cellData) => { + // expect(cellData).to.be.equal("2"); + // }); + // //agHelper.ValidateNetworkExecutionSuccess("@postExecute"); + // deployMode.NavigateBacktoEditor(); + // agHelper.AssertElementVisible(jsEditor._dialog("Confirmation Dialog")); + // agHelper.AssertElementVisible( + // jsEditor._dialogBody((jsName as string) + ".getEmployee"), + // ); + // agHelper.ClickButton("Yes"); + // agHelper.ValidateToastMessage("getEmployee ran successfully"); //Verify this toast comes in EDIT page only ee.SelectEntityByName(jsName as string, "Queries/JS"); ee.ActionContextMenuByEntityName( @@ -182,11 +184,10 @@ describe("JSObjects OnLoad Actions tests", function() { "Are you sure?", true, ); - ee.ActionContextMenuByEntityName("GetEmployee", "Delete", "Are you sure?"); }); - it("8. Tc 60, 1912 - Verify JSObj calling API - OnPageLoad calls & Confirmation No then Yes!", () => { + it("7. Tc 60, 1912 - Verify JSObj calling API - OnPageLoad calls & Confirmation No then Yes!", () => { ee.SelectEntityByName("Page1"); cy.fixture("JSApiOnLoadDsl").then((val: any) => { agHelper.AddDsl(val, locator._widgetInCanvas("imagewidget")); @@ -331,7 +332,7 @@ describe("JSObjects OnLoad Actions tests", function() { // cy.get("div.t--draggable-inputwidgetv2 > div.iPntND").invoke('attr', 'style', 'height: 304px') }); - it("9. Tc #1912 - API with OnPageLoad & Confirmation both enabled & called directly & setting previous Api's confirmation to false", () => { + it("8. Tc #1912 - API with OnPageLoad & Confirmation both enabled & called directly & setting previous Api's confirmation to false", () => { deployMode.NavigateBacktoEditor(); agHelper.AssertElementExist(jsEditor._dialogInDeployView); agHelper.ClickButton("No"); @@ -379,7 +380,7 @@ describe("JSObjects OnLoad Actions tests", function() { agHelper.ClickButton("No"); }); - it("10. Tc #1646, 60 - Honouring the order of execution & Bug 13826 + Bug 13646", () => { + it("9. Tc #1646, 60 - Honouring the order of execution & Bug 13826 + Bug 13646", () => { homePage.NavigateToHome(); homePage.ImportApp("JSObjOnLoadApp.json"); homePage.AssertImportToast(); @@ -497,7 +498,7 @@ describe("JSObjects OnLoad Actions tests", function() { }); }); - it("11. Tc #1646 - Honouring the order of execution & Bug 13826 + Bug 13646 - Delpoy page", () => { + it("10. Tc #1646 - Honouring the order of execution & Bug 13826 + Bug 13646 - Delpoy page", () => { deployMode.DeployApp(); agHelper.AssertElementVisible(jsEditor._dialogBody("getBooks")); agHelper.ClickButton("No"); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/OnLoadTests/JSOnLoad_cyclic_dependency_errors_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/OnLoadTests/JSOnLoad_cyclic_dependency_errors_spec.js index 41ed0776ee68..58c6d74dcccb 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/OnLoadTests/JSOnLoad_cyclic_dependency_errors_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/OnLoadTests/JSOnLoad_cyclic_dependency_errors_spec.js @@ -1,17 +1,12 @@ import { ObjectsRegistry } from "../../../../support/Objects/Registry"; const dsl = require("../../../../fixtures/inputdsl.json"); -const buttonDsl = require("../../../../fixtures/buttondsl.json"); const datasource = require("../../../../locators/DatasourcesEditor.json"); const widgetsPage = require("../../../../locators/Widgets.json"); const queryLocators = require("../../../../locators/QueryEditor.json"); -import jsActions from "../../../../locators/jsActionLocators.json"; -import { Entity } from "draft-js"; const jsEditor = ObjectsRegistry.JSEditor; const ee = ObjectsRegistry.EntityExplorer; -const explorer = require("../../../../locators/explorerlocators.json"); const agHelper = ObjectsRegistry.AggregateHelper; -const pageid = "MyPage"; let queryName; /* @@ -160,9 +155,7 @@ describe("Cyclic Dependency Informational Error Messagaes", function() { // Case 6: When updating Datasource query it("7. Update Query and check for errors", () => { - cy.get(".t--entity-name") - .contains(queryName) - .click({ force: true }); + ee.SelectEntityByName(queryName, "Queries/JS"); // update query and check for cyclic depedency issue cy.get(queryLocators.query).click({ force: true }); cy.get(".CodeMirror textarea") diff --git a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/OnLoadTests/OnLoadActions_Spec.ts b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/OnLoadTests/OnLoadActions_Spec.ts index 371472ea200b..c9219234ad0d 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/OnLoadTests/OnLoadActions_Spec.ts +++ b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/OnLoadTests/OnLoadActions_Spec.ts @@ -36,6 +36,8 @@ describe("Layout OnLoad Actions tests", function() { }); }); + //Skipping others tests due to RTS server changes + it("2. Bug 8595: OnPageLoad execution - when Query Parmas added via Params tab", function() { cy.fixture("onPageLoadActionsDsl").then((val: any) => { agHelper.AddDsl(val, locator._imageWidget); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Params/ExecutionParams_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Params/ExecutionParams_spec.js index f0168b09b3df..a5f69cf5cec2 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Params/ExecutionParams_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Params/ExecutionParams_spec.js @@ -16,7 +16,7 @@ describe("API Panel Test Functionality", function() { cy.get(datasource.PostgreSQL).click(); cy.fillPostgresDatasourceForm(); cy.testSaveDatasource(); - cy.get("@createDatasource").then((httpResponse) => { + cy.get("@saveDatasource").then((httpResponse) => { datasourceName = httpResponse.response.body.data.name; }); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Params/PassingParams_Spec.ts b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Params/PassingParams_Spec.ts index d08d339286c6..054070f3a138 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Params/PassingParams_Spec.ts +++ b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Params/PassingParams_Spec.ts @@ -57,11 +57,10 @@ describe("[Bug] - 10784 - Passing params from JS to SQL query should not break", table.ReadTableRowColumnData(0, 0, 3000).then((cellData) => { expect(cellData).to.be.equal("7"); }); - - deployMode.NavigateBacktoEditor(); }); it("2. With Optional chaining : {{ (function() { return this?.params?.condition })() }}", function() { + deployMode.NavigateBacktoEditor(); ee.SelectEntityByName("ParamsTest", "Queries/JS"); dataSources.EnterQuery( "SELECT * FROM public.users where id = {{(function() { return this?.params?.condition })() || '1=1'}} order by id", @@ -73,10 +72,10 @@ describe("[Bug] - 10784 - Passing params from JS to SQL query should not break", table.ReadTableRowColumnData(0, 0, 2000).then((cellData) => { expect(cellData).to.be.equal("9"); }); - deployMode.NavigateBacktoEditor(); }); it("3. With Optional chaining : {{ (() => { return this?.params?.condition })() }}", function() { + deployMode.NavigateBacktoEditor(); ee.SelectEntityByName("ParamsTest", "Queries/JS"); dataSources.EnterQuery( "SELECT * FROM public.users where id = {{(() => { return this?.params?.condition })() || '1=1'}} order by id", @@ -88,10 +87,10 @@ describe("[Bug] - 10784 - Passing params from JS to SQL query should not break", table.ReadTableRowColumnData(0, 0, 2000).then((cellData) => { expect(cellData).to.be.equal("7"); }); - deployMode.NavigateBacktoEditor(); }); it("4. With Optional chaining : {{ this?.params.condition }}", function() { + deployMode.NavigateBacktoEditor(); ee.SelectEntityByName("ParamsTest", "Queries/JS"); dataSources.EnterQuery( "SELECT * FROM public.users where id = {{this?.params.condition || '1=1'}} order by id", @@ -103,10 +102,10 @@ describe("[Bug] - 10784 - Passing params from JS to SQL query should not break", table.ReadTableRowColumnData(0, 0, 2000).then((cellData) => { expect(cellData).to.be.equal("9"); }); - deployMode.NavigateBacktoEditor(); }); it("5. With Optional chaining : {{ (function() { return this?.params.condition })() }}", function() { + deployMode.NavigateBacktoEditor(); ee.SelectEntityByName("ParamsTest", "Queries/JS"); dataSources.EnterQuery( "SELECT * FROM public.users where id = {{(function() { return this?.params.condition })() || '1=1'}} order by id", @@ -118,10 +117,10 @@ describe("[Bug] - 10784 - Passing params from JS to SQL query should not break", table.ReadTableRowColumnData(0, 0, 2000).then((cellData) => { expect(cellData).to.be.equal("7"); }); - deployMode.NavigateBacktoEditor(); }); it("6. With Optional chaining : {{ (() => { return this?.params.condition })() }}", function() { + deployMode.NavigateBacktoEditor(); ee.SelectEntityByName("ParamsTest", "Queries/JS"); dataSources.EnterQuery( "SELECT * FROM public.users where id = {{(() => { return this?.params.condition })() || '1=1'}} order by id", @@ -133,10 +132,10 @@ describe("[Bug] - 10784 - Passing params from JS to SQL query should not break", table.ReadTableRowColumnData(0, 0, 2000).then((cellData) => { expect(cellData).to.be.equal("9"); }); - deployMode.NavigateBacktoEditor(); }); it("7. With No Optional chaining : {{ this.params.condition }}", function() { + deployMode.NavigateBacktoEditor(); ee.SelectEntityByName("ParamsTest", "Queries/JS"); dataSources.EnterQuery( "SELECT * FROM public.users where id = {{this.params.condition || '1=1'}} order by id", @@ -148,10 +147,10 @@ describe("[Bug] - 10784 - Passing params from JS to SQL query should not break", table.ReadTableRowColumnData(0, 0, 2000).then((cellData) => { expect(cellData).to.be.equal("7"); }); - deployMode.NavigateBacktoEditor(); }); it("8. With No Optional chaining : {{ (function() { return this.params.condition })() }}", function() { + deployMode.NavigateBacktoEditor(); ee.SelectEntityByName("ParamsTest", "Queries/JS"); dataSources.EnterQuery( "SELECT * FROM public.users where id = {{(function() { return this.params.condition })() || '1=1'}} order by id", @@ -163,10 +162,10 @@ describe("[Bug] - 10784 - Passing params from JS to SQL query should not break", table.ReadTableRowColumnData(0, 0, 2000).then((cellData) => { expect(cellData).to.be.equal("8"); }); - deployMode.NavigateBacktoEditor(); }); it("9. With No Optional chaining : {{ (() => { return this.params.condition })() }}", function() { + deployMode.NavigateBacktoEditor(); ee.SelectEntityByName("ParamsTest", "Queries/JS"); dataSources.EnterQuery( "SELECT * FROM public.users where id = {{(() => { return this.params.condition })() || '1=1'}} order by id", @@ -178,10 +177,10 @@ describe("[Bug] - 10784 - Passing params from JS to SQL query should not break", table.ReadTableRowColumnData(0, 0, 2000).then((cellData) => { expect(cellData).to.be.equal("9"); }); - deployMode.NavigateBacktoEditor(); }); it("10. With Optional chaining : {{ this.params.condition }} && direct paramter passed", function() { + deployMode.NavigateBacktoEditor(); ee.SelectEntityByName("ParamsTest", "Queries/JS"); dataSources.EnterQuery( "SELECT * FROM public.users where id = {{(() => { return this.params.condition })() || '7'}} order by id", @@ -197,10 +196,10 @@ describe("[Bug] - 10784 - Passing params from JS to SQL query should not break", table.ReadTableRowColumnData(0, 0, 2000).then((cellData) => { expect(cellData).to.be.equal("7"); }); - deployMode.NavigateBacktoEditor(); }); it("11. With Optional chaining : {{ this.params.condition }} && no optional paramter passed", function() { + deployMode.NavigateBacktoEditor(); ee.SelectEntityByName("ParamsTest", "Queries/JS"); dataSources.EnterQuery( "SELECT * FROM public.users where id = {{(() => { return this.params.condition })()}} order by id", @@ -211,10 +210,10 @@ describe("[Bug] - 10784 - Passing params from JS to SQL query should not break", table.ReadTableRowColumnData(0, 0, 2000).then((cellData) => { expect(cellData).to.be.equal("8"); }); - deployMode.NavigateBacktoEditor(); }); it("12. Delete all entities - Query, JSObjects, Datasource + Bug 12532", () => { + deployMode.NavigateBacktoEditor(); ee.ExpandCollapseEntity("Queries/JS"); ee.ActionContextMenuByEntityName("ParamsTest", "Delete", "Are you sure?"); agHelper.ValidateNetworkStatus("@deleteAction", 200); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Postgres_DataTypes/Array_Spec.ts b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Postgres_DataTypes/Array_Spec.ts index b2f67d5d5e25..fdc4bd210c02 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Postgres_DataTypes/Array_Spec.ts +++ b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Postgres_DataTypes/Array_Spec.ts @@ -4,10 +4,10 @@ let dsName: any, query: string; const agHelper = ObjectsRegistry.AggregateHelper, ee = ObjectsRegistry.EntityExplorer, dataSources = ObjectsRegistry.DataSources, - propPane = ObjectsRegistry.PropertyPane, table = ObjectsRegistry.Table, locator = ObjectsRegistry.CommonLocators, - deployMode = ObjectsRegistry.DeployMode; + deployMode = ObjectsRegistry.DeployMode, + appSettings = ObjectsRegistry.AppSettings; describe("Array Datatype tests", function() { before(() => { @@ -22,8 +22,7 @@ describe("Array Datatype tests", function() { agHelper.AddDsl(val); }); ee.NavigateToSwitcher("widgets"); - propPane.ChangeThemeColor(-31, "Primary"); - propPane.ChangeThemeColor(-27, "Background"); + appSettings.OpenPaneAndChangeThemeColors(-31, -27); }); it("1. Creating table query - arraytypes", () => { @@ -34,6 +33,7 @@ describe("Array Datatype tests", function() { dataSources.EnterQuery(query); dataSources.RunQuery(); + ee.ExpandCollapseEntity("Datasources"); ee.ActionContextMenuByEntityName(dsName, "Refresh"); agHelper.AssertElementVisible( ee._entityNameInExplorer("public.arraytypes"), diff --git a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Postgres_DataTypes/Binary_Spec.ts b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Postgres_DataTypes/Binary_Spec.ts index d174e92f8793..8335c8c2763e 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Postgres_DataTypes/Binary_Spec.ts +++ b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Postgres_DataTypes/Binary_Spec.ts @@ -4,10 +4,10 @@ let dsName: any, query: string, imageNameToUpload: string; const agHelper = ObjectsRegistry.AggregateHelper, ee = ObjectsRegistry.EntityExplorer, dataSources = ObjectsRegistry.DataSources, - propPane = ObjectsRegistry.PropertyPane, table = ObjectsRegistry.Table, locator = ObjectsRegistry.CommonLocators, - deployMode = ObjectsRegistry.DeployMode; + deployMode = ObjectsRegistry.DeployMode, + appSettings = ObjectsRegistry.AppSettings; describe("Binary Datatype tests", function() { before(() => { @@ -22,8 +22,7 @@ describe("Binary Datatype tests", function() { agHelper.AddDsl(val); }); ee.NavigateToSwitcher("widgets"); - propPane.ChangeThemeColor(24, "Primary"); - propPane.ChangeThemeColor(-37, "Background"); + appSettings.OpenPaneAndChangeThemeColors(24, -37); }); it("1. Creating table query - binarytype", () => { @@ -34,6 +33,7 @@ describe("Binary Datatype tests", function() { dataSources.EnterQuery(query); dataSources.RunQuery(); + ee.ExpandCollapseEntity("Datasources"); ee.ActionContextMenuByEntityName(dsName, "Refresh"); agHelper.AssertElementVisible( ee._entityNameInExplorer("public.binarytype"), diff --git a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Postgres_DataTypes/BooleanEnum_Spec.ts b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Postgres_DataTypes/BooleanEnum_Spec.ts index 17776cbc2e54..526f36479a08 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Postgres_DataTypes/BooleanEnum_Spec.ts +++ b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Postgres_DataTypes/BooleanEnum_Spec.ts @@ -4,18 +4,17 @@ let dsName: any, query: string; const agHelper = ObjectsRegistry.AggregateHelper, ee = ObjectsRegistry.EntityExplorer, dataSources = ObjectsRegistry.DataSources, - propPane = ObjectsRegistry.PropertyPane, table = ObjectsRegistry.Table, locator = ObjectsRegistry.CommonLocators, - deployMode = ObjectsRegistry.DeployMode; + deployMode = ObjectsRegistry.DeployMode, + appSettings = ObjectsRegistry.AppSettings; describe("Boolean & Enum Datatype tests", function() { before(() => { cy.fixture("Datatypes/BooleanEnumDTdsl").then((val: any) => { agHelper.AddDsl(val); }); - propPane.ChangeThemeColor(-18, "Primary"); - propPane.ChangeThemeColor(-20, "Background"); + appSettings.OpenPaneAndChangeThemeColors(-18, -20); }); it("1. Create Postgress DS", function() { @@ -40,7 +39,8 @@ describe("Boolean & Enum Datatype tests", function() { dataSources.EnterQuery(query); dataSources.RunQuery(); - //ee.ExpandCollapseEntity(dsName); //Clicking Create Query from Active DS is already expanding ds + ee.ExpandCollapseEntity("Datasources"); + ee.ExpandCollapseEntity(dsName); //Clicking Create Query from Active DS is already expanding ds ee.ActionContextMenuByEntityName(dsName, "Refresh"); agHelper.AssertElementVisible( ee._entityNameInExplorer("public.boolenumtypes"), diff --git a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Postgres_DataTypes/Character_Spec.ts b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Postgres_DataTypes/Character_Spec.ts index 10ee2fc38c6b..c5479b68c0e6 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Postgres_DataTypes/Character_Spec.ts +++ b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Postgres_DataTypes/Character_Spec.ts @@ -4,17 +4,17 @@ let dsName: any, query: string; const agHelper = ObjectsRegistry.AggregateHelper, ee = ObjectsRegistry.EntityExplorer, dataSources = ObjectsRegistry.DataSources, - propPane = ObjectsRegistry.PropertyPane, table = ObjectsRegistry.Table, locator = ObjectsRegistry.CommonLocators, - deployMode = ObjectsRegistry.DeployMode; + deployMode = ObjectsRegistry.DeployMode, + appSettings = ObjectsRegistry.AppSettings; describe("Character Datatype tests", function() { before(() => { cy.fixture("Datatypes/CharacterDTdsl").then((val: any) => { agHelper.AddDsl(val); }); - propPane.ChangeTheme("Pacific"); + appSettings.OpenPaneAndChangeTheme("Pacific"); }); it("1. Create Postgress DS", function() { diff --git a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Postgres_DataTypes/DateTime_Spec.ts b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Postgres_DataTypes/DateTime_Spec.ts index fb9d32f239c9..61618e11b077 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Postgres_DataTypes/DateTime_Spec.ts +++ b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Postgres_DataTypes/DateTime_Spec.ts @@ -4,18 +4,17 @@ let dsName: any, query: string; const agHelper = ObjectsRegistry.AggregateHelper, ee = ObjectsRegistry.EntityExplorer, dataSources = ObjectsRegistry.DataSources, - propPane = ObjectsRegistry.PropertyPane, table = ObjectsRegistry.Table, locator = ObjectsRegistry.CommonLocators, - deployMode = ObjectsRegistry.DeployMode; + deployMode = ObjectsRegistry.DeployMode, + appSettings = ObjectsRegistry.AppSettings; describe("DateTime Datatype tests", function() { before(() => { cy.fixture("Datatypes/DateTimeDTdsl").then((val: any) => { agHelper.AddDsl(val); }); - propPane.ChangeThemeColor(22, "Primary"); - propPane.ChangeThemeColor(32, "Background"); + appSettings.OpenPaneAndChangeThemeColors(22, 32); }); it("1. Create Postgress DS", function() { diff --git a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Postgres_DataTypes/Json_Spec.ts b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Postgres_DataTypes/Json_Spec.ts index 979ecfc0e2bd..8d4c631c0ab9 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Postgres_DataTypes/Json_Spec.ts +++ b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Postgres_DataTypes/Json_Spec.ts @@ -4,10 +4,10 @@ let dsName: any, query: string; const agHelper = ObjectsRegistry.AggregateHelper, ee = ObjectsRegistry.EntityExplorer, dataSources = ObjectsRegistry.DataSources, - propPane = ObjectsRegistry.PropertyPane, table = ObjectsRegistry.Table, locator = ObjectsRegistry.CommonLocators, - deployMode = ObjectsRegistry.DeployMode; + deployMode = ObjectsRegistry.DeployMode, + appSettings = ObjectsRegistry.AppSettings; describe("Json & JsonB Datatype tests", function() { before(() => { @@ -32,8 +32,7 @@ describe("Json & JsonB Datatype tests", function() { agHelper.AddDsl(val); }); ee.NavigateToSwitcher("widgets"); - propPane.ChangeThemeColor(33, "Primary"); - propPane.ChangeThemeColor(39, "Background"); + appSettings.OpenPaneAndChangeThemeColors(33, 39); }); it("1. Creating table query - jsonbooks", () => { @@ -357,8 +356,7 @@ describe("Json & JsonB Datatype tests", function() { agHelper.AddDsl(val); }); ee.NavigateToSwitcher("widgets"); - propPane.ChangeThemeColor(12, "Primary"); - propPane.ChangeThemeColor(23, "Background"); + appSettings.OpenPaneAndChangeThemeColors(12, 23); }); it("15. Creating enum & table queries - jsonBbooks", () => { diff --git a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Postgres_DataTypes/Numeric_Spec.ts b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Postgres_DataTypes/Numeric_Spec.ts index 0bafa8b087df..377e188e9806 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Postgres_DataTypes/Numeric_Spec.ts +++ b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Postgres_DataTypes/Numeric_Spec.ts @@ -4,17 +4,17 @@ let dsName: any, query: string; const agHelper = ObjectsRegistry.AggregateHelper, ee = ObjectsRegistry.EntityExplorer, dataSources = ObjectsRegistry.DataSources, - propPane = ObjectsRegistry.PropertyPane, table = ObjectsRegistry.Table, locator = ObjectsRegistry.CommonLocators, - deployMode = ObjectsRegistry.DeployMode; + deployMode = ObjectsRegistry.DeployMode, + appSettings = ObjectsRegistry.AppSettings; describe("Numeric Datatype tests", function() { before(() => { cy.fixture("Datatypes/NumericDTdsl").then((val: any) => { agHelper.AddDsl(val); }); - propPane.ChangeTheme("Moon"); + appSettings.OpenPaneAndChangeTheme("Moon"); }); it("1. Create Postgress DS", function() { diff --git a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Postgres_DataTypes/UUID_Spec.ts b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Postgres_DataTypes/UUID_Spec.ts index 33ea06f5ad91..68227c02b812 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Postgres_DataTypes/UUID_Spec.ts +++ b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/Postgres_DataTypes/UUID_Spec.ts @@ -4,11 +4,11 @@ let dsName: any, query: string, imageNameToUpload: string; const agHelper = ObjectsRegistry.AggregateHelper, ee = ObjectsRegistry.EntityExplorer, dataSources = ObjectsRegistry.DataSources, - propPane = ObjectsRegistry.PropertyPane, table = ObjectsRegistry.Table, locator = ObjectsRegistry.CommonLocators, deployMode = ObjectsRegistry.DeployMode, - apiPage = ObjectsRegistry.ApiPage; + apiPage = ObjectsRegistry.ApiPage, + appSettings = ObjectsRegistry.AppSettings; describe("UUID Datatype tests", function() { before(() => { @@ -23,7 +23,7 @@ describe("UUID Datatype tests", function() { agHelper.AddDsl(val); }); ee.NavigateToSwitcher("widgets"); - propPane.ChangeTheme("Earth"); + appSettings.OpenPaneAndChangeTheme("Earth"); }); it("1. Creating supporting api's for generating random UUID's", () => { @@ -201,7 +201,9 @@ describe("UUID Datatype tests", function() { agHelper.AssertContains("New V1 UUID available!"); agHelper.ClickButton("Update"); - agHelper.AssertElementAbsence(locator._specificToast("failed to execute")); //Assert that Insert did not fail + agHelper.AssertElementAbsence( + locator._specificToast("failed to execute"), + ); //Assert that Insert did not fail agHelper.AssertElementVisible(locator._spanButton("Run UpdateQuery")); table.WaitUntilTableLoad(); table.ReadTableRowColumnData(2, 0).then(($cellData) => { @@ -232,7 +234,9 @@ describe("UUID Datatype tests", function() { agHelper.AssertContains("New GUID available!"); agHelper.ClickButton("Update"); - agHelper.AssertElementAbsence(locator._specificToast("failed to execute")); //Assert that Insert did not fail + agHelper.AssertElementAbsence( + locator._specificToast("failed to execute"), + ); //Assert that Insert did not fail agHelper.AssertElementVisible(locator._spanButton("Run UpdateQuery")); table.WaitUntilTableLoad(); table.ReadTableRowColumnData(2, 0).then(($cellData) => { @@ -269,7 +273,7 @@ describe("UUID Datatype tests", function() { expect($cellData).to.eq("0"); }); - agHelper.Sleep(2000);// Above entensions settling time + agHelper.Sleep(2000); // Above entensions settling time //Validating generation of new uuid via the extension package query = `SELECT uuid_generate_v1() as v1, uuid_generate_v4() as v4, gen_random_uuid() as cryptov4, uuid_in(overlay(overlay(md5(random()::text || ':' || random()::text) placing '4' from 13) placing to_hex(floor(random()*(11-8+1) + 8)::int)::text from 17)::cstring) as form_uuid1, uuid_in(md5(random()::text || random()::text)::cstring) as form_uuid2;`; @@ -348,8 +352,7 @@ describe("UUID Datatype tests", function() { agHelper.ValidateNetworkStatus("@postExecute", 200); agHelper.ValidateNetworkStatus("@postExecute", 200); table.ReadTableRowColumnData(1, 0).then(($cellData) => { - expect($cellData) - .not.to.eq("2"); //asserting 2nd record is deleted + expect($cellData).not.to.eq("2"); //asserting 2nd record is deleted }); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/AddWidgetTableAndBind_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/AddWidgetTableAndBind_spec.js index bbe0aea41120..24cda85b4fad 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/AddWidgetTableAndBind_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/AddWidgetTableAndBind_spec.js @@ -18,7 +18,7 @@ describe("Addwidget from Query and bind with other widgets", function() { it("1. Create a PostgresDataSource", () => { cy.createPostgresDatasource(); - cy.get("@createDatasource").then((httpResponse) => { + cy.get("@saveDatasource").then((httpResponse) => { datasourceName = httpResponse.response.body.data.name; }); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/AddWidget_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/AddWidget_spec.js index b2232e0dfd0b..1291e3140fd4 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/AddWidget_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/AddWidget_spec.js @@ -7,7 +7,7 @@ describe("Add widget - Postgress DataSource", function() { beforeEach(() => { cy.startRoutesForDatasource(); cy.createPostgresDatasource(); - cy.get("@createDatasource").then((httpResponse) => { + cy.get("@saveDatasource").then((httpResponse) => { datasourceName = httpResponse.response.body.data.name; }); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/ConfirmRunAction_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/ConfirmRunAction_spec.js index 04bab49d0939..d69ab67f1d0c 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/ConfirmRunAction_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/ConfirmRunAction_spec.js @@ -9,7 +9,7 @@ describe("Confirm run action", function() { beforeEach(() => { cy.createPostgresDatasource(); - cy.get("@createDatasource").then((httpResponse) => { + cy.get("@saveDatasource").then((httpResponse) => { datasourceName = httpResponse.response.body.data.name; }); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/DSDocs_Spec.ts b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/DSDocs_Spec.ts index a5727b4ac258..383ee7c2b0f6 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/DSDocs_Spec.ts +++ b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/DSDocs_Spec.ts @@ -9,6 +9,10 @@ let agHelper = ObjectsRegistry.AggregateHelper, describe("Check datasource doc links", function() { it("1. Verify Postgres documentation opens", function() { dataSources.CreateDataSource("Postgres"); + + // go back to active ds list + dataSources.NavigateToActiveTab(); + cy.get("@dsName").then(($dsName) => { dsName = $dsName; dataSources.CreateQuery(dsName); @@ -22,6 +26,10 @@ describe("Check datasource doc links", function() { it("2. Verify Mongo documentation opens", function() { dataSources.CreateDataSource("Mongo"); + + // go back to active ds list + dataSources.NavigateToActiveTab(); + cy.get("@dsName").then(($dsName) => { dsName = $dsName; dataSources.CreateQuery(dsName); @@ -33,6 +41,10 @@ describe("Check datasource doc links", function() { it("3. Verify MySQL documentation opens", function() { dataSources.CreateDataSource("MySql"); + + // go back to active ds list + dataSources.NavigateToActiveTab(); + cy.get("@dsName").then(($dsName) => { dsName = $dsName; dataSources.CreateQuery(dsName); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/EmptyDataSource_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/EmptyDataSource_spec.js index 53266091efa8..3c41e06ebd6c 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/EmptyDataSource_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/EmptyDataSource_spec.js @@ -12,7 +12,7 @@ describe("Create a query with a empty datasource, run, save the query", function cy.NavigateToDatasourceEditor(); cy.get(datasource.PostgreSQL).click(); cy.testSaveDatasource(false); - cy.get("@createDatasource").then((httpResponse) => { + cy.get("@saveDatasource").then((httpResponse) => { datasourceName = httpResponse.response.body.data.name; }); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/GoogleSheetsQuery_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/GoogleSheetsQuery_spec.js index c2c5fc2d5c8f..b4b9c9fc7ee4 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/GoogleSheetsQuery_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/GoogleSheetsQuery_spec.js @@ -10,40 +10,41 @@ let placeholderText = '{\n "name": {{nameInput.text}},\n "dob": {{dobPicker.formattedDate}},\n "gender": {{genderSelect.selectedOptionValue}} \n}'; describe("Google Sheets datasource row objects placeholder", function() { - it("Bug: 16391 - Google Sheets DS, placeholder objects keys should have quotes", function() { + //Skiiping due to open bug #18035: Should the Save button be renamed as "Save and Authorise" in case of Google sheets for datasource discard popup? + it.skip("Bug: 16391 - Google Sheets DS, placeholder objects keys should have quotes", function() { // create new Google Sheets datasource dataSources.NavigateToDSCreateNew(); dataSources.CreatePlugIn(pluginName); // navigate to create query tab and create a new query - cy.get("@createDatasource").then((httpResponse) => { - datasourceName = httpResponse.response.body.data.name; - // clicking on new query to write a query - cy.NavigateToQueryEditor(); - cy.get(explorer.createNew).click(); - cy.get("div:contains('" + datasourceName + " Query')") - .last() - .click(); + // cy.get("@saveDatasource").then((httpResponse) => { + // datasourceName = httpResponse.body.data.name; + // // clicking on new query to write a query + // cy.NavigateToQueryEditor(); + // cy.get(explorer.createNew).click(); + // cy.get("div:contains('" + datasourceName + " Query')") + // .last() + // .click(); - // fill the create new api google sheets form - // and check for rowobject placeholder text - cy.get(datasource.gSheetsOperationDropdown).click(); - cy.get(datasource.gSheetsInsertOneOption).click(); + // fill the create new api google sheets form + // and check for rowobject placeholder text + cy.get(datasource.gSheetsOperationDropdown).click(); + cy.get(datasource.gSheetsInsertOneOption).click(); - cy.get(datasource.gSheetsEntityDropdown).click(); - cy.get(datasource.gSheetsSheetRowsOption).click(); + cy.get(datasource.gSheetsEntityDropdown).click(); + cy.get(datasource.gSheetsSheetRowsOption).click(); - cy.get(datasource.gSheetsCodeMirrorPlaceholder).should( - "have.text", - placeholderText, - ); + cy.get(datasource.gSheetsCodeMirrorPlaceholder).should( + "have.text", + placeholderText, + ); - // delete query and datasource after test is done - cy.get("@createNewApi").then((httpResponse) => { - queryName = httpResponse.response.body.data.name; - cy.deleteQueryUsingContext(); - cy.deleteDatasource(datasourceName); - }); - }); + // delete query and datasource after test is done + // cy.get("@createNewApi").then((httpResponse) => { + // queryName = httpResponse.response.body.data.name; + // cy.deleteQueryUsingContext(); + // cy.deleteDatasource(datasourceName); + // }); + //}); }); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/Mongo_Spec.js b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/Mongo_Spec.js index 900fae6df98e..28601908ee63 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/Mongo_Spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/Mongo_Spec.js @@ -264,6 +264,7 @@ describe("Create a query with a mongo datasource, run, save and then delete the it("9. Bug 7399: Validate Form based & Raw command based templates", function() { let id; + ee.ExpandCollapseEntity("Datasources"); ee.ExpandCollapseEntity(`${datasourceName}`); cy.xpath(queryLocators.listingAndReviewContext) .invoke("show") diff --git a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/Mongo_Spec.ts b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/Mongo_Spec.ts index 72e636793ba7..6e8db078df7c 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/Mongo_Spec.ts +++ b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/Mongo_Spec.ts @@ -2,14 +2,14 @@ import { ObjectsRegistry } from "../../../../support/Objects/Registry"; let dsName: any; -let agHelper = ObjectsRegistry.AggregateHelper, +const agHelper = ObjectsRegistry.AggregateHelper, ee = ObjectsRegistry.EntityExplorer, locator = ObjectsRegistry.CommonLocators, homePage = ObjectsRegistry.HomePage, dataSources = ObjectsRegistry.DataSources, deployMode = ObjectsRegistry.DeployMode, table = ObjectsRegistry.Table, - propPane = ObjectsRegistry.PropertyPane; + appSettings = ObjectsRegistry.AppSettings; describe("Validate Mongo Query Pane Validations", () => { before(() => { @@ -49,11 +49,11 @@ describe("Validate Mongo Query Pane Validations", () => { cy.get("@dsName").then(($dsName) => { dsName = $dsName; }); - propPane.ChangeTheme("Modern"); + appSettings.OpenPaneAndChangeTheme("Sunrise"); }); it("2. Create new CRUD collection 'AuthorNAwards' & refresh Entity Explorer to find the new collection", () => { - let authorNAwardsArray = `[{ + const authorNAwardsArray = `[{ "_id" : 1, "name" : { "first" : "John", @@ -331,7 +331,7 @@ describe("Validate Mongo Query Pane Validations", () => { }); it("5. Validate 'Insert' record from new collection & verify query response", () => { - let insertauthorNAwards = `[{ + const insertauthorNAwards = `[{ "_id" : 8, "name" : { "first" : "Yukihiro", @@ -632,7 +632,7 @@ describe("Validate Mongo Query Pane Validations", () => { }); it("17. Validate Drop of the Newly Created - AuthorNAwards - collection from datasource", () => { - let dropCollection = `{ "drop": "AuthorNAwards" }`; + const dropCollection = `{ "drop": "AuthorNAwards" }`; dataSources.NavigateFromActiveDS(dsName, true); dataSources.ValidateNSelectDropdown("Commands", "Find Document(s)", "Raw"); @@ -651,7 +651,7 @@ describe("Validate Mongo Query Pane Validations", () => { }); it("18. Verify application does not break when user runs the query with wrong collection name", function() { - let dropCollection = `{ "drop": "AuthorNAwards" }`; + const dropCollection = `{ "drop": "AuthorNAwards" }`; dataSources.NavigateFromActiveDS(dsName, true); dataSources.ValidateNSelectDropdown("Commands", "Find Document(s)", "Raw"); agHelper.GetNClick(dataSources._templateMenu); @@ -668,7 +668,7 @@ describe("Validate Mongo Query Pane Validations", () => { }); it("19. Bug 13285 - Verfiy application can parse dates before and on or after Jan 1, 1970", () => { - let birthNDeathArray = `[{ + const birthNDeathArray = `[{ "name": { "first": "John", "last": "Backus" @@ -751,7 +751,7 @@ describe("Validate Mongo Query Pane Validations", () => { agHelper.ActionContextMenuWithInPane("Delete"); //Drop the collection `BirthNDeath` - let dropCollection = `{ "drop": "BirthNDeath" }`; + const dropCollection = `{ "drop": "BirthNDeath" }`; dataSources.NavigateFromActiveDS(dsName, true); dataSources.ValidateNSelectDropdown("Commands", "Find Document(s)", "Raw"); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/Postgres_Spec.js b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/Postgres_Spec.js index 8450c4ec5e2c..27c7292c3dad 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/Postgres_Spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/Postgres_Spec.js @@ -30,7 +30,7 @@ describe("Validate CRUD queries for Postgres along with UI flow verifications", cy.testSaveDatasource(); - // cy.get("@createDatasource").then((httpResponse) => { + // cy.get("@saveDatasource").then((httpResponse) => { // datasourceName = httpResponse.response.body.data.name; // }); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/SwitchDatasource_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/SwitchDatasource_spec.js index e5138299ce58..71267d94a997 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/SwitchDatasource_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/SwitchDatasource_spec.js @@ -23,11 +23,6 @@ describe("Switch datasource", function() { .should("have.value", postgresDatasourceName) .blur(); }); - cy.wait("@saveDatasource").should( - "have.nested.property", - "response.body.responseMeta.status", - 200, - ); cy.fillPostgresDatasourceForm(); cy.testSaveDatasource(); }); @@ -45,11 +40,6 @@ describe("Switch datasource", function() { .should("have.value", postgresDatasourceNameSecond) .blur(); }); - cy.wait("@saveDatasource").should( - "have.nested.property", - "response.body.responseMeta.status", - 200, - ); cy.fillPostgresDatasourceForm(); cy.testSaveDatasource(); }); @@ -67,12 +57,6 @@ describe("Switch datasource", function() { .should("have.value", mongoDatasourceName) .blur(); }); - cy.wait("@saveDatasource").should( - "have.nested.property", - "response.body.responseMeta.status", - 200, - ); - cy.fillMongoDatasourceForm(); cy.testSaveDatasource(); }); @@ -91,7 +75,6 @@ describe("Switch datasource", function() { "response.body.data.isValid", true, ); - cy.get(".t--switch-datasource").click(); cy.contains(".t--datasource-option", postgresDatasourceNameSecond) .click() @@ -111,7 +94,6 @@ describe("Switch datasource", function() { it("6. Delete the query and datasources", function() { cy.deleteQueryUsingContext(); - cy.deleteDatasource(postgresDatasourceName); cy.deleteDatasource(postgresDatasourceNameSecond); cy.deleteDatasource(mongoDatasourceName); diff --git a/app/client/cypress/integration/Smoke_TestSuite_Fat/ClientSideTests/Branding/Branding_spec.js b/app/client/cypress/integration/Smoke_TestSuite_Fat/ClientSideTests/Branding/Branding_spec.js new file mode 100644 index 000000000000..c375a1cea7d3 --- /dev/null +++ b/app/client/cypress/integration/Smoke_TestSuite_Fat/ClientSideTests/Branding/Branding_spec.js @@ -0,0 +1,101 @@ +const commonlocators = require("../../../../locators/commonlocators.json"); + +const locators = { + AdminSettingsEntryLink: ".admin-settings-menu-option", + LeftPaneBrandingLink: ".t--settings-category-branding", + AdminSettingsColorInput: ".t--settings-brand-color-input input[type=text]", + AdmingSettingsLogoInput: ".t--settings-brand-logo-input input[type=file]", + AdmingSettingsLogoInputImage: ".t--settings-brand-logo-input img", + AdmingSettingsFaviconInput: + ".t--settings-brand-favicon-input input[type=file]", + AdmingSettingsFaviconInputImage: ".t--settings-brand-favicon-input img", + BrandingBg: ".t--branding-bg", + BrandingLogo: ".t--branding-logo", + BrandingFavicon: "img.t--branding-favicon", + BrandingFaviconHead: "link.t--branding-favicon", + dashboardAppTab: ".t--apps-tab", + createNewAppButton: ".t--new-button", + loginContainer: ".t--login-container", + signupLink: ".t--signup-link", + authContainer: ".t--auth-container", + submitButton: "button[type='submit']", + appsmithLogo: ".t--appsmith-logo", + appsmithLogoImg: ".t--appsmith-logo img", + AdminSettingsColorInputShades: ".t--color-input-shades", +}; + +describe("Branding", () => { + let logo; + let favicon; + let shades = {}; + + it("super user can access branding page", () => { + cy.LogOut(); + cy.LoginFromAPI(Cypress.env("USERNAME"), Cypress.env("PASSWORD")); + cy.visit("/applications"); + cy.get(locators.AdminSettingsEntryLink).should("be.visible"); + cy.get(locators.AdminSettingsEntryLink).click(); + cy.url().should("contain", "/settings/general"); + cy.get(locators.LeftPaneBrandingLink).should("be.visible"); + cy.get(locators.LeftPaneBrandingLink).click(); + cy.wait(2000); + }); + + it("should test that changing logo,favicon and color changes the preview", () => { + // branding color + cy.get(locators.AdminSettingsColorInput) + .focus() + .clear() + .type("red"); + + cy.get(".t--branding-bg").should( + "have.css", + "background-color", + "rgb(255, 0, 0)", + ); + + // branding logo + cy.get(locators.AdmingSettingsLogoInput).attachFile("appsmithlogo.png"); + cy.wait(1000); + cy.get(locators.AdmingSettingsLogoInputImage).should("be.visible"); + cy.get(locators.BrandingLogo) + .invoke("attr", "src") + .then((src) => { + cy.get(locators.AdmingSettingsLogoInputImage) + .invoke("attr", "src") + .should("equal", src); + }); + + // branding favicon + cy.get(locators.AdmingSettingsFaviconInput).attachFile("appsmithlogo.png"); + cy.wait(1000); + cy.get(locators.AdmingSettingsFaviconInputImage).should("be.visible"); + cy.get(locators.BrandingFavicon) + .invoke("attr", "src") + .then((src) => { + cy.get(locators.AdmingSettingsFaviconInputImage) + .invoke("attr", "src") + .should("equal", src); + }); + + // validations - logo + cy.get(locators.AdmingSettingsLogoInput).attachFile("testFile.mov"); + cy.wait(1000); + cy.get(commonlocators.toastMsg).contains( + Cypress.env("MESSAGES").ADMIN_BRANDING_LOGO_FORMAT_ERROR(), + ); + + // validations - favicon + cy.get(locators.AdmingSettingsFaviconInput).attachFile("testFile.mov"); + cy.wait(1000); + cy.get(commonlocators.toastMsg).contains( + Cypress.env("MESSAGES").ADMIN_BRANDING_FAVICON_FORMAT_ERROR(), + ); + }); + + it("checks if the form can be submitted", () => { + if (Cypress.env("Edition") === 0) { + cy.get(locators.submitButton).should("be.disabled"); + } + }); +}); diff --git a/app/client/cypress/integration/Smoke_TestSuite_Fat/ClientSideTests/EmbedSettings/EmbedSettings_spec.js b/app/client/cypress/integration/Smoke_TestSuite_Fat/ClientSideTests/EmbedSettings/EmbedSettings_spec.js new file mode 100644 index 000000000000..3fc3e281e084 --- /dev/null +++ b/app/client/cypress/integration/Smoke_TestSuite_Fat/ClientSideTests/EmbedSettings/EmbedSettings_spec.js @@ -0,0 +1,145 @@ +import { ObjectsRegistry } from "../../../../support/Objects/Registry"; +const adminSettings = require("../../../../locators/AdminsSettings"); + +describe("Embed settings options", function() { + const { + AggregateHelper: agHelper, + DeployMode: deployMode, + EntityExplorer: ee, + HomePage: homePage, + } = ObjectsRegistry; + + const getIframeBody = () => { + // get the iframe > document > body + // and retry until the body element is not empty + return ( + cy + .get(".t--widget-iframewidget iframe") + .its("0.contentDocument.body") + .should("not.be.empty") + // wraps "body" DOM element to allow + // chaining more Cypress commands, like ".find(...)" + // https://on.cypress.io/wrap + .then(cy.wrap) + ); + }; + + before(() => { + ee.DragDropWidgetNVerify("buttonwidget", 100, 100); + deployMode.DeployApp(); + cy.get("[data-cy='viewmode-share']").click(); + cy.get(".t--deployed-url input") + .invoke("attr", "value") + .as("embeddedAppUrl"); + cy.enablePublicAccess(); + cy.get(".t--back-to-home").click(); + homePage.CreateNewApplication(); + ee.DragDropWidgetNVerify("iframewidget", 100, 100); + cy.get("@embeddedAppUrl").then((url) => { + cy.testJsontext("url", url); + }); + // cy.testJsontext("url", this.embeddedAppUrl); + deployMode.DeployApp(); + cy.get("[data-cy='viewmode-share']").click(); + cy.get(".t--deployed-url input") + .invoke("attr", "value") + .as("deployUrl"); + cy.enablePublicAccess(); + cy.wait(6000); + getIframeBody() + .contains("Submit") + .should("exist"); + }); + + beforeEach(() => { + agHelper.RestoreLocalStorageCache(); + }); + + afterEach(() => { + agHelper.SaveLocalStorageCache(); + }); + + describe("Wrapper to get access to the alias in all tests", () => { + it("1. Allow embedding everywhere", function() { + cy.log(this.deployUrl); + cy.get(".t--back-to-home").click(); + cy.get(".admin-settings-menu-option").click(); + cy.get(".t--admin-settings-APPSMITH_ALLOWED_FRAME_ANCESTORS").within( + () => { + cy.get("input") + .eq(0) + .click(); + }, + ); + cy.get(adminSettings.saveButton).click(); + cy.waitForServerRestart(); + // TODO: Commented out as it is flaky + // cy.wait(["@getEnvVariables", "@getEnvVariables"]).then((interception) => { + // const { + // APPSMITH_ALLOWED_FRAME_ANCESTORS, + // } = interception[1].response.body.data; + // expect(APPSMITH_ALLOWED_FRAME_ANCESTORS).to.equal("*"); + // }); + cy.get(adminSettings.restartNotice).should("not.exist"); + cy.visit(this.deployUrl); + getIframeBody() + .contains("Submit") + .should("exist"); + }); + + it("2. Limit embedding", function() { + cy.log(this.deployUrl); + cy.get(".t--back-to-home").click(); + cy.get(".admin-settings-menu-option").click(); + cy.get(".t--admin-settings-APPSMITH_ALLOWED_FRAME_ANCESTORS").within( + () => { + cy.get("input") + .eq(1) + .click(); + cy.get(".bp3-tag-remove") + .eq(1) + .click(); + cy.get(".bp3-tag-remove") + .eq(0) + .click(); + cy.get(".bp3-input-ghost") + .type(window.location.origin) + .blur(); + }, + ); + cy.get(adminSettings.saveButton).click(); + cy.waitForServerRestart(); + cy.get(adminSettings.restartNotice).should("not.exist"); + cy.visit(this.deployUrl); + getIframeBody() + .contains("Submit") + .should("exist"); + }); + it("3. Disable everywhere", function() { + cy.log(this.deployUrl); + cy.get(".t--back-to-home").click(); + cy.get(".admin-settings-menu-option").click(); + cy.get(".t--admin-settings-APPSMITH_ALLOWED_FRAME_ANCESTORS").within( + () => { + cy.get("input") + .last() + .click(); + }, + ); + cy.get(adminSettings.saveButton).click(); + cy.waitForServerRestart(); + cy.get(adminSettings.restartNotice).should("not.exist"); + cy.visit(this.deployUrl); + // TODO: Commented out as it is flaky + // cy.wait(["@getEnvVariables", "@getEnvVariables"]).then((interception) => { + // const { + // APPSMITH_ALLOWED_FRAME_ANCESTORS, + // } = interception[1].response.body.data; + // expect(APPSMITH_ALLOWED_FRAME_ANCESTORS).to.equal("'none'"); + // }); + getIframeBody() + .contains("Submit") + .should("not.exist"); + }); + }); +}); diff --git a/app/client/cypress/locators/AdminsSettings.js b/app/client/cypress/locators/AdminsSettings.js index ebfc82bd16d5..b0bc515d877f 100644 --- a/app/client/cypress/locators/AdminsSettings.js +++ b/app/client/cypress/locators/AdminsSettings.js @@ -25,4 +25,5 @@ export default { disconnectBtn: "[data-testid='disconnect-service-button']", formSignupDisabled: "[data-cy='APPSMITH_SIGNUP_DISABLED']", formLoginDisabled: "[data-cy='APPSMITH_FORM_LOGIN_DISABLED']", + embedSettings: ".t--admin-settings-APPSMITH_ALLOWED_FRAME_ANCESTORS", }; diff --git a/app/client/cypress/locators/FormWidgets.json b/app/client/cypress/locators/FormWidgets.json index 1ec89366f04f..560c31522f78 100644 --- a/app/client/cypress/locators/FormWidgets.json +++ b/app/client/cypress/locators/FormWidgets.json @@ -28,8 +28,12 @@ "Textinput": ".t--property-control-options .CodeMirror-code", "labelvalue": ".t--draggable-selectwidget label", "dropdownInput": ".bp3-tag-input-values", + "selectButton": ".select-button", + "multiSelect": ".rc-select-selector", "mulitiselectInput": ".rc-select-selection-search-input", "treeSelectInput": ".rc-tree-select-selection-search-input", + "multiTreeSelect": ".rc-tree-select-selector", + "treeSelect": ".rc-tree-select-selector", "treeSelectClearAll":".rc-tree-select-clear", "treeSelectPlaceholder":".rc-tree-select-single .rc-tree-select-selection-placeholder", "treeSelectFilterInput": ".tree-select-dropdown .bp3-input", diff --git a/app/client/cypress/locators/Widgets.json b/app/client/cypress/locators/Widgets.json index 5bd3fdb7f033..c96d7e80243d 100644 --- a/app/client/cypress/locators/Widgets.json +++ b/app/client/cypress/locators/Widgets.json @@ -124,6 +124,10 @@ "toggleDisable": ".t--property-control-disabled .t--js-toggle", "inputToggleDisable": "div.t--property-control-disabled div.CodeMirror-code pre span", "toggleOnClick": ".t--property-control-onclick .t--js-toggle", + "toggleOnFocus": ".t--property-control-onfocus .t--js-toggle", + "toggleOnBlur": ".t--property-control-onblur .t--js-toggle", + "toggleOnDropdownOpen": ".t--property-control-ondropdownopen .t--js-toggle", + "toggleOnDropdownClose": ".t--property-control-ondropdownclose .t--js-toggle", "toggleChartType": ".t--property-control-charttype .t--js-toggle", "inputToggleOnClick": ".t--property-control-onclick div.CodeMirror-lines", "tableBtn": ".t--draggable-tablewidget .bp3-button", @@ -189,6 +193,7 @@ "selectedTextSize": ".t--property-control-textsize .bp3-popover-target .sub-text", "colorPickerV2Popover": ".t--colorpicker-v2-popover", "colorPickerV2Color": ".t--colorpicker-v2-color", + "colorPickerV2TailwindColor": ".t--tailwind-colors .t--colorpicker-v2-color", "modalCloseButton": ".t--draggable-iconbuttonwidget .bp3-button", "filepickerwidgetv2": ".t--widget-filepickerwidgetv2", "filepickerwidgetv2CancelBtn": "button.uppy-DashboardContent-back", diff --git a/app/client/cypress/locators/commonlocators.json b/app/client/cypress/locators/commonlocators.json index c5a62b979e02..3a7c2d7fd5bc 100644 --- a/app/client/cypress/locators/commonlocators.json +++ b/app/client/cypress/locators/commonlocators.json @@ -99,6 +99,8 @@ "chooseAction": ".single-select", "chooseMsgType": ".t--open-dropdown-Select-type", "onPause": ".t--property-control-onpause .t--open-dropdown-Select-Action", + "chooseWidget": ".t--open-dropdown-Select-Widget", + "onClick": ".t--property-control-onclick .t--open-dropdown-Select-Action", "changeZoomlevel": ".t--property-control-maxzoomlevel .bp3-popover-target", "selectedZoomlevel": ".t--property-control-maxzoomlevel .bp3-popover-target ", "imgWidget": "div[data-testid='styledImage']", @@ -166,10 +168,6 @@ "debuggerContextMenu": ".t--debugger-contextual-error-menu", "cyclicDependencyError": "//div[@class='Toastify']//span[contains(text(),'Cyclic dependency found while evaluating')]", "openDocumentationfromErrorTab": "//span[@name='book-line']", - "appNameDeployMenu": ".t--app-name-menu-deploy-parent", - "appNameDeployMenuPublish": ".t--app-name-menu-deploy", - "appNameDeployMenuCurrentVersion": ".t--app-name-menu-deploy-current-version", - "appNameDeployMenuConnectToGit": ".t--app-name-menu-deploy-connect-to-git", "selectInner": ".t--draggable-selectwidget span.t--widget-name, Select1", "toastifyError": "//div[@class='Toastify']//span]", "selectButton": ".select-button", @@ -190,7 +188,22 @@ "propertyStyle": "li:contains('STYLE')", "propertyContent": "li:contains('CONTENT')", "cancelActionExecution": ".t--cancel-action-button", + "menuButtonMenuItemsSource": ".t--property-control-menuitemssource", + "menuButtonSourceData": ".t--property-control-sourcedata", + "menuButtonConfigureArrayItems": ".t--property-control-configuremenuitems button", "codeScannerScannerLayout": ".t--property-control-scannerlayout", "codeScannerVideo": ".code-scanner-camera-container video", - "codeScannerDisabledSVGIcon": ".code-scanner-camera-container div[disabled] svg" + "codeScannerDisabledSVGIcon": ".code-scanner-camera-container div[disabled] svg", + "layoutHeightDropdown": ".t--property-control-height .remixicon-icon", + "fixed": "[data-cy='t--dropdown-option-Fixed']", + "autoHeight": "[data-cy='t--dropdown-option-Auto Height']", + "autoHeightWithLimits": "[data-cy='t--dropdown-option-Auto Height with limits']", + "minHeight": "minheight\\(inrows\\)", + "maxHeight": "maxheight\\(inrows\\)", + "overlayMin": "[data-cy='t--auto-height-overlay-min']", + "overlayMax": "[data-cy='t--auto-height-overlay-max']", + "addOption": ".t--property-control-options-add", + "showTabsControl": ".t--property-control-showtabs .bp3-control-indicator", + "checkboxIndicator": ".t--draggable-checkboxwidget .bp3-control-indicator", + "generalSectionHeight": ".t--property-pane-section-general .t--property-control-label:contains('Height')" } diff --git a/app/client/cypress/locators/gitSyncLocators.js b/app/client/cypress/locators/gitSyncLocators.js index d84a95b72da0..cb71317f56df 100644 --- a/app/client/cypress/locators/gitSyncLocators.js +++ b/app/client/cypress/locators/gitSyncLocators.js @@ -58,4 +58,6 @@ export default { regenerateSSHKeyECDSA: "[data-cy='t--regenerate-sshkey-ECDSA']", regenerateSSHKeyRSA: "[data-cy='t--regenerate-sshkey-RSA']", confirmButton: "//span[text()='Yes']", + mergeConflicts: + "//span[contains(text(), 'There are uncommitted changes present in your local branch master. Please commit them first and try again')]", }; diff --git a/app/client/cypress/manual_TestSuite/CommentedScriptFiles/MsSQL_Spec.js b/app/client/cypress/manual_TestSuite/CommentedScriptFiles/MsSQL_Spec.js index b9c4ac3171b5..3551f38ee250 100644 --- a/app/client/cypress/manual_TestSuite/CommentedScriptFiles/MsSQL_Spec.js +++ b/app/client/cypress/manual_TestSuite/CommentedScriptFiles/MsSQL_Spec.js @@ -40,11 +40,11 @@ // // cy.wait("@saveDataSourceStub").should( // // "have.nested.property", // // "response.body.responseMeta.status", -// // 200, +// // 201, // // ); // //Verify page after save clicked -// cy.get("@createDatasource").then((httpResponse) => { +// cy.get("@saveDatasource").then((httpResponse) => { // datasourceName = httpResponse.response.body.data.name; // }); diff --git a/app/client/cypress/manual_TestSuite/Modal_Spec.js b/app/client/cypress/manual_TestSuite/Modal_Spec.js index c61db5c3552a..99f967b0a720 100644 --- a/app/client/cypress/manual_TestSuite/Modal_Spec.js +++ b/app/client/cypress/manual_TestSuite/Modal_Spec.js @@ -1,7 +1,7 @@ const dsl = require("../../../fixtures/ModalWidgetDsl.json"); describe("Modal Functionality ", function() { - it("Collapse the tabs of Property pane", function() { + it("1. Collapse the tabs of Property pane", function() { // Add a modal widget from teh entity explorer // Click on the property Pane // Select Form Type as Modal Type @@ -18,7 +18,7 @@ describe("Modal Functionality ", function() { // Ensure the modal pop up }); - it("Rename a modal", function() { + it("2. Rename a modal", function() { // Click on the entity explore // Ensure modal is dispalyed to user // Rename the modal @@ -26,7 +26,7 @@ describe("Modal Functionality ", function() { // Click on the action button // Ensure the modal pop up }); - it("Convert Modal to ", function() { + it("3. Convert Modal to ", function() { // Click on the entity explore // Ensure modal is dispalyed to user // Add a button widget @@ -38,4 +38,13 @@ describe("Modal Functionality ", function() { // Click on the button // Ensure a form modal is dispalyed to user }); + it("4. Does not flicker when 'Show More' popover of a truncated text shows over it ", function() { + // Click on the entity explore + // Ensure modal is dispalyed to user + // Add a text widget + // Fill the text widget with long text + // Set the overflow property of the text widget to "Truncate" + // Click on the triple dot icon on the text widget + // Ensure that the 'Show More' popover on top of the Modal does not flicker on hover + }); }); diff --git a/app/client/cypress/snapshots/Smoke_TestSuite/ClientSideTests/VisualTests/AppPageLayout_spec.js/apppage.snap.png b/app/client/cypress/snapshots/Smoke_TestSuite/ClientSideTests/VisualTests/AppPageLayout_spec.js/apppage.snap.png index dc4438391db537c3047f0746eec787d15ac690d5..6b03585b9ba026d8948c7db73fdc72afd3bc1200 100644 GIT binary patch literal 59581 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νdw|EYGL*Kv{^VQ=X5P5U-`d~=|{qAr6h8Lr`7H&d$h zBQ@oOt4fayOyktDue5gEd93rK1m?<^-;uK+P@Crh%lM&hpE(0Wn3{i<9E8!!96sa8 zf8A&O>J`l6+xgxc{%QgTbRntEplpA+PiUMf))Txn?;G1Z2c+RfZuPk!&Oro%OF+m0 zr()46+2)tyoP&Vi^Vjd(I$wog2FYTGC4YR-{ASP7HzyClpqr<$A;1eziEqFIdt_kY zNV(&$wW?Y?RNy){vOd2HEO=s!tPh*R(pYwq$X@a5sH5bJA1l z{)F&=gs}b!v(Axg_R#&PpPQ~Hv%OEDB-^~sV?xn=rr&tv2!5o9OXY!iE+05`7yM&= zpv-W^mdPTP{8yhp^Y7lhn@6s`3*&CRMQcRNU=P7pmTtL@y>zUd#@_Kji%fkVq%vo*37Gh)g0eLcROX1cXn(88%zu~olT1YGd$1s z@Y*{++@h33fO~^EC<&Gd(ME8Z!$n~(>Y)C%WKI|f1O1T`0>-8m$xW(oUe~?;8 zT%r1XLqql_7U`-nM+C`R&z?K$AiBDg8yl!4?B+_b>NNgyXG8GD+_Uk)cJ~Sa11KVd^}B6=$5mqr0Vh<7C5GY$A?t6S6UK#Wp9z)q;sS) zhDJ{4@o6F&$ASB|pWOTQ#|DIYyF!!F69cm)!Zi?C>U`q|L*R~{5gF{O_a`NsC7JwI zSX7yb^T0^^dNpw#77PkW(iNifi}BG(tTaFXiD)MjKiZU)VcR(-F^#~`;YrZs(w&mn z;8QUYY!zlE&PE;^5_kmWu2Ze;y(W31k3RG=b4k|4(w^@$DEnTV*7wvpF))$sPjVNZlc+*RbJ zQDnHe|EiK8-qo53b|>`U>i&m+a1or`i|0y~i2lGMts34SM-_<;Ye}U4NK3%tKsK%K z{`AViCpy;eP94?X9)+#KEmm1aNWZsCI4haI$NgB(M31%qPJ z6H8{@wmffL0yAMFj33Q)SZKY>ZS1T08$D8Gn#q6tq+~b`g0L1Xf#7Ts54q&1!+_u$ zJ{fLN#oPbwP9pmPSX&g9PPu5=wtK@2303oiSBr)p8&|m-Uwx!%y#=1SZtFOGKpzN* zUw{d7@~=Mma)p@&g*T0jr5M4klPZ|hpzTJXe4cL({Xvjojl5yXI$df!y&`I|#*)m{ znzDBO&hiQDf*sS0zW;Nie7*jd!5+Hh1h^Q_AAgCBW8q{NO#WyCx;8>=-ylJxU`UM4 zEuu7^bBZ)RIUB>z-61W|OKD+tLV(e+yBJmo0XKVjp#=6cT&8%q6&p{CC`Fi_%bS~e z@43Mb8m;51(gIZFj-oVy`-{60SXqGw-I&;ZRR-!c(LO~!?cyPUrmXDXFsp&ZC@>e@ z3L?JcdnycQicPRTRFq6B3Y&N31tXln*0gCY6jet~Xv*ryzk1U?x%w?S@aSjnJ=Qr@ zvbuC>w(F7xc3zq-h^dg_sy*6Uqbh^Dqvct>#l70(pqHs=I3XEUMIerl3~}l8nA_RN zBa711)f>ph@(sOL+F@a0pMoO_VJ`9p+tz9Fo0VP-FZSR)b4nOBmV8Q+P(U zR-J%BaR`p(N2AvvMuOZ4GDpJ0hz|6%0W7QMPvUuaCL5c(O2Sda5^HJqxjaF#0f68l z37#GwJ@swV$7-8r;c+@ijIbea3>MZ|t%xTT4=3v2+`_lNiuq`y-art&UEp!@_M49@ zLk6=d%_i)MM7gP#(t7@UNvN8U$Y(%nC)wY9W03Ln(qSKX^}G*p!i zmo$Io_pVC3$m2LE7iV?*v5p}7L4hXT!=b`Ej=Nk4Qw(x6WC;2qo*on*?~((8>az_c zAM$S<8xMga$L96LGd8GdFt^yI8LS%HU`1OpBF7MFv$8c)^|}6bSUB%0Q5x*`)Xc#h zV)6%t!mA-olS)y(tjo0T#?d6FVYnb-Y2RVAexmp3@L7x0) zOxx)l^73^C2=hp&O}8A+i#DV(a}%J6Ku&7s%tsAu4BCv^a-rvTeL94Qp8@YDKydCR zPVO}gB_+!-nkhHvKAuY-(7t;fDmHqelQ`iXKDWJS+)aNwM?oVcZr{t@5POPNvov@T zB$=8}srnlS(a$)Tq;D>aBkIbfoT0UaMse)h2SvW{!c3vBMprWj;Yat-Ii|d23&s() zv9?|V z0Z)|Hu~iMvvC(hbx;diGv}^SZe+i!fU}ku7Z$oJYGV3QkOXJ|_;)5M*dJ$hjznE$D z3Ic8*Y*&&D?)ej!fH*6<`7lqA8hjMiPR_8@pPPiS9|kVzl6;}k%wCHYld9vbCBvRj z3v)ksJ2vmD?GD9EHg_pNiW#5QdnqCZlY>SQfPBBlb&{2{8J`*;tJ*m1644%>L zqgTxJ7GN4dYOCJxpmA6rI6k3C&R^PbeVi4ZZZ(@_1W+T7X5cLqBX^FC10vDnyMHkK z@p*a#_wj&gm6G)5BL}|Fun45Kh=qddktc;zck&*f{!ol?h-n{UBnH1JX6;Cl9?|Lu zM_D3SY`4Q-BAs2S0DPZ@A=$Bf0PbFf9U9?wRvd~Ky?lbI*H_^L*a-U#PT|a7HE)%4 zJ21uiVTaOFgJ*|?kEy^?xwe_l?J##(9I||NR-DiN(4LXaCl#aK6|D~T?BPdH zDGH1`#Q#IxERF3~@c51#kGqIanD)U*Bb)Kp3ee9egI9gv;i7PJ|FUGG_{LF(2670G z2$qg^ORPdw1QW*DsRh(8eQ)Mj{gz7kAQm?L4C4n9ct3 zz=f8Vr=+|E18ZMrJ->wm3Tw=N9RZqwu=3y?BZL68JKiAiu{{o02r)NeGsGId^YJ*V zA7&R=L`>g?KG1qb%1L+%(YgIdd1$HQhA;J_xEl^6pmGP#-$8e%Eyb zbrisW#iAhzNDam0_tZGpAj7NF;!?1|D2!cQz!kYqQpp`B z&(}o(TSFpmB>)HhPAOz{!PUQSkNrvMfeQ|X-ug)g)8FvWC{nyk0a1MU`jtDdmXLJS z3vVH!Q)}M7el5@ZbBvQ4i1$FIreM#oI{byd@gOISqIyGjMWeDKe+;#2fv;^mAY)5F z0XPMB!#nMLW2g&y0v)yGjw1ld8~~rTCaASGZ^cI`Ka*!@kno*MVvaM1@vKKiB&N>T zni*m@eeaf4D3I?Rz|(N(eR_sve%%`zI-*~T;O0{-JMI45w)wLFU560^gxxxcp5`sh znWY`?tTUm;x3z~e?NGLVsezZ**qio(<*@cE=B<4pT;P$a9+PkFQhT4Krz;_JrY`nL z4WLKlbg$N2`iR$(guB&FrJ^)ir>_@R&i`hqjk`S~lfdMCHPA66IpS_{;Rj%@$i{#a z+%pgOF(Y|m%LyJl8>%~w+U%<_wQU>T2N8xB&#Ke8dPu=Opf8fZz!iH|U%6!cNSz5s z3|Kk)R!`GE6C05v09$+g@-bDwY3ic>kQl9}o8Z0q&)E6KV9Ts31|S?W&9n1+{S+v z^9LFC(W7#kE(F~ddv>bUOemVr>%Ex&EWF8V50Z2kki5Hj-m_K}|ccz%^h zt$yInQyTA|?)2SvSm~wF*1*c!YLD>N;)S>NzMOw6&(}sDk-wUI3#ay4w9XzX8S`G6 zKRLdnUn<1LHCRK^jt;oz{4LPyu`b-PnGs5Bz*ZJ=rVpi_I@T=G@Ke zRNtlvdM_I8$KIB6`my?v*_;?qz`b} zMx*H3zIDiRV=G=+QKtN9jZ~)UL_T&;-t7$i?M0$WX8GxhudW_xnLqOvYBTR&I%X4$fc;k01-*3rxBQcm?9w2K9Sv7 zwas_;#HXvSzv99}2A%+MA<4L;tZY@UrbieAbXglDWKOrqesuWqh-sIskK4$`_D}uB zQyU6QSpkj@b)N9=7}u01%H#nPy;AvwO$B2+Eq3*oi=624WMOCpP@d&FmM)j$w(Fkx zkVnCAP~J~lyDj$*ZV~7B-LQa%o!2uZ?`wNpF){(akOw!Je+w0U73>jAz>kiO(oMB! zdQ>GOJFoC9-|TkxCX12EU}q^0iaNf4XWuroR)@C_Gh66?Xi~08Y;vj3ZLbBFC`zS9 zYPoH_{-DO58NA3L<@T!qm6FxSgt81>#Zrfc9NcG`PvrGo-}b_!Yk!^Wlntd(&C@`* zyj3_qBosKmFL+hMPd&>IbE@BUYuwOnXG4#87WyAYE*I{3HdCRTV!JV5vTSi*C5h6g z+O%#MR<~{4;F#jt-q*!3!(VWro1S1HKfL8jMn@$Gc=dknXMytBdcz?k8UXvg-#76+cok;(=%R&uiK0Y zbAI+`_s$IV-3@+uqgv9#cV2w=+E z!ojGSde4n0WOrdsdb?k{!laqeyjsE@8%N3&N@_(s=<7(61*+EeW2cOY=@WI3d96ki z%rG@R*thL?rhTuTB{X$3dnOnUat788_m^*en425E43^oePfWXd+;^b8fFoeCVqtN+ ztt!mY^M;={Ox&8ASa)#u&m;Z{9m@gNe&!Ey?y-ueZ-sgQFvpw89`_Yw`i08M%gN=1 z0!^CB{;`(=UdHL}a)sU-JLUAxg|_w%#cJbvXgi8YY*vt`}w~drYl1&(@xJ^pom@OPIG$e~ zUZ)UMGIzGq*Y&I2+&w5U+#Pi7KaLKS!13AIc6(0L+$gCk6wo6WjE-gP(4I>(FEJX8 zVHt{(DhMr1Ot!#x)HOAoMqm9=9%a82=;ps&K9FX*#+N1@|+s1(?F6PZ*OD&^k?I_pjNxwoAnkcIh(;qN5#YbGX_cGSy< zhaw>16>&jmXnP{gM)H7UxH%wsvRQ^&KbHOYAqozPo=TE)czKZ`t*?Q6y;%9?>OdDX zVGL8j49rcwRwOMeUGI~+Y+(&NJQTP`0x-lr|5bC`zw5Sc`uomM{q*-d=*`gVv1Ks^ zJY`gg;o4@jicjA{qr}*G<7sY=nTc}Wnc+gJ$#)u3Vh!(zKYrcXdf;0e2$-Uij)z*= z*xdN|(TJijQ>i&I4ZKXy>(s?@kh78af_ zDsA$9>^&$qq(mY6WTwfWnE0x2G`vvj=G(AwK=03>iFc@?c%4(>WFKRHe}7kLIeRe` z#MlQM%EcPCfSz?lUsSz%{C#)X!3*QLdT*P`gHZMT%ei>1>({t&pkc%$OBM8N_o+Fy zf7F;RfF(9M+D+)|z;DSV+I3B-A=QYue@zAdQ2+K<>eZk`9kF0hnuH^QJm(ZP4`p$j z7Lza9^X0qef%ag^WWY)^QeCnNV;4Tmgo3A1v~H}fka+NgmvvZulz59tUxsinj30Uw z(gmoOl8v9|EcUFQd)THm`vldtV@aJY{{$6bfaTNm7^-hcuujKTV;4~kzdaU4?;_$jIX#_f^3tnW+Ucn%k z1@xQv?mV^j{nLy;{FYUc%K23vA)$pv$TXArO8P4YcG`Pg(r^dt<$IKvH!o}*y+cTR z!fTPi2jaiel@ap{;tsAu#haNH`b*#-tBmS=8L4r~mipRQ&gz z#IFG>{zasWS!m%SLs`y!Z@%nP5xp44_PxZjSI&|mT5bBy2T@85gvpEOzfo5| z;Fu^@m{#=PJ*KN<^q35)lU`VhR?v8Z=U=3L=i(ui$~@gxwasW~H4s7SrD$k<@%2ff z3QCBc5B+7Y-$KJGun545g(wD>WB9)gp5;I%7{B^@h?^l|0wgLTi}C@XV*kKEPhiwE zkMD8l;hgP%KQudD5Y-GNnksa&RuYkkexq?&3hIDZIc>opIu|aG0npIWVmyP7TqlL_ zgQnt}P8hr=ygz?0?XiD0;be2UJcu}KZrHhxgd5lwu5iT~#iPTAY=*DL3N}=y`JjCG z{+VGlp$k{ zZf6ZG09FFCR0mvgQes01{3lBOq;(2L8n~tB-pfRc7F~9MMBU^vawO~#baZ!Pg0bV{ z#X)mi78dAEb&vD2fAYGc!ol;|zOT6+``LGgiFjBjZlVk|EY0~ua}3*@>}m>VUqx3{ zr%k+UpW03OqIP{8qD9RB7y@0u95Rj*_V%~&_~xta`;`i2n-88uDJNDP_MYe^INmCymC!2L+Dpvzm95$d{j9?_8PQuKSEqa82_Hu>3 zRnnXcRP;=Te7&!#T15YulUB2|TB!1^xUIGRy$c0l$B$ZOX}CA9Y6`^U?(jSTW%KV5z~e=rOAh6)wYVF9#e&-jv{}>v{#6TuB4-3MGBVWf%LWl^1m=p@?=fq9DcZ6=5}Ys)P*c&+FfZ|; zH?V=g3}#ME!D!?lMn)5G=(>6a2Q@*RLsTqzc&>3g%+3xa6kJ4=1_Z{fb_F+~HiI-( zId;n;ZSVamkOPPkUR=V!IYtONFhhCL#-@8>s4EYIR}wpSl0DxLaP-(QMmoAzU``Yz zRLEdS!JM-*sMS|dQ-6xT08%ONnERYLvN&<84W@ikh$#sH)dwLJh}DMlp&*IzhWO~{ z%b@CFU}7o(-M6ZyW_ikqa$5PBQEjhv9CgUPPM$nTv>3pj%SNUR!G{IGC+}#Fx!G@! zJO@R{r=y03wRqtKPUX<}_*NO2P$IV#Sb44EmdErK@DmY}hfgJHf7;rNST0T}DHgxd zA`IA9w-7Q;j17PgmlNzBV8_Qji<0YvJdMNGcLIMiqY)-q%Mtu|dHK!BAaW4{Q}^)5 z2oZe1q&Noou?l{z1AO2cWZPi@rg$+B6s1Ba6l4C1&d$U;Po<5Uw^g^Gz?A@YY9 zJ9fqp$`5_?A0YVW`1o{CCt!plst`&a?ktS2eUShKnhK~Knwo+Uw+umc!wK{I zARzq#CMm+8hbc5X88AI_LRbfidNk1MBV;s~t~bG@_ra1A5&%rx>W^TI8f(`XmYA*vZqUEx;Q@?g?AS6t;lS z>N)pxchfO5mw`D!6f_H3kYfU;+g^zH5k@LVWt2=FIeL`L0MUSGhP1|jFp6;460JiR zhEIcow>q$Zgl_EBp+krMJbaj*@J@(|(t!bUUdybUusDPFs*^B86Ark8QSvucj{kG? zul(l82ZvfD$VFd+C2_-s4b9Mq!gF^nEiWfad2W|Rf!-loBCT((VG zIs}RUgsB#HR0}pVHEawom_2$nHdPZ7P9w0T_LXmdO35537$-q%uL-dZSfgv+-do<@ zTO?PAw+EvosIm0Y%vKQ%$9@Eb(D7O&0w%5l^78T$5)xn>5}lZsczvgoQfMQoZEReh zP1I5l`cuHZ9tXQj6axv*_pe_UFr~cxx4P2WwQHZFMP@VXQKR)EUJee9ys!QJ7!ZbR zMBeFwnRB2Vo8SUFg2zNL$Go_OB`|F4$2=mXL5Qbu&`nKEF_Z~T*~`)RQGZ|4v@Xa> zdL9KEaf1{P)Jp+iApzSJn}WL>w{7X78(9sq6G-=ngX}F2#?A#{IP3-&C+8oE*eKgE zp^UY{P-&6iI zGX<8x#KdGm_=llqBaH9j&S^m79^n@iMYtiKpZPUvwRGvyAFF9;d;NEP|AhZdfCCbc z3&tAj%x$|!;c~_q7mB>czU!t`XMew6aF+ys$!q7qNN3d>;>PD3KI)y zHhh;*b%KNSQ&*SbDO?Xuj1UM%K(%Il>J&*SK?#=(GO>|Ck2!L2uiw2ZA;LFA#tpV& zA{BSX3jI%vs?dT16cSt@ZUu`uEHX$1OJTc*uHU>_T3>$)t*Y9`jwwS;gh&n(Vs2@d zVIC4dK)v<1TF#I@a2q0P4CPW*Yinz2T3QgBalO@9-09v~$jNZ1@Q5O8!BuI~mJpsPDJv67u{2=TgnXTb zhy%TQe0Z9PNJv0m7MsLrY9I@|7ZRke4GFV@H2ZwA+iyHq1pgWeu2I6a0>@emYl}}M zBH(YsK%5U59U?{vp=PjY5K5IM(iR6UHZsErU9sgWSBk?1!a-Dyw9^GdJ?iWX9R1Ap z`@|Tq=S!|h5)hVGRZ@~3=zvk@<>&VSIT1YOkD;MLO6sQ!WZ;DJo{+I@*tik)3ujsQ z9*#ZMmD$g84dNrrtb?w=K^T4n-vncqZ6S>1(WCodl=uRD-@kkJj@WJ3=P6i37+Y#; z>IXaV{Ignb78EFO+tw}~U{z4!jDXq~&PSNf*x1;eUqL}3UlWp>ClBC9LFmIte#w$0 z2K=CNskP`bz#asbS-_ad0t-ma4MZB9eSPKGB_$*)+#2Gz{h3c+SZ1v(OJ~y}Fzvw=GNn2oR zK>QnQzHnu-&)ueI-@Kt={S>=%)v9HmPttDwOZn8;z%C`_9VV9;1N{D31?OcerN8jw zTP2h1axZTAb90&g(T5iW`cvBT6@8ws_}{zK#ZX}s1C=fY>T8(g4qS8|e3_9q{!t%H z*D6Pku2(24ygK#yWL5RH=G^f=j6K|z2Pe`7C;H#HW2c+)Nl)oR_`**KIHzM^06LzU zEh2VX4Lz%mDj%#nhe%LUwNlFG4AWvmV>!3#$#?d5g)hg zc5Dm?OuwIWT;b8PXS7#g5RVzPM3t0mOKF_}FOrhd=-zIvMFxPk{$TRnJv@BQu|Z?Y zM76O|^p5K4*3u6d{RxSQ#kWJ6qxzFM)NH=>{L2L>eRy+mFsT$87J*-C&~IJiwU)ya z45576ZI`E}25))$Mt9+%TPZ^_sOWZt+_k#LF)??(ebWHN_L|~rtIh{RxUS{i6!g!% zBU;&KdGG#9Li*osJ-^Z<_N9B)Pby zyCcTF@4jS;0K@0Z{>6WdJ=)W={_b5@@=V0U-u(NwP_Zqy2frleTfN026QDEDmdjC$ zto^czMxtbQt%iv%@FN;yIe2&{H5tTv9E+1fR06c^8F4k$?$zW#R)_R}*C;H>qT)}{4c z_VN>V1Pg z)Nx=~1NG&Y#o|*}a=){Ca`Jh%V|H`MeU1Cop3$){Ldd^7->MJ=V>inJ`Aip=6|~Bt z#K0o;2@4M=38~hT$I==mCQ>lY3P(?z*g{f>4tx95p1wg|p{H9-O?PJ>S-sjHPRrN- z``^OzAg8?>9A5pTM*V`alKo(bFYdv_+3dZLe%jR33GjFH4g_rz5D>N~avKkbxU=_o z+S?^C=_z)DM%OhXUeVFBRbDDlf@eReiFv+VI!g}^=AXO@-iN6K_|cx1fuT<$9BAAP`MK~B12;bIWm1$=K? z20f~kNt4NQzcerWT-|50Vudmh+74Bilc7mGR-mZ!em@PC9@AeCAz|a;HN5#BFKMVx zVa}K^Znc1}tm38=ZF$OtpS#S=ZOwj-CeUmOyM9btdk3_r1&I zUQn1ba_v|_p`4J3x{_Q_24NXR8v}OR+A7|2IWbpmN!fvLuX zq2ZrDdo(?Mt$UJ~h)$55)xnl`?w;9c#KlDmx$o=8PM%yx0};mu5KMo0aXkHW8x}+w)%)rC^mFzOg}tKk?k2ciS&#-0b#zrnVyMB z$;@oIQDI>)J^h>Thsu7CfHHNijocl0%t)_hXOqFrV)`3}T^$}u%Hvza9R2_pALHS% zbtSCNfulzoi9yKoqSfG%khlxdxXw-rLQdyLkJ{ArKh#biLP`d{#+5XmZ$(E(FUiUc zaOe_G(ceCOq}}bZ z-WYasbQ%Hiwds#fJP7pz>lNBde{?i;Akko&oV>W;le0teXTNR(RpH8GhMQPoVq=#h zf#%|qyq9VAXqolNlMi<9l$3n)?%g#K@gi$QXx?FiNAj+*@r@Rx^N+W0Pg}I4WrfJQ zZAb!7+Z%v|x1>Jmmc6>{8ZiMW+B#jWp10lX>Azc{#d1`RzR)Y@{aPE{Ie$ z8h3pUXUwuD`R)B=eDDADlP6`j!@_EO z8B3q)jcm58J91dp`EIFSuzj}*m%@=`qYfI@*HKyRMzsN3JmLOP?y~@fUIzh|$pjb_ zQGzJk!(}BrdlvWfX#}XPS+C+09zztu%gYO;LPAG<^ytp^ty1T=y8Tj0J@bVDFm=%# zUJsH|-``d6DiOgpTrU*Y{x-=z20~vR?hlZ+X2NeB_R=rttQPBqGS~feLw&fiieZ)(ipIqBplA6tb*+s|#P>u%9svuY$)qV-LeW4?KF*E@t;p+ZhP{ zz1GtkJsa5SpIa4})rO7hW(lu>WDv{_-g$oH1PD%pq}r!9LRia*)z!@{RN7(bs#OQw zpWLRS!b2)5ddR~gJ@n+M9LQ-4is)c%a|i1<<>hIU-@Y9Tks1`pgY~Yq&AQoX00?Sg zdPL+!&%FTW78}WxwCX%qJyZf-9m3Oj@k09VcvI=aUSLr@>(?7l!(?9+bD7;}s0%R( zeB7GGzy2bste~X?TCA^q{P+O`9+xk-y#QBd6*UbF*g(Cb2%&qg`ug1$u9>9jzR(3z z3DNGT&2uT}(Sivwsf)-aw-pS5cvc(J~;cw<#%+IG@6CD9!{y7sCl#ELiJcog^%=idxQw zh0xm1s{h`0oBy>l=Z*mfgfxNI%s$-r+hRKCUJM}P!{^V*&S7HF+jADKjTFpu2AcZw z^Zds_(w0Rm!0pADG57@9ypk# zq#FKdlg`M7byr^pclShE5xDj0yk)78&qol`;+ E0C7}>^oeWUr9y$Ou{4SxJ(anLRSI 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 diff --git a/app/client/cypress/snapshots/Smoke_TestSuite/ClientSideTests/VisualTests/AppPageLayout_spec.js/loginpage.snap.png b/app/client/cypress/snapshots/Smoke_TestSuite/ClientSideTests/VisualTests/AppPageLayout_spec.js/loginpage.snap.png index bdf99f7d7e513e625ea811bd9b94b11b644b4fe6..c2a5512b4e666b579b5e96bb43e1124549458e9b 100644 GIT binary patch literal 35931 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 literal 25163 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 diff --git a/app/client/cypress/snapshots/Smoke_TestSuite/ClientSideTests/VisualTests/WidgetsLayout_spec.js/inlineDisabled.snap.png b/app/client/cypress/snapshots/Smoke_TestSuite/ClientSideTests/VisualTests/WidgetsLayout_spec.js/inlineDisabled.snap.png index bb646fa2d0d7734c6fc24d2657f0a2f8b25ba162..34696421e14f224772660ed241ad032ce1a09812 100644 GIT binary patch literal 4689 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 literal 2086 zcmbuAX*3&J0>>Fj5E2ziEfu`l8kI^B)n_EJHANpuN>NK|n|c%}Cbd*0h}cTCR4}Nm z)Y?>IX=^PiQdFZ9B|~&-X=rV=ygYNx%sKOQzWnb!=iYPgIluF}|4Y5(U?VMgTvA9# zNZQU8<0K>`Y#^8;0Ahb8X-u#nzHEoF#6<}&cSzkFrKrBs8O zJO}Fcy%VdsWK0LW-+o;sf|a#GIz$7l)bvEo^JwuVp5YoUs?N`n$JKiz7-(-NTA7!t z6uS7nvHc^&L0rT^>q0uW5@8v+$DZL`lSu58C4)OL5J6o3pSaI*8(;?PmUVL}4?Vpn zQuDg27dOsD?Nc6tQOPbk04TZtFe>TR^XaPQtv>R2PeS%m7(5gu+~m)~;PEAeZp;+M zFCoQW&9dW=RAwZ**Qdd2+^sdE?Cpt)782-rH)f?JTp?RqUV{wgJ0a7!_N-4~qd4n~ zfzmHqp}_@L2P@mmd)-YXyR;|u6wL#`A+@gdtm)$m7SA#tYl$1tg1L*s*1`Kz#D!1i z^Ld4&tab(iG81}i``VkiFr+LhIZB7#yA`_aj)(vfs&fhh15?-C2b1zmh^9@dy57&WS*?j6Oo7E3?^N8J9T1NnQ!%}HXXewETM4Q-A}cFP04%DC z-uvnH3hzUHMHuUTwW0a500|fihKX@)7q=g^wY7a+e|z)c_f-xE&3@MOg;RH;?$yqj ziV_{kE2gPnR5z+Pq4w)yOZuPTpmnqZ3_LP?OSP*5eK5P1cH*YSs~L86UAu#+O8Lv; z_^jsrMC}6~VD?D|FV3r%UfgofFhUw_HE&Th)eJ&!e4EAL2ql5XQGj9mgH4maYc^kO zP0RlAuQA&1^ZDCbCnj^l+3k;8rUV3fQ8H&m&mo(*QAt3c0S=u`ydecOv`y`>g={1N(W=`TQ%oFg`bAj7sUMG7JfIYz$kb z>*#YF2(_y0ZsQ00`)P=Ldt-uqQ*TyYsB((LMS{*M%Cxz+8bHa z?h73vKuoj7Jpve4?NGDgF_K;5ey*T@fMFy!b@$gaf3@GWR89sf&?rkwD!~QDX^zby zHUK`i^xf?8SysSt_&UU_Z?GZU%#|5oPLa_tt8ts^&f8gPV)rS*?+!uii+U#*HnMT@o+`SKR3L9TH=DOuQf)5h5ry4b zssX@#^*kcJ_sKpub(IVS1L|WYjp+v3)rAK}1;|~NcQg!!p>%ogdt6Sc{i+!Jh$O=E zqhmiVz(oHd99)DH32{|=2!W`R7pEa+D(2vmY6O-wr(!zC`R1jKCQ2 zC-kbp!BZQY4|AJ8Lrqn*w6wr7Nz@Aw%x~M#m>7mX=(l##o~S*?b=_J*AOMui)FJi_ zVYSqzrD4RJNVHTUT`Gu9-TS%GunWQ}A=jc+WW*C?HEkN_OEZ$!QkB`gX{&Mo(SV?! z>th4*7+-&$VZdkg!l-EEuSFoVhllEPf!&?6xEGOjjsi&Zffe>-)KYv1b;J%4yxCKhndmRxF+k@et#HSWd@eVHT? zT2E)yGX_HzC}wA8X9@6@kyz(hytpGHA~yLBQBZdSF)^u0#NXDr8;RDRQ47}Y&$}~P z^-eA28y=s@IIYU#J>_u8k#gPXph`Gp4tlj zNXU*1uAixXPrugD@;5>MuTm~(LdUF diff --git a/app/client/cypress/snapshots/Smoke_TestSuite/ClientSideTests/VisualTests/WidgetsLayout_spec.js/inlineEnabled.snap.png b/app/client/cypress/snapshots/Smoke_TestSuite/ClientSideTests/VisualTests/WidgetsLayout_spec.js/inlineEnabled.snap.png index 8b4040961299b8f801554d4106473525a4746eaa..02c692a097432f50f0a1803c4f412e2021d517ba 100644 GIT binary patch delta 4128 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 Fe*%qqCrDRX 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 { cy.get(adminSettings.authenticationTab).click(); cy.url().should("contain", "/settings/authentication"); }); + +Cypress.Commands.add("waitForServerRestart", () => { + cy.get(adminSettings.restartNotice).should("be.visible"); + // Wait for restart notice to not be visible with a timeout + // Cannot use cy.get as mentioned in https://github.com/NoriSte/cypress-wait-until/issues/75#issuecomment-572685623 + cy.waitUntil(() => !Cypress.$(adminSettings.restartNotice).length, { + timeout: 120000, + }); + cy.get(adminSettings.saveButton).should("be.visible"); +}); diff --git a/app/client/cypress/support/Objects/CommonLocators.ts b/app/client/cypress/support/Objects/CommonLocators.ts index 98fc9299a492..95b18b993d9c 100644 --- a/app/client/cypress/support/Objects/CommonLocators.ts +++ b/app/client/cypress/support/Objects/CommonLocators.ts @@ -1,4 +1,5 @@ export class CommonLocators { + _chevronUp = ".bp3-icon-chevron-up"; _loading = "#loading"; _spinner = ".bp3-spinner"; _runBtnSpinner = ".cs-spinner"; @@ -19,7 +20,7 @@ export class CommonLocators { _codeEditorTarget = "div.CodeEditorTarget"; _entityExplorersearch = "#entity-explorer-search"; _propertyControl = ".t--property-control-"; - _textWidget = ".t--draggable-textwidget span"; + _textWidget = ".t--draggable-textwidget .t--text-widget-container span"; _inputWidget = ".t--draggable-inputwidgetv2 input"; _publishButton = ".t--application-publish-btn"; _widgetInCanvas = (widgetType: string) => `.t--draggable-${widgetType}`; @@ -45,7 +46,7 @@ export class CommonLocators { _contextMenuSubItemDiv = (item: string) => "//div[text()='" + item + "'][contains(@class, 'bp3-fill')]"; _visibleTextDiv = (divText: string) => "//div[text()='" + divText + "']"; - _visibleTextSpan = (spanText: string) => "//span[text()='" + spanText + "']"; + _visibleTextSpan = (spanText: string) => `//span[text()="` + spanText + `"]`; _openWidget = ".widgets .t--entity-add-btn"; _dropHere = ".t--drop-target"; _crossBtn = "span.cancel-icon"; @@ -71,6 +72,10 @@ export class CommonLocators { "//div[contains(@class, 't--property-control-" + ddName.replace(/ +/g, "").toLowerCase() + "')]//button[contains(@class, 't--open-dropdown-Select-Action')]"; + _selectPropPageDropdown = (ddName: string) => + "//div[contains(@class, 't--property-control-" + + ddName.replace(/ +/g, "").toLowerCase() + + "')]//button[contains(@class, 't--open-dropdown-Select-Page')]"; _dropDownValue = (dropdownOption: string) => ".single-select:contains('" + dropdownOption + "')"; _selectOptionValue = (dropdownOption: string) => @@ -159,4 +164,6 @@ export class CommonLocators { "']"; _dropDownMultiTreeSelect = ".rc-tree-select-multiple"; _omnibarDescription = "[data-cy='description']"; + _previewModeToggle = ".t--switch-preview-mode-toggle"; + _editModeToggle = ".t--switch-comment-mode-off"; } diff --git a/app/client/cypress/support/Objects/ObjectsCore.ts b/app/client/cypress/support/Objects/ObjectsCore.ts new file mode 100644 index 000000000000..d74b57dfce02 --- /dev/null +++ b/app/client/cypress/support/Objects/ObjectsCore.ts @@ -0,0 +1,13 @@ +import { ObjectsRegistry } from "../Objects/Registry"; + +export const agHelper = ObjectsRegistry.AggregateHelper; +export const locators = ObjectsRegistry.CommonLocators; +export const ee = ObjectsRegistry.EntityExplorer; +export const jsEditor = ObjectsRegistry.JSEditor; +export const propPane = ObjectsRegistry.PropertyPane; +export const deployMode = ObjectsRegistry.DeployMode; +export const appSettings = ObjectsRegistry.AppSettings; +export const generalSettings = ObjectsRegistry.GeneralSettings; +export const pageSettings = ObjectsRegistry.PageSettings; +export const homePage = ObjectsRegistry.HomePage; +export const theme = ObjectsRegistry.ThemeSettings; diff --git a/app/client/cypress/support/Objects/Registry.ts b/app/client/cypress/support/Objects/Registry.ts index 547f103654da..dffc1c734394 100644 --- a/app/client/cypress/support/Objects/Registry.ts +++ b/app/client/cypress/support/Objects/Registry.ts @@ -12,6 +12,10 @@ import { DeployMode } from "../Pages/DeployModeHelper"; import { GitSync } from "../Pages/GitSync"; import { FakerHelper } from "../Pages/FakerHelper"; import { DebuggerHelper } from "../Pages/DebuggerHelper"; +import { AppSettings } from "../Pages/AppSettings/AppSettings"; +import { GeneralSettings } from "../Pages/AppSettings/GeneralSettings"; +import { PageSettings } from "../Pages/AppSettings/PageSettings"; +import { ThemeSettings } from "../Pages/AppSettings/ThemeSettings"; export class ObjectsRegistry { private static aggregateHelper__: AggregateHelper; @@ -118,12 +122,44 @@ export class ObjectsRegistry { return ObjectsRegistry.fakerHelper__; } - private static DebuggerHelper__: DebuggerHelper; + private static debuggerHelper__: DebuggerHelper; static get DebuggerHelper(): DebuggerHelper { - if (ObjectsRegistry.DebuggerHelper__ === undefined) { - ObjectsRegistry.DebuggerHelper__ = new DebuggerHelper(); + if (ObjectsRegistry.debuggerHelper__ === undefined) { + ObjectsRegistry.debuggerHelper__ = new DebuggerHelper(); } - return ObjectsRegistry.DebuggerHelper__; + return ObjectsRegistry.debuggerHelper__; + } + + private static appSettings__: AppSettings; + static get AppSettings(): AppSettings { + if (ObjectsRegistry.appSettings__ === undefined) { + ObjectsRegistry.appSettings__ = new AppSettings(); + } + return ObjectsRegistry.appSettings__; + } + + private static generalSettings__: GeneralSettings; + static get GeneralSettings(): GeneralSettings { + if (ObjectsRegistry.generalSettings__ === undefined) { + ObjectsRegistry.generalSettings__ = new GeneralSettings(); + } + return ObjectsRegistry.generalSettings__; + } + + private static pageSettings__: PageSettings; + static get PageSettings(): PageSettings { + if (ObjectsRegistry.pageSettings__ === undefined) { + ObjectsRegistry.pageSettings__ = new PageSettings(); + } + return ObjectsRegistry.pageSettings__; + } + + private static themeSettings__: ThemeSettings; + static get ThemeSettings(): ThemeSettings { + if (ObjectsRegistry.themeSettings__ === undefined) { + ObjectsRegistry.themeSettings__ = new ThemeSettings(); + } + return ObjectsRegistry.themeSettings__; } } diff --git a/app/client/cypress/support/Pages/AggregateHelper.ts b/app/client/cypress/support/Pages/AggregateHelper.ts index 2167f12db8cb..4c713b36a846 100644 --- a/app/client/cypress/support/Pages/AggregateHelper.ts +++ b/app/client/cypress/support/Pages/AggregateHelper.ts @@ -104,7 +104,7 @@ export class AggregateHelper { public RenameWithInPane(renameVal: string, query = true) { const name = query ? this.locator._queryName : this.locator._dsName; const text = query ? this.locator._queryNameTxt : this.locator._dsNameTxt; - cy.get(name).click({ force: true }); + this.GetNClick(name, 0, true); cy.get(text) .clear({ force: true }) .type(renameVal, { force: true }) @@ -426,9 +426,14 @@ export class AggregateHelper { .invoke("text"); } - public EnterActionValue(actionName: string, value: string, paste = true) { + public EnterActionValue( + actionName: string, + value: string, + paste = true, + index = 0, + ) { cy.xpath(this.locator._actionTextArea(actionName)) - .first() + .eq(index) .scrollIntoView() .focus() .type("{uparrow}", { force: true }) @@ -437,7 +442,7 @@ export class AggregateHelper { if ($cm.contents != "") { cy.log("The field is not empty"); cy.xpath(this.locator._actionTextArea(actionName)) - .first() + .eq(index) .scrollIntoView() .click({ force: true }) .focused() @@ -447,7 +452,7 @@ export class AggregateHelper { } this.Sleep(); cy.xpath(this.locator._actionTextArea(actionName)) - .first() + .eq(index) .scrollIntoView() .then((el: any) => { if (paste) { @@ -476,6 +481,26 @@ export class AggregateHelper { .wait(waitTimeInterval); } + public GetSiblingNClick( + selector: string, + siblingSelector: string, + index = 0, + force = false, + waitTimeInterval = 500, + ) { + return this.GetElement(selector) + .siblings(siblingSelector) + .first() + .eq(index) + .scrollIntoView() + .click({ force: force }) + .wait(waitTimeInterval); + } + + public GoBack() { + this.GetNClick(this.locator._visibleTextSpan("Back")); + } + public SelectNRemoveLineText(selector: string) { const locator = selector.startsWith("//") ? cy.xpath(selector) @@ -497,7 +522,16 @@ export class AggregateHelper { } } - public TypeText(selector: string, value: string, index = 0) { + public ClearTextField(selector: string) { + this.GetElement(selector).clear(); + } + + public TypeText( + selector: string, + value: string, + index = 0, + parseSpecialCharSeq = false, + ) { const locator = selector.startsWith("//") ? cy.xpath(selector) : cy.get(selector); @@ -505,7 +539,7 @@ export class AggregateHelper { .eq(index) .focus() .type(value, { - parseSpecialCharSequences: false, + parseSpecialCharSequences: parseSpecialCharSeq, //delay: 3, //force: true, }); @@ -537,13 +571,14 @@ export class AggregateHelper { } public CheckUncheck(selector: string, check = true) { - const locator = selector.startsWith("//") - ? cy.xpath(selector) - : cy.get(selector); if (check) { - locator.check({ force: true }).should("be.checked"); + this.GetElement(selector) + .check({ force: true }) + .should("be.checked"); } else { - locator.uncheck({ force: true }).should("not.be.checked"); + this.GetElement(selector) + .uncheck({ force: true }) + .should("not.be.checked"); } this.Sleep(); } @@ -567,6 +602,18 @@ export class AggregateHelper { } } + public AssertAttribute( + selector: string, + attribName: string, + attribValue: string, + ) { + return this.GetElement(selector).should( + "have.attr", + attribName, + attribValue, + ); + } + public ToggleSwitch( switchName: string, toggle: "check" | "uncheck" = "check", @@ -854,6 +901,11 @@ export class AggregateHelper { .should("deep.equal", expectedData); } + public AssertElementFocus(selector: ElementType, isFocused = true) { + if (isFocused) return this.GetElement(selector).should("be.focused"); + return this.GetElement(selector).should("not.be.focused"); + } + public AssertElementVisible(selector: ElementType, index = 0) { return this.GetElement(selector) .eq(index) @@ -912,6 +964,10 @@ export class AggregateHelper { .should(exists); } + public ValidateURL(url: string) { + cy.url().should("include", url); + } + public ScrollTo( selector: ElementType, position: diff --git a/app/client/cypress/support/Pages/ApiPage.ts b/app/client/cypress/support/Pages/ApiPage.ts index 851e1d0a2043..18804bff8517 100644 --- a/app/client/cypress/support/Pages/ApiPage.ts +++ b/app/client/cypress/support/Pages/ApiPage.ts @@ -1,4 +1,7 @@ import { ObjectsRegistry } from "../Objects/Registry"; + +type RightPaneTabs = "datasources" | "connections"; + export class ApiPage { public agHelper = ObjectsRegistry.AggregateHelper; public locator = ObjectsRegistry.CommonLocators; @@ -30,6 +33,7 @@ export class ApiPage { verb + "')]"; private _bodySubTab = (subTab: string) => `[data-cy='tab--${subTab}']`; + private _rightPaneTab = (tab: string) => `[data-cy='t--tab-${tab}']`; _visibleTextSpan = (spanText: string) => "//span[text()='" + spanText + "']"; _visibleTextDiv = (divText: string) => "//div[text()='" + divText + "']"; _noBodyMessageDiv = "#NoBodyMessageDiv"; @@ -192,25 +196,14 @@ export class ApiPage { ToggleOnPageLoadRun(enable = true || false) { this.SelectPaneTab("Settings"); if (enable) - cy.get(this._onPageLoad).check({ - force: true, - }); - else - cy.get(this._onPageLoad).uncheck({ - force: true, - }); + this.agHelper.CheckUncheck(this._onPageLoad, true); + else this.agHelper.CheckUncheck(this._onPageLoad, false); } ToggleConfirmBeforeRunningApi(enable = true || false) { this.SelectPaneTab("Settings"); - if (enable) - cy.get(this._confirmBeforeRunningAPI).check({ - force: true, - }); - else - cy.get(this._confirmBeforeRunningAPI).uncheck({ - force: true, - }); + if (enable) this.agHelper.CheckUncheck(this._confirmBeforeRunningAPI, true); + else this.agHelper.CheckUncheck(this._confirmBeforeRunningAPI, false); } SelectPaneTab( @@ -241,6 +234,17 @@ export class ApiPage { this.agHelper.GetNClick(this._bodySubTab(subTabName)); } + AssertRightPaneSelectedTab(tabName: RightPaneTabs) { + cy.get(this._rightPaneTab(tabName)).should( + "have.class", + "react-tabs__tab--selected", + ); + } + + SelectRightPaneTab(tabName: RightPaneTabs) { + this.agHelper.GetNClick(this._rightPaneTab(tabName)); + } + ValidateQueryParams(param: { key: string; value: string }) { this.SelectPaneTab("Params"); this.agHelper.ValidateCodeEditorContent(this._paramKey(0), param.key); diff --git a/app/client/cypress/support/Pages/AppSettings/AppSettings.ts b/app/client/cypress/support/Pages/AppSettings/AppSettings.ts new file mode 100644 index 000000000000..699d75a2ce7c --- /dev/null +++ b/app/client/cypress/support/Pages/AppSettings/AppSettings.ts @@ -0,0 +1,117 @@ +import { ObjectsRegistry } from "../../Objects/Registry"; +export class AppSettings { + private agHelper = ObjectsRegistry.AggregateHelper; + private theme = ObjectsRegistry.ThemeSettings; + + private locators = { + _appSettings: "#t--app-settings-cta", + _closeSettings: "#t--close-app-settings-pane", + _themeSettingsHeader: "#t--theme-settings-header", + _generalSettingsHeader: "#t--general-settings-header", + _getPageSettingsHeader: (pageName: string) => + `#t--page-settings-${pageName}`, + }; + + public errorMessageSelector = (fieldId: string) => { + fieldId = fieldId[0] === "#" ? fieldId.slice(1, fieldId.length) : fieldId; + return `//input[@id='${fieldId}']/following-sibling::div/span`; + }; + + public OpenAppSettings() { + this.agHelper.GetNClick(this.locators._appSettings); + } + + public ClosePane() { + this.agHelper.GetNClick(this.locators._closeSettings); + } + + public GoToThemeSettings() { + this.agHelper.GetNClick(this.locators._themeSettingsHeader); + } + + public GoToGeneralSettings() { + this.agHelper.GetNClick(this.locators._generalSettingsHeader); + } + + public GoToPageSettings(pageName: string) { + this.agHelper.GetNClick(this.locators._getPageSettingsHeader(pageName)); + } + + public OpenPaneAndChangeTheme(themeName: string) { + this.OpenAppSettings(); + this.GoToThemeSettings(); + this.theme.ChangeTheme(themeName); + this.ClosePane(); + } + + public OpenPaneAndChangeThemeColors( + primaryColorIndex: number, + backgroundColorIndex: number, + ) { + this.OpenAppSettings(); + this.GoToThemeSettings(); + this.theme.ChangeThemeColor(primaryColorIndex, "Primary"); + this.theme.ChangeThemeColor(backgroundColorIndex, "Background"); + this.ClosePane(); + } + + public CheckUrl( + appName: string, + pageName: string, + customSlug?: string, + editMode = true, + ) { + cy.location("pathname").then((pathname) => { + if (customSlug && customSlug.length > 0) { + const pageId = pathname + .split("/")[2] + ?.split("-") + .pop(); + expect(pathname).to.be.equal( + `/app/${customSlug}-${pageId}${editMode ? "/edit" : ""}`.toLowerCase(), + ); + } else { + const pageId = pathname + .split("/")[3] + ?.split("-") + .pop(); + expect(pathname).to.be.equal( + `/app/${appName}/${pageName}-${pageId}${ + editMode ? "/edit" : "" + }`.toLowerCase(), + ); + } + }); + }; + + public AssertErrorMessage( + fieldId: string, + newValue: string, + errorMessage: string, + resetValue = true, + ) { + this.agHelper.GetText(fieldId, "val").then((currentValue) => { + if (newValue.length === 0) this.agHelper.ClearTextField(fieldId); + else + this.agHelper.RemoveCharsNType( + fieldId, + (currentValue as string).length, + newValue, + ); + this.agHelper.AssertText( + this.errorMessageSelector(fieldId), + "text", + errorMessage, + ); + if (resetValue) { + this.agHelper.RemoveCharsNType( + fieldId, + newValue.length, + currentValue as string, + ); + } + }); + } + + +} diff --git a/app/client/cypress/support/Pages/AppSettings/GeneralSettings.ts b/app/client/cypress/support/Pages/AppSettings/GeneralSettings.ts new file mode 100644 index 000000000000..6c46b860cd11 --- /dev/null +++ b/app/client/cypress/support/Pages/AppSettings/GeneralSettings.ts @@ -0,0 +1,58 @@ +import { ObjectsRegistry } from "../../Objects/Registry"; + +export class GeneralSettings { + private agHelper = ObjectsRegistry.AggregateHelper; + private appSettings = ObjectsRegistry.AppSettings; + + private locators = { + _appNameField: "#t--general-settings-app-name", + _appNonSelectedIcon: ".t--icon-not-selected", + _appIconSelector: "#t--general-settings-app-icon", + }; + + UpdateAppNameAndVerifyUrl( + reset: boolean, + newAppName: string, + verifyAppNameAs?: string, + pageName = "page1", + ) { + const appNameToBeVerified = verifyAppNameAs ?? newAppName; + this.agHelper + .GetText(this.locators._appNameField, "val") + .then((currentAppName) => { + this.agHelper.RemoveCharsNType( + this.locators._appNameField, + (currentAppName as string).length, + newAppName, + ); + this.agHelper.PressEnter(); + this.agHelper.Sleep(); + this.agHelper.ValidateNetworkStatus("@updateApplication", 200); + this.appSettings.CheckUrl(appNameToBeVerified, pageName); + if (reset) { + this.agHelper.RemoveCharsNType( + this.locators._appNameField, + newAppName.length, + currentAppName as string, + ); + this.agHelper.PressEnter(); + this.agHelper.ValidateNetworkStatus("@updateApplication", 200); + this.appSettings.CheckUrl(currentAppName as string, pageName); + } + }); + } + + AssertAppErrorMessage(newAppName: string, errorMessage: string) { + this.appSettings.AssertErrorMessage( + this.locators._appNameField, + newAppName, + errorMessage, + true, + ); + } + + UpdateAppIcon() { + this.agHelper.GetNClick(this.locators._appNonSelectedIcon, 0); + this.agHelper.ValidateNetworkStatus("@updateApplication", 200); + } +} diff --git a/app/client/cypress/support/Pages/AppSettings/PageSettings.ts b/app/client/cypress/support/Pages/AppSettings/PageSettings.ts new file mode 100644 index 000000000000..debe15f7f22e --- /dev/null +++ b/app/client/cypress/support/Pages/AppSettings/PageSettings.ts @@ -0,0 +1,145 @@ +import { ObjectsRegistry } from "../../Objects/Registry"; + +export class PageSettings { + private agHelper = ObjectsRegistry.AggregateHelper; + private homePage = ObjectsRegistry.HomePage; + private appSettings = ObjectsRegistry.AppSettings; + + private locators = { + _pageNameField: "#t--page-settings-name", + _customSlugField: "#t--page-settings-custom-slug", + _showPageNavSwitch: "#t--page-settings-show-nav-control", + _setAsHomePageSwitch: "#t--page-settings-home-page-control", + _setHomePageToggle : ".bp3-control-indicator", + _homePageHeader: "#t--page-settings-default-page", + }; + + UpdatePageNameAndVerifyTextValue(newPageName: string, verifyPageNameAs: string) { + this.AssertPageValue( + this.locators._pageNameField, + newPageName, + verifyPageNameAs, + ); + } + + UpdateCustomSlugAndVerifyTextValue( + newCustomSlug: string, + verifyCustomSlugAs: string, + ) { + this.AssertPageValue( + this.locators._customSlugField, + newCustomSlug, + verifyCustomSlugAs, + ); + } + + public AssertPageValue( + locator: string, + newValue: string, + verifyValueAs: string, + ) { + this.agHelper.GetText(locator, "val").then((currentValue) => { + const currentValueLength = (currentValue as string).length; + if (currentValueLength === 0) this.agHelper.TypeText(locator, newValue); + else + this.agHelper.RemoveCharsNType(locator, currentValueLength, newValue); + + this.agHelper.GetText(locator, "val").then((fieldValue) => { + expect(fieldValue).to.equal(verifyValueAs); + if (currentValueLength === 0) this.agHelper.ClearTextField(locator); + else + this.agHelper.RemoveCharsNType( + locator, + (fieldValue as string).length, + currentValue as string, + ); + }); + }); + } + + UpdatePageNameAndVerifyUrl( + newPageName: string, + verifyPageNameAs?: string, + reset = true, + ) { + const pageNameToBeVerified = verifyPageNameAs ?? newPageName; + this.agHelper + .GetText(this.locators._pageNameField, "val") + .then((currentPageName) => { + const currentPageNameLength = (currentPageName as string).length; + + this.homePage.GetAppName().then((appName) => { + this.agHelper.RemoveCharsNType( + this.locators._pageNameField, + currentPageNameLength, + newPageName, + ); + this.agHelper.PressEnter(); + this.agHelper.ValidateNetworkStatus("@updatePage", 200); + this.appSettings.CheckUrl(appName as string, pageNameToBeVerified); + if (reset) { + this.agHelper.RemoveCharsNType( + this.locators._pageNameField, + newPageName.length, + currentPageName as string, + ); + this.agHelper.PressEnter(); + this.agHelper.ValidateNetworkStatus("@updatePage", 200); + this.appSettings.CheckUrl(appName as string, currentPageName as string); + } + }); + }); + } + + UpdateCustomSlugAndVerifyUrl(customSlug: string) { + this.agHelper + .GetText(this.locators._customSlugField, "val") + .then((currentCustomSlug) => { + const currentCustomSlugLength = (currentCustomSlug as string).length; + + this.homePage.GetAppName().then((appName) => { + if (currentCustomSlugLength === 0) { + this.agHelper.TypeText(this.locators._customSlugField, customSlug); + } else { + this.agHelper.RemoveCharsNType( + this.locators._customSlugField, + currentCustomSlugLength, + customSlug, + ); + } + this.agHelper.PressEnter(); + this.agHelper.ValidateNetworkStatus("@updatePage", 200); + this.appSettings.CheckUrl(appName as string, "", customSlug); + }); + }); + } + + AssertPageErrorMessage(newPageName: string, errorMessage: string) { + this.appSettings.AssertErrorMessage( + this.locators._pageNameField, + newPageName, + errorMessage, + true, + ); + } + + TogglePageNavigation() { + this.agHelper.GetSiblingNClick( + this.locators._showPageNavSwitch, + this.locators._setHomePageToggle, + ); + this.agHelper.ValidateNetworkStatus("@updatePage", 200); + } + + ToggleHomePage() { + this.agHelper.GetSiblingNClick( + this.locators._setAsHomePageSwitch, + this.locators._setHomePageToggle, + ); + this.agHelper.ValidateNetworkStatus("@makePageDefault", 200); + } + + AssertHomePage(pageName: string) { + this.agHelper.AssertText(this.locators._homePageHeader, "text", pageName); + } +} diff --git a/app/client/cypress/support/Pages/AppSettings/ThemeSettings.ts b/app/client/cypress/support/Pages/AppSettings/ThemeSettings.ts new file mode 100644 index 000000000000..9a1f2bbc5488 --- /dev/null +++ b/app/client/cypress/support/Pages/AppSettings/ThemeSettings.ts @@ -0,0 +1,41 @@ +import { ObjectsRegistry } from "../../Objects/Registry"; + +export class ThemeSettings { + private agHelper = ObjectsRegistry.AggregateHelper; + private locators = { + _changeThemeBtn: ".t--change-theme-btn", + _themeCard: (themeName: string) => + "//h3[text()='" + + themeName + + "']//ancestor::div[@class= 'space-y-1 group']", + _colorPickerV2Popover: ".t--colorpicker-v2-popover", + _colorPickerV2Color: ".t--colorpicker-v2-color", + _colorRing: ".border-2", + _colorInput: (option: string) => + "//h3[text()='" + option + " Color']//parent::div//input", + _colorInputField: (option: string) => + "//h3[text()='" + option + " Color']//parent::div", + }; + + public ChangeTheme(newTheme: string) { + this.agHelper.GetNClick(this.locators._changeThemeBtn, 0, true); + this.agHelper.GetNClick(this.locators._themeCard(newTheme)); + this.agHelper.AssertContains("Theme " + newTheme + " Applied"); + } + + public ChangeThemeColor( + colorIndex: number | string, + type: "Primary" | "Background" = "Primary", + ) { + const typeIndex = type == "Primary" ? 0 : 1; + this.agHelper.GetNClick(this.locators._colorRing, typeIndex); + if (typeof colorIndex == "number") { + this.agHelper.GetNClick(this.locators._colorPickerV2Popover); + this.agHelper.GetNClick(this.locators._colorPickerV2Color, colorIndex); + } else { + this.agHelper.GetElement(this.locators._colorInput(type)).clear(); + this.agHelper.TypeText(this.locators._colorInput(type), colorIndex); + //this.agHelper.UpdateInput(this._colorInputField(type), colorIndex);//not working! + } + } +} diff --git a/app/client/cypress/support/Pages/AppSettings/Utils.ts b/app/client/cypress/support/Pages/AppSettings/Utils.ts new file mode 100644 index 000000000000..dd1af382fb46 --- /dev/null +++ b/app/client/cypress/support/Pages/AppSettings/Utils.ts @@ -0,0 +1,28 @@ +export const checkUrl = ( + appName: string, + pageName: string, + customSlug?: string, + editMode = true, +) => { + cy.location("pathname").then((pathname) => { + if (customSlug && customSlug.length > 0) { + const pageId = pathname + .split("/")[2] + ?.split("-") + .pop(); + expect(pathname).to.be.equal( + `/app/${customSlug}-${pageId}${editMode ? "/edit" : ""}`.toLowerCase(), + ); + } else { + const pageId = pathname + .split("/")[3] + ?.split("-") + .pop(); + expect(pathname).to.be.equal( + `/app/${appName}/${pageName}-${pageId}${ + editMode ? "/edit" : "" + }`.toLowerCase(), + ); + } + }); +}; diff --git a/app/client/cypress/support/Pages/DataSources.ts b/app/client/cypress/support/Pages/DataSources.ts index 71b43f437f61..0006b3070b3a 100644 --- a/app/client/cypress/support/Pages/DataSources.ts +++ b/app/client/cypress/support/Pages/DataSources.ts @@ -17,6 +17,7 @@ export class DataSources { private _addNewDataSource = ".t--entity-add-btn.datasources"; private _createNewPlgin = (pluginName: string) => ".t--plugin-name:contains('" + pluginName + "')"; + private _collapseContainer = ".t--collapse-section-container"; private _host = "input[name='datasourceConfiguration.endpoints[0].host']"; private _port = "input[name='datasourceConfiguration.endpoints[0].port']"; _databaseName = @@ -28,7 +29,10 @@ export class DataSources { "input[name = 'datasourceConfiguration.authentication.password']"; private _testDs = ".t--test-datasource"; private _saveDs = ".t--save-datasource"; + private _saveAndAuthorizeDS = ".t--save-and-authorize-datasource"; private _datasourceCard = ".t--datasource"; + _editButton = ".t--edit-datasource"; + _dsEntityItem = "[data-guided-tour-id='explorer-entity-Datasources']"; _activeDS = "[data-testid='active-datasource-name']"; _templateMenu = ".t--template-menu"; _templateMenuOption = (action: string) => @@ -45,6 +49,7 @@ export class DataSources { "//div[contains(@class, 't--ds-list')]//span[text()='" + dbName + "']"; _runQueryBtn = ".t--run-query"; _newDatabases = "#new-datasources"; + _newDatasourceContainer = "#new-integrations-wrapper"; _selectDatasourceDropdown = "[data-cy=t--datasource-dropdown]"; _selectTableDropdown = "[data-cy=t--table-dropdown]"; _selectSheetNameDropdown = "[data-cy=t--sheetName-dropdown]"; @@ -106,10 +111,22 @@ export class DataSources { _gsScopeOptions = ".ads-dropdown-options-wrapper div > span div span"; private _queryTimeout = "//input[@name='actionConfiguration.timeoutInMillisecond']"; + _getStructureReq = "/api/v1/datasources/*/structure?ignoreCache=true"; + public _datasourceModalSave = ".t--datasource-modal-save"; + public _datasourceModalDoNotSave = ".t--datasource-modal-do-not-save"; + + public AssertViewMode() { + this.agHelper.AssertElementExist(this._editButton); + } + + public AssertEditMode() { + this.agHelper.AssertElementAbsence(this._editButton); + } public StartDataSourceRoutes() { - cy.intercept("PUT", "/api/v1/datasources/*").as("saveDatasource"); + cy.intercept("POST", "/api/v1/datasources").as("saveDatasource"); cy.intercept("POST", "/api/v1/datasources/test").as("testDatasource"); + cy.intercept("PUT", "/api/v1/datasources/*").as("updateDatasource"); } private ReplaceApplicationIdForInterceptPages(fixtureFile: any) { @@ -133,12 +150,6 @@ export class DataSources { }); } - public startRoutesForDatasource() { - cy.server(); - cy.route("PUT", "/api/v1/datasources/*").as("saveDatasource"); - cy.route("POST", "/api/v1/datasources/test").as("testDatasource"); - } - public StartInterceptRoutesForMySQL() { //All stubbing - updating app id to current app id for Delete app by api call to be successfull: @@ -203,13 +214,53 @@ export class DataSources { cy.get(this._createNewPlgin(pluginName)) .parent("div") .trigger("click", { force: true }); - this.agHelper.WaitUntilEleAppear(this.locator._toastMsg); + this.agHelper.Sleep(); + //this.agHelper.WaitUntilEleAppear(this.locator._toastMsg); this.agHelper.AssertElementAbsence( this.locator._specificToast("Duplicate key error"), ); - if (waitForToastDisappear) - this.agHelper.WaitUntilToastDisappear("datasource created"); - else this.agHelper.AssertContains("datasource created"); + this.agHelper.PressEscape(); + // if (waitForToastDisappear) + // this.agHelper.WaitUntilToastDisappear("datasource created"); + // else this.agHelper.AssertContains("datasource created"); + } + + public EditDatasource() { + this.agHelper.GetNClick(this._editButton); + } + + public ExpandSection(index: number) { + cy.get(this._collapseContainer) + .eq(index) + .click(); + cy.get(this._collapseContainer) + .eq(index) + .find(this.locator._chevronUp) + .should("be.visible"); + } + + public ExpandSectionByName(locator: string) { + // Click on collapse section only if it collapsed, if it is expanded + // we ignore + cy.get(`${locator} span`) + .invoke("attr", "icon") + .then((iconName) => { + if (iconName === "chevron-down") { + cy.get(locator).click(); + } + }); + } + + public AssertSectionCollapseState(index: number, collapsed = false) { + cy.get(this._collapseContainer) + .eq(index) + .within(() => { + if (collapsed) { + cy.get(this.locator._chevronUp).should("not.exist"); + } else { + cy.get(this.locator._chevronUp).should("exist"); + } + }); } public NavigateToDSCreateNew() { @@ -217,6 +268,7 @@ export class DataSources { // cy.get(this._dsCreateNewTab) // .should("be.visible") // .click({ force: true }); + cy.get(this._newDatasourceContainer).scrollTo("bottom"); cy.get(this._newDatabases).should("be.visible"); } @@ -244,7 +296,7 @@ export class DataSources { cy.get(this._databaseName) .clear() .type(databaseName); - cy.get(this._sectionAuthentication).click(); + this.ExpandSectionByName(this._sectionAuthentication); cy.get(this._username).type( username == "" ? datasourceFormData["postgres-username"] : username, ); @@ -259,7 +311,7 @@ export class DataSources { : datasourceFormData["mongo-host"]; cy.get(this._host).type(hostAddress); cy.get(this._port).type(datasourceFormData["mongo-port"].toString()); - cy.get(this._sectionAuthentication).click(); + this.ExpandSectionByName(this._sectionAuthentication); cy.get(this._databaseName) .clear() .type(datasourceFormData["mongo-databaseName"]); @@ -277,7 +329,7 @@ export class DataSources { cy.get(this._databaseName) .clear() .type(databaseName); - cy.get(this._sectionAuthentication).click(); + this.ExpandSectionByName(this._sectionAuthentication); cy.get(this._username).type(datasourceFormData["mysql-username"]); cy.get(this._password).type(datasourceFormData["mysql-password"]); } @@ -321,9 +373,9 @@ export class DataSources { } public SaveDatasource() { - cy.get(this._saveDs).click(); - this.agHelper.ValidateNetworkStatus("@saveDatasource", 200); - this.agHelper.AssertContains("datasource updated successfully"); + this.agHelper.GetNClick(this._saveDs); + this.agHelper.ValidateNetworkStatus("@saveDatasource", 201); + this.agHelper.AssertContains("datasource created"); // cy.wait("@saveDatasource") // .then((xhr) => { @@ -331,6 +383,17 @@ export class DataSources { // }).should("have.nested.property", "response.body.responseMeta.status", 200); } + public AuthAPISaveAndAuthorize() { + cy.get(this._saveAndAuthorizeDS).click(); + this.agHelper.ValidateNetworkStatus("@saveDatasource", 201); + } + + public updateDatasource() { + this.agHelper.GetNClick(this._saveDs); + // this.agHelper.ValidateNetworkStatus("@updateDatasource", 200); + this.agHelper.AssertContains("datasource updated"); + } + public DeleteDatasouceFromActiveTab( datasourceName: string, expectedRes = 200, @@ -363,8 +426,9 @@ export class DataSources { .should("be.visible") .click(); this.agHelper.Sleep(2000); //for the Datasource page to open - this.agHelper.ClickButton("Delete"); - this.agHelper.ClickButton("Are you sure?"); + //this.agHelper.ClickButton("Delete"); + this.agHelper.GetNClick(this.locator._visibleTextSpan("Delete")); + this.agHelper.GetNClick(this.locator._visibleTextSpan("Are you sure?")); this.agHelper.ValidateNetworkStatus("@deleteDatasource", expectedRes); if (expectedRes == 200) this.agHelper.AssertContains("datasource deleted successfully"); @@ -372,8 +436,8 @@ export class DataSources { } public DeleteDSDirectly() { - this.agHelper.ClickButton("Delete"); - this.agHelper.ClickButton("Are you sure?"); + this.agHelper.GetNClick(this.locator._visibleTextSpan("Delete")); + this.agHelper.GetNClick(this.locator._visibleTextSpan("Are you sure?")); this.agHelper.AssertContains("deleted successfully"); } @@ -564,7 +628,6 @@ export class DataSources { } else { this.SaveDatasource(); } - cy.wrap(dataSourceName).as("dsName"); }); } @@ -585,21 +648,12 @@ export class DataSources { //Click on Authenticated Graphql API cy.get(this._createGraphQLDatasource).click({ force: true }); //Verify weather Authenticated Graphql Datasource is successfully created. - cy.wait("@createDatasource").should( - "have.nested.property", - "response.body.responseMeta.status", - 201, - ); - + // this.agHelper.ValidateNetworkStatus("@saveDatasource", 201); this.FillGraphQLDSForm(datasourceName); // save datasource - cy.get(".t--save-datasource").click({ force: true }); - cy.wait("@saveDatasource").should( - "have.nested.property", - "response.body.responseMeta.status", - 200, - ); + this.agHelper.GetNClick(this._saveDs); + this.agHelper.ValidateNetworkStatus("@saveDatasource", 201); } public UpdateGraphqlQueryAndVariable(options?: { @@ -680,4 +734,48 @@ export class DataSources { this.agHelper.AssertAutoSave(); this.agHelper.GetNClick(this._queryResponse("QUERY")); } + + //Update with new password in the datasource conf page + public updatePassword(newPassword: string) { + this.ExpandSectionByName(this._sectionAuthentication); + cy.get(this._password).type(newPassword); + } + + //Fetch schema from server and validate UI for the updates + public verifySchema( + dataSourceName: string, + schema: string, + isUpdate = false, + ) { + cy.intercept("GET", this._getStructureReq).as("getDSStructure"); + if (isUpdate) { + this.updateDatasource(); + } else { + this.SaveDatasource(); + } + this.ee.ActionContextMenuByEntityName(dataSourceName, "Refresh"); + cy.wait("@getDSStructure").then(() => { + cy.get(".bp3-collapse-body").contains(schema); + }); + } + + public SaveDSFromDialog(save = true) { + this.agHelper.GoBack(); + if (save) { + this.agHelper.GetNClick( + this.locator._visibleTextSpan("SAVE"), + 0, + false, + 0, + ); + this.agHelper.ValidateNetworkStatus("@saveDatasource", 201); + this.agHelper.AssertContains("datasource created"); + } else + this.agHelper.GetNClick( + this.locator._visibleTextSpan("DON'T SAVE"), + 0, + false, + 0, + ); + } } diff --git a/app/client/cypress/support/Pages/EntityExplorer.ts b/app/client/cypress/support/Pages/EntityExplorer.ts index 0881e71134a7..77a8c468244f 100644 --- a/app/client/cypress/support/Pages/EntityExplorer.ts +++ b/app/client/cypress/support/Pages/EntityExplorer.ts @@ -56,6 +56,7 @@ export class EntityExplorer { "//div[contains(@class, 't--entity-name')][text()='" + modalName + "']/ancestor::div[contains(@class, 't--entity-item')]/following-sibling::div//div[contains(@class, 't--entity-name')][contains(text(), 'Text')]"; + private _newPageOptions = (option: string) => `[data-cy='${option}']`; public SelectEntityByName( entityNameinLeftSidebar: string, @@ -90,10 +91,8 @@ export class EntityExplorer { | "generate-page" | "add-page-from-template" = "add-page", ) { - cy.get(this.locator._newPage) - .first() - .click(); - cy.get(`[data-cy='${option}']`).click(); + this.agHelper.GetNClick(this.locator._newPage); + this.agHelper.GetNClick(this._newPageOptions(option)); if (option === "add-page") { this.agHelper.ValidateNetworkStatus("@createPage", 201); } @@ -230,4 +229,13 @@ export class EntityExplorer { else this.agHelper.Sleep(200); //do nothing }); } + + public RenameEntityFromExplorer(entityName: string, renameVal: string) { + cy.xpath(this._entityNameInExplorer(entityName)).dblclick(); + cy.xpath(this.locator._entityNameEditing(entityName)).type( + renameVal + "{enter}", + ); + this.AssertEntityPresenceInExplorer(renameVal); + this.agHelper.Sleep(); //allowing time for name change to reflect in EntityExplorer + } } diff --git a/app/client/cypress/support/Pages/GitSync.ts b/app/client/cypress/support/Pages/GitSync.ts index 5e2ef760d6b4..60e82d90158e 100644 --- a/app/client/cypress/support/Pages/GitSync.ts +++ b/app/client/cypress/support/Pages/GitSync.ts @@ -1,4 +1,5 @@ import { ObjectsRegistry } from "../Objects/Registry"; +const GITHUB_API_BASE = "https://api.github.com"; export class GitSync { public agHelper = ObjectsRegistry.AggregateHelper; @@ -7,14 +8,139 @@ export class GitSync { private _connectGitBottomBar = ".t--connect-git-bottom-bar"; private _gitSyncModal = ".git-sync-modal"; private _closeGitSyncModal = ".t--close-git-sync-modal"; + private _gitRepoInput = ".t--git-repo-input"; + private _useDefaultConfig = + "//span[text()='Use default configuration']/parent::div"; + private _gitConfigNameInput = ".t--git-config-name-input"; + private _gitConfigEmailInput = ".t--git-config-email-input"; + _branchButton = "[data-testid=t--branch-button-container]"; + private _branchSearchInput = ".t--branch-search-input"; - openGitSyncModal() { - cy.get(this._connectGitBottomBar).click(); - cy.get(this._gitSyncModal).should("be.visible"); + + OpenGitSyncModal() { + this.agHelper.GetNClick(this._connectGitBottomBar); + this.agHelper.AssertElementVisible(this._gitSyncModal); + } + + CloseGitSyncModal() { + this.agHelper.GetNClick(this._closeGitSyncModal); + this.agHelper.AssertElementAbsence(this._gitSyncModal); + } + + CreateNConnectToGit(repoName: string = "Test") { + this.agHelper.GenerateUUID(); + cy.get("@guid").then((uid) => { + repoName += uid; + this.CreateTestGithubRepo(repoName); + this.ConnectToGitRepo(repoName); + cy.wrap(repoName).as("gitRepoName"); + }); + } + + private ConnectToGitRepo(repo: string, assertConnect = true) { + // const testEmail = "test@test.com"; + // const testUsername = "testusername"; + const owner = Cypress.env("TEST_GITHUB_USER_NAME"); + let generatedKey; + this.OpenGitSyncModal(); + + cy.intercept( + { url: "api/v1/git/connect/app/*", hostname: window.location.host }, + (req) => { + req.headers["origin"] = "Cypress"; + }, + ); + + cy.intercept("POST", "/api/v1/applications/ssh-keypair/*").as( + `generateKey-${repo}`, + ); + + this.agHelper.AssertAttribute( + this._gitRepoInput, + "placeholder", + "git@example.com:user/repository.git", + ); + this.agHelper.TypeText( + this._gitRepoInput, + `git@github.com:${owner}/${repo}.git`, + ); + + this.agHelper.ClickButton("Generate key"); + + cy.wait(`@generateKey-${repo}`).then((result: any) => { + generatedKey = result.response.body.data.publicKey; + generatedKey = generatedKey.slice(0, generatedKey.length - 1); + // fetch the generated key and post to the github repo + cy.request({ + method: "POST", + url: `${GITHUB_API_BASE}/repos/${Cypress.env( + "TEST_GITHUB_USER_NAME", + )}/${repo}/keys`, + headers: { + Authorization: `token ${Cypress.env("GITHUB_PERSONAL_ACCESS_TOKEN")}`, + }, + body: { + title: "key0", + key: generatedKey, + }, + }); + + this.agHelper.GetNClick(this._useDefaultConfig); //Uncheck the Use default configuration + this.agHelper.TypeText( + this._gitConfigNameInput, + "testusername", + //`{selectall}${testUsername}`, + ); + this.agHelper.TypeText(this._gitConfigEmailInput, "test@test.com"); + this.agHelper.ClickButton("CONNECT"); + if (assertConnect) { + this.agHelper.ValidateNetworkStatus("@connectGitRepo"); + } + this.CloseGitSyncModal(); + }); + } + + private CreateTestGithubRepo(repo: string, privateFlag = false) { + cy.request({ + method: "POST", + url: `${GITHUB_API_BASE}/user/repos`, + headers: { + Authorization: `token ${Cypress.env("GITHUB_PERSONAL_ACCESS_TOKEN")}`, + }, + body: { + name: repo, + private: privateFlag, + }, + }); + } + + DeleteTestGithubRepo(repo: any) { + cy.request({ + method: "DELETE", + url: `${GITHUB_API_BASE}/repos/${Cypress.env( + "TEST_GITHUB_USER_NAME", + )}/${repo}`, + headers: { + Authorization: `token ${Cypress.env("GITHUB_PERSONAL_ACCESS_TOKEN")}`, + }, + }); } - closeGitSyncModal() { - cy.get(this._closeGitSyncModal).click(); - cy.get(this._gitSyncModal).should("not.exist"); + CreateGitBranch(branch: string = "Test") { + //this.agHelper.GenerateUUID(); + this.agHelper.GetNClick(this._branchButton); + this.agHelper.Sleep(2000); //branch pop up to open + cy.get("@guid").then((uid) => { + //using the same uid as generated during CreateNConnectToGit + this.agHelper.TypeText( + this._branchSearchInput, + `{selectall}` + `${branch + uid}` + `{enter}`, + 0, + true, + ); + cy.wrap(branch + uid).as("gitbranchName"); + }); + this.agHelper.AssertElementExist(this.locator._spinner); + this.agHelper.AssertElementAbsence(this.locator._spinner, 30000); } } diff --git a/app/client/cypress/support/Pages/HomePage.ts b/app/client/cypress/support/Pages/HomePage.ts index 5f473d2ea3a4..ff9029cfa0f1 100644 --- a/app/client/cypress/support/Pages/HomePage.ts +++ b/app/client/cypress/support/Pages/HomePage.ts @@ -27,11 +27,9 @@ export class HomePage { private _email = "//input[@type='text' and contains(@class,'bp3-input-ghost')]"; _visibleTextSpan = (spanText: string) => "//span[text()='" + spanText + "']"; - private _userRole = (role: string, workspaceName: string) => + private _userRole = (role: string) => "//div[contains(@class, 'label-container')]//span[1][text()='" + role + - " - " + - workspaceName + "']"; private _manageUsers = ".manageUsers"; @@ -53,8 +51,7 @@ export class HomePage { "//td[text()='" + email + "']/following-sibling::td//span[contains(@class, 't--deleteUser')]"; - private _userRoleDropDown = (role: string, WorkspaceName: string) => - "//span[text()='" + role + " - " + WorkspaceName + "']"; + private _userRoleDropDown = (role: string) => "//span[text()='" + role + "']"; //private _userRoleDropDown = (email: string) => "//td[text()='" + email + "']/following-sibling::td" private _leaveWorkspaceConfirmModal = ".t--member-delete-confirmation-modal"; private _workspaceImportAppModal = ".t--import-application-modal"; @@ -63,7 +60,7 @@ export class HomePage { private _lastWorkspaceInHomePage = "//div[contains(@class, 't--workspace-section')][last()]//span/span"; private _leaveWorkspace = "//span[text()='Leave Workspace']"; - private _leaveWorkspaceConfirm = "//span[text()='Are you sure?']" + private _leaveWorkspaceConfirm = "//span[text()='Are you sure?']"; _editPageLanding = "//h2[text()='Drag and drop a widget here']"; _usersEmailList = "[data-colindex='0']"; private _workspaceImport = "[data-cy=t--workspace-import-app]"; @@ -83,7 +80,7 @@ export class HomePage { "//span[text()='" + action + "']/ancestor::a"; public CreateNewWorkspace(workspaceNewName: string) { - let oldName: string = ""; + let oldName = ""; cy.xpath(this._visibleTextSpan("New Workspace")) .should("be.visible") .first() @@ -146,7 +143,7 @@ export class HomePage { .first() .click({ force: true }); this.agHelper.Sleep(500); - cy.xpath(this._userRole(role, workspaceName)).click({ force: true }); + cy.xpath(this._userRole(role)).click({ force: true }); this.agHelper.ClickButton("Invite"); cy.wait("@mockPostInvite") .its("request.headers") @@ -179,7 +176,7 @@ export class HomePage { } //Maps to CreateAppForWorkspace in command.js - public CreateAppInWorkspace(workspaceName: string, appname: string = "") { + public CreateAppInWorkspace(workspaceName: string, appname = "") { cy.xpath(this._existingWorkspaceCreateNewApp(workspaceName)) .scrollIntoView() .should("be.visible") @@ -204,6 +201,10 @@ export class HomePage { cy.get(this._applicationName).type(appName + "{enter}"); } + public GetAppName() { + return this.agHelper.GetText(this._applicationName, "text"); + } + //Maps to LogOut in command.js public LogOutviaAPI() { cy.request("POST", "/api/v1/logout"); @@ -313,12 +314,12 @@ export class HomePage { ) { this.OpenMembersPageForWorkspace(workspaceName); cy.log(workspaceName, email, currentRole); - cy.xpath(this._userRoleDropDown(currentRole, workspaceName)) + cy.xpath(this._userRoleDropDown(currentRole)) .first() .click({ force: true }); //cy.xpath(this._userRoleDropDown(email)).first().click({force: true}); - cy.xpath(this._visibleTextSpan(`${newRole} - ${workspaceName}`)) + cy.xpath(this._visibleTextSpan(`${newRole}`)) .last() .click({ force: true }); this.agHelper.Sleep(); @@ -335,11 +336,7 @@ export class HomePage { cy.xpath(this._uploadFile).attachFile(fixtureJson); this.agHelper.Sleep(3500); } - public InviteUserToWorkspaceFromApp( - workspaceName: string, - email: string, - role: string, - ) { + public InviteUserToWorkspaceFromApp(email: string, role: string) { const successMessage = "The user has been invited successfully"; this.StubPostHeaderReq(); cy.xpath(this._email) @@ -349,7 +346,7 @@ export class HomePage { .first() .click({ force: true }); this.agHelper.Sleep(500); - cy.xpath(this._userRole(role, workspaceName)).click({ force: true }); + cy.xpath(this._userRole(role)).click({ force: true }); this.agHelper.ClickButton("Invite"); cy.wait("@mockPostInvite") .its("request.headers") @@ -403,8 +400,8 @@ export class HomePage { cy.get(this._workspaceList(workspaceName)) .scrollIntoView() .should("be.visible"); - cy.get - (this._optionsIcon).first() + cy.get(this._optionsIcon) + .first() .click({ force: true }); cy.xpath(this._leaveWorkspace).click({ force: true }); cy.xpath(this._leaveWorkspaceConfirm).click({ force: true }); @@ -413,7 +410,8 @@ export class HomePage { "response.body.responseMeta.status", 200, ); - this.agHelper.ValidateToastMessage("You have successfully left the workspace"); + this.agHelper.ValidateToastMessage( + "You have successfully left the workspace", + ); } - } diff --git a/app/client/cypress/support/Pages/PropertyPane.ts b/app/client/cypress/support/Pages/PropertyPane.ts index 6a354a4550ce..19e8737764d3 100644 --- a/app/client/cypress/support/Pages/PropertyPane.ts +++ b/app/client/cypress/support/Pages/PropertyPane.ts @@ -37,6 +37,18 @@ export class PropertyPane { "']//ancestor::div[@class= 'space-y-1 group']"; private _jsonFieldConfigList = "//div[contains(@class, 't--property-control-fieldconfiguration group')]//div[contains(@class, 'content')]/div//input"; + private _tableEditColumnButton = ".t--edit-column-btn"; + private _tableColumnSettings = (column: string) => + `[data-rbd-draggable-id='${column}'] ${this._tableEditColumnButton}`; + private _sectionCollapse = (section: string) => + `.t--property-pane-section-collapse-${section}`; + private _sectionCollapseWithTag = (section: string, tab: string) => + `.t--property-pane-section-collapse-${section} .t--property-section-tag-${tab}`; + private _propertyControl = (property: string) => + `.t--property-control-${property}`; + _propertyPaneSearchInputWrapper = ".t--property-pane-search-input-wrapper"; + _propertyPaneSearchInput = `${this._propertyPaneSearchInputWrapper} input`; + _propertyPaneEmptySearchResult = ".t--property-pane-no-search-results"; _propertyToggle = (controlToToggle: string) => ".t--property-control-" + controlToToggle.replace(/ +/g, "").toLowerCase() + @@ -95,7 +107,15 @@ export class PropertyPane { this.agHelper.GetNClick(this._colorPickerV2Popover); this.agHelper.GetNClick(this._colorPickerV2Color, colorIndex); } else { - this.agHelper.GetElement(this._colorInput(type)).clear(); + this.agHelper + .GetElement(this._colorInput(type)) + .clear() + .wait(200); + this.agHelper.TypeText(this._colorInput(type), colorIndex); + this.agHelper + .GetElement(this._colorInput(type)) + .clear() + .wait(200); this.agHelper.TypeText(this._colorInput(type), colorIndex); //this.agHelper.UpdateInput(this._colorInputField(type), colorIndex);//not working! } @@ -161,11 +181,22 @@ export class PropertyPane { .click({ force: true }); } - public SelectPropertiesDropDown(endpoint: string, dropdownOption: string) { - cy.xpath(this.locator._selectPropDropdown(endpoint)) - .first() - .scrollIntoView() - .click(); + public SelectPropertiesDropDown( + endpoint: string, + dropdownOption: string, + action: "Action" | "Page" = "Action", + index = 0, + ) { + if (action == "Action") + this.agHelper.GetNClick( + this.locator._selectPropDropdown(endpoint), + index, + ); + else + this.agHelper.GetNClick( + this.locator._selectPropPageDropdown(endpoint), + index, + ); cy.get(this.locator._dropDownValue(dropdownOption)).click(); } @@ -258,4 +289,38 @@ export class PropertyPane { this.agHelper.AssertAutoSave(); //Allowing time for Evaluate value to capture value } + + public OpenTableColumnSettings(column: string) { + this.agHelper.GetNClick(this._tableColumnSettings(column)); + } + + public Search(query: string) { + cy.get(this._propertyPaneSearchInput) + .first() + .then((el: any) => { + cy.get(el).clear(); + if (query) cy.get(el).type(query, { force: true }); + }); + } + + public ToggleSection(section: string) { + this.agHelper.GetNClick(this._sectionCollapse(section)); + } + + public AssertSearchInputValue(value: string) { + this.agHelper.AssertText(this._propertyPaneSearchInput, "val", value); + } + + // Checks if the property exists in search results + public AssertIfPropertyOrSectionExists( + section: string, + tab: "CONTENT" | "STYLE", + property?: string, + ) { + this.agHelper.AssertElementExist( + this._sectionCollapseWithTag(section, tab), + ); + if (property) + this.agHelper.AssertElementExist(this._propertyControl(property)); + } } diff --git a/app/client/cypress/support/WorkspaceCommands.js b/app/client/cypress/support/WorkspaceCommands.js index 538539d4b175..64ef1ce389c2 100644 --- a/app/client/cypress/support/WorkspaceCommands.js +++ b/app/client/cypress/support/WorkspaceCommands.js @@ -296,6 +296,11 @@ Cypress.Commands.add("CreateAppInFirstListedWorkspace", (appname) => { //cy.get("#loading").should("not.exist"); // eslint-disable-next-line cypress/no-unnecessary-waiting //cy.reload(); + + cy.get("#loading").should("not.exist"); + // eslint-disable-next-line cypress/no-unnecessary-waiting + cy.wait(2000); + cy.AppSetupForRename(); cy.get(homePage.applicationName).type(appname + "{enter}"); cy.wait("@updateApplication").should( diff --git a/app/client/cypress/support/commands.js b/app/client/cypress/support/commands.js index ebcc317d372d..ab9397afd8a4 100644 --- a/app/client/cypress/support/commands.js +++ b/app/client/cypress/support/commands.js @@ -377,7 +377,7 @@ Cypress.Commands.add( cy.wait("@saveDatasource").should( "have.nested.property", "response.body.responseMeta.status", - 200, + 201, ); }, ); @@ -667,7 +667,7 @@ Cypress.Commands.add("getPluginFormsAndCreateDatasource", () => { "response.body.responseMeta.status", 200, ); - cy.wait("@createDatasource").should( + cy.wait("@saveDatasource").should( "have.nested.property", "response.body.responseMeta.status", 201, @@ -890,8 +890,9 @@ Cypress.Commands.add("setTinyMceContent", (tinyMceId, content) => { Cypress.Commands.add("startRoutesForDatasource", () => { cy.server(); - cy.route("PUT", "/api/v1/datasources/*").as("saveDatasource"); + cy.route("POST", "/api/v1/datasources").as("saveDatasource"); cy.route("POST", "/api/v1/datasources/test").as("testDatasource"); + cy.intercept("PUT", "/api/v1/datasources/*").as("updateDatasource"); }); Cypress.Commands.add("startServerAndRoutes", () => { @@ -899,7 +900,7 @@ Cypress.Commands.add("startServerAndRoutes", () => { cy.server(); cy.route("PUT", "/api/v1/themes/applications/*").as("updateTheme"); cy.route("POST", "/api/v1/datasources/test").as("testDatasource"); - cy.route("PUT", "/api/v1/datasources/*").as("saveDatasource"); + cy.route("POST", "/api/v1/datasources").as("saveDatasource"); cy.route("GET", "/api/v1/applications/new").as("applications"); cy.route("GET", "/api/v1/users/profile").as("getUser"); cy.route("GET", "/api/v1/plugins").as("getPlugins"); @@ -918,9 +919,12 @@ Cypress.Commands.add("startServerAndRoutes", () => { "getTemplateCollections", ); cy.route("PUT", "/api/v1/pages/*").as("updatePage"); + cy.route("PUT", "api/v1/applications/*/page/*/makeDefault").as( + "makePageDefault", + ); cy.route("DELETE", "/api/v1/applications/*").as("deleteApp"); cy.route("DELETE", "/api/v1/pages/*").as("deletePage"); - cy.route("POST", "/api/v1/datasources").as("createDatasource"); + //cy.route("POST", "/api/v1/datasources").as("createDatasource"); cy.route("DELETE", "/api/v1/datasources/*").as("deleteDatasource"); cy.route("GET", "/api/v1/datasources/*/structure?ignoreCache=*").as( "getDatasourceStructure", @@ -1008,6 +1012,7 @@ Cypress.Commands.add("startServerAndRoutes", () => { cy.intercept("GET", "/api/v1/app-templates").as("fetchTemplate"); cy.intercept("POST", "/api/v1/app-templates/*").as("importTemplate"); cy.intercept("GET", "/api/v1/app-templates/*").as("getTemplatePages"); + cy.intercept("PUT", "/api/v1/datasources/*").as("updateDatasource"); }); Cypress.Commands.add("startErrorRoutes", () => { diff --git a/app/client/cypress/support/dataSourceCommands.js b/app/client/cypress/support/dataSourceCommands.js index 170391fdfdea..6babb8cc50ce 100644 --- a/app/client/cypress/support/dataSourceCommands.js +++ b/app/client/cypress/support/dataSourceCommands.js @@ -3,12 +3,14 @@ require("cy-verify-downloads").addCustomCommand(); require("cypress-file-upload"); +import { ObjectsRegistry } from "../support/Objects/Registry"; const pages = require("../locators/Pages.json"); const datasourceEditor = require("../locators/DatasourcesEditor.json"); const datasourceFormData = require("../fixtures/datasources.json"); const explorer = require("../locators/explorerlocators.json"); const apiWidgetslocator = require("../locators/apiWidgetslocator.json"); const apiEditorLocators = require("../locators/ApiEditor"); +const dataSources = ObjectsRegistry.DataSources; const backgroundColorBlack = "rgb(0, 0, 0)"; const backgroundColorGray1 = "rgb(250, 250, 250)"; @@ -39,13 +41,11 @@ Cypress.Commands.add("testSaveDeleteDatasource", () => { cy.wait("@saveDatasource").should( "have.nested.property", "response.body.responseMeta.status", - 200, + 201, ); // select datasource to be deleted by datasource title - cy.get(`${datasourceEditor.datasourceCard}`) - .contains(datasourceTitle) - .last() - .click(); + cy.contains("EDIT").click(); + // delete datasource cy.get(".t--delete-datasource").click(); cy.get(".t--delete-datasource") @@ -92,7 +92,7 @@ Cypress.Commands.add("saveDatasource", () => { .then((xhr) => { cy.log(JSON.stringify(xhr.response.body)); }) - .should("have.nested.property", "response.body.responseMeta.status", 200); + .should("have.nested.property", "response.body.responseMeta.status", 201); }); Cypress.Commands.add("testSaveDatasource", (expectedRes = true) => { @@ -118,7 +118,7 @@ Cypress.Commands.add( //cy.get(datasourceEditor["selConnectionType"]).click(); //cy.contains(datasourceFormData["connection-type"]).click(); //cy.get(datasourceEditor["defaultDatabaseName"]).type(databaseName);//is optional hence removing - cy.get(datasourceEditor.sectionAuthentication).click(); + dataSources.ExpandSectionByName(datasourceEditor.sectionAuthentication); cy.get(datasourceEditor["databaseName"]) .clear() .type(datasourceFormData["mongo-databaseName"]); @@ -150,7 +150,7 @@ Cypress.Commands.add( cy.get(datasourceEditor.databaseName) .clear() .type(databaseName); - cy.get(datasourceEditor.sectionAuthentication).click(); + dataSources.ExpandSectionByName(datasourceEditor.sectionAuthentication); cy.get(datasourceEditor.username).type( datasourceFormData["postgres-username"], ); @@ -196,8 +196,7 @@ Cypress.Commands.add( cy.get(datasourceEditor.databaseName) .clear() .type(databaseName); - - cy.get(datasourceEditor.sectionAuthentication).click(); + dataSources.ExpandSectionByName(datasourceEditor.sectionAuthentication); cy.get(datasourceEditor.username).type( datasourceFormData["mysql-username"], ); @@ -222,8 +221,7 @@ Cypress.Commands.add( cy.get(datasourceEditor.databaseName) .clear() .type(databaseName); - - cy.get(datasourceEditor.sectionAuthentication).click(); + dataSources.ExpandSectionByName(datasourceEditor.sectionAuthentication); cy.get(datasourceEditor.username).type( datasourceFormData["mssql-username"], ); @@ -249,7 +247,7 @@ Cypress.Commands.add( .clear() .type(databaseName); - cy.get(datasourceEditor.sectionAuthentication).click(); + dataSources.ExpandSectionByName(datasourceEditor.sectionAuthentication); cy.get(datasourceEditor.username).type( datasourceFormData["arango-username"], ); @@ -274,8 +272,7 @@ Cypress.Commands.add( cy.get(datasourceEditor.databaseName) .clear() .type(databaseName); - - cy.get(datasourceEditor.sectionAuthentication).click(); + dataSources.ExpandSectionByName(datasourceEditor.sectionAuthentication); cy.get(datasourceEditor.username).type( datasourceFormData["redshift-username"], ); @@ -410,11 +407,11 @@ Cypress.Commands.add("createNewAuthApiDatasource", (renameVal) => { //Click on Authenticated API cy.get(apiWidgetslocator.createAuthApiDatasource).click(); //Verify weather Authenticated API is successfully created. - cy.wait("@createDatasource").should( - "have.nested.property", - "response.body.responseMeta.status", - 201, - ); + // cy.wait("@saveDatasource").should( + // "have.nested.property", + // "response.body.responseMeta.status", + // 201, + // ); cy.get(datasourceEditor.datasourceTitleLocator).click(); cy.get(`${datasourceEditor.datasourceTitleLocator} input`) .clear() @@ -461,7 +458,7 @@ Cypress.Commands.add("createGraphqlDatasource", (datasourceName) => { //Click on Authenticated Graphql API cy.get(apiEditorLocators.createGraphQLDatasource).click({ force: true }); //Verify weather Authenticated Graphql Datasource is successfully created. - cy.wait("@createDatasource").should( + cy.wait("@saveDatasource").should( "have.nested.property", "response.body.responseMeta.status", 201, @@ -483,7 +480,7 @@ Cypress.Commands.add("createGraphqlDatasource", (datasourceName) => { cy.wait("@saveDatasource").should( "have.nested.property", "response.body.responseMeta.status", - 200, + 201, ); }); diff --git a/app/client/cypress/support/gitSync.js b/app/client/cypress/support/gitSync.js index 8965f1d1a754..9281bbc07a83 100644 --- a/app/client/cypress/support/gitSync.js +++ b/app/client/cypress/support/gitSync.js @@ -32,6 +32,7 @@ Cypress.Commands.add("revokeAccessGit", (appName) => { expect(id).to.eq(""); }); }); + Cypress.Commands.add( "connectToGitRepo", (repo, shouldCommit = true, assertConnectFailure) => { @@ -123,6 +124,7 @@ Cypress.Commands.add( }); }, ); + Cypress.Commands.add("latestDeployPreview", () => { cy.server(); cy.route("POST", "/api/v1/applications/publish/*").as("publishApp"); diff --git a/app/client/cypress/support/index.js b/app/client/cypress/support/index.js index ab4babdef8d3..7bf56bc6d5bf 100644 --- a/app/client/cypress/support/index.js +++ b/app/client/cypress/support/index.js @@ -70,6 +70,16 @@ before(function() { Cypress.env("TESTPASSWORD2"), ); cy.LogOut(); + cy.SignupFromAPI( + Cypress.env("TESTUSERNAME3"), + Cypress.env("TESTPASSWORD3"), + ); + cy.LogOut(); + cy.SignupFromAPI( + Cypress.env("TESTUSERNAME4"), + Cypress.env("TESTPASSWORD4"), + ); + cy.LogOut(); } }); }); diff --git a/app/client/cypress/support/widgetCommands.js b/app/client/cypress/support/widgetCommands.js index 8e4a6dfa4642..109306c40830 100644 --- a/app/client/cypress/support/widgetCommands.js +++ b/app/client/cypress/support/widgetCommands.js @@ -48,20 +48,23 @@ Cypress.Commands.add("changeZoomLevel", (zoomValue) => { }); }); -Cypress.Commands.add("changeColumnType", (dataType) => { - cy.get(commonlocators.changeColType) - .last() - .click(); - cy.get(".t--dropdown-option") - .children() - .contains(dataType) - .click(); - cy.wait("@updateLayout").should( - "have.nested.property", - "response.body.responseMeta.status", - 200, - ); - /* +Cypress.Commands.add( + "changeColumnType", + (dataType, doesPropertyTabExist = true) => { + if (doesPropertyTabExist) cy.moveToContentTab(); + cy.get(commonlocators.changeColType) + .last() + .click(); + cy.get(".t--dropdown-option") + .children() + .contains(dataType) + .click(); + cy.wait("@updateLayout").should( + "have.nested.property", + "response.body.responseMeta.status", + 200, + ); + /* cy.get(commonlocators.selectedColType) .first() .invoke("text") @@ -70,7 +73,8 @@ Cypress.Commands.add("changeColumnType", (dataType) => { expect(someText).to.equal(dataType); }); */ -}); + }, +); Cypress.Commands.add("switchToPaginationTab", () => { cy.get(apiwidget.paginationTab) @@ -431,6 +435,37 @@ Cypress.Commands.add("updateComputedValueV2", (value) => { cy.wait(1000); }); +Cypress.Commands.add("testCodeMirrorWithIndex", (value, index) => { + cy.EnableAllCodeEditors(); + cy.get(".CodeMirror textarea") + .eq(index) + .focus() + .type("{ctrl}{shift}{downarrow}", { force: true }) + .then(($cm) => { + if ($cm.val() !== "") { + cy.get(".CodeMirror textarea") + .eq(index) + .clear({ + force: true, + }); + } + + cy.get(".CodeMirror textarea") + .eq(index) + .type("{ctrl}{shift}{downarrow}", { force: true }) + .clear({ force: true }) + .type(value, { + force: true, + parseSpecialCharSequences: false, + }); + // eslint-disable-next-line cypress/no-unnecessary-waiting + cy.wait(200); + cy.get(".CodeMirror textarea") + .eq(index) + .should("have.value", value); + }); +}); + Cypress.Commands.add("testCodeMirrorLast", (value) => { cy.EnableAllCodeEditors(); cy.get(".CodeMirror textarea") @@ -678,6 +713,7 @@ Cypress.Commands.add("assertControlVisibility", (endp) => { }); Cypress.Commands.add("tableColumnDataValidation", (columnName) => { + cy.backFromPropertyPanel(); cy.get("[data-rbd-draggable-id='" + columnName + "'] input") .scrollIntoView() .first() @@ -686,6 +722,7 @@ Cypress.Commands.add("tableColumnDataValidation", (columnName) => { }); Cypress.Commands.add("tableV2ColumnDataValidation", (columnName) => { + cy.backFromPropertyPanel(); cy.get("[data-rbd-draggable-id='" + columnName + "'] input[type='text']") .scrollIntoView() .first() @@ -694,6 +731,7 @@ Cypress.Commands.add("tableV2ColumnDataValidation", (columnName) => { }); Cypress.Commands.add("tableColumnPopertyUpdate", (colId, newColName) => { + cy.backFromPropertyPanel(); cy.get("[data-rbd-draggable-id='" + colId + "'] input") .scrollIntoView() .should("be.visible") @@ -712,6 +750,7 @@ Cypress.Commands.add("tableColumnPopertyUpdate", (colId, newColName) => { }); Cypress.Commands.add("tableV2ColumnPopertyUpdate", (colId, newColName) => { + cy.backFromPropertyPanel(); cy.get("[data-rbd-draggable-id='" + colId + "'] input[type='text']") .scrollIntoView() .should("be.visible") @@ -732,7 +771,20 @@ Cypress.Commands.add("tableV2ColumnPopertyUpdate", (colId, newColName) => { .should("be.visible"); }); +Cypress.Commands.add("backFromPropertyPanel", () => { + cy.wait(500); + cy.get("body").then(($body) => { + let count = $body.find(commonlocators.editPropBackButton)?.length || 0; + if (count > 0) { + cy.get(commonlocators.editPropBackButton).click({ force: true }); + cy.wait(500); + cy.backFromPropertyPanel(); + } + }); +}); + Cypress.Commands.add("hideColumn", (colId) => { + cy.backFromPropertyPanel(); cy.get("[data-rbd-draggable-id='" + colId + "'] .t--show-column-btn").click({ force: true, }); @@ -741,6 +793,7 @@ Cypress.Commands.add("hideColumn", (colId) => { }); Cypress.Commands.add("showColumn", (colId) => { + cy.backFromPropertyPanel(); cy.get("[data-rbd-draggable-id='" + colId + "'] .t--show-column-btn").click({ force: true, }); @@ -749,6 +802,7 @@ Cypress.Commands.add("showColumn", (colId) => { .should("be.visible"); }); Cypress.Commands.add("deleteColumn", (colId) => { + cy.backFromPropertyPanel(); cy.get("[data-rbd-draggable-id='" + colId + "'] .t--delete-column-btn").click( { force: true, @@ -758,17 +812,24 @@ Cypress.Commands.add("deleteColumn", (colId) => { cy.wait(1000); }); -Cypress.Commands.add("openFieldConfiguration", (fieldIdentifier) => { - cy.get( - "[data-rbd-draggable-id='" + fieldIdentifier + "'] .t--edit-column-btn", - ).click({ - force: true, - }); - // eslint-disable-next-line cypress/no-unnecessary-waiting - cy.wait(1000); -}); +Cypress.Commands.add( + "openFieldConfiguration", + (fieldIdentifier, shouldClosePanel = true) => { + if (shouldClosePanel) { + cy.backFromPropertyPanel(); + } + cy.get( + "[data-rbd-draggable-id='" + fieldIdentifier + "'] .t--edit-column-btn", + ).click({ + force: true, + }); + // eslint-disable-next-line cypress/no-unnecessary-waiting + cy.wait(1000); + }, +); Cypress.Commands.add("deleteJSONFormField", (fieldIdentifier) => { + cy.backFromPropertyPanel(); cy.get( "[data-rbd-draggable-id='" + fieldIdentifier + "'] .t--delete-column-btn", ).click({ @@ -779,6 +840,7 @@ Cypress.Commands.add("deleteJSONFormField", (fieldIdentifier) => { }); Cypress.Commands.add("makeColumnVisible", (colId) => { + cy.backFromPropertyPanel(); cy.get("[data-rbd-draggable-id='" + colId + "'] .t--show-column-btn").click({ force: true, }); @@ -811,7 +873,10 @@ Cypress.Commands.add("addColumnV2", (colId) => { cy.get(widgetsPage.defaultColNameV2).type(colId, { force: true }); }); -Cypress.Commands.add("editColumn", (colId) => { +Cypress.Commands.add("editColumn", (colId, shouldReturnToMainPane = true) => { + if (shouldReturnToMainPane) { + cy.backFromPropertyPanel(); + } cy.get("[data-rbd-draggable-id='" + colId + "'] .t--edit-column-btn").click({ force: true, }); @@ -852,8 +917,8 @@ Cypress.Commands.add("addAction", (value, property) => { cy.enterActionValue(value, property); }); -Cypress.Commands.add("addEvent", (value) => { - cy.get(commonlocators.dropdownSelectButton) +Cypress.Commands.add("addEvent", (value, selector) => { + cy.get(selector + " " + commonlocators.dropdownSelectButton) .last() .click(); cy.get(commonlocators.chooseAction) @@ -892,6 +957,23 @@ Cypress.Commands.add("addSuccessMessage", (value) => { cy.enterActionValue(value); }); +Cypress.Commands.add("selectResetWidget", () => { + cy.get(commonlocators.chooseAction) + .children() + .contains("Reset widget") + .click(); +}); + +Cypress.Commands.add("selectWidgetForReset", (value) => { + cy.get(commonlocators.chooseWidget) + .last() + .click({ force: true }); + cy.get(commonlocators.chooseAction) + .children() + .contains(value) + .click(); +}); + Cypress.Commands.add("SetDateToToday", () => { cy.get(formWidgetsPage.datepickerFooterPublish) .contains("Today") @@ -1581,3 +1663,62 @@ Cypress.Commands.add("moveToContentTab", () => { .first() .click({ force: true }); }); + +Cypress.Commands.add("openPropertyPaneWithIndex", (widgetType, index) => { + const selector = `.t--draggable-${widgetType}`; + cy.wait(500); + cy.get(selector) + .eq(index) + .scrollIntoView() + .trigger("mouseover", { force: true }) + .wait(500); + cy.get( + `${selector}:first-of-type .t--widget-propertypane-toggle > .t--widget-name`, + ) + .eq(index) + .scrollIntoView() + .click({ force: true }); + // eslint-disable-next-line cypress/no-unnecessary-waiting + cy.wait(1000); +}); + +Cypress.Commands.add("changeLayoutHeight", (locator) => { + cy.get(".t--property-control-height .remixicon-icon") + .scrollIntoView() + .click({ force: true }); + cy.get(locator).click({ force: true }); + cy.wait("@updateLayout").should( + "have.nested.property", + "response.body.responseMeta.status", + 200, + ); +}); + +Cypress.Commands.add("changeLayoutHeightWithoutWait", (locator) => { + cy.get(".t--property-control-height .remixicon-icon") + .scrollIntoView() + .click({ force: true }); + cy.get(locator).click({ force: true }); +}); + +Cypress.Commands.add("checkMinDefaultValue", (endp, value) => { + cy.get(".cm-m-null") + .first() + .invoke("text") + .then((text) => { + const someText = text; + cy.log(someText); + expect(someText).to.equal(value); + }); +}); + +Cypress.Commands.add("checkMaxDefaultValue", (endp, value) => { + cy.get(".cm-m-null") + .last() + .invoke("text") + .then((text) => { + const someText = text; + cy.log(someText); + expect(someText).to.equal(value); + }); +}); diff --git a/app/client/generators/widget/templates/index.js.hbs b/app/client/generators/widget/templates/index.js.hbs index 49d9092a0b66..99ca3872db38 100644 --- a/app/client/generators/widget/templates/index.js.hbs +++ b/app/client/generators/widget/templates/index.js.hbs @@ -7,6 +7,9 @@ export const CONFIG = { iconSVG: IconSVG, needsMeta: false, // Defines if this widget adds any meta properties isCanvas: false, // Defines if this widget has a canvas within in which we can drop other widgets + features: { + dynamicHeight: false, + }, defaults: { widgetName: "{{name}}", rows: 1, diff --git a/app/client/package.json b/app/client/package.json index c0827508440a..f62fda0c2334 100644 --- a/app/client/package.json +++ b/app/client/package.json @@ -13,7 +13,6 @@ "@blueprintjs/icons": "^3.10.0", "@blueprintjs/popover2": "^0.5.0", "@blueprintjs/select": "^3.10.0", - "@craco/craco": "^7.0.0-alpha.3", "@draft-js-plugins/editor": "^4.1.0", "@draft-js-plugins/mention": "^4.5.1", "@fusioncharts/powercharts": "^3.16.0", @@ -37,25 +36,23 @@ "algoliasearch": "^4.2.0", "astring": "^1.7.5", "axios": "^0.27.2", - "caniuse-lite": "^1.0.30001208", "classnames": "^2.3.1", "codemirror": "^5.59.2", "codemirror-graphql": "^1.2.14", "copy-to-clipboard": "^3.3.1", "core-js": "^3.9.1", + "country-flag-emoji-polyfill": "^0.1.4", "craco-alias": "^2.1.1", "cypress-log-to-output": "^1.1.2", "dayjs": "^1.10.6", "deep-diff": "^1.0.2", - "design-system": "npm:@appsmithorg/design-system@1.0.32", + "design-system": "npm:@appsmithorg/design-system@1.0.39", "downloadjs": "^1.4.7", "draft-js": "^0.11.7", - "emoji-mart": "^3.0.1", "exceljs-lightweight": "^1.14.0", "fast-deep-equal": "^3.1.3", "fast-xml-parser": "^3.17.5", "fastdom": "^1.0.11", - "flow-bin": "^0.148.0", "focus-trap-react": "^8.9.2", "fuse.js": "^3.4.5", "fusioncharts": "^3.18.0", @@ -66,20 +63,17 @@ "husky": "^3.0.5", "immer": "^9.0.6", "instantsearch.css": "^7.4.2", - "instantsearch.js": "^4.4.1", "interweave": "^12.7.2", "interweave-autolink": "^4.4.2", "js-beautify": "^1.14.0", "js-sha256": "^0.9.0", "jshint": "^2.13.4", - "json-fn": "^1.1.1", "klona": "^2.0.5", "libphonenumber-js": "^1.9.44", "lint-staged": "^13.0.3", "localforage": "^1.7.3", "lodash": "^4.17.21", "lodash-es": "4.17.21", - "lodash-move": "^1.1.1", "loglevel": "^1.7.1", "lottie-web": "^5.7.4", "mammoth": "^1.4.19", @@ -102,7 +96,6 @@ "rc-tree-select": "^5.4.0", "re-reselect": "^3.4.0", "react": "^16.12.0", - "react-base-table": "^1.9.1", "react-beautiful-dnd": "^12.2.0", "react-custom-scrollbars": "^4.2.1", "react-device-detect": "^2.2.2", @@ -117,12 +110,10 @@ "react-google-recaptcha": "^2.1.0", "react-helmet": "^5.2.1", "react-hook-form": "^7.28.0", - "react-infinite-scroller": "^1.2.4", "react-instantsearch-dom": "^6.4.0", "react-json-view": "^1.21.3", "react-masonry-css": "^1.0.16", "react-media-recorder": "^1.6.1", - "react-mentions": "^4.1.1", "react-modal": "^3.15.1", "react-page-visibility": "^7.0.0", "react-paginating": "^1.4.0", @@ -136,12 +127,11 @@ "react-scripts": "^5.0.1", "react-select": "^3.0.8", "react-spring": "^9.4.0", - "react-syntax-highlighter": "^15.4.4", + "react-syntax-highlighter": "^15.5.0", "react-table": "^7.0.0", "react-tabs": "^3.0.0", "react-timer-hook": "^3.0.4", "react-toastify": "^5.5.0", - "react-transition-group": "^4.3.0", "react-use-gesture": "^7.0.4", "react-virtuoso": "^1.9.0", "react-webcam": "^7.0.1", @@ -155,13 +145,12 @@ "scroll-into-view-if-needed": "^2.2.26", "shallowequal": "^1.1.0", "showdown": "^1.9.1", - "smartlook-client": "^4.5.1", - "socket.io-client": "^4.5.1", + "smartlook-client": "^8.0.0", + "socket.io-client": "^4.5.4", "styled-components": "^5.2.0", "tern": "^0.21.0", "tinycolor2": "^1.4.2", "toposort": "^2.0.2", - "ts-loader": "^6.0.4", "tslib": "^2.3.1", "typescript": "4.5.5", "unescape-js": "^1.1.4", @@ -198,6 +187,7 @@ "devDependencies": { "@babel/helper-create-regexp-features-plugin": "^7.18.6", "@babel/helper-string-parser": "^7.19.4", + "@craco/craco": "^7.0.0", "@faker-js/faker": "^7.4.0", "@sentry/webpack-plugin": "^1.18.9", "@testing-library/jest-dom": "5.16.1", @@ -209,7 +199,6 @@ "@types/dom-mediacapture-record": "^1.0.11", "@types/downloadjs": "^1.4.2", "@types/draft-js": "^0.11.1", - "@types/emoji-mart": "^3.0.4", "@types/jest": "^27.4.1", "@types/js-beautify": "^1.13.2", "@types/jshint": "^2.12.0", @@ -293,6 +282,7 @@ "semver": "^7.3.5", "ts-jest": "27.0.0", "ts-jest-mock-import-meta": "^0.12.0", + "ts-loader": "^9.4.1", "webpack-merge": "^5.8.0", "workbox-webpack-plugin": "^6.5.3" }, @@ -310,6 +300,8 @@ "focus-trap-react/**/tabbable": "5.2.1", "json-schema": "0.4.0", "node-fetch": "2.6.7", - "babel-plugin-styled-components": "2.0.7" + "babel-plugin-styled-components": "2.0.7", + "minimatch": "^5.0.0", + "loader-utils": "^2.0.4" } } diff --git a/app/client/public/index.html b/app/client/public/index.html index a14fd12f76d2..356c1c901975 100755 --- a/app/client/public/index.html +++ b/app/client/public/index.html @@ -3,8 +3,6 @@ - Appsmith diff --git a/app/client/public/manifest.json b/app/client/public/manifest.json index 935423bfede2..ef61a85e57fb 100755 --- a/app/client/public/manifest.json +++ b/app/client/public/manifest.json @@ -3,7 +3,7 @@ "name": "Appsmith Client Web UI", "icons": [ { - "src": "favicon-orange.ico", + "src": "static/img/favicon-orange.ico", "sizes": "64x64 32x32 24x24 16x16", "type": "image/x-icon" } diff --git a/app/client/public/static/img/appsmith-logo.svg b/app/client/public/static/img/appsmith-logo.svg new file mode 100644 index 000000000000..ffb571c3a21b --- /dev/null +++ b/app/client/public/static/img/appsmith-logo.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/app/client/public/favicon-orange.ico b/app/client/public/static/img/favicon-orange.ico similarity index 100% rename from app/client/public/favicon-orange.ico rename to app/client/public/static/img/favicon-orange.ico diff --git a/app/client/src/AppRouter.tsx b/app/client/src/AppRouter.tsx index 138d52cfb94a..5f292412068a 100644 --- a/app/client/src/AppRouter.tsx +++ b/app/client/src/AppRouter.tsx @@ -3,7 +3,6 @@ import history from "utils/history"; import AppHeader from "pages/common/AppHeader"; import { Redirect, Route, Router, Switch } from "react-router-dom"; import { - ADMIN_SETTINGS_CATEGORY_DEFAULT_PATH, ADMIN_SETTINGS_CATEGORY_PATH, ADMIN_SETTINGS_PATH, APPLICATIONS_URL, @@ -36,17 +35,14 @@ import LandingScreen from "./LandingScreen"; import UserAuth from "pages/UserAuth"; import Users from "pages/users"; import ErrorPage from "pages/common/ErrorPage"; -import PageNotFound from "pages/common/PageNotFound"; +import PageNotFound from "pages/common/ErrorPages/PageNotFound"; import PageLoadingBar from "pages/common/PageLoadingBar"; import ErrorPageHeader from "pages/common/ErrorPageHeader"; -import { getCurrentThemeDetails, ThemeMode } from "selectors/themeSelectors"; import { AppState } from "@appsmith/reducers"; -import { setThemeMode } from "actions/themeActions"; -import { connect } from "react-redux"; +import { connect, useSelector } from "react-redux"; +import { polyfillCountryFlagEmojis } from "country-flag-emoji-polyfill"; import * as Sentry from "@sentry/react"; -import AnalyticsUtil from "utils/AnalyticsUtil"; -import { trimTrailingSlash } from "utils/helpers"; import { getSafeCrash, getSafeCrashCode } from "selectors/errorSelectors"; import UserProfile from "pages/UserProfile"; import { getCurrentUser } from "actions/authActions"; @@ -54,58 +50,46 @@ import { selectFeatureFlags } from "selectors/usersSelectors"; import Setup from "pages/setup"; import Settings from "@appsmith/pages/AdminSettings"; import SignupSuccess from "pages/setup/SignupSuccess"; -import { Theme } from "constants/DefaultTheme"; import { ERROR_CODES } from "@appsmith/constants/ApiConstants"; import TemplatesListLoader from "pages/Templates/loader"; import { fetchFeatureFlagsInit } from "actions/userActions"; import FeatureFlags from "entities/FeatureFlags"; import WDSPage from "components/wds/Showcase"; import { getCurrentTenant } from "@appsmith/actions/tenantActions"; +import { getDefaultAdminSettingsPath } from "@appsmith/utils/adminSettingsHelpers"; +import { getCurrentUser as getCurrentUserSelector } from "selectors/usersSelectors"; +import { getTenantPermissions } from "@appsmith/selectors/tenantSelectors"; +import useBrandingTheme from "utils/hooks/useBrandingTheme"; + +/* + We use this polyfill to show emoji flags + on windows devices, this polyfill loads a font family + */ +polyfillCountryFlagEmojis(); const SentryRoute = Sentry.withSentryRouting(Route); const loadingIndicator = ; -function changeAppBackground(currentTheme: any) { - if ( - trimTrailingSlash(window.location.pathname) === "/applications" || - window.location.pathname.indexOf("/settings/") !== -1 || - trimTrailingSlash(window.location.pathname) === "/profile" || - trimTrailingSlash(window.location.pathname) === "/signup-success" - ) { - document.body.style.backgroundColor = - currentTheme.colors.homepageBackground; - } else { - document.body.style.backgroundColor = currentTheme.colors.appBackground; - } -} - function AppRouter(props: { safeCrash: boolean; getCurrentUser: () => void; getFeatureFlags: () => void; getCurrentTenant: () => void; - currentTheme: Theme; safeCrashCode?: ERROR_CODES; featureFlags: FeatureFlags; - setTheme: (theme: ThemeMode) => void; }) { const { getCurrentTenant, getCurrentUser, getFeatureFlags } = props; useEffect(() => { - AnalyticsUtil.logEvent("ROUTE_CHANGE", { path: window.location.pathname }); - const stopListener = history.listen((location: any) => { - AnalyticsUtil.logEvent("ROUTE_CHANGE", { path: location.pathname }); - changeAppBackground(props.currentTheme); - }); getCurrentUser(); getFeatureFlags(); getCurrentTenant(); - return stopListener; }, []); - useEffect(() => { - changeAppBackground(props.currentTheme); - }, [props.currentTheme]); + useBrandingTheme(); + + const user = useSelector(getCurrentUserSelector); + const tenantPermissions = useSelector(getTenantPermissions); return ( @@ -146,7 +130,10 @@ function AppRouter(props: { ({ - currentTheme: getCurrentThemeDetails(state), safeCrash: getSafeCrash(state), safeCrashCode: getSafeCrashCode(state), featureFlags: selectFeatureFlags(state), }); const mapDispatchToProps = (dispatch: any) => ({ - setTheme: (mode: ThemeMode) => { - dispatch(setThemeMode(mode)); - }, getCurrentUser: () => dispatch(getCurrentUser()), getFeatureFlags: () => dispatch(fetchFeatureFlagsInit()), getCurrentTenant: () => dispatch(getCurrentTenant()), diff --git a/app/client/src/LandingScreen.tsx b/app/client/src/LandingScreen.tsx index e557bedf125e..bf66ac8a81c1 100755 --- a/app/client/src/LandingScreen.tsx +++ b/app/client/src/LandingScreen.tsx @@ -6,7 +6,7 @@ import { ANONYMOUS_USERNAME, User } from "constants/userConstants"; import { Redirect } from "react-router"; import { APPLICATIONS_URL, AUTH_LOGIN_URL, BASE_URL } from "constants/routes"; import PageLoadingBar from "pages/common/PageLoadingBar"; -import ServerUnavailable from "pages/common/ServerUnavailable"; +import ServerUnavailable from "pages/common/ErrorPages/ServerUnavailable"; type Props = { user?: User; diff --git a/app/client/src/RouteBuilder.ts b/app/client/src/RouteBuilder.ts index 0c7a9b4a545a..f77214cfe270 100644 --- a/app/client/src/RouteBuilder.ts +++ b/app/client/src/RouteBuilder.ts @@ -17,6 +17,7 @@ export type URLBuilderParams = { hash?: string; params?: Record; pageId: string; + persistExistingParams?: boolean; }; export const fillPathname = ( @@ -49,12 +50,6 @@ export function getQueryStringfromObject( return queryParams.length ? "?" + queryParams.join("&") : ""; } -export const pageListEditorURL = (props: URLBuilderParams): string => { - return urlBuilder.build({ - ...props, - suffix: "pages", - }); -}; export const datasourcesEditorURL = (props: URLBuilderParams): string => urlBuilder.build({ ...props, diff --git a/app/client/src/RouteParamsMiddleware.ts b/app/client/src/RouteParamsMiddleware.ts index 0225d81390f6..77d0a6130c4e 100644 --- a/app/client/src/RouteParamsMiddleware.ts +++ b/app/client/src/RouteParamsMiddleware.ts @@ -4,6 +4,7 @@ import { ReduxAction, ReduxActionTypes, } from "@appsmith/constants/ReduxActionConstants"; +import { UpdatePageResponse } from "api/PageApi"; import urlBuilder, { ApplicationURLParams, PageURLParams, @@ -69,7 +70,7 @@ const routeParamsMiddleware: Middleware = () => (next: any) => ( break; } case ReduxActionTypes.UPDATE_PAGE_SUCCESS: { - const page = action.payload; + const page: UpdatePageResponse = action.payload; pageParams = [ { pageSlug: page.slug, diff --git a/app/client/src/actions/apiPaneActions.ts b/app/client/src/actions/apiPaneActions.ts index 78fa168edb4e..d9dc4928455c 100644 --- a/app/client/src/actions/apiPaneActions.ts +++ b/app/client/src/actions/apiPaneActions.ts @@ -116,3 +116,10 @@ export const setApiPaneResponsePaneHeight: ( type: ReduxActionTypes.SET_API_PANE_RESPONSE_PANE_HEIGHT, payload: { height: payload }, }); + +export const setApiRightPaneSelectedTab: ( + payload: number, +) => ReduxAction<{ selectedTab: number }> = (payload: number) => ({ + type: ReduxActionTypes.SET_API_RIGHT_PANE_SELECTED_TAB, + payload: { selectedTab: payload }, +}); diff --git a/app/client/src/actions/appSettingsPaneActions.ts b/app/client/src/actions/appSettingsPaneActions.ts new file mode 100644 index 000000000000..0da800589d70 --- /dev/null +++ b/app/client/src/actions/appSettingsPaneActions.ts @@ -0,0 +1,16 @@ +import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants"; +import { AppSettingsPaneContext } from "reducers/uiReducers/appSettingsPaneReducer"; +import { Action } from "redux"; + +export const openAppSettingsPaneAction = (context?: AppSettingsPaneContext) => { + return { + type: ReduxActionTypes.OPEN_APP_SETTINGS_PANE, + payload: context, + }; +}; + +export const closeAppSettingsPaneAction = (): Action => { + return { + type: ReduxActionTypes.CLOSE_APP_SETTINGS_PANE, + }; +}; diff --git a/app/client/src/actions/applicationActions.ts b/app/client/src/actions/applicationActions.ts index d3117390b7df..30c984ab1955 100644 --- a/app/client/src/actions/applicationActions.ts +++ b/app/client/src/actions/applicationActions.ts @@ -5,6 +5,7 @@ import { ImportApplicationRequest, FetchApplicationPayload, } from "api/ApplicationApi"; +import { AppIconName } from "design-system"; import { Datasource } from "entities/Datasource"; export enum ApplicationVersion { @@ -60,6 +61,13 @@ export const updateApplication = ( }; }; +export const updateCurrentApplicationIcon = (icon: AppIconName) => { + return { + type: ReduxActionTypes.CURRENT_APPLICATION_ICON_UPDATE, + payload: icon, + }; +}; + export const publishApplication = (applicationId: string) => { return { type: ReduxActionTypes.PUBLISH_APPLICATION_INIT, diff --git a/app/client/src/actions/autoHeightActions.ts b/app/client/src/actions/autoHeightActions.ts index 50cdca16ed5b..72247d1180f7 100644 --- a/app/client/src/actions/autoHeightActions.ts +++ b/app/client/src/actions/autoHeightActions.ts @@ -20,13 +20,13 @@ export function setAutoHeightLayoutTreeAction( } export function generateAutoHeightLayoutTreeAction( - shouldCheckContainersForDynamicHeightUpdates: boolean, + shouldCheckContainersForAutoHeightUpdates: boolean, layoutUpdated?: boolean, ) { return { type: ReduxActionTypes.GENERATE_AUTO_HEIGHT_LAYOUT_TREE, payload: { - shouldCheckContainersForDynamicHeightUpdates, + shouldCheckContainersForAutoHeightUpdates, layoutUpdated: !!layoutUpdated, }, }; diff --git a/app/client/src/actions/controlActions.tsx b/app/client/src/actions/controlActions.tsx index 1962f772e7c8..7bc6442f5436 100644 --- a/app/client/src/actions/controlActions.tsx +++ b/app/client/src/actions/controlActions.tsx @@ -1,6 +1,7 @@ import { ReduxActionTypes, ReduxAction, + ReduxActionType, } from "@appsmith/constants/ReduxActionConstants"; import { UpdateWidgetsPayload } from "reducers/entityReducers/canvasWidgetsReducer"; import { DynamicPath } from "utils/DynamicBindingUtils"; @@ -24,6 +25,7 @@ export interface BatchPropertyUpdatePayload { modify?: Record; //Key value pairs of paths and values to update remove?: string[]; //Array of paths to delete triggerPaths?: string[]; // Array of paths in the modify and remove list which are trigger paths + postUpdateAction?: ReduxActionType; // Array of action types we need to dispatch after propert updates. } export const batchUpdateWidgetProperty = ( diff --git a/app/client/src/actions/datasourceActions.ts b/app/client/src/actions/datasourceActions.ts index 29c4e8622bd4..5a703cf1285b 100644 --- a/app/client/src/actions/datasourceActions.ts +++ b/app/client/src/actions/datasourceActions.ts @@ -8,11 +8,27 @@ import { Datasource } from "entities/Datasource"; import { PluginType } from "entities/Action"; import { executeDatasourceQueryRequest } from "api/DatasourcesApi"; import { ResponseMeta } from "api/ApiResponses"; +import { TEMP_DATASOURCE_ID } from "constants/Datasource"; -export const createDatasourceFromForm = (payload: CreateDatasourceConfig) => { +export const createDatasourceFromForm = ( + payload: CreateDatasourceConfig & Datasource, + onSuccess?: ReduxAction, + onError?: ReduxAction, +) => { return { type: ReduxActionTypes.CREATE_DATASOURCE_FROM_FORM_INIT, payload, + onSuccess, + onError, + }; +}; + +export const createTempDatasourceFromForm = ( + payload: CreateDatasourceConfig | Datasource, +) => { + return { + type: ReduxActionTypes.CREATE_TEMP_DATASOURCE_FROM_FORM_SUCCESS, + payload, }; }; @@ -36,6 +52,13 @@ export type UpdateDatasourceSuccessAction = { queryParams?: Record; }; +export type CreateDatasourceSuccessAction = { + type: string; + payload: Datasource; + isDBCreated: boolean; + redirect: boolean; +}; + export const updateDatasourceSuccess = ( payload: Datasource, redirect = true, @@ -47,6 +70,17 @@ export const updateDatasourceSuccess = ( queryParams, }); +export const createDatasourceSuccess = ( + payload: Datasource, + isDBCreated = false, + redirect = false, +): CreateDatasourceSuccessAction => ({ + type: ReduxActionTypes.CREATE_DATASOURCE_SUCCESS, + payload, + isDBCreated, + redirect, +}); + export const redirectAuthorizationCode = ( pageId: string, datasourceId: string, @@ -93,6 +127,14 @@ export const saveDatasourceName = (payload: { id: string; name: string }) => ({ payload: payload, }); +export const updateDatasourceName = (payload: { + id: string; + name: string; +}) => ({ + type: ReduxActionTypes.UPDATE_DATASOURCE_NAME, + payload: payload, +}); + export const changeDatasource = (payload: { datasource?: Datasource; shouldNotRedirect?: boolean; @@ -135,16 +177,29 @@ export const deleteDatasource = ( }; }; -export const setDatsourceEditorMode = (payload: { - id: string; - viewMode: boolean; -}) => { +export const setDatasourceViewMode = (payload: boolean) => { return { type: ReduxActionTypes.SET_DATASOURCE_EDITOR_MODE, payload, }; }; +export const setAllDatasourceCollapsible = (payload: { + [key: string]: boolean; +}) => { + return { + type: ReduxActionTypes.SET_ALL_DATASOURCE_COLLAPSIBLE_STATE, + payload, + }; +}; + +export const setDatasourceCollapsible = (key: string, isOpen: boolean) => { + return { + type: ReduxActionTypes.SET_DATASOURCE_COLLAPSIBLE_STATE, + payload: { key, isOpen }, + }; +}; + export const fetchDatasources = (payload?: { workspaceId?: string }) => { return { type: ReduxActionTypes.FETCH_DATASOURCES_INIT, @@ -250,6 +305,39 @@ export const setUnconfiguredDatasourcesDuringImport = ( payload, }); +export const removeTempDatasource = () => { + return { + type: ReduxActionTypes.REMOVE_TEMP_DATASOURCE_SUCCESS, + }; +}; + +export const deleteTempDSFromDraft = () => { + return { + type: ReduxActionTypes.DELETE_DATASOURCE_DRAFT, + payload: { + id: TEMP_DATASOURCE_ID, + }, + }; +}; + +export const toggleSaveActionFlag = (isDSSaved: boolean) => { + return { + type: ReduxActionTypes.SET_DATASOURCE_SAVE_ACTION_FLAG, + payload: { + isDSSaved: isDSSaved, + }, + }; +}; + +export const toggleSaveActionFromPopupFlag = (isDSSavedFromPopup: boolean) => { + return { + type: ReduxActionTypes.SET_DATASOURCE_SAVE_ACTION_FROM_POPUP_FLAG, + payload: { + isDSSavedFromPopup: isDSSavedFromPopup, + }, + }; +}; + export default { fetchDatasources, initDatasourcePane, diff --git a/app/client/src/actions/debuggerActions.ts b/app/client/src/actions/debuggerActions.ts index 4c634a2996fb..8a0402790337 100644 --- a/app/client/src/actions/debuggerActions.ts +++ b/app/client/src/actions/debuggerActions.ts @@ -15,7 +15,7 @@ export interface LogDebuggerErrorAnalyticsPayload { analytics?: Log["analytics"]; } -export const debuggerLogInit = (payload: Log) => ({ +export const debuggerLogInit = (payload: Log[]) => ({ type: ReduxActionTypes.DEBUGGER_LOG_INIT, payload, }); @@ -35,30 +35,26 @@ export const showDebugger = (payload?: boolean) => ({ }); // Add an error -export const addErrorLogInit = (payload: Log) => ({ +export const addErrorLogInit = (payload: Log[]) => ({ type: ReduxActionTypes.DEBUGGER_ADD_ERROR_LOG_INIT, payload, }); -export const addErrorLog = (payload: Log) => ({ - type: ReduxActionTypes.DEBUGGER_ADD_ERROR_LOG, +export const addErrorLogs = (payload: Log[]) => ({ + type: ReduxActionTypes.DEBUGGER_ADD_ERROR_LOGS, payload, }); -export const deleteErrorLogInit = ( - id: string, - analytics?: Log["analytics"], +export const deleteErrorLogsInit = ( + payload: { id: string; analytics?: Log["analytics"] }[], ) => ({ type: ReduxActionTypes.DEBUGGER_DELETE_ERROR_LOG_INIT, - payload: { - id, - analytics, - }, + payload, }); -export const deleteErrorLog = (id: string) => ({ +export const deleteErrorLog = (ids: string[]) => ({ type: ReduxActionTypes.DEBUGGER_DELETE_ERROR_LOG, - payload: id, + payload: ids, }); // Only used for analytics diff --git a/app/client/src/actions/editorActions.ts b/app/client/src/actions/editorActions.ts index 4021eb6f3af1..990a1acae567 100644 --- a/app/client/src/actions/editorActions.ts +++ b/app/client/src/actions/editorActions.ts @@ -36,17 +36,12 @@ export const deleteCanvasCardsState = () => ({ * action that update canvas layout * * @param width - * @param height * @returns */ -export const updateCanvasLayoutAction = ( - width: number, - height: number | undefined, -) => { +export const updateCanvasLayoutAction = (width: number) => { return { type: ReduxActionTypes.UPDATE_CANVAS_LAYOUT, payload: { - height, width, }, }; diff --git a/app/client/src/actions/editorContextActions.ts b/app/client/src/actions/editorContextActions.ts index 758cf0316c44..8ecbc5e7e3f5 100644 --- a/app/client/src/actions/editorContextActions.ts +++ b/app/client/src/actions/editorContextActions.ts @@ -1,5 +1,9 @@ import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants"; -import { EvaluatedPopupState } from "reducers/uiReducers/editorContextReducer"; +import { + CodeEditorContext, + EvaluatedPopupState, + PropertyPanelContext, +} from "reducers/uiReducers/editorContextReducer"; export const setFocusableCodeEditorField = (path: string | undefined) => { return { @@ -8,12 +12,12 @@ export const setFocusableCodeEditorField = (path: string | undefined) => { }; }; -export const setCodeEditorCursorHistory = ( +export const setCodeEditorCursorAction = ( path: string, cursorPosition: { ch: number; line: number }, ) => { return { - type: ReduxActionTypes.SET_CODE_EDITOR_CURSOR_HISTORY, + type: ReduxActionTypes.SET_CODE_EDITOR_CURSOR, payload: { cursorPosition, path }, }; }; @@ -26,21 +30,137 @@ export type CodeEditorFocusState = { }; }; -export const generateKeyAndSetCodeEditorLastFocus = ( - payload: CodeEditorFocusState, -) => { +export const setEditorFieldFocusAction = (payload: CodeEditorFocusState) => { return { - type: ReduxActionTypes.GENERATE_KEY_AND_SET_CODE_EDITOR_LAST_FOCUS, + type: ReduxActionTypes.SET_EDITOR_FIELD_FOCUS, payload, }; }; -export const generateKeyAndSetEvalPopupState = ( +export const setCodeEditorHistory = (codeEditorHistory: { + [key: string]: CodeEditorContext; +}) => { + return { + type: ReduxActionTypes.SET_CODE_EDITOR_CURSOR_HISTORY, + payload: codeEditorHistory, + }; +}; + +export const setEvalPopupState = ( key: string | undefined, evalPopupState: EvaluatedPopupState, ) => { return { - type: ReduxActionTypes.GENERATE_KEY_AND_SET_EVAL_POPUP_STATE, + type: ReduxActionTypes.SET_EVAL_POPUP_STATE, payload: { key, evalPopupState }, }; }; + +export const setPropertySectionState = (key: string, isOpen: boolean) => { + return { + type: ReduxActionTypes.SET_PROPERTY_SECTION_STATE, + payload: { key, isOpen }, + }; +}; + +export const setAllPropertySectionState = (payload: { + [key: string]: boolean; +}) => { + return { + type: ReduxActionTypes.SET_ALL_PROPERTY_SECTION_STATE, + payload, + }; +}; + +export const setSelectedPropertyTabIndex = ( + selectedIndex: number, + panelPropertyPath?: string, +) => { + return { + type: ReduxActionTypes.SET_SELECTED_PROPERTY_TAB_INDEX, + payload: { index: selectedIndex, panelPropertyPath }, + }; +}; + +export const setCanvasDebuggerSelectedTab = (selectedTab: string) => { + return { + type: ReduxActionTypes.SET_CANVAS_DEBUGGER_SELECTED_TAB, + payload: selectedTab, + }; +}; + +export const setPanelSelectedPropertyTabIndex = ( + index: number, + panelPropertyPath: string, +) => { + return { + type: ReduxActionTypes.SET_PANEL_SELECTED_PROPERTY_TAB_INDEX, + payload: { index, panelPropertyPath }, + }; +}; + +export const setPanelPropertySectionState = ( + key: string, + isOpen: boolean, + panelPropertyPath: string, +) => { + return { + type: ReduxActionTypes.SET_PANEL_PROPERTY_SECTION_STATE, + payload: { key, isOpen, panelPropertyPath }, + }; +}; + +export const setWidgetSelectedPropertyTabIndex = (index: number) => { + return { + type: ReduxActionTypes.SET_WIDGET_SELECTED_PROPERTY_TAB_INDEX, + payload: { index }, + }; +}; + +export const setWidgetPropertySectionState = (key: string, isOpen: boolean) => { + return { + type: ReduxActionTypes.SET_WIDGET_PROPERTY_SECTION_STATE, + payload: { key, isOpen }, + }; +}; + +export const setPanelPropertiesState = ( + propertyPanelContext: PropertyPanelContext, +) => { + return { + type: ReduxActionTypes.SET_PANEL_PROPERTIES_STATE, + payload: propertyPanelContext, + }; +}; + +export const setEntityCollapsibleState = (name: string, isOpen: boolean) => { + return { + type: ReduxActionTypes.SET_ENTITY_COLLAPSIBLE_STATE, + payload: { name, isOpen }, + }; +}; + +export const setAllEntityCollapsibleStates = (payload: { + [key: string]: boolean; +}) => { + return { + type: ReduxActionTypes.SET_ALL_ENTITY_COLLAPSIBLE_STATE, + payload, + }; +}; + +export const setAllSubEntityCollapsibleStates = (payload: { + [key: string]: boolean; +}) => { + return { + type: ReduxActionTypes.SET_ALL_SUB_ENTITY_COLLAPSIBLE_STATE, + payload, + }; +}; + +export const setExplorerSwitchIndex = (payload: number) => { + return { + type: ReduxActionTypes.SET_EXPLORER_SWITCH_INDEX, + payload, + }; +}; diff --git a/app/client/src/actions/focusHistoryActions.ts b/app/client/src/actions/focusHistoryActions.ts index 2edf52eec711..3a5049965dd5 100644 --- a/app/client/src/actions/focusHistoryActions.ts +++ b/app/client/src/actions/focusHistoryActions.ts @@ -1,10 +1,30 @@ import { FocusState } from "reducers/uiReducers/focusHistoryReducer"; import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants"; +import { Location } from "history"; +import { AppsmithLocationState } from "utils/history"; -export const routeChanged = (pathname: string, hash?: string) => { +export const routeChanged = (location: Location) => { return { type: ReduxActionTypes.ROUTE_CHANGED, - payload: { pathname, hash }, + payload: { location }, + }; +}; +export const pageChanged = ( + pageId: string, + currPath: string, + currParamString: string, + fromPath: string, + fromParamString: string, +) => { + return { + type: ReduxActionTypes.PAGE_CHANGED, + payload: { + pageId, + currPath, + currParamString, + fromPath, + fromParamString, + }, }; }; diff --git a/app/client/src/actions/globalSearchActions.ts b/app/client/src/actions/globalSearchActions.ts index 12ec22b21738..78ba5644b8e1 100644 --- a/app/client/src/actions/globalSearchActions.ts +++ b/app/client/src/actions/globalSearchActions.ts @@ -68,11 +68,6 @@ export const setGlobalSearchFilterContext = (payload: any) => ({ payload, }); -export const updateRecentEntity = (payload: RecentEntity) => ({ - type: ReduxActionTypes.UPDATE_RECENT_ENTITY, - payload, -}); - export const restoreRecentEntitiesRequest = (payload: { applicationId: string; branch?: string; diff --git a/app/client/src/actions/metaActions.ts b/app/client/src/actions/metaActions.ts index 3efe0d2ab597..6898e67266b8 100644 --- a/app/client/src/actions/metaActions.ts +++ b/app/client/src/actions/metaActions.ts @@ -29,12 +29,12 @@ export const updateWidgetMetaPropAndEval = ( export type ResetWidgetMetaPayload = { widgetId: string; - evaluatedWidget: DataTreeWidget; + evaluatedWidget: DataTreeWidget | undefined; }; export const resetWidgetMetaProperty = ( widgetId: string, - evaluatedWidget: DataTreeWidget, + evaluatedWidget: DataTreeWidget | undefined, ): BatchAction => { return batchAction({ type: ReduxActionTypes.RESET_WIDGET_META, diff --git a/app/client/src/actions/pageActions.tsx b/app/client/src/actions/pageActions.tsx index 624f56afef1c..f15a5a1fed1e 100644 --- a/app/client/src/actions/pageActions.tsx +++ b/app/client/src/actions/pageActions.tsx @@ -11,7 +11,13 @@ import { } from "@appsmith/constants/ReduxActionConstants"; import AnalyticsUtil from "utils/AnalyticsUtil"; import { WidgetOperation } from "widgets/BaseWidget"; -import { FetchPageRequest, PageLayout, SavePageResponse } from "api/PageApi"; +import { + FetchPageRequest, + PageLayout, + SavePageResponse, + UpdatePageRequest, + UpdatePageResponse, +} from "api/PageApi"; import { UrlDataState } from "reducers/entityReducers/appReducer"; import { APP_MODE } from "entities/App"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; @@ -96,9 +102,13 @@ export const fetchAllPageEntityCompletion = ( payload: undefined, }); -export const updateCurrentPage = (id: string, slug?: string) => ({ +export const updateCurrentPage = ( + id: string, + slug?: string, + permissions?: string[], +) => ({ type: ReduxActionTypes.SWITCH_CURRENT_PAGE_ID, - payload: { id, slug }, + payload: { id, slug, permissions }, }); export const initCanvasLayout = ( @@ -206,17 +216,32 @@ export const clonePageSuccess = ( }; }; -export const updatePage = (id: string, name: string, isHidden: boolean) => { +export const updatePage = (payload: UpdatePageRequest) => { return { type: ReduxActionTypes.UPDATE_PAGE_INIT, - payload: { - id, - name, - isHidden, - }, + payload, + }; +}; + +export const updatePageSuccess = (payload: UpdatePageResponse) => { + return { + type: ReduxActionTypes.UPDATE_PAGE_SUCCESS, + payload, }; }; +export const updatePageError = (payload: UpdatePageErrorPayload) => { + return { + type: ReduxActionErrorTypes.UPDATE_PAGE_ERROR, + payload, + }; +}; + +export type UpdatePageErrorPayload = { + request: UpdatePageRequest; + error: unknown; +}; + export type WidgetAddChild = { widgetId: string; widgetName?: string; @@ -499,11 +524,3 @@ export const resetApplicationWidgets = () => ({ export const fetchPageDSLs = () => ({ type: ReduxActionTypes.POPULATE_PAGEDSLS_INIT, }); - -export const setPageSlug = (payload: { - customSlug: string; - pageId: string; -}) => ({ - type: ReduxActionTypes.UPDATE_CUSTOM_SLUG_INIT, - payload, -}); diff --git a/app/client/src/actions/propertyPaneActions.ts b/app/client/src/actions/propertyPaneActions.ts index e1fc876ca4e2..505ffc607045 100644 --- a/app/client/src/actions/propertyPaneActions.ts +++ b/app/client/src/actions/propertyPaneActions.ts @@ -1,4 +1,5 @@ import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants"; +import { SelectedPropertyPanel } from "reducers/uiReducers/propertyPaneReducer"; export const updateWidgetName = (widgetId: string, newName: string) => { return { @@ -29,10 +30,19 @@ export const resetSnipingMode = () => ({ type: ReduxActionTypes.RESET_SNIPING_MODE, }); -export const setPropertySectionState = (key: string, isOpen: boolean) => { +export const setPropertyPaneWidthAction = (width: number) => ({ + type: ReduxActionTypes.SET_PROPERTY_PANE_WIDTH, + payload: width, +}); + +export const setPropertySectionState = ( + key: string, + isOpen: boolean, + panelPropertyPath?: string, +) => { return { type: ReduxActionTypes.SET_PROPERTY_SECTION_STATE, - payload: { key, isOpen }, + payload: { key, isOpen, panelPropertyPath }, }; }; export const setAllPropertySectionState = (payload: { @@ -50,16 +60,36 @@ export const setSelectedPropertyTabIndex = (selectedIndex: number) => { }; }; -export const generateKeyAndSetFocusablePropertyPaneField = (path?: string) => { +export const setFocusablePropertyPaneField = (path?: string) => { return { - type: ReduxActionTypes.GENERATE_KEY_AND_SET_FOCUSABLE_PROPERTY_FIELD, + type: ReduxActionTypes.SET_FOCUSABLE_PROPERTY_FIELD, payload: { path }, }; }; -export const setFocusablePropertyPaneField = (path?: string) => { +export const setSelectedPropertyPanel = ( + path: string | undefined, + index: number, +) => { return { - type: ReduxActionTypes.SET_FOCUSABLE_PROPERTY_FIELD, - payload: { path }, + type: ReduxActionTypes.SET_SELECTED_PANEL_PROPERTY, + payload: { + path, + index, + }, + }; +}; + +export const unsetSelectedPropertyPanel = (path: string | undefined) => { + return { + type: ReduxActionTypes.UNSET_SELECTED_PANEL_PROPERTY, + payload: path, + }; +}; + +export const setSelectedPropertyPanels = (payload: SelectedPropertyPanel) => { + return { + type: ReduxActionTypes.SET_SELECTED_PANELS, + payload, }; }; diff --git a/app/client/src/actions/recentEntityActions.ts b/app/client/src/actions/recentEntityActions.ts index ca27c6c82dbf..7b8af27976de 100644 --- a/app/client/src/actions/recentEntityActions.ts +++ b/app/client/src/actions/recentEntityActions.ts @@ -1,6 +1 @@ import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants"; - -export const handlePathUpdated = (location: typeof window.location) => ({ - type: ReduxActionTypes.HANDLE_PATH_UPDATED, - payload: { location }, -}); diff --git a/app/client/src/actions/widgetActions.tsx b/app/client/src/actions/widgetActions.tsx index a6afeee3e9ca..0f4b2d9afa53 100644 --- a/app/client/src/actions/widgetActions.tsx +++ b/app/client/src/actions/widgetActions.tsx @@ -9,6 +9,7 @@ import PerformanceTracker, { PerformanceTransactionName, } from "utils/PerformanceTracker"; import { WidgetProps } from "widgets/BaseWidget"; +import { UpdateWidgetsPayload } from "reducers/entityReducers/canvasWidgetsReducer"; export const executeTrigger = ( payload: ExecuteTriggerPayload, @@ -154,3 +155,12 @@ export const groupWidgets = () => { type: ReduxActionTypes.GROUP_WIDGETS_INIT, }; }; + +export const updateMultipleWidgetProperties = ( + widgetsToUpdate: UpdateWidgetsPayload, +) => { + return { + type: ReduxActionTypes.UPDATE_MULTIPLE_WIDGET_PROPERTIES, + payload: widgetsToUpdate, + }; +}; diff --git a/app/client/src/actions/widgetSelectionActions.ts b/app/client/src/actions/widgetSelectionActions.ts index fbf7c2c176b6..aa514a497d24 100644 --- a/app/client/src/actions/widgetSelectionActions.ts +++ b/app/client/src/actions/widgetSelectionActions.ts @@ -4,10 +4,17 @@ import { } from "@appsmith/constants/ReduxActionConstants"; import { CanvasWidgetsStructureReduxState } from "reducers/entityReducers/canvasWidgetsStructureReducer"; +export type SelectWidgetActionPayload = { + widgetId?: string; + isMultiSelect?: boolean; +}; + +export type SelectMultipleWidgetsActionPayload = { widgetIds?: string[] }; + export const selectWidgetAction = ( widgetId?: string, isMultiSelect?: boolean, -): ReduxAction<{ widgetId?: string; isMultiSelect?: boolean }> => ({ +): ReduxAction => ({ type: ReduxActionTypes.SELECT_WIDGET, payload: { widgetId, isMultiSelect }, }); @@ -15,7 +22,7 @@ export const selectWidgetAction = ( export const selectWidgetInitAction = ( widgetId?: string, isMultiSelect?: boolean, -): ReduxAction<{ widgetId?: string; isMultiSelect?: boolean }> => ({ +): ReduxAction => ({ type: ReduxActionTypes.SELECT_WIDGET_INIT, payload: { widgetId, isMultiSelect }, }); @@ -28,7 +35,7 @@ export const deselectAllInitAction = () => { export const selectMultipleWidgetsAction = ( widgetIds?: string[], -): ReduxAction<{ widgetIds?: string[] }> => { +): ReduxAction => { return { type: ReduxActionTypes.SELECT_MULTIPLE_WIDGETS, payload: { widgetIds }, @@ -37,7 +44,7 @@ export const selectMultipleWidgetsAction = ( export const silentAddSelectionsAction = ( widgetIds?: string[], -): ReduxAction<{ widgetIds?: string[] }> => { +): ReduxAction => { return { type: ReduxActionTypes.SELECT_WIDGETS, payload: { widgetIds }, @@ -46,7 +53,7 @@ export const silentAddSelectionsAction = ( export const deselectMultipleWidgetsAction = ( widgetIds?: string[], -): ReduxAction<{ widgetIds?: string[] }> => { +): ReduxAction => { return { type: ReduxActionTypes.DESELECT_WIDGETS, payload: { widgetIds }, diff --git a/app/client/src/api/ApplicationApi.tsx b/app/client/src/api/ApplicationApi.tsx index 8c3ebe995b16..dfb3d1d20523 100644 --- a/app/client/src/api/ApplicationApi.tsx +++ b/app/client/src/api/ApplicationApi.tsx @@ -28,6 +28,7 @@ export interface ApplicationPagePayload { slug: string; isHidden?: boolean; customSlug?: string; + userPermissions?: string; } export type GitApplicationMetadata = @@ -166,6 +167,35 @@ export interface ImportApplicationRequest { onSuccessCallback?: () => void; } +export interface UpdateApplicationResponse { + id: string; + modifiedBy: string; + userPermissions: string[]; + name: string; + workspaceId: string; + isPublic: boolean; + pages: PageDefaultMeta[]; + appIsExample: boolean; + unreadCommentThreads: number; + color: string; + icon: AppIconName; + slug: string; + lastDeployedAt: Date; + evaluationVersion: number; + applicationVersion: number; + isManualUpdate: boolean; + appLayout: AppLayoutConfig; + new: boolean; + modifiedAt: Date; +} + +export interface PageDefaultMeta { + id: string; + isDefault: boolean; + defaultPageId: string; + default: boolean; +} + class ApplicationApi extends Api { static baseURL = "v1/applications"; static publishURLPath = (applicationId: string) => @@ -243,7 +273,7 @@ class ApplicationApi extends Api { static updateApplication( request: UpdateApplicationRequest, - ): AxiosPromise { + ): AxiosPromise> { const { id, ...rest } = request; return Api.put(ApplicationApi.baseURL + "/" + id, rest); } diff --git a/app/client/src/api/PageApi.tsx b/app/client/src/api/PageApi.tsx index e386c3f252c7..b03c616cc37b 100644 --- a/app/client/src/api/PageApi.tsx +++ b/app/client/src/api/PageApi.tsx @@ -46,6 +46,7 @@ export type FetchPageResponseData = { layouts: Array; lastUpdatedTime: number; customSlug?: string; + userPermissions?: string[]; layoutOnLoadActionErrors?: LayoutOnLoadActionErrors[]; }; @@ -77,6 +78,18 @@ export type UpdatePageRequest = { customSlug?: string; }; +export type UpdatePageResponse = { + id: string; + name: string; + slug: string; + customSlug?: string; + applicationId: string; + layouts: Array; + isHidden: boolean; + lastUpdatedTime: number; + defaultResources: unknown[]; +}; + export type SetPageOrderRequest = { order: number; pageId: string; @@ -93,6 +106,7 @@ export type FetchPageListResponseData = { isHidden?: boolean; layouts: Array; slug: string; + userPermissions?: string[]; }>; workspaceId: string; }; @@ -212,7 +226,9 @@ class PageApi extends Api { return Api.post(PageApi.url, createPageRequest); } - static updatePage(request: UpdatePageRequest): AxiosPromise { + static updatePage( + request: UpdatePageRequest, + ): AxiosPromise> { return Api.put(PageApi.updatePageUrl(request.id), request); } diff --git a/app/client/src/assets/svg/upgrade/access-control/prevent-accidental-damage.png b/app/client/src/assets/svg/upgrade/access-control/prevent-accidental-damage.png new file mode 100644 index 0000000000000000000000000000000000000000..bb607f368878d589d569c23b9adaba81cdfe76e5 GIT binary patch literal 37071 zcmb@u30RVO+cs{ES!8dE9#MT)*o&&+|Gj|8TUo zQ26QKPckwx3Rad^oMmKWi)3Ve*!JUg;3w99o!tw3+X8pCxGYmMcz7PzkPW_McS%O( zb?T0dz-_>`{B285xQxuMF6qB5-BD#XWn|KBT3xvWx$Czi+L0UNid2u|u$8_sTdwGEYu-WcwszgYCEe{QkfX+Z%efSUkA5d*-lX&2MU3<-Zy}-%k1C zrmR-h4Feu*1)cUIC`~qLYg^hc&94eZCstGv z^8F>_6^1u%+(=gdZct|VW~5O1d@}93Ew=#Q-fP_dWph*MADfkpzgmhmnBb_)v%b_w z=^h=oD@ER`;uU{Mqd-~m1$922(;myKaBr_zivM(9E{DKwt<28N4fo@|z+~LO-+iZ% zV+dzte372jvD)p?hy!YUm=RAG`swQE46#FU)Nu)Qf= zq|8ftXZH%+G}2lnVj)(*V;X3o1pSjHcDxZ62R$w1ybYGDKUoF2A{x>XIZsFibSW8+ zPfM@#H7Dj6+PX|x{4IqX<}ak~P5vA~6flYpF0?T7H6|S{e!s$7`;uxvF%^bSNI%Yd zH6h!avuihmbV(vlgT&P!mtZ5QY~*yNccdrdN$dB&5A{zuOTH;e90#+FW2?!SUBFQt z+i!ZM=SaRd!5 zj_do2A-%~vc9l<0^R9^&@Z!*V(dwi@VPPS3u&layx^;=-NQ7KF<5^dfZ4l)rkLTvV zlboc-?92gP;Y;tR_Qr|dx{qMp+ZSA+#w1rxemwiMxlqYjw2qgwE}W9?n(S3*7Mitj zt$pW2e6Nk)5L^23-P`9Crkh%N(0ac#KszedT<=QvUA8_Z-8RF!8K#W%^jHeWanc{x z>g?IGhrG6Sm!PM&7l{A=w4?0*{igPljg8)7vV(H=DSZ8C?(5gDdt;Dg&!0aJx^t(2 zwiP(*O9%P<{5RrG#{41F$BXftFJuaCJtV#;gfWP+(v2Fg&N-tv^%^nZH+f!bBC&q8 zXZ_r{b040jrZ#)K?AeUe0WpoAH4_3q1d&~%pHSom-6?aBSA)N0x=cr&g$ zG=ZS7FkP=^l%|>vv$Kh^F8J23XBLaSLR;@=7y_$XMkXjN$?1l2E8@?$Wge|dtuWDv zJ*@m}i(`%X7r+0thiqp+o7!9m=gC7BYSvlj^HkzAbcfAP-yeyCz5T8nxyxj>mN7)} zqp}gxds4w4e4%7>j z=kN0|fwkx#0)*T97)9yn&j~U8oRIOcn&?sXlXG9TmQ%!nVzP>yGPO+{9e*BA4J>8k zaqhaqr(1BLsivHI98#S7Xb1i*lfy4*)&v^}TVZ~ga>@y}5d=@ugtrzsDMu5^OcoKb zNM_AmcHVg5Go$rw%sFZ<#_%N&oAfriH-@i^?_#I(;E}%5f@iT9k?Wvnt(@Y1%|yJm z2Ws5!p0bl3zwy1hA2~!?4*b>vMB}}Q6Rx&h6@`pJdJ#h4M5z9Dh^LR5jOow(5VMHD zY8vPBlC37g!uEfn>I8>}hu8Tkhu*wdbl`~Zca?GUBlk1mrW-Tc$iNbvXcKnCqLBt7 zR8-{AxUU6_VJ0dRq4z;vJ)x*2Zf2DH%$V)*tbMLJ>h$N`v?;PqCHx~zMMq&`tG9y3=f7oOj>Ee_J^I6=|@Bx zFYIn@ZkszpCL7SFmfj545gRW-R*R2=_-iAjroO9Z+7<*dAxkrI{b3`-UF$s$6b*L# znov|V4jpowK`~E#<2k?1io%ws8*oBwAgA>`DsB&p|&|Pv(9<4)yJWX8s z)9!7qa^6ZT)Yz|ST%TNdHqHi4Cy6+${JnEIXLe1!rg_Dxsj20B2hJ51s#|e+p#&Rs z`qJQhaPC;b`Y;Y;^ES(}NB67X+k68fC+t^!jDKPXZv~HPoVbx{lEWJmF7IG?=YE^H+>XYkfhVyCJ|_>DIGq7 z9B@+Iuxf;i7o;3@_b!~Atva6YSG(KZo*5$xL{u+&iPWAU$@<YgaAzI$yl7dtgN5OH+xJuRlxJ z7}9CrRQe0Nq)S!{`7w&roPo$KIdn>4aky1`2_e0b!th^ok&S$DlW>+uo)Tz<@CW^L z)zacVe^<|OaWIJxyl)z|m)w>Ay>+kSL!IyZmj?r&J>KKB0UCTNtiPICT>`lTtts32 zs6W@?z}c+#6}I`}37g4!;M_7}HJEUIT}(vimmQK8h!ND8m({%Jrm^#jP~c)S*|=}P z{;}zvfh@Cy@9IbWO{X*iM}&0L0IzG-O=m7{YcV_OQ~zo)uDz-%_=2~)+Rq$uQ`<%i z5h&(_os$}qxb_Cy{rP{8;Sr3azA3xtG+%t)pJohLb+(KkxuuD?deR(42)iBrmI>m?CO4! z%oV&Y<`^K-dIfon4*6sV{-@!PVuC&3|mS9t)xYQc$wyHSzNLp>f?<(zAz#am1eFJJ=!Co~J)GARu zQ{qOHg-Bom6Y|=qK34b9D9fn_gV{%}$D&o<4ilcR2YL>Mb(1`xq z#521R0)y(#+S=Nl|MKMvYiuM4@@VAAInAdB)h>-BAHMc()|?)enJ*!p#^4g!8LPI^ zqf2^Z=fdK--v^G2y6#uzks zYfVp`(GT&9{_q61C*C&;ZN93r|XzN9!t3=Jdhq!^V@< zdM;gM2>g6(#>YD;OXNN?DJBdsUtRt)RUbSMHIBXhii_^mZfBXcIB)_X;dkJha3K7Y zw2qMNQ3$j8cb7Dj59r_OhcY?K`-FImc4%~2niO?pb>_ExdCmA^Q5l2>BX7Q<33p`w zh!jsYg#icO;?=b`h45>Xlb58pDLg2QF!aA|R&Jti*198{IT+!$T8C-$2F@>|J+%ppCxUSi%_aKPW(#v#CB zPvf|~>XX&w)jDYh4;%=&W??aTM*5rmk=L)kI-sl^)m##^YZLg!ss8hxw*J>mH9fg| zM@`l~J%nxv$~56m)lb0s&j2S0u(OMGh6CVe`D@Z1`&+-B_4ydNMfY7qGi_|?&6j7a zUu9}bHZuRQyiWdCZt!o<^?%shtk5;p`FicUWbXZm1wO-|vS9~syP;%*htWzo65|nn zlO>$y6UTvq8Wqx9!fBd}^e$X1;fnbrlHl_`(#HhRqJNaBXx_oVnEmEP{LQXfpiJwH z`@ro8dX2fwtuQhGo2ijx`yIb(o#nCn_^{i)cG-#QLd{scHd4;d9Vj2-*h8pusZIU08h&Te7qa{#Wg$!d~Z z4&B&0_V>ZMHX)4@xyy9zQ_#%YN35ejI07&I-T}?~kgYgbXJyG+cfZ`KCDamBru6ZS zc}0>@6mroU+pk4x%N5qdwpSjev`kWz+WKZ<8sJe$j1vhnV!nFPwR zwd#1SY4KxFHw?Lc{uCuW?3vL*8MjT*WZ4luUyRYmiI##f?g4Y_KHfsKaE#d(#tIvp zpFY7F(;vL><>{R0a8<_wQHv&Wr7pQWRA}<1^E($ysmd!RtfMw`ZaAV^Sh6tLM`bT0 zvKA0Dyzd=SjSKQj={g*0d&?%yE8ed}??ssperT z%c6qCJuK5Pa1wZM&*REXi*jtp`;$dRDx< zYKao>;g7rf`Hvy|vKCXds$bYv(L|^Nt3YZ{-#X>o*IMt8cG2)qYPEm=#)0xqNAc8L z`=B_19O^Cb8VR;K6pa$W;lL9B@6hd;f6N55Sd_pTilO%5K3t6Q;{eo;F^W%I`Dsf) z6TyG{l7N@XKMhN5BO&~^+{?B)wypj>hPYhJumG<+0YRPctw-*C>#Uq;6s@@3^WMq{ zmCl(IWk1s%rg`Gql!UK?{MgIc3I6I&%Q~HB%@akpl?9)0$4)R+!!NKJhWq4JWKABr z>d7lA9iHt%4jRs;nOhqR?%|N%%N>vvkg(`t2SL0Vzc<>@&-`gm%sMu4PLCB`?B5cF zW?Ge{7bhQjPGlaH*B-*JYtFlDeS&tCznWjL-3tMRMHu|m^SuGb^z)s!i7@cTEuSl} zWV_Qb2@w^!m@vjbwhQrBLoh5dznaKsx3tXU)oJl9aXfL5q_&B`^;w}@W(#H%QE#do zR9PitYGk!nzsX{}F9(AUX2o7EnPN&AI~R{+=OtRyEUumt4%<&JwlR)A2U{Q{ZtmjT zap4#^6^K67v+Ju-clNnlO6;Y87p7aDlzu__Yz8FTYL6tLW9~JvW(pp)zPt{*v-x zBeYocRJqcrqSUUPiw>*N6o)ry6*-G|QsS+i41T~jL-!a;#dd@%%VwIyhR2%M&hJks znL2^A0u#Dh`Sy36>0c6#l)li1MT+8>EsOOr53!MxzVi2Beig)IBx`#yjZGJ}==Xn$ z&u8`_CX^dd7RtzFE;v;HPX`|Y{sUDUJgJss?ST;&9n@g9c?jyUL;#XI3k-J4WVOgT zYd<;pyLI%4I#|%lz@P+}$#xBl$^k(jxJ#339w$=%lJK!VWir8|O!*c!hqa`<`Y@qp zKJn|ev33Z+gL<4bEEB&yRLMx(!LO*IM^9o_YeZSpGF=piMqqA2i^ zM{{*&j9l|@*?L{55RrTj=7?$~Sa z`j8vtPVE!wEKw9>wBg|y_aTX8hgN^k7`PdxbnU9C*-6VjoNz4!AwTu#av$jFtg0gMas9WA*Fu1vSarX6IeRM;E8Hkk-GWNdzV9qCDAS zwD)RCJ+mHuJpT1*%0(A`i;MhukKZz4;8jhNBB3_3RI$aB6=Yi6faR-3FgJWKE1>b& z#mq2g&kX)cYez^IR@ShQGJz@hZ9X=_c(0pbrham7ypu3fI0^9OyO#6e!-tqKTEAkJ;I|qb2i|esn=^R=DKIY-$8d(KF{(#{M zoxEZ4oU<5bN2C*GtZT~olt!9a$+X7!2+4rU360PYF3^=%5@|r?V&k+`QS>{)43c@M z@^;}zwEj@{1~w1g%5iPxjMkc7Wk1Ksh0+(|zkTac#Nd8_`QE|d{nA-%aSdDa%G5nM zzj*zY2LVyi3P$17ZuZj&h`*O8LRVB`d8aK>3yy2>r(-&acB88=shs85Zzg0B9Jiid z%8HY8;-RxShm{j3izeIoipHc#&JqQALRd#b5`A!{H(&9n-En?JI9l)^=|&VvIEj%U zur-sBga|KJ51EcWyZ=DnKF3x{4BA5klAp$u-*X?jlKC*p*+#tArQDS^Vkl3fl$_Ah8I?YA8 zfpd3mcXm~14*7{eD)usS;r0R?ew`F7FcV2dQ>Zbwz=#Q58>*+6dOxU0 z@Guq3ruR#7peA@8gRC)nM5^n0P-~d^cZRIUQjkaOPhrVIP{L-^qz|lDl`IiT#H6|0 zq(CR(o$v4VVGMTx1#|-^VMh?itYGRmuY6-+bF7AqL>6THuv5OY8DnaMQ*>b8BeUHkq~yg0|M1 zy)UGKOX#lhjooRgTO7`GJ%hxKD1OKuS4+R4X4}^yz%h&*%tVcEpRZ^-mhGx zf|I%vPUdgv2yd*Z7*1A5FZ35UWNp}bXv_e3BjRs`37Zuc)05*Jr<5~wXP~D0OJQFC zT}bqkITse0_{O$8!8W+ZkDn`RFX+hsrwUvXi=#3jMyhRI?#}4Z(KA0dIJ$P#@0_=B%na3(Qlt|7 z!mZ^4y&0i}>DSK*QY@4YP!mrFtL`I{g8 z2K%Rs8edf&@{)4D+E>8Ep`W?tiY5(^1JECR61O2UjY97F?OFGUn=kH9RgNBU(nzw) zCY?6lfVD-c|J4_xrsaQR-hqt)o*Yp+gg0u5E27!f<~PA9RYHYL?xtbzMNKc2jsI(~ z_KlId3mR0E>+-Oo^G1t4DUjpKi5ve(=p2YHh|XR-?P?c7aJw=3^7M>bqKkbq&1)rt z=koJt-W8K>uD37DmugRt(cYV$(=~9@87|{tTw+`7 zaOT4+Zz~6Ux5-r69NVUw4)CzCUH3=4URYQ0295C$kK)StAwi_Uu_q8Yf(r+3+R*XJ2P=O(N< z;EO)$$(bBX+pH>UY{}rib^+d3$|>J{DJaH}+Gr2{9H*efJf%V6^~s1ylX{#~Ik&|L$A#6K@$BCCq$!kC|xpu&k{Kl|o=zCaAONrr^r@A?m1coj-6}9nY)e=<$ z$Y2a0bA(H8M#cjhl}_43C*cFd4Jy4s4T*J`pZ)eSTBYz1J9WO<}^j z;g*x9y~)Z*jKlC$qL4)fgrw=s9uRDPeG#uERJ`!%9wW_s*XeG!-Q`M6De082uk+KW zf{7k%+mQp%@Fs}goZh%^qF}Cn(pDz={UtfHzDogm(R8g-F*6nkNk)!fGo>1Lon$m}|N(6Cf<#Y_5}V|mggch}nNmvRs5ITr@ZdM%?C)UBim^sx{C zSA8PufQ@MKL{yuzlr91Umk!$NBwttOTijaF7adZ+-A#IuDsNp-n)SUz04Lw2G&U$E2dF1Hu%OqEKpWohbW0dYFSl>eH zJKrz9{8+(PyjApNk3+aAzlG8mub+Tm9NMQ9DHd}iMA{F)y9-63n0e%4l$c4z`QCBj zq5Kd~&$Gg%V64sAw%Wtk)0vu{LsKz})w&f9!+M2kdU&0gyQY>U$3p=!d#2L~eWUzk zg)rSy^m^DQSy+peyjTe5AzEY^+>B>B)73r?tMVi zXd^~QL|D2+MAurBFFf0__PI{{tuR#|=XzgOF-~M_JFIjxI-Pzqg z+Et!%y=fbeQ+kfphRSEU>C0BTIV)JFMfXQEN1P8FS^oy~X)(7oBsaFon1Q)7d|)+# zNFyNpgr72ugIX^|g*FE@J-yf`Y%GyBsR3EXKjdh_x6T+!vP#!<=>C<}SR%ad<>^bV z*9wcSgSzI%N@Iya8kEgi7Bu4HeF?EKyDd94ms@}ZKJJ#GJxh|1=?&f*)|X8#;GCQ4 zcb1H=Oli8t8h7t`iWI|f-ZQy@u<3XE{CzS5nY&gin~X;Vt~HfR5xH?ZGuVF;CZVRV zMcak0xPt9Yv1f%Tnv-4Y4J8~4zY`HLlQpdBO1@NmwOPx+rRY!GOb;Tknlyys)hJ05 zV(E2vUmQ+psE7*HUz_t6&jmHpV#1o;{HHlm%?|NS!|wxoLi?l-HFUAs=2HX6kU471 znVKTz|4?pp=QeZ;^(k5t+a`}T*fXJ)i)`!`C1{pgQMMtVPA3AcTA4qnjCJWSz zsQa+XAkZ%~?+Dpgh@nv2VzV! zEi)>(H)b`KhG6DtU5NVFLQ4d~K(w8ryyWe3=fl-Gll>Q)C+R)FF7%YZAsqFZJB?oq zH0rnU`^R4;DwxL3p*0Ry+BAc^rkgeAz4>8=qe3TKfvJn%;y9k*(^91+Bcn?*2d`Zs ztbR^l8q>2k2qYl^=mtub9VF)w-A7^@yeaYP^QG!2ZaOYw{1U~4?_;(A#ceHVL7>OWkWsS1pNMEN(2is}@-tEtE zQHUo7@*j5+6^`QM^AU{f-ul4{H@H!mq9w3z^EAw+g^{9l+`zN-&irgDYPm2btSb%A zuT2QK?WD4;-jabbT7C1yT6K^KmIz`vS|gww6rUqOB7D&Ul^K#PJ=!F%>U8>B%;Ve) zWa3hWOCJ#s0(p7lv;EIR%FKN4XUsO`i{H#9=x9Mk9chrBQJgQtQLV-a> zNk6Zc<+G1QeAhVc=goMVQy*@^Cxyn;9aeLLO-4*Pmze~_zjbIi1w(}XI*<^ zWUnOc%3yCy-`x{v8w6@W_}p}ye}c1uTJMnSL--23#PfB`UdMi)^zoXD!PI`_!nn%N zIK*>{Ot|Lul&>NziWik*K>YqE%WZfHPB9_>X)x`PJWK;XD3s6_2dd!asD#r3!^`6+ zF*ViZ!_u&09HOsXJCKi2U8D(vO_<{`5&=%qVp8`C=x@JjK=Q~21}J_k%AJjFYTfHL zf%!7e5AdECtR?thO|Ryk86Q~<1=r<`ZzOn5I(Rp>FSnSwut$+44Zb+iYIC0`RMjh@ zFdiR?_FBiT61oYTN!zrU*w;qzTTETc5q#&m<29HM0-7yckSgi?L8UI|3ihkZKaG(h zM{TPa^KM30K{x8a=<_QZka^gZCqA~6(H4N`d z)Y^No{P2Rcs$)c{+qTg}^rD>D;FX(9-kX3+gy6bmUEy|MMKAG73>xFd{2oD?hfasi z4SfQ*IpMCSf9u(B`j^hcS(7r3FOo#ySb#dWj|Rul%!)k$WkmRc%)t4u%FChGuV3c^ zFYWJH1%oL^PzG1f=#At(gz|g8Lkg}8lEmwan5fcMr-jGI=RB%qTjxu#YEgjMq|R^7 zw7jDNhRo~{?bGt}89RYCld>3U_1?LU^bImSV|)Q%SlG738baTCZDMJ4YhKtQvND%W z!?37+vj}ODtea-V`VlCCo)f8>9QQ*?gie4 zcdU%F5fxCk?%$Nx9YeJCf*T)~djF>T73eL5jpt){i5z|+zAE@=VGpsvsq&8sHvWq5 zsv?rG+P1-YOOjO6L!0y6n-^QGB*TjDt|YG-e6@?^a*gog z97P1!8L=18XJ^OzcM->Z%lj4*{~U4?krNp&w#w-Lsue%cpF>~!lYVRUZ^bX77@Xt33l;vG+{2MaTq`30q15Zv;*>-ZX?0=Gfd`rJdT`)C#mSf1HKT?G+bIsHR?#3_VaG$iss^meGXzdYlOqjqF8}kH zD1;4<3I+G8AM1bLei#?}d9{-m0mQV1!fa3u=B#kyqRZ`B@S9TO~=PxSqC!!RV_QlgEZbWW#H;eGt=T- z9KBKg7d4iS#KyKS`a%hAzeN|5C>MFQgq4kG&pMR;L@a=XRBbZPKLJF!7|O`6T3!&M zSXV`s%4)>>;tM|JC;?BjLTZ|EQF~pJo`w%O=Bj>@l7>&R?oykm5f@ZDFZ4-{U#ZUT zyD}TSm4i=Ug%xkD+^(f{t$95RI%Cgo%YG^Ta=T-T+)h?hW~WvE78#GmBz>9+R6eE7 z*Svh^L!s1_ruNN#Eh8|beA()*vqs!P;{kTFj=a0lkE6RayBeL(jVN4qb8oIrGMkS& zDZ36h@@m_n3^T3%@*ij~c`*|cV$7igq(Rj0Q(iCY?scFA-g8YpHx}Icg0Dky{-J5& z_-FuWJ=x%VGd;#zK#`Q6e7dgO|E#nkRBfXhcDX^b{!g=?g31T9GOP={j-VZH*yRFc zRwKzm94$QRb}RDopE8T}W%`v@U)!Nw;n;(YbRLHKTrBI9xv%$~V;# zt#t_L{+TNgx7U0{7NASCmu>3v@FUNDl<8Pj{n6_8w2sp=RtlFx8(p?O-*}qi2)bfCHR$AGrG0ZRdujt}xb}gw>94o|(x8?^Yj#m1)PCuN|5H1Qa z@oKXDuagkvBNg`{{KS>TG~oT{bEn-NipbxWgVo}?jp-9htxF~XV33-rFWkFqwWy#W_U$J*i^c(8k>1}t5 z6&GL9yKD2tWzyc?dCpbi-54g0law$&*~kLh%PYb)CH>r1sKl|9;yJ{WM0nD5W8L%8 z4$IsdO|LyM$~X|2InF-$<+-Dp`QXC)w#Y{gHDvK&hkNOcWk)INpLVy%YRh{q=;@g~ zu8LJ>Kbwkzw(>dlnMWUr*vU2%M|0~>t2px~whQSje?J0}+|uHZ$p+q8&?!LMZ*K1t zjs}-U+z}dlq)!oq)8v!oaYoDEy09pWf9?5T`|`|nPJdzz<;Ahk-g-(V0{biiWjF$! zLNMbLiWhF76YL4hc)G^o=Bs+W@oVdbYF1j1-GeM6h!!e)Y>}6Gmzo#))o*L1VZYxV zeN~x0M2p&}t80x!E8=zrT&Z=|fQ$^Bd0UW>3hB0vfb$+=Lo4lu`B(e@#EBDHmWcb}(ZGOVg?w0|bs&MH5?T9aIn@>lITGWZ zI%n|qT`UQJbogiGgX^DGT&a{7rfop$^DN-AYpF=wX=;HsJ-K@lf$`03rZ-kB65LAt z&*k0dVq*&sic=_*Cj|_voLue84~fp-Cli1?82_!4;mS&|(w?MA=~zV;{f+@Z&tf>s zH?Ir7qeSD3y_1oXY)|A$^VS-6`#5^%IdX8i>(7>3hIRG>zKRYYQN1p(I@O?d|2_4` zqM#f=4El#XqU}fBVxtfsGjADo?zRiQupeOXZTmNW_g=$%^%LSByO}xsn{6~`jbQ8H6gnIwIpa#;g*jYpbvyE_k8rw zjjdV#aZNx$O}B;f$XKa$mZmGTi``W&6)&Ey4+NVIOZ663eu*Ks=fwDhw2Ce656W~0 zFBD2UqHebi0XKTDF?h^AH2iSES*QcRp*}Xe&PpCz_m>u+ zr|TIn)tY-cxIzYdR{>|E&$ITG>Zmj3LYSt_SRosbOn{bvck7*y`hUpZ|K2Z>x*27X zZGQW(=^1W3U|23S^vE1K`#-gg{C7+G|G{VSf4y^#)BO>txEBQ2XJ~PLtL^*wbQFqL z9fhpi^dko#vA`bD)$R4e0#0o(SMA34xRK z#?Q+gx#Kut4;{`mgy%GqM66=i!lJ(pk`YDq!L zMbYwbam8t+qbwl)ir>x`rQ3rFAeT48{IEmXayBKQL?A@~P53vTAJ-89z>0>$5~0*7I_QZ=1;%IDg3K*wnq4a~?}|{b$KP zTO9td1UNeSQy5VC)qTE@2c@7(?pv$&0FZO)3vi&%(aUompIaQAI1KAiDMp%aNP__o z7WY}I-m!Mfa0HDyJ-06PVr8hRxGG>59NX4D$$>pqgFpH+^855$#|T?olNs{)(xcW5 zPW(`wWP2bH^XYV59;hU|wDt5QXM^7>Y5;}=8!Ru)_j+3jadU0_Gs#!JROb%9NxEXp zP=HvwxBRKKHd>XN^X)6ZV$Z;>pq73oZ!Thaf5two$2l(V$XuP)aRf@jUX4D5CfdcP7%oG?7XtEfuZcEv*?`$|5m>Y

XE0jm(rtP>JNSn@xh8pSmj5wOOHp8HC5@QL$ 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 F{u2U$j$i-) literal 0 HcmV?d00001 diff --git a/app/client/src/assets/svg/upgrade/access-control/secure-apps-least-privilege.png b/app/client/src/assets/svg/upgrade/access-control/secure-apps-least-privilege.png new file mode 100644 index 0000000000000000000000000000000000000000..2850b11ce8083856f5b621ccf74943e1848d4740 GIT binary patch literal 58967 zcmcG#bzGDG-!`m*f}(^VAte?fF|lZ{5D_FqIwl~}-L)AA(tedvV$#h7>DWMGI%3id z0*=^#$p(z|bE@Cpb>FY&zW%tL=XpJQ(VXYGPrN_xIF92TYh<8v?8wO@`}XZSrhD(s z!+rbMi}&qgd&YSP_zNM4Hxl@^-~XY`t$kGkVr#%J?9MkI+}yXXI+=@Ze-QZnu+Ke9 z|9$(oJAZ%fM|qb#*|!h7u6yU^qY%4w#<470r$_=6y#M&E@ht89C3DY>|fJg3&i*zh*& zO_SFP-)wT8@t3T@z>0pS8=S-tw zjB)05&--c{wI1{E<0YakywftzQ1G0r zDJ*^^&@ocgviSj~fh86HJG<#OF(&R~72g^*2i^W%qR#0z(I)V=T`MXD1I?ME3o$>#*^-sU% zs9b~4_pUze%W(L+qPiQ=E3v^rL4vpIsU+T`z)+=TzNxB@}qRCB*; zZ&_kfK5{g16?iF7@ox0JE!Vyb#s658Tisghe8Dk2yL)&RG+QO*;bGT!)i zfA-Vu7D#^2l8S}Nm8xWXSr)opIDibf$e=b$54+Y*U7j&F!4^h1>|L6V>JeUkC2DUgpSG-{%S7=C& z3u1TlnsjHWVBLyD_|Nf99xXX!S|r|j-hfLmBHvB9JER%R#HDu&|G9lc&PA~f`|3gW z_a7hoo+8@dm7S$vCegg9TyW@-gT(S|PKI~5)2{QZqPL|tJ~lL2Yawd8Q%6#FJc8-p zE%5glXgh19N<+X&kCc|(=gXc2Lum^m;|NuyHvF5;AzzdPOpPk*?Y|V%*gy!rL7iEt z(lx=TmpemxW+R4$;+r0Z?JejKm5Yows|k_$`f25@8c9yFmy$9F7MrNsa04~uXG3f``&FoKV9AJI^6vo-(yH?s~wl-R*0@QP5$owsn%Ax?nlnS zwGZx^u-zGd%uLy;Uc*clx7zxr2w72#YuQz@dvHN9%r0$!^)oaSvdmZ%Le>V->q*C# zjW+FLB-1oCmQ2P z#dn4yG;Qt*cL!Zbq<)5KZb`2Qnf!4YLq=z^%A$?j8uE7y_~BYb!taKvI5w&uSiG{8 zCj1lhUb#qkYK)MkR2}a}?Ou;XG~QLIQ+5ZM}j# zk6~pKAhP06`qtWVCi0PfWM?ffw^v+31tM28v%Q+I@uA}E_tlpU^Joz}>0$IO6m|!) zdRwxO^gxYP5zwwy7Nu)ly{SgX%G{)9j(1+d8QNO#hzE9n;+sEvHM4Pt_=mz^J5)bD z1LO0Z9_Ht6eb#|)-wWFw)EP=gY=`xh`N_)tF|@0-)FW>iuX+DIoqMhXxt?vFUT@2~ zYGF48Pa%VB);E&>1SuZOyga{rmn+v@lT2~IW%3c%!F<~hzivx@l`?k9(Ej@9?!H&W zFbG42>TDPusap9)(+9^fEUoElN1d8!TBDON_`}c8F5)NfMcrn5-(4l;s6&~!>lT`n z;+35!3lz-929o;6AM}^H&n}NpgbcCMyDn1Kcx@k0jkd41RuV)hBKO%J&eZ4_2}tQz zIzX(ncg}gG$4^sTs&@)OvsvJ#W51^7sRq==e(-%s<(=DW{^n$fH>PD_y*YoLd~&RD zgHU)-%aFfH>pWnyPZaBS>THz*N&WB!Bh??z*oMA@3l%s+hyjqhCf;_CP*1}~fsnfb z=*suMwv)l$QYZ4dA4TaNB_IRa8Y0%>L))|>wihDslFI|x(|FHUe?4m|=1tkt{%p+h zG%0UKK)!TQYL(p#UKxIkY<{ICeZ}~im9?h-5%!1b@K`e=zU!u)bXW`ZLk@U&X`F(n z{voO|Q|94&EqF6q|7 zJ2z!WDqIkW*!-3Q-pRjq0yz}?Ly385Bj~4HlauK+c9Xg&aN_r%1pyv;6dw779Dt|< zy^yxjHO(94Cy9lxZ>??6#NTwDyH%>Txo4$_5^Zj2A<2jJ1tyX zd-GET8U!V1b4w(8=+qeHqP4>@c6?EMyC?4bF!9nfFK0JzVB*-5zga-&R(!-L?+uOM zF>-uv%5~%?8OHkU`TKZ5#zjMGm7R`S9Ew_!tr1Qk^b3bZaC9%iY^ zdbfhT;l9xv7;0yAbJnMGhhmTYF=<>|Z~*y}Oq{B7#b}WGZVqb;*=8uTu40H+*INh( ziVziWMc=Q6H4Lsj3l@7bPKH*sp3e3kkQDMSto53mO1bMZ{UcYSvZ~z^T9JPF%Tzkp zAUtXQO12Mus|liL=p;1U!8Q;oA&5_Q$^U#OA+lV!yxl zQeiqoHO63usS`3P>O}%}ST%=0+rPH4Scla!89zflaof9oxWi;<0L3s?{9_Vt4{8BVxVgA173B z{gU4oFG^7B^V*M7uH=6x`&Vy^>mN&z|2J{@Ak3o+2BQD8r?&QmI+QENPb0Tt-xdMK zn6VpH1|t9F!gbFc7}C#shLl`kcu{)S>UJ_s#;Nj6uTIRa=FflV$yUy_&wX_1zf$PE zzJHVMI)VSk>370^5+%T#^z(I+6{7#H`G^;=$V2fUgvHU(r(eIwyePE|nk*Fl>lz-m zgl^Q05A`I9#^+8QwN(B;Q@k{jzX?>tzbpDbuLAy)a{V_c-r4^bMaBQ{aZm5)=;#Cw zg3Xsu$%c+S2{M0|A#Bzi-j=53+W4oSK#Yx#j;5-rsR6V8w`@5H#8OioovFa>RfmMD ztpT2sZ|^3aOr5JC&vj>vJ^fqnxXD=8+hwqx?%=lTWBKD1hkg;!MGssV)Sb0T+PX=C zt0&vx;{Sf7uI+OluX->~9cv)|TVNcjlJAtcm5+(s^#5D#Y&3vXcScP7Gai-4NJ2s~ zf5bw14pOzYVhug682sHY#yF63!RX&$KQFnBRamagg%cjchzD8p+Yf=Dl-{d>xRLGc zVM8IuhgP*N#?+BwP1B==+qpcE7b3|m^ZoF@PdV9`?cA5)`v6!az)%d%=p@NSKvAZx4!j4GIrG=OgbfW?gEH6ZZ?9{zxI!g z#5cUN#4ZX!+ss_a)(8)h5E_=z`#rqqmq6tv?>W_|m^d9tc1ReBumeh?2u3nsw&#%C zf{<$Bs5$t7V^5NNPi_6?=4P#ZXS74fb=qDn6kMoQZ%1HFn;4$(zb3I0v0JR=xkei26Bies)rQ(8N@{5)id-oYE(6_? zIgrO0m88Yo3)r1>E_NL*!tPtOC4`wbW7DjOm#Pp{oS2|5*Qt)-YLrAK&61m4A532q z8qQDF{177pYhTmRq8b~pzen24y?4%e{}Qf7X!Xy8e3Cd&k^x(B*h0&{{^Thtl z+WbAAW=8#VT&~i3(++E?Ah`8RcF2$}g|%$7*Vp&HNueTqlnW%_C>j&#KRrgJ(~u17 zu%eA}mzrB!S= zzu;bG4ix41ak!!PtTW6(pEbk8f>tVCQnOe{rg@d~nZUrn!hwN-znhJV;y3u(!MA^S zMTg<7%)OHRc-iS6_2^+7N9)kk5ypibd z^`dmJWh0FlLfsa-kzKQ+{;TVq;Q(C7F2War4U;`kaW`)ISg9d>p=}hXNVG59ImK?J z$;ae<#K*~ZL;Lj|y|VY#pzmug-FM!Yf>ImSQe#4|3(D7MGS&=NwwOmg;;`jln$;!~ zSCr}J&N~FPTDtSg*c%!#pyIkT#?$W|xuVvc(Q|6#RMygMTERPm`(!zbH#w9wL;tpt zGtq9akr%%bme_IhU?@4b3EJoY<~`e^DRQ6ZgjCU4T{kZzrQh2xpS`*9x|mxML<(RwK``rRFKO-*2s7{Bqfgk?%FwtannM9TXsB7hud?%=v_A=-Yez=24-!QTjAs ztPnGSVVaxb7qAfLLx;Kh`h*P?&1yN@iu@`1(ez?|f$e(4+?~}f$f-9}IBNrm{h0JI z?$;)Wg@Y3Vmg57ZaQv%qr`c*2dU{NfwQa~s0Wa;=KweYQ#EMopVxqstas*a!dI=YbrBSzI708~sq3CoDJ@S?`N5pmf`0Cw=a)9n4=1z* zD~?b@(genF>Icr{n9OF*vl#P+1kJ_Q^w#3L^*N_6s1XcVSH($lWI;zOUQE`>IIqs= zW3#uhMVVf>IjUp-j?M!(TPPLA zqQF+RBpItZ^N$Q&bt-L|YQh_AgZpj0i?RdFa+n<^hVnhFo2A7$I40vI&!(kjvpe$F zD~BYdzAVM12c&8_r>O3Dp_Q#v{Ch1ZgdEk-=9%ea$zi)4!{cF_=9^+_+2Gi~L{c}r zq1Jttvr2>(5GMTVnA4KXtnGg9dL0Mw9K1ng<1YMiQdk2)fOfzAVeWK^p7%9?L`pQ6sVHDSB}p z+UlIqQ{PSB(geKUq{m!q6YbRs3=|!7ble?;*mF=EnPuS0;md>{00h}2Oj3=OMIEfN zl`S?l2uK|+>u3FTg~5ti@U~b#;5lVv;X|IC9q<1z%0SD)1stjM#1$f&#t2wnL$tb#zLQt zj74Ur$y=xO&QM|J$?1{~67n!%p~`{~CslEtn=AWQxAq0TjX*owV*LulwR;ceUm%AJJfXABhN0wH`?ddU~aoR?QZjUnsB#Mb$_W&$>bO^AG9$e* z@JCyZ@6-3nhiaL*cJy=cW4aYExLVZ(Ld1nS@xB)Pd;@v-W3S9N0A zQx7H9xG)EPwLm6q`!E7NMbs>MNoby@nD;rVmL)68Gobbv)p(XA!0?1)aqp8jM!U@ zZz?oRF?2t_%(3KhtY>>RXj(p96rXETr2~%K8J)3`%ORnuY$UO;)!a~x)WF;d0+dBt zL(r`<)4<0kKL_fUR$$BX1^mES@VscF_isWI-Q z&MBr%1P8Ebnyxt|r)2C_Nh>&?`P%cNtRo|#yh10mEp_;U(ynn+9e>$qX+vO_ZxTbq zL6jOMiK_2bOiVLKa8|~d&p9W^N@lveH*I20MEl7$mB?1ofe0Ox$x)3R!9aFDZ$hcX zP;_uT9iS{wnN_zXBYt(L8$Q;wQeJo^WH<42>YbtHkDc z>53+~34^Wu5ChkVmn$92OFiULec)m69leYj<-Ar;zlTMciM&obS*tIjH5{F0TB|CP z4vvihy+}LfR2!Fj@PW5W^+Pk2nal~qMsA(1w@V@abizzrVF}bjtI{p+pdn?_w69k) z*33CFHchEQ)@r+pSzM?p8cva;rDoO8^0s70 z$*z~{ODqBWZLxAVWT%|6lQ|#3c<)gAI)7k2M!4vebt?zjp^arTl3$TV{s1JAQD&OI zkJ4|N$FKL8)xqv;`C44=HD38J7mYhQ(3y=}cnK@krM|0HXHqee_$q_kx(`PiIv8XwG`4-r>9O>Q{ZSt`{II;3FJcxkIVLVdfH{`}mvlR|A$ z3Snnn{|L%gwAT88wq-wPbKzxtc-qqG%;`57(hwXpT!&eVRiwAX{X=h7k`O0`7`p#+_X;0id z^PKaha8i;l-#UHWS*v%xqxEv)^g!6{kQay-In0?PGs}SzYXO0#Z*eiUAa?9n~ z&$VDur=Sql^l9b&4R!ulE|2J@XUmhyDDiNQ2bIQ$rW^{9rzc_s@3zJA7Eis3tPE^( z!)xYg^U>aXf9~`Aaomp>XDd%04EaDWFI8J;!K^94#py1mHY0?Aki;;WCBeUy8S z&neZKy_!k%fj-iN#q5g^iHil zOvXj6@ry>|F0q2j$))E?9L1v^t6xb7G!jHTDe>)p!)kBYiSP(tVY0j0SpRf7DSV{b zE9aW2SL&GC?`2HjndUqdaw_n3+F77(U^J8obp$B#tQMzo^K_qI-CIb8q@ef$9<_{K zxFa(9C6DvbA(L9G+;x`+k79jJjfq#9q0#dFw`wyk68JCC&4Cn1(xsnmZOMM58WYan z;8Pt@7l}zfck{jnTP2iJF=wh-(oqb>o%QGv=sEw*C+t#AZKGEXe441@G`LtjU_tm@ zxg*Wgim+yPQ)y*6r=KK@-FF(1Ndi+?H!3HRIDeM46{9N$4iIE_*$7vX{97@L6ThI* zSY=8)79Ci=oAZ$*sHr>TqqEoL8Ls{@^Udo|XCUnHJ~3-Ees`aB2dGld?6X(3XCn=V zxy=H=b%eJImLGrGCQp0tMyJdj%P7qenFANTd~O^~YOa3Jn-0PZ9};c2IiwmNiE?Lu zG7>YUUjYl_Rpv7?%^MhjIlWJAP7+tEFPbK0C`lAa;9u|X6b|O=km>gT)=3LF{tVZY zDoYBWYguEaPkM&mwEBD{>ut1*LV|0o3HQw}EV-Os{z|n2k|DM9h5EL=UrRzi_qyvE z+c~6+Z1l}rj>&a}HSW~4Mqo+hhq#1TgU1W~vL9#uu0b%Gr4gEZAwfo=D%3L`ag@o1 z^>=9Ai#bZQZGx)w(~>3bPWx_>e}~M_Fm)K`xz-90sU&PKBG*8y9Ie`YDG-Nmv+AMc%k8>4#Va2u z3Xw!HZ+mF5f_aC94K+&n@fSEAvYS>P8w^wM5u<2*gNxjMdo`=u)0K`Ey~$yJM2c%E z?}UJS;T&_fVP)T|eR0mx+PyMXXML?t_7d-ZKfeE{yvfB*;j}X!^W-FWZu~m&{5Z+H zzF-Y+rtT%5Fm^BMG**?rqC2cabXo-S!XXDeb~$zFVvMC`^jdP*pq3LlMeMt;f)Fx| zqduO|L!I>YaF^3*%dd(u8cDnqPwi1WEZ6DzGPe~r!HP1j=B{soN17SF<5s@}WV0(> zGjrtB$ zXgd#CrM%{S2PbpP+2(5x$ptO1)L2(0rtV{HS9|e*WCcnx+`~@ufOrp|neSkq{Nvi% zo9a5TV=^Lh60465Rk8ni^UIiLq7!-=9R%>yw}~MKWS5;eB8G8Wyt*yya?v>&4vf9g)YERxJi(3)dge>ve?k!fu)nXDgA?F!GY5!1#Tc&je(6oX<8Wcam1WbW6jJ04?jrPy$wNje)DBs*RJ=@Cqo84hJPBI6 z8IJXqPZy`>%yyiqOU2v0;#&QzM_c}sA1gkN5h8f|&@t_7hbA|z6-lj5FtaJLE2t(y z-XUAgWYTm@t&1Mq51S_qGq6q<$!DCpFccjW)6sI*(%V7H)7v`F1ILEW`cy|JHuCON znmvW$DE+j}a#h0mU$snp=*9W;w`Z^A_h%{vBY)9J-QRzG zb{j3*1OP3;+iqqU27RR|ycDf6WddZ(k+T(+vkkLh?ywbV+hnb;BN-6gGz;U9=d|{2 z+Og*QGtCoRE99-~AN9YY;yhH&l^WI}J}$8?$v+OG>FtwOMq|b&2ggp%5W6-!Q!5P) z@lSVyZ|=o&x*Qv^Zj-g%>)Q45wtgQI+R(r{S*f)nhq3muH9J=M?vgMe_P&@F^VaBm z0<1B#4X)FaocI zZLePS`}x(O1d;;)2it(0pcsI}lz5WG)vhx-B7VUucrYsraVJ}OvDK+s&amT+|`+l3}fsl>Q&1NdDJlqQv{^L0ZF+v&^;itM*@QShI)^ zG6|Klrbs#85-WAo%}1#wa5e`&YH z^7GR|fX-}O5xik_E7kh8HmCNFuF8nMUiSo0%%c=Cz<;fbnM65Mu#LX>D=0bZ>Hfd5 zg7fG1{oT!n;cNe;51Xp`kA%%C=D$R5|5Lr=|B#ye|MPLK?PFE8nG%q_g&d|g0PZzG zFO8Pl=+;@bg$0JKSEJ&j>hb{pw!EZlgy8tlTjSLGhxC=weLL0pTbj8E#Oa=7Nf=<> zCPzojv4~73H#gi;zN%qSP=BRLfiJxUs@5MROEU#8vzZb zbR^CMb|0g~ir{{u36Pcr01J2d=|fB9?O^h~Iu2seI3NB4z?O++*s{7E9nrTeXJxo>U9+P~E5=q{pkh2wJdLx>Iuk3LqA!xINO`m7Ak2_w7-9309zvDu~ z(6FIaHUJEOvAhYJ&)B%X?w80-rN+eT`Q+X~IiH8M+4zjT_yMEDoKd*H_H-cYy#a81 zl@3OxW&}+-rDM$%9-qmI-B&7MfjOo$^ZgMtW8sCyT(Hx6y4w=|ImTp;aOG*=*M*y4 z0=WdVz}EW)uKRB29K!0DKRLQ7t(`TLV~90*Q6AX8)Y`$BBUh1yUzOc z%By6ZraQFF)0dUPt`hJD+pP!)k%IR9`GOAcvbr>Y-Dk>SZ>9J~D7^IT!@ODp zG|ZU|aCI&8a1Jn3Pfa+@Dk&auL9!x~5dZC+^a_D<4iq620WjlXXM@U>r75X#NTze4ZADvv6gOku?bfQSVkjBK26(WZ>;Ny`dN_`a&zu z1!=@|yCwvP2PEbV@161)9=c>MEb<%VmN)8nIN)_X~-S6f7^MR~3wGw}OP-E1nsRnuC9xz1LvxO=;)D%Huy>zdn0Nh`rYTuzA4 zU4w@sHId2i#>F@r=<*Wj<>h*h$+u+wg`+H;G!S@>gm`M3((mIJGtu`Vjw)vt;A~7x*hd6o(0yN|K?yB8!l^R?d`g?L4)^-BZDC=y<~(Y}qX`IlF?g2);iFQ@A$@+!ykzgzA!x@>z4!cVOg9~^-a*~U zi&mU`2wnqTvlKoLA2kVS(htzOb57{fO=*35)lPuZSNXA88ED+stG@S6i}Frgzwt}0 zBS412E-V3I|68tL?gR+36(0bY{xUX?EpXYvI~-dhx4;$RG$n-G>yEJR(G9UF82ck& zfTx`3eQ2mDxoDP~aaVkzSl_V?>pGIYre8h-tCl~N;0S)^G>L@KW>)+d?~}2vx_2!v z5LmylM2(#rp(i^8utveExPj*A`|B-xM~musgp6$jwzRdm%oRc?S%qvu2Kq~>7#D?4 z8ORyGXRayT5w({;_PIEHpY}7>gze~ajU*D-jD(6%zU(lDoMDp<@8vc>+mV-nrkT-h z@>1x=_JRb8H4_CmhPa=j>cPf-kO0-I<&V`oyz`L~RoxM_VNu558k`waU%UTQ6IH zE@iv1GGER(eyK8Hq zu7Uip)o^u!N8cHISqs~PNIC0zq0ULUJ6#D}KX}k!-`-I15?6+~0dXnn)$~PW;~!jM zr*vT>37oQf<#PKEVoXk-x)^ij)Z+z*XJ&T-*iEBuKTo>@rVgbO)mLCpW{}~SC5IEe zf1dt;N?*}}r8~~DJFy!y%!Ro#%jM;(GGeH_eY_tB`RYF*&}N*YXeTE6Q^Gs%G-qqh zC=2gv`e(Xwm!4?g_Mr;~Qf_L5o}0+8N{=>q$XjA4F`uZM+n;1e()v(p{ET=97TO?&29Ue9xgEl}P&T!e6LR|a+)i_uB4e!Ya!d>XM1 z+GU$xTZis)k-F*+)+cYJbX=Va_mC!i2BY!+oOLa!by7Ydbbat(4>|hXKBn6@a9Kgn ztqWfcE5O+$qhzBLoz%x=?mXLCl4L0(^Tdu18j4_ll^xIJLECdK9KOjNc#3}RPHwkZ z?ZqxKdE8 z>>FA_UXM=-1&%!Tf(#O0bG0{Kiy;UUXuttPzREnh)LaOCZ@9f7gm&IUyO?4y1b{0ysv>#11nr)ssp_1Ey&kdBnRWFiW zcrLf0iFB={I;B=`t=;NmEbHBfltkV@*jk?E7UUu_xR=mv)C6^GZ1~Q`ew+?2NEO&Q z6!#gXPv2Op`9^3p!on~VY7{*861y~>POL5z zL$8GVx-Y~E7NtmR;)P$3Z7&cU=dSSi40l7G&~AlVegRpq@sP3hD*VJiiK(5p;UYUZX?T^wQNmfvJ#2I3dwDOF%aS+oDY7lk! z7+6{acjU!OMa8K;l*E0#+Vk1Aw#*^Jn%VeE_@a&2mI_+i#3o<~PzRw=NO-)Yg28(j z*M()L_w5HoF|udQF6O=Ea#?n=wQ21+xc>NK1p>ikp|z8L#wO3e12lbCoU+8(=__h{ zuKt}f^^-^Ki^rNTVu{_3kEdl?=sE&c0~KX?o>6GTqnKuYwkIt(8w(a zXgr3 z)5J~_IEy&TT?(~P%*Smb$vz;j{ZF7nM7dq*UTv=PU78;gT$;N4>u*p7Rm)DI9U`$y zo!W<>3q=D!0MZthQNm#$xgNNCF5T`;uPiluJG6OwRxal56SB6s!qXv)%CnLu%w9;=tBqOTjVy5-N&oo5Z?b1j;p}^9G!HvxM6hCF*mtP| z05y0c+md5PVLY4a!L#E?vy67?rIkc@CL;*;7PX8gkl?B2 z*3o?C?9bL`H8M(Q{IOwRj*R|BH$t)BzAM5Pk|Vza)fjTVEaAW}YoM8qj8<<7%M>!t z{(@&Y*|oJsj*!TFjI2J2mxIUDOf<9|eC4~|DZtA1eTHO3(6wq+%ue@-(qEF7em7&+j~4J+QV1A=-9(2mjljT>Br zO?oGcs95TX1lhgUe#3O3>~WG z*7Pq?lj3*Uc@^3Jxv+$zmp7tY(WylwN!4sq51p)f+-j@!Q`o|R`03PDPQAucizES? zZNNTaegB!MGqu=eR#GVroWO4{LtDby5E#7*0 zk(9$2sL^+}t)83v#ppOFjy0O2Q%L@nBiVsHS8*M(fsQA!#MZJ2v)u;~m=tYGIZ4`i z1)^oq)WOr(PuWq%Vqk^!QVtf6gLnmOSj)xNM zpRgKrTQSkGZvnWQN)r>~GsjWX2_9el4wllkspM{?_p~wB?zP-Qy?2GCztIYi%0;iK zzv8U$CbGMttcDQpPln1&AYVj3+X z4SkZofJ|5JNu{0oMY*Xbvs}1zrE$tP{cP#n-2mVv#0c!hko^vvc6H%>5X=| zdHcp4i!Ycb{<6ZITkB^J2=Kc2yhXD$A9hv%4%XorS7DB~>WKo<-FvuP@{1ouAwE?=J=S}IC1E$2sL zQyYY)GtE4;j$PmN6%o3bDs(GK%0_@R&f=r zV=W-z!>f J3SRnU{NX+R9T=+P-zJ)7DjTR=z&_zX9IUaeA%HJ}d>pTv&V?Bc}89 zd5Nf#Uxzi~#zrM1p}2e|4IMc=uDR(68(|B)00-@YEx|i$o@&Bi2-J)ppTs~D6Y|S# zcxZ6C#O8y#W;5%0KbxcJc$>Ls^hWH)(+o_eFs5;CPN)vSA&gO0ePmsKeEI>Sl!KJo zo3B}7gRmJ#FWIRiGAAInK5`=Ss<1n`aB|<{{3>ju<%ULIZN@+?+OfYD9chCPKWxnL z$QpT8mofUktNc4{qD)fvA3-lXQi=Fy#3roeS$b_HXR(L)S>1<>qi3_G{ts;T-oq4ajZ??BJ5Rl+|b9(!!O*##S!>Y;vK#sFH0d)WeZsh*as|F?-_=%esGK)(`x zs`)RJ`M<-7)6|0NqdOyHld0Nz;kR~54)BKJY;Mh>Kg}2g9a#8|E^Z4a4}II54w8>9 z7~i-`$+$`!LjLVxaP^`5ul5jY16@yCDzZsE5dI09Qh#l6Bc|XV7QXjSdu+kNla=gX z1u70TO+C9~@%ib4u$@u!cmVAC4a+wC?u-T?)5;A2Z{gMXTE-i|!{{{V8jw)nX1;6|Hd0i0vyZ?g&WH~KuZ*d70ORDg;;4bTRs zDcF8k?t5=?v`xYcko!Bi0%BHxrkZw!MFPn2ySP6y_E7u0kPvSAUFLm zPM&Z~{@wpwU}B(h4iKhZp7lkE|L!}`eJ5>b;D}1a|GqBQdYu&)gck}(&TjW z?oX!=6`5>7rX7$n}$uQ5md0r=yhuSvqVEaLqK0U;m|G5alg37s{j_b-gGQ^ z91vHjG<>82m=HbyK+p@{8K}zif4C+)kJFjB^YJRcy#}o2#w?qe{{|IRNqtv_c_6=D z+8pXInNj%am!hNcyPOT{;Rh7Xaeip|hQG!$(o!FBiV6Vuq>ad^yXxL??XP*`ekOg4 z_1(TA9`%6zA<*FRZ9&lf1SuO>+R(6%M_+($$*HF0)(N|dssM8bwLgXx<6w$^@ zTix!5X=x!}yt(Mrk)I#<=$g8E_8I_{dMhp;Gd|Qy1eADHaliibZve+`Jc#EG572*u zv5hiOZ&ml|45whzhoLM2OT&;uz9jF&D6kLjSEQmGa)enxv$_V^q~{7o{hhV(;Fq|q zkY4M#n*Q70U9`BEZNH6 z7qBIE&Fht?4&BPxLw5Ex-fapR3ki_MTWiqs9DX#_rh>gb*cni0y115Xi zKT}C5#O%Wx1;i{lRSEfr8y{jL{RnO3B7Os9De^qfvqDKq%2ke8N;9J`)fZX}nY^El(EHW&{wP}$z1MnKk81vrxV`&rf)sbwI13}mR5PCzG zj$`ze^kPAA+VO%|&~zuaT7^8@8=&P<-CKIPH(e3%EGww6i0<~>FL~Nw{xPDX? z$*bBpDRKYpqwCHCS@b>&1(%Scs7Uf`I>vLh` zJWW3wz^RC!moFKe+A1U>hvGmWE)de$ta9C0=Swv+$D~@FprefR$v$`3Hm~<)-GbJo36f^8!?{(bRw`#E02_L!C-2B3`L7W#|`SRfFs&T0%c5o zb|wczWRS2XcNZ~2oM1%iq05ON+U00R3A_Awp z)f5?0NK`p6-5yb`vGb_PIm?8PJ4HL&y|nj6l3pD!qrL9nfq2<*KKmoIOwua)aq-mk zD^)ej$YVU$Z#{RLHr{A7;#r=Z3(s-t4Sd?@*HT{Ex_iSW{85@N6dVXJ#CN}U`^s>* z_E{6i`i(zcaMe>-9haz6hz!Ay+kxIASz%K?RF3dP0@UBBQ#r2UtM^a z)x`n;^a06L{Uy*{df{8&h*(7)$?b@*gHc(obnIs4JpHMc$Q_gUjNKhLR0GthE~n#$ zx%y$UW?+x&Fi8Q7P=^j;velG8D0v?_6xe0&);WrE261%}9@fEue3xLlcipx7B0fb4@#9~V|k(QJumnHN|Ty(xuKbbJG$CNi*3J(K7 zK#H1Rgc2#cmn=WuZEhQAnxkbEGJ@ZgAJd!7Kli9mS?eQrfQMn9u&tx3@Whs;MhiU;Fb#FJ)CsHHi%?bCdA$0K4;H8?kA+M^WMM*4b3$q!f{z*JQ<&;q^=+>iIO(&z94QqZQMkf_ zL`9`^hClbqi181`wQq-Dx>$~?V~P_jB?l-9Qr5M*^?wof)?rcYZ`b${K}JPFK$;;` zkVX{gK|(~jyAKl5-5f$%Fes%vhm!6_2W04$bV!lzdOsV_`ToxHKF=TTb^Yr6cRZJ8 zX7AbibFX{dYppxk$TOF76fZT|iYZ0XtEf~jns`ugeF{;THnB&%W=!)_9K~|sYmE)i z#VVPI;!$ci!BZ?eks+J9^~$M%5Kg+rw;!V>=lS@JD1G<%Ko#4mpOpbF#hpGAnoR&Z%_Tl?mJ;c5Z zw@Q8EQwVfXskGcZ=Y{PeKmLq&kB{SI-Zy1~JE!J7USK((TFb<1^csivg!57Su@~7i zk?1mE;ZBL+N){-kDaA*9xSRKJGl^=6(bUUq;006r0nLnsSBhhA^MQ#M+2HoQM)=1A zh?E;#j!)U}&|j#F-k3d1j^d(6VeO3GoEaQ?TwBeD@%&`en{%k2Lk>C zyfUrToneW6DDGHki7#g^W^UVEZPs6~cW1u3WW!UC!<%NIrLzMuNxZih!$sHn{$X}Z z)knv|v6qq8G41>`3)PxYjwSIq`h2{F8TBH^-IGLJMP5B_O&XO0b3*U8WS4}Jj4D5@ zf1fTb3Uc0Ox;x*Xwmy-`S#LJhggL<5DD|KZG=WQyoxDY}h)fy_pwfs=96-rFy5Lwa zL6k-1C!O$ca1nHKy;*2wy$GIn%pe8E<2F3F8~kLf%i$nY!HXd(JUhqB&7rJ0?hkmZ zS91rM#x61T)|GC9LfBJd{>Gp2xDiV-e?7EpiNn=%_x2)Jxpw{&eO{OoIh4X#p3Aty zRaC|KU~#THB(f>B1b?K1R}p{Bm=VvNVewN$n*MBsR}p?Bd?~5^S%acToT<=K-;_-^uiw#p zypj#|+H@rS!h)_)GMZ$Ru)^?rv_jW~#VXlGsO7w{IM=s_Lwk-XC%Whg3E>h2Ol9PTI3IowW#SFqDBZQ|RPfDm zI+i$6Oy3qg+<-85|0&`vfp259xkYCjw*hKu@k^sALehIIF3{{WE9arP316g=VRbR* zV)Irqx5=|<{YG*v*P~(%poY5INL=7uyhSrMd7$TIu$@FXgNY_4JM*g22jZHelPHS{ zFIy4t#!p6f94Rzx>d`L!>14mv9r=Y;T9yTTYB5O)_TGb!S zsFPt95A&JPN}JFuA@%QBB#3UE9H~6b3#R>(Hb5 zZa+--;CN?CXJcv>#R40oIu~ z8ptm3_nB$U2akQ*^GQVaTU%i>m@Yf!;t#b4y*E8rUok7i zp*GG?jsedyY!VYBzYv~q?$gkmL^{Sl>}^|yWmt}*-|%tRPj{RI?`hbGhwLomlIgi8 zxWCy9@~Ql|Q$=3%WTXWd=zBKmn}o(;9sSDBw6|2Bywh%Z5`BS)Ff&lJwL9fw+OV!; zEhXsQiCxMl+&-zFf*Uu4_>9aX9Qx|Jvp*Fbg?ahd|2(n}u6DT*GpgW3UEnqYK8=0p z9NA;{g0k~4eMu&qliJSzsom3VN|9#w`+u-|H@ltJwz|pF9(5ag*_BZn*m)g3Od3JR zE(Gw&i^V^qsNZ+??})p#moDD}?{1GHHSqB&;YoH@YVs?`PVZIXL>YNo8B}m~&~ylx zlXk@2pjn#@|HzqV{#EsSAmWVj@VzND&~i!*8txD7mlSK^oA=T%0)~k*NDvR^gH;k4I2A8o-2(H1ekQz+b5HCvGqzW z)R$H*+};`8sV)%_80mOrJBHtz!M|ZT%@-G8y~yW4+gX}HU76%4(EITtFUg_!Bj;R~ z-sgQ!_41zV6}yOhD^10VH523yHjQ^2Tc=smR>VCw=-hg9@KkH)%}QZ7;_dnotO2cQ_OV7`&a;;6L080o}{P;_xG_qB40yS`;QT&{Uoe#6rznocN*#51q+Z zWra1#OwXb&wh;L~jS?_ml~Ur2&GM4JeOQ6FO5J3vVXY<45l_2d9B;seZGbxUH}V>n zLSPhW4epc*pK9NnY>8u?l%^4-(Y*GPYW2(cT+Bm0UPE{JFhy(lHfsc%`1f4SCK9nO z0e+hvPJr)1HZX@p3sHt%8uqU|jyxe~=ZNQH#BS5Yn!=+g@0(X!2Oou$;|Kj!eap(p>ouF#1y`jaQfy4sa-=}bqrI$X8+mhyBcj+vcMNW2 z5@@{F&Ra^WfAxeR@~U&zuSBxX-|RUkCVQMp8ti?pcM@+7-CX)>Ypp-g%t3^5DI(+m zzsr9!wARt<(6^1Zy$oaW>`W`NY<#~l(wU;W{i4P)7jC+(Z>Hb7LbPF$E!o_;WoB@D zx7|~y^0m<_m2*zyR!#GT{W<68ny@%c_Xbf3dJL{xoCt)7?ic-~z6-~Gf5cjZAdU?j zzTOkQ$Qdg-6#BDfJ?SLCXrHv(K7!JMu4&{6l>Aq>T+Hg?aQa&8vfa1fORu0X8dSta zzUli9bN08YQyK(+#Qq)CMoIDy+ode?Kjr`ZA5ulx)@^w}ZkYozrtXM)n@pEjjhB6j zTa&4gMd^U_3U1Xp)Wbz|iox#$wYwF;mA7(Wj3y=tO=;k3~j8mSC}~k3sfc zvV%OZ6;ADG)abJzKn%td=Osxn#Nh%eUFTycbAeV^;0-l@8F@ZJn<5F^s}+!IAtOa? zFF`A12(dHSr6ygAvzABOm=(~MDuk19%Co8$K8J!tAg81s;ez>k`{s|Y9|l2Q%HZCp zL5g@Wh%YS6Nrc+=xHBM(;}-@cas^1dWT7yOL8$*9^#)8=HuRoBEL5C$4J1U>F8fm& z3jA~657dmvu!`mXS<>b}L4Y{5Q)tM272R@d+psqR%*sAI4ZwNnev;ln=DZkltX%n< zgHl}i^-CUzTA6{^ESCL}HWN=9low<_ARbDcVhFQ$Dt~S~4P|*i`bvwQCyt9fj{sLh zy=ypK@6P}8BM991^vFUP?Y2n6Z7fl{)k(F}Yvv8&vOFdrt__Dw_wCXfXKqep8k9ew z%jx|23LfQuXV)?rF={0+1$WW2Ai?Z8PAgk7y_%MW zKby8zsnC^CQQFGiBHSUo|Iq&84cB#6-_V7X1QRk5{vyOH^=VDq-gPrg-hPPL>i-m) z`d~5K9BO_GOvtR5Gg<|`8NRf?SGl3->KpYMJSv$ZrhNA- zM3dd-Lm#qF84|fY=<0oLE2O)tuOK8y&Rs z@+qH-ek9votRCAhQQmYQ7`G0xXDsI1L;0EcK_-J^?jK?a&_nGCc;K%KFguR=&DN=Y zmiyY6_WFn%c(jU5sB<@a)ej5>T8P^=`s;Qy1wB!BXKi{SYOkx_uPmboQs;aUbhS~a zX{ew}$HPKy7rQERj<^Hz)AsWjaMmy6iPsDht{GFaC#UNV`K?E~YsGGBCLi~GcLy0b zCt)j?hbCvygJjuK528Fip95UG`o*ahu%kRtCcr}cnp~sidxH_}{;NVN%$byy zlKheIQ;q3qha?NkeQ8R4*mbwcTTRp7@h40@53{y_6p&l);B%{5|Pv_$^rMQSotG(NAAd06*+&*d1~yLW!C{qd@xTuL0y19i+&3n00(GhqhBG z^_r2R$+=W+D8}95>^1CL{KdFg%KB#kd0Yb@7Q6Smtg(F7r{eNSJ%~ zNmLnXgS6AYd04H+t1@Y!rL4Y-yz3#J%=-zQqpmmxW>H_b@prZfBTp7z!thl8&7hru za}kNU@$$(NYp(L0t)e)T_cJcZD!aMXQ^NDPc7TuXn_G*0(8hY8t+a(Ay9W2UD!D1q zO`_dPh;Q!+PkZX>>WYsC9CcuH@bN0Hb{Szg;h)sbLrVO*@x0dB_>V*h%Nhv%`WP0O z;zJBX=@Uck%vuAlyPr7)@?+mAjOz0jApWT+Z<`CJf%$4S-ky}Ei0-J2yGplu8;ODy zGfah9XN6mv(xP&3Zku-OOVGr}ZD%wp6*sn*EhEoOm0WChlzlrtZ;aVsWhx%se8esL zQR&Wt8>fWgSC{=Dlg?6OEfjOr3)T$;D&pYA314O07WK`23R6PE+mZINz?U4pL!oU( zcsn`(tJhU~%c#{WrJ`+Laf5G^4VM&2J6qnpMPaq>QNQ;30DmK6%&dNZNVfmh6X=<1 zCBwc#TJ>z*oC`;LweR8UK03l{L?nb8AcRn zu5+djlceEX(JFsN2@@mG#t;q>R`=zHa84^yG%BXNC`AI-q0$g%>*r^ALbQHcL!A2$ z_K>6}VX^AeuC3XYC+QKh;as6-qKBHuVQm9rDoq0RYPT8t#Oex#_d|{8YS+lR)(J@@ zy7$#*XaTrO05WOwg4A@qNVMUr(FHxudV?{kpFGo2ZT#{6;3VnUl;j3gl?Fc>Wga6| zB1ZUhOsraz{HbrbSK2U%(bX$BG&}81v5bZET*j2_De3oa@9d2X1RXK_IUl3`5L*e- z$)k_2-`C_E%%~u~a&wKES^H-RtQe zu3tfPT;mnHwDMZG`dr#@0M_a>{v=H0oImF`>pKg~fI z*Q;iHBxjn5)Cunrs{gEAD{`NLbn3PmCh>-|K!GF&Wk*1fUprpdH=K3>`^FFH|23Ry?a$aKk`A19JrM#H>v85{hv^n?ZgK5ttPH|L(VW2(6 zePN_dw3^8Dvo%q6bQdlb;i|#-jgUL4Y~8#iKK|K=lh26m?g8{U!(T^`doy`SAZD#& zzkeXYq1!p+VIJ;Ns*_;Ruv+fqGjY+j?cFZ7_EV>{dby!BNRYd5-4$}#Kw+;!2tS6& z_QvC_DBr{=+#Md-hVDJFiZb$-<8{AkS>ZA$(~l^V>-$$vUYA`>yRBzMH7U*atR`BV zW*->sy$Z*RBNn{dJ;S}fdE}9uROlAn&D-0`;J_@6ZR*Me)iD2JXP(6>(SkpeP0I5$ zA6^FxGJI;Th97SRsL(s&pDYkSvq<)!PbziPDJ>&KU3JMUGdw?eYJsYHz>t@X{Kl!D zJ2VAVwEhQVKpA~cgnxpQNlqv;i3=HF7;Jc7`y|)bW74s)l#;^F>;cWd$4X6hHZK7;eP38fi0G@;p*(j!e~!g_ijWz_Q(KIiz!Wk_*iIS+ z)5Jn^+8mr!v7~g_zVw@g2@YDgh;;=T1;p7dGD7Pm`IjO z;LR#3DtcryRc#z`dDj90*1r86qz0zgbisYsutl(6fZ#}7+Lx4VP}RpTHja-p46%PQ zWhajB8~=>ZsfWAk#}>AD4D$sD9sI^#4q51^{bO{@ajW!S4QgHcLp$V2Fc=8 zY36V4U*3IHPN-qP0%LBj;M7o^mH9g&{?u4Wu($D?t@#e&Ihrm&M$`8}tNyqhi@l`!EXo13Ln!Nq0k>faFk#=76iP(2wZ6r3GgfVesT=+N=1B7;3Z9p` zDj*4Uxj$p@%R9#Kv||czfaw!dEfM{ac$N9NSAZ2)2pYnk-)p)j6ANyd*aJM&3Hps)*+b;?1CoqS2l`g{5d+jI8w4|!6zh*7(fmF_k?a|}A7F^$b zFU~ALq(Mg?)%>{gBxp`P7im)jq>Xq$q4vr=;G>3i)sfmUzE-oLOv%ihcxwTKxEEkrS)r zi^@rx?j|~qVKohm(dwPG5RbU7*YWE7w58sr+`b_KduovYfkha!WWOR?FB2ZjQLycv zto;wfzZoT73&z4h-%Z{DF6I^`ZIIib+>^aiO4E1{v*xooBPtBcZ2WZd`xOp-VltU) zMRgoi&uTYYZ#t5TAAnLQRCS+W2;{=!7)12Qbh+t*hOZ{WsplKX~Ib+c)ls^|(h~TNL z0sJnucATz*G`=`V0gT6u+_Vl*}~2sP_l ztX}JG^=qML#O#?2@eI-P9{Ej8_~Wb4)v`1O9(Kdvxn4y5#$g2GhkfguSUF%xz@lMr zUy!=0(e{t8k~3>(P-py$>GXO8QwTjXWS4Zf$)HU_)CaPI?*GtOTq)Mnux;{r4ts1n zUGD@=rKQ@wfjr8}4;FEHE?NpQ%o0bKTFGV{{qD)hgl(Z+DiC8laebx=I5d06JODf{ z@7_;z9??1ydfS{%ty;p4WQ!zNie2CIp>@P1*SgmadiKfB=paWf9FSog5Xs4i zl}~dkTl7^XIHeFN*nP?&hWA?&U>VJau#avdDj{^irVPv=q=$6}X@eomrLA6%oLm;A zF|SZV#;BB6Gc-f=l%uZ!EI<916|JT3|As5aU;MZh!KsFGt*@<1 zg@t}yNt`KHQ6%aLHcX?LwhhSAi*jwS%enrP#BT^h2n0J{el6z~vc1S_DuIHya4);Y zd+W;?QN1+2XVVTdw71M553=K+}b+A(v{P zP{{47e}A{)Lp7mITxu#!W$@BFhtdX+U|V^UWsKHmIa?2W=d>j7oe3pM(Z(~kMh$NW zqT{(2J^_O+v;qWdE78J}wu}ae1DPQF_7Skq8OxsGbzU4TL<-s@5Qa7ft80~urZeWw z-P}xc?EOklumJOLyGBBfhqg+OfIN~-;8iL(s!^6TqsG|#3c(!7Bll>4bJzmtoq89T z6OOK-SX6 z?<>x16U_Aq1g>B5Ig8&87iiGz)-Q)6(JBhU!)5;zgE2&XN-GHQjSi64_v8WeZhG-! zyN0iiwSC7Nz$qKnoE2f0B@`K}=GCIs+r_oe%^pZDrP+Q6d#5SFC5$o>cuD|s7mE(x zK16xEj!)6K&8ov%Dzq1Ro~scp^F2K|T^{Foe+`mTDr;zr@}e0*L9u)+l#o@d zlb>&~I9ZtgowrgLR4bV{j_-@S#oB#kb>qdPlmFB!Rd*c&cJ1{_k)kJp|0GklMf^6S z=jj6xl>6^|1&E0UrA4p380!+S4v~TP%(>KMePS21B&6b(Kt;{$-x&I`>i>H~&;KDq z|Nn?%z30FA%faLOZ%iudU8wc>{yI1}EFjrxC#b}?puoaxU;ziqWdP6P?Jybv>VkT) zep1~zA1GZ3=h~&;r)`ym?Hk6&$F=Z=!8fIRiI?N?WFw9shw(s&j;6Hh3bg!WsPGP_ ze&VI5boeEJ)-|u5K|6y0=fwiZ^N7hd?zyquE^sm=h_qY8Eim4C?H8zG?X5F2QSoXJ z%G5T_bynN}%(ws)nQ2*ji~vpU$}5oj#(lRUN(=47^G&mouEFdp*}$oRh;GDp$dsGq zyf#8A9uQszB&Pbm9~Do_H9HW}Yx4qHcqH@s0k-mn_lD=CRuwYK|2Q@P^8o%5O1x;v z=0}QALfg+FDiM4Ju7BQ9lgHh&HR%+&3y*oTL9mnLZP(uG*=4Y`M9y`}UaIzVyW5eB zewe*#>b-&M5R}*#%ciA_cMEUtbQTu@whC;vCPzA4Hf&nu&U})6j$=l_m{E06)(5Q_ zfVCAl`@w{eh8PK8Kx~X&Lug)^^anXVkf+r6`2bV$aRRlh_r~Ezivb(Y9L9YiI$@V> zHH+yHdv6?7Y(?<`&F`+gQOf52eD~7$;oLw!M zY&e$vKqJ`DTOne#<#;9jTIHEa z%CYLN9DO{gGsPm#HKW=g^P6NGUV}R~$@QVB)DVKxdOCjk;)|TcSKDN{sHEL-YT4IU zT|HkNPjB%IfJCA*{j2$*#7s8CtD6_c?ZgT_>`5>ubES@5fF}g?>^n$Twz}YGWC6pE zqr)ylGK46dz*?B45m_QE`?Ah5CpIc5AvJwt?=3%833ISF5oXli^udR{u4B>hXaGYG zGRgGa=;P!FyS#eP<(fh6;Gj_t#D=mx1i(!X=SCs{sJ7;V(oP`RH1~QMcv%J%UrpMD z-7JI@t-*qT$G_kuQwn6C0cle^IHM3f+GT%}CVnH_;M5pD5K1OkG`(&K`w;K)?eS=4 z(@{dx*Fp0+l0jIT@kEr*ZpLN)1C-`o(*tGLAH{63k)c22x4W%+HD8$&i8%U@a{6a2 zz_*URLw@$rk3rgl?g4BRHm3>SGn*tw?-RP%zq`JFJ&h*5*V?1-$v(~`b`a2ipqIQU zCK(>z@YQ2Q7=9Q9{ZJOvJGxglnX|u^u4{PwW(M;OF(N8jv6TCLK;@O-fH$X*7EqdA zM8Tq~O*zDp$szuC>5f{troi!X#^t20S8$_V2CYviuZh=gCO|aGl&>!zUN$XT4 zGE`q7GsH~J!*pO~u(z;ySRTw1_7P^O8EIs5{ZH*vmaBeuc8E2J_PGqORpz=7=k}|= ze)Y~NV!tqKgo58|dSEg6bEDsm#lGh~!KmCR9?GJ2iK%+CM<}Yq(PqeB+bk5_wrg(R z>B7^~Xku_k==50IYh~xPf)tlzUylL-L3>Km&Y8vW6($n#hLicU!GT~V04W;$VpXvD zyf_F6cP@mn@*iTQTs8%fEDB~vqVYJ|!bIjcVg>6D;#9^zu@X8Eu}R{ts_Bz;4=CSI zO0h(N)meF3o8jgVQe3(TkYZn1E`)9z9U7mBs^Mpk#j1~RbV*s>hq#p?!jest!Qu4? z6#eZp3}Hy@3+zXMLYz&f+q~>?@x?-KM&!0Wr0V|Ys4o{{m<6*s^}6kKmvWJG>exAT ze(n2`GC26=$?cB@qu8po&b-ojY39vo!r>JJqNMRG-Y-v^g`#Hm;76zre(VrstX z@Co93_9QLVR5fW7VJJEj=87^eea_05yeAQ!*+_UQv39TTlZ7X0M=Y=gYoSq zHAjEzmEE|uG*}Tsc|M*o3QT3!%Xb03v>EJwaXnDQevK9xX)f2|Fyu$TMeERubime~ zveXDYeu#WnJujYQCgo>MZ^dfjgO%ArXrz8bp=WjVL>4?~V?D{jo_+oggsQflSd4tW zsF$k}4ir3nyy=Ha`=x4f8)eYn+}3aLn!<=^aHaB~+f(Rs819?TY-<{54VTj5>q)_@2FPH;rD`CUeZxQsT@lk(;Ta@>P1PAX}QP4J0 z54v&j6Hw&H#)bx`5$-{*CN>V6O+SQg?<1=L*a*+JeU$vk=QO5lS&VT>AdwW;a3as#0th&wG z8~Zu7AjDTG6lA*TldykCPqEk?^axAMJN1pV#}=QG@7hU;zYyMVS3sYJY&k_l(SB*XzF5>QAu+wq);%(b+U*IA3K?5PjQ$LpFyu=yTWxU&O^F zrtH{>Gp0-yo?U{|??zQZkNeA5uviwe zvLvvrQt@|p{&L7x;Xu-ky_BH4v&>2GnXS8Bd?HWA-DAk_ti6)3(7xMP$Ti&EL{GVZ zs;S%ND7FzqeaNW^ZYu6h6Rr~CZGBYoktg;eqQ@`phl3!M9x8eQ@~%hir`S)qeJi%O z5z9;xEWt0*9P~^}b#0xmOp>?NKl46j0pS>VR)Ff1Ou4J}k4mIqYoTk&;IX2$`7OyO znpJDkMxc4l1Ec9Gue=xj-wIP zXUL{Bc^(@AgzgRiJ>Wm2-E;kHz*-5;)F3d7$y^7t4TN!ryG2`}?BL%keC}3U1IU;? z_-O$I(KG@bQ8~65Of= z5Hbbp70kJ#2SAyR8*Kn&5Q$0V+^fHDHUPy%f&DWUL%B*d z;I>=hvT78`f}AmcZXE$-n+XIUB;wzU0L^{mQHbaT+$MdUI(6=Ux%qM+BCSB9SRR1l z7r$9*A%NhRmgFga1v&CPcKl_lykuJ!mgaZz<+{m$)J1CJ30RU9YyBX$*`gA`cssR<25I@Xr3Swxu|YH5@L~ zsp4>L(5kRhgQ%{-icx)?2n(Ab@VraE&!=!<&#A6>s|al+{U*nDy>R}cYM?T-&<1wDs)9g{gP#%5>a-EA7k}kJsqI2B3{GzHqeYh${{rkJvH!|WO`0Y{b1a2Z55I#Y*1rX7ZBMMVI)H}CD`-z^ZouuAc?Ey!ad7Yx8)7k}FQ&q2^k6inH_qjhU#*-PY87^_A~qn}5{MbV zUPY+OpjG&*%?n#0q7Gz$3?i$hwR`riWq&>e=|LXZ(4q%nQ!ZMF-lSEy$*SM&oZ6vQ z-osF0M%&p{784)mT&i0>k{$`=;pj6tixo0zgFmV7+>cobJ(fDM(ufAX<#cleZ1e%r zi$;vAV~>1d-6NkSGHU2KAnUjMo`6UxuM=80-qfcqcDINHe&qBcY?mo#$0te`TJc>1wGlHKz1Za;Bnc2$FEoKP~|>Wt61<)_Wqq^d~Y zv$Q3loK=LJm-n$K(Q_IwR6pW@q#i3!tz6gu!jDJB?IA+v?`1fo!7dv;8zSfPqoo{3 z;1E@y%AZ8I2Dsnnm+A)y>3tC&+iAVY`QwfzG!7Af)FR7{1JXlKt=129UzdXc%RIdnE-IN7%w-Z)QifiVnwhJ`$hCy9ibq42a3 zVG(5>i|$wzFTTyWt`0bvf*0&zPcRG}F@H6{&H`k(34=lAD;AD?b1krv;^_@-Rc?ar zfei)!7f9==Wug_c0$yA1?+?6}w&Yf3eD>>vk_lIbGF-aZ^RE%l5&7XACejyPnIpPF zbmbN@7FyRU)QCv8Lft*(-)bjztf3<`@d{Kcr0Vg7L_5y%@m%z{YF;n5StCoWvcO1} zFAjI{-ZIvj5J2CKN4tZ^p{b!O+`g%_R-DLMP|gZiQMri*AU!MuQRv9t>imKg7e3`I zc2?tK8E1Hd8s3{XmaQ&wd5v~qhM{*oA}bbI<{ z)B200sok~cXPLWx2N7Vnn{RV`66d&<*P?Foj^r%xDG{o#-@}5=++Fp~+~~eUD(M$sabP(Oj;RD@Uk;ofUQ?r{4X;JNxpW2v2;`7h3htM_phSE7|gNKi0Bpgzp!Ks-}bw$ z8KnL^x`n4eSrDXw6Ys2un~Bl|LF{0Um~TyA7zMZM;Z2YlGS*`E#m z*da}5^Rk|v-sIGKHG+RtvKDU_Vwg3wa}7{DOT|7(zJ5_lvbRXOcm8m;x%rczl{|S| zqJZ9?-k*;4zJ55$pqNo8e+g+K?w1Cwq~F4Bp)!{_2Hb}t58{$BmJ zYDza3O&|ftL{MG+|DxXhW)A^ln7!Vb2R1ooLV8tlkM9FnGJ~tb1>tEer2uU`@;yJD zV%M*)CTIN(gwD-@V;dkq3!oMSyK^CvG#G%ZY4<~@j()#NAoTX~yc|)(ivnBFg5@F@+aW3J01%vg>hgx- z?iQdxJg0UdDUz%#Ax_MoT(S^94L~z1fZzGLqj(Oe;g~iSR8Bqf31lGM>8pJOe&)+r zDW?B8%!4cG=ojExtXErkm9FijaexhZV{d=TROI{@(%jo>qWr-{(~Lz<3%VKFVNwoW z*rm%wdqYCi({~S+qm1@?9@wnc0bR-<M{#ZBcIK@0|lo1rO0voX|b5G@}d4{1&a6^q;Z{kmxGf{tgZ*{5D=^= z()uZt=Q4<1l8zMEYO;(W=^W8=;Y_PyZ}-5?h(&ev=oU9!Q1xmmNHB!&I|Q!xL*w7Q zp#%Y?PH5eqj*s-!mUS0!1Y9Ql#velg1rq>ftU0WG2P@ontV7Ql9&aJkt%1b3K^^bb zRHNtm`}+|Gv0@;**o|*=i82k5{~gnBu1m0YfSXaxF(0)9sac{PkyKCr{5ygZoj7kC z^EP;ir{6O4uh2PY_7p5>Uq`b54G6{Wg3e!&6dj!2p~T+<_uHkV)!IvlXZ13& zj|5$o?xp=iiEI%l(xkRo(eI7Vu+?^XB9GkPIB!3tUC;oV>_txJ2*C8-0GjU~SNWb) zQLB%8ytw)}F@@jxtHH(nvo=PmLD%H5a`Erg{fq-dO;xXy@<9eeWH>_g0CyDb^eu3W zyy)F=KoIs6{Xh>GT|XB$qC+QI*2StAtqf!xiNA9mIA$LC7JOsyaFY>)f&djMoE97L z^Es^pEi#rxk{|m8hJXf(y*1Ba+dzw#Jkq^|wT&o*yFnMx3BK#;UxSh4z3e%?M+F8s zXjz(+>9bxxTt}Xz^ZPWV&PR^ZO`hD@_D~RJJts8Wg!CbnYKa?@4*BkvCuS>HW?He% zlLK=(q@#Py8Eove%pLg$;B1d5;B!QCYPKyT>C_`i_~FaNr-n#2svdEg_$CL6Al5aq z1-F=?a}eE_+?F%VwGJd>VE5__=s#3;L6_$@|0!)-JA-u3IcXRLUH2QVLfVk&pRknb zbp_?KhRw;M-HRG)=i}y>f&sI|^ZQ8OEWxU*$v(BgaSh6B{B~fQql|*k zmc%au0PB5l7OrO)Q?~^wOxei#X2o9s_ElDj##xnne@`@=g9}cBiS5qDkHM&O=*UHr z?MV8%>nPUXXLD+n7$zLCEL}Nsnk)#uE8RwvhKtU&-dn>tuSNisU**l*%kMbgf(H74 zs=3piJYP3A@#6q-sT=@5mz)M0IJ1a!F7$S~Y^9s@F0?7DCgoQIqin&}XOF5&lxpJR zTjlgc@EOKb#Bot)nOFUtDVDU_79kq9&{87*EyuX0}>!70U$^ifO3a+ z<9v_eYAB9@Rwv09k72cW8(Jw!>kI9y!oXhVlA-$wB!r5Yt3$ zGSA>kjkPdJq$7MDf%W0GzbN3YMvp#h^W;<${|T(tHE{a`Dm2cb7oA<4bb+vAe!Vr) z$tyFB>#dnQhzTDkk&wxoN#J`3es}g2gV=`m>OvQmv=;{iw$FW?H@bClnnrfQy%t=k z-gQROzB|DUGazgLy5s@SPQ@5xkP;zW?4bGmFWZZKTSq&|%{J)?h_vU%bRN{fZaE`?9_@w_Q}|jQS5J)qc&Mbq;E2zB8~K~7n*NLkarxfkY^VSx0j`! zZnM9ZHPQ0Vg2oF~Kl1kzAH*)qg;=q!F~*1O#dOErwcrcCBM?Z7r1a{u^d}6HirehU zak-w|OrW0@6BRXN1t^YNGVrs2QvSf>@tZmWxYIoM4;;JU$V>r))fZH}&N-zzClm;q z-AG^60e|M?ED2F@O>CdX?_!?;QZ_?U$yezbG7AeMbx2B{t)^NSH=0X;E@COZ07jUe zg_GMxs)k8J0b9{LV2~`7Jc->MAKaALdCaHP0J^L2y!Usn2U24Uf?f^J2E2V^u(1%F zHgPXrj5>wPz>AxJN>*C1*9?gMBG8E*XCGCB5Mk=dOja}^^GU@U;SX?|L}U+Qzq_J# zBz(!2!9f-5R!NKugjvCW1a&Df*-V5xiI~qOJVTOr&|RJQTSq|!GlOQB84ovFqj5W? z-r$m|dnmH=5pBhzFiWA((-q3Fc0YbTR=o3Uo~-H1g1f$&X|*JOv<#06;X}y=FgH2h zot!!6wIx6{SAToE?S`pQ2bnUx?3r%pe)OaLE2i5&3(Wh~IG_$E$S&hlmN8kUeJ6o@ zRA;mQCpr=9EW-0}E*Qw0I}d!pyAZYjWkUa0x6HbnJ_jpLTyL5~`$B-^w)t}!^k|V(VYm&j zwb*VRcQeNc#I9-RoAIlrPZtL4#g4XYD^;P~&A+Nas-3p$2t#EXcC~P~NEMzr0zC|2 z&Kv(Rb+tl_Txrt=%X4NcFkDZJT zJBk1gw?$c4hn&bzY4qWU1`-FF!BYo;O$N$!=3ni?7WGwi+iq5Q3@^BFTF=B``L_tDa$emR(g>r=LA>lu9N)b~aamH+?tu^VZ9A)_)OP_V z+myxfzd^d3#%}=ozwN1J57xQekaf8zd?MigRwVq@5}WU}>BYQfNd}i7&!QTF*Xt7% zD`Bif%PZ$|@bk3zhkVQ%QRmB1pq~J$4SoxdWZu!&ECkuf(!)7k0EHKNa=5==N%r04 zx3$y*+)4k7&_7n71)l;x-ii-^?55Z{#aFD^+g_M+NOipezu1C@r;X;Q#cJ{Y0{K;_ z?$@Xz@rp80LP_R5D0ipHe{>r4)L_u^F}W$Ee|vv-FUuMzGw}otrW_q#Wta;~hG#(g zlVl;WxPqbN7jdb#dq57w+;DNWfo|)Ox-dBHFxcHCzO6I@w#>!HW#MJ_zh4ESs10Z8 z9R#QuH3(GXEZ$J~@x$a=waPN%Mkp5UKQsp_MD&7%H4YYC z5Lm0~`NLJ_tFNlGj6<^PaHDc;}&^O{+`RnO?D`2%?b7uITQt3^aX)r8819q=piP z*kf}93JP^y4^#?N08Qy4_@lS^w~Yvq>E>zRMp1wd_J@nXJ0-`Jf;9<>%R^X9&S5q2 z&uXxBh+-u_g519fm58WqPvX5)&(XT+nC?H}TQ6=FZnH=HZUq96)@={8vHI=9sas^Q z#qb7+G>JNY!dl@#v8nw)`>7fgnI>Y6h~c^M)V?Cx+*uM)wX3GXTgfx>l#6shv{_OE zzbV}Ze1a4}yaz-rBs=PrKi*rGf&kmBBp1ucD4>o2Sd@-}S0j>WSt6XpKLcic&A%_` zj%Il@!!iSQdI#wObCVDUm&j_fW3suPWPF8iVSt0?{a91o3R4ONnlXK`Ec1KM$Uy#( zNU|%Aww^G+CGH~oO4#bkzC`jwPQUdjasO@WwMQFhCw~WlwLDD~TUNeRRY3GYa`tvr zx1?f6*;H$noxApKfFTR2n2lgkN6KJug)`2e(}5dVRo22QkxR1hF)CIyA_K3|9(1TR zvi^@c*TJzYyraQtatSqS2Ky$tGKle@kf@ zr1HiD8UakSfIPB6;4201plp)BW7luy<7K86Knn%b_n+)N`R-FEoW}#}B;=6{OIsWQ z15L^s(8hPV9GO zb^)d(5ihO-4ED^UB2H`-@Tw|stA5|lmI2$O>ryQNXyg`-7x0+q{KA?cNZC@C@u(!B;>T`0hjjM?F#Hcb=i5jwk` zAQt^p18J&Sah&epjdzKZ-5-3`PV!(?0mGW7&+R~XTFy<5>QwT<382w&yEX{q-jf`u zmQ@D{kA@&ms{Bpo=8hn*SUiTO10rl&&g}`WsKQQev(G^mo9>^&e49$O?fQ{tG zQT!4}Hg>Zym3)5t=-O7YqB6ZYxeeTPKyzb?xx-3Ck_wIL@2mqa z8X|<00JaqM_V<|naZDAG2Iz`1Ag`m-X@@x%7K@MEdo!7>dre>l0eXgY62`av0T zke(FCS27$@9Rc>tE-0#HxzN5xPfCx!Kq2J{v;|g!=^^l2h7|hO>zHl7zP|^~9VO0A zNXlQ#gc=7iTsuM)46|W-DFB_;*g(2p{9#St2oYFLHU1U7WJKLf?Y-OYE;54l0JgxE zGQ1SmX?OmR^qwS_i99nzqSJu1WGGPAabOX&UJ>(mP;9Pw&UzklV~GpQse!BHzami) zE1kOfnar1lAoGvoF#UdY8ddIg@xEWmW)>AQRw@vPl88SDei4zuSdW#~+a5~nhyW#{ zb}B#A>{)+$*b2756wATkLe%Ak`Xg9Sd{pd<^1#(%O$2jFNobL-fmm*HZJvrP8p*(C z;=bB0gsn1k0a^%oDO&eG&nx7!Z|#4s06#$B;Q}fBQ$cK;Q$KuUy>Q~y+hYCs$%xzv z8LroSB35xN(U16`r$#_tfKU2I_oug(srW!jqaQ^8LCd^|KOSg{1Cx;3Vvc^y z!c+djfLI0KYC7R%&&Rq#)yX`O15I!xl6^>~;JEZ`B6f1j!Tj9HYAP+OSUJZ^o? zEA-ALF0Gp(nEi(=5WB+4k5A+*ul;v*X2TqqEXe@NI7iSU-JfOM9MEY5)-MAchdc-v zWa-4?5c>cLPk}nYoJNwaL%IiIv=nU87dY)Aa-<*c?7mi)bOI55B}ZT(H(0(LYR17W zs}-nP&NcxWGsh!rnJHeW=N)CTO+chE0E~{?0$RT|&VOw<@WYS3NNs!c0^mT+%)OBj zCGu&>#AB9MO1@Vg5IDO)+`~}3QPPIry~gBZ+_Y8u^4lk`gt?3JnF~Qk19~)453O2? zGNVNaVWL&G-A+M_o9xWR`N0m~Dk(b4*t;;?G?#cU>)eW%Jug0PM5DZj;V&??7~VY7 z^sym&dZo=P?%)o|W-w>%ONoCxPaw3tYX!uPadg&eu`fBjQC~mY%*CxNAf5Rw;hPaO z$IBTa1P8IohMnc<;TFH$_`5qQMDkmvy`eXJtqXoy0_%Sk%xNF=v()?T?2Zvpia}d} z{N>6}$Lk>F=6Jf*wX}jQzLe-FPut}+`y+5YlUk9Zn}-uwsA)jqoT}@e2iYZ7hEx>R zuUCTq7m(gC2W(+$7=ApA$ZP~|Tr=-zNkA@~A1rMZ25b%adBm=v-2@gFV!UOcw1`9? zH_m>Th~@XR{AFJD$2Nb|=2c|1aLgQuKAKa+>+I3mRuKJt&{^J^m+NfkTtk z7fG*Ze+yAQ-}g^dLLt3A0m_hxmk3*}?Q08=AG$JQQ!#6^_cMg*EL^+F9?u*#xeF?Y zH6PjX24_ZxcufM4FCWVcN+bMoj&2(AS@?}W&RW!e9ptJ6s=N=9XAM*VQ(&(_12NVu z_f~^(NfA<+=3a*vuIM zpV;VfKLbjLJvRM%K41^aAkg8BNi&E-wA^afUzPrVQk4#`SRNAsp@p?y{sSHVu*zon!9 zxrdZafIV4rb^P@L3TLpH|E{A^00ew#%LNFrJiwVg(epXN)YR>b=!8Fb09jNd4K@I7 z@DXPR!)2nXu*WCs#%KK;lRO}SG`+Y1xRvE7Ux3fQ_WdhUN`2dv;nvqT2cZpH`gMME zhgywZN4!@QZ5Oc4Fh*F+i^YqvIyTbK(K-!SktShhSJxb-WH$!R&tPHEDdqoa@4cg% zYP9XqfGDCUiVajmRHO@nAiY^os*(WGu~4Kc2uh72HdL?>dPf2YP3b)XpN%dMkd6f+ zAfU961j1W8c+Ti9|cLW*=3cv=A29P`Q@_;d4+tIuP(e%_!_MP z?Sr6D4dNzx2wVv-=)1T;)a2I(PMB-OO(Khe2nErR?;Amywm4k5pKY7SArLuT&$Gs! z<*|^HGS5Fi;+5IF9ogqr_NEQEZ@*L@Up2G3dg2wPKBWyoWEik1uBUJ37&s4n0;u5= zWYH3?Tx1>9wT`no!=7BR=fXz4D9ugdV55G=JAWe{EJbgMkE9Gh2Q|f&4Ud%f9~Ho` z;B#m41YuDt`-Y|>HY5Pql=UiyfDEVgAqkOJvu%^gRHO~DQC7Wzb#aTXV%6V}RZh2K zZ{4*H(I_lc28&4+3Wd#i(&_{%45j1rUeZ!;-{7v1H?V-|Nd>ab!yqSCG;Y)AD!~QK zjR|X$G+o4lB{~}*!;QYUwlvp8IZqdh>*b5_0n>wTnh%phgT{QmK0Z3)i5xA*o{&`` z9K*>sDVX9f;h9&!4h<{e(SpbZ6^{RgnULvI(+Vlv_OFHf+{G;OoO)7^&T?1?nnS0s z^h?Z{Lc@LWU;bi$l-0>geDw0Z%oq%`8~fKwS3ZG#khc1P{Yb_inoXDr5CLws6w8qy z#gxAR1Owh>yDc0{B5xxYdPzsBeHUW%PzN^{#yVc`%D~5sT5QU+2sdXdJ{S=T}O+Z8-{=l?-lkM|0W3b+<$^MF^<)VqWA&UNR=sragHp#pBMqBb-)&f1b%6<~Oqmli+`Gu$f=BYYmr^5o!`yj+ z!k7EDe)Yw`ld7!ER(q>;OiK&gA1JpAZ)ThbgsG?#)S(^Rtct=y7m=J-e^Yu812QOP zTe$n)8+W&)Y|`S%Nkj;tyANJv2zrhwlvyawqQ>O(mmgY?NEu z&lW~r*i-J5a6(UNOtX9c$w%K~Y~0P?sKTsPOrvx8M?uTHZ$rv~(|-G9oIv6MMBJO9 zGdXZVR>)($|HfrH4UBJumqYttL3HTh+u?ifP`J4}U`5-sgj9y;JyNlWPyEoPMOP2C z5QC0;k@|VnbZ2+4Mu%lWj%rBK_SH}Ys_!TiXGW~_FRRaZBOUZKN)5zMh;MQmf&#-6 zQ<~UIyAj0xwLVp~Qw#MbCnSO`GTm3DA>OlItZWe;EQ!45zI-jyQ1-b@Qj|;@fe;jH zbq~yDC4mX~%mzA*ma5T|9@RC;d5+ee?pj@s)+&k%+O~$WX!H^A6zZL@^)8BUm$zUg z4v=UR8DSrNhd+nt<49s(6*T$iMNI+Bk2}C&$a3_EM6igV>)Jbhzq4SeH^_RN4X!qv z!~Vs}dGTr646!WjviPz1iOYAq%Ks_jv3O&J+KNRm#*7WJ`6TTRmL}4BbgP^`re4TY zP>qkv<-f|TOXFr4q-e;VlvuE6idQNaWeG2Sk8F#yR~TT<2CQc8w{th5vS8(|(;J{Y zB)BiwYqGKl9puRbcUZS)`H$@KJE8p3EB;-AWW68ZJlu_}|NRe;6YGEF|1q@vPu>o5 z6saQVthXRG1D~z`5%9vk1N#ek2!Qgo@;?7Jx{E`W@{pq=i(&gEGDw6I1ryCCYdMQz zS94@)co-9hR2f|F@EQ&+i3uQf zEQh*x+BU;)9D!A|)@LHEWoM418A}ZD?Cvv2$oOmY(G1GLfhzCYqi}Pc0-I(mJR@Uy_U|ygYHeen_&j6kN92sO*o%; zC|d6My&!-#t!}A%*u(zRUyJ0(i|TKq<`g`TRN*!LQkFeG-`W0n!i}8tJ@;i(*W+g1 zU3>e^59tg;ILvGVy%FJL=n8}0Q3knHCfJES{-o2#nqa!03{|~N^_;s7`#(C_P5CT< z72y$Wyv?T$4@o;rxt_n9u^5FItB<(COzxBUl9Gs!z7mtVkOUg;X!G{tVU7A38%WHyl zJZiIN5|cbHFj5B)=bX9pctjZx4)r)L!ZQ2#nVU@YBUR=RSbd^NNyaqit1oPy7dKA> zPA2iT81JJ96dPnJ@qOXp+3VM-8#yr`O=@b1{Gt~GmIF!)qf>dLp}V`>PXV>lW}`E* zv+)`zQqv*7ktVnT*>8P>q%pI24dI4u^yRzW>sB&6EP9W|XOw_sWK||2ez5NYmZEQW zbV>dR%uw+gfE0ZlG~4z%?1N62y|LKeKZ+hXOW zoqyz>{Q;+^oHR|CWOX_xui@rW4vWLUCj0WhRJ+ec=l#~T-fNws4NE}ctLpyO{mo@3 zHa8sqbi8btrm;-v<9+z6aeb#&!+}W00Y2LYPb9TPETl}n&jr~v{3)Z z{*ZQV5cF#i@6bHjS+4`^)C#A?JuVi~A##{(Ubksuh$+uHKyegxQlLKv`&NaTt)MDC zD8CBA%Iz>y!+_IU^e5gQ6QVt%9B=@Px~%g1n$UxvY!_;GOy{^Eg;!2u{TEXe_kypu z$P1RRX= zhH(xhP|waAX+o7vLzT~NlzJh`$=wb^(tLFbPaGxVqMF?zxDE9c7)<%L__r~j&rGC= zM!BVJ>??n#@QyBC^3?9J_fPTD#u7JRNy5PF88@b8XV+H_#gb%~lN`Ho;nGfTCO$`B zajpN|Zdx>t?#%g3did5kz~uY*@#8N#+H>3$Nwd{OI&DMUD-dXX%89C1-XE=;P z)B^)!m?{EITrY9Cu?<)@Jy0)Z-*v=)UJkKNxQ({QAmuTrzF((KzxwZ8XfMXCfVH{I{;MZPDFU)ffMPh zrVU7Nm>q~hoRoC*Ofj^6sZ-t@1~@+#eiNlcX8mVpzLtEPpJg~CQJeqgBx-E^^NlJ@ zg4tGML=vtUmjme-hE#Pz7D@b1$u}?AEr4{#5OIeuhlx@< zFa+W#!m1b4-ws}>+aSNZPe^O4R1sp2UfIX*82|JhSH?lZ_-I!4dyFtiN9ug*5bD@5 z1fI>yf?7$LTJp^2QzGIu>2^QzIBrgPpBaRD|DvXw7Q2-a;!m?K`mV(YjeV zjhW-EA=QaeCMSI*jUHw>d3!^XI{PSXZb!*ZPZwDD?yMfd9)JCJ-atkj0dc#e#(H6Z zk4zqZ!_Ck3ILT~7Ga<6`l@w!r#HvnbGUb{MgP#w66}}V?&xk*_V?5=~_J%|xyQ&Ub`wrujULZ14PgpI4+~^Zq5kG`%tFrvx zW$Dh#e*j9@_B{DhJN!xte`J)HU(ki~_<`vG%o}&ZrE?auW%P8l!TK2t_uL>N(N<^v z3eRxKt5OJ@O%pVL%3cirgRhnVVJ`(aeo!$X&M|tl)_dxRdPFAqrZWt^Xme1g7_jO< zL@-k4*iJ6y^{#~c360L zS`Z9f`ixw%5LGCr`8B&9#Ca(yCFvJJzDx80=Ge&j$TCPP(~5~zuNS%H`k%n3)At=f z_LHlP!f9h8NCjE;Rd1PFSjBPg=;%VY^*^@58Io* zX%bxlRJ#lcr#G!}S1b0kdqAv3gu5>QSF08YNtJw?1OroFJ`U?gjyB~twr^wWMBq%M zPZgW?ZGkR^2gK@V84R6|85WT;6RhW zj)}36x6W-@xC=h}79FPv*6D_`)(>&LXU^~wR@ zTq&@~YpWEU>SABGc%kA`V!a&^8HpgVxx;uZUQuYMA<1%@e%sXer;`w*p&bl3a)?lw%Fw>e?moW zw1?crS-)%8ozHq%rX+~#9zYStv(ii7gulE`IVptSy!&I%gTUiX&{HTXpEr4wmcC6Y zRWtg{LOsn_)Pdt{MAH!qXB@U00^6Km&1=?tzw5DpiUOe7)SX*f>9M)|Kn%|oc`x>L zClLp`M{8e+jl1lU@t^+@Fm5a7g1ADg{?nAB@d{6d_-64pZZO**cw^RQ6lcIZXrMN{ z8h|R+m$*7Y^^qBd0WkyIB*572@;SL^H$G1HTq|q4a`7VZkp-eX*sW znZLlIJap9m|1*y7R0RRH5weX9fF)ye?3K(BDZ)}e z5S#vk4+wJQJ}rzhy3(;XE*+ycZ;4ye*W_#G=69)E0vWx-1r<>mQF>CW8g>MhLa z*7CowZCi|ld@x+ft4@`+t*u!2V!(bXXDgQ%?yn;M!>{(oJbb19P&hMLR(i+Qt!Y;= zPqZdiC%0wt;tdBR9`z7&T#tB2Co6cEl<_(!7k8i;c5h=IEvG-(LYlG8rd;xM>{X{M z?9MBoG6Pk(lRVc>D6W3=LL+D#Yerq{kv7p-AeoukRx?J%l8k~ZsLPZ=E7OUA@>DwAEYf7q zf3dS%x{g*|?Bhe~*ZV#VOw5iv!oHsBKn6yp0G=a1CJg=N0)k^g)Q5|b2B@|-KHpL9 z_@H2X7PnUnF|Qs&8jp2Bp4?nTp4>6vh*_l{Aa`TT*|{}lC+45JIjTIpD8&c>5H(}| zseQeA{mZ&Sy;2+@aJTuvX^Y|{D$1@SotSBT3mqIDu1?SFsq(+xUh#9dw0E2FFxzJa zbt-QFqA&isOp)M_dM1@~JMZVMGWqUHwFcy6YRj_elE(5BZH%-$?)bUGR-pRa(&%8L zwdS);V#j_KyZ2jwVIZ0eZZ;Y8DInpOV}TImy$q!|8VorIxuLq)3T2`bDQPcdi8`29 z66c1f_RzPCe)JS&*#r$%2hO=fiZvV@(ml}*nGjO~-h}4T*M-fc8TzfW>C%ChomCJN zvdX5o1Tgn8VE1GVS^`}u&`^mg`J5(6{aMFjB%o7a?EEfFU~b+l$HJt9%`ReT-ecKE zz~?}+Gp8WU|2^||wC(?g>2yUFEO}!cu%E2 z6CeJu)GYEY&N(N)C2N8Qb<(^x4zSKrKhWQCDlaTdUME@h`;F(!N!Q&Kjt<2HCMaG_ z-IFL99b6`a9PY@Vh0RjbJ#|~D3g5GHj9t}d0^KGk#5}C;X#tDCt3U95G3iN`n85L) zQ6{#FwT=_rr$jsUq?&2RamaD`BG9m(uJA_yyNSqSyEQT7QBxqo(H>&X7u$;U;l66|`gK9wHQ+b0&9x!@TE!Jq^h6X&c|=TlsckI$Hur zDGyPvTiBmy@u>5j8IE~g$E&}sOv9f#xlO-jxA_ru^&mn_lG(+J7mv{>+UHxXDsQ}s zjgS9fd0wuoIbI13KwawPrUc9v7}=L5I_$$fo@&VQ$Yp-5Z@K%k=`9a)GGGo@;gNeR zYgCjC9j4T!dfrewo%_~9(Y=pt1X(%+gz zD&%D8iAD@=A5k)jFhaxWY1&#aa^pol0#rSWZ*lyB<<9Q8hUT!jE z>pB8UZOwGa#$hTgIAb9W)U0wKefH74>Kx0G+;U)k$92{|u@l#7*8g^R)%vB1FE9H9 zOE-t0U(0i4piro2=>o0{)lCBM&zsF_k?)|sKf1E+fA&ksoq~d-j>eX(SFc_<16j^s z{rCDe4|imJ`0xS2Z4Fu$mAppui!Qtg#mirBPEavJGIk;gb+-oAhY)8dEk|=k&C9Qi z1~bCs<7P^SBQ0ag?zlic=vzyxRyut6V9%d5e1jm(TYm!<})$Thb)<2q-dGH|Sc6yu2o{3>j|GeYS6r*>i{njc@;(nd5j+u#w@+P^PWp#`={pI1yQ9|!Qntp zDenQ2Tf1EJG&kO@x0uXEmf??Ntg_w=zvF25AbpUmMw-_n+p3ok>Gb8I7qJVUFZC}c zVZF?Y{KrGb#%;~vZ2htvukPnrW{kPGI8uA7LJlELXmfy^cbFXSE%8WN=K#jtR-8w< z{A82t4$l0Ml26}^2qjj&1t{q((#oJ~j2L{ff_atGy4LAn7)U73J6bB9AMUEs`yl>U zHwDr96opGZAChFee_1!1Ze5!b)NtIU;nj#5iqD%M?rb&`G4vHC0z{vwf&SS;0ZNlT zcQC%13(O@(7PR3NqXPO9oTWTtOSkSEhpU}!NxFib8-zQ6{&kgO7IuMx&0;Xee!dwq zT6UOZe-?6zKez-)nJ~3aB*O4mg^Y!XA$cjLZ5ePe48TOhtW-$Xe&*hok3kw*hMgaL z+Jk04>SVAdeJXM>t6GGTWUR}R8_gJ88@FsaK?$;r{7!tb-HMF zF0Xy<$CEpj1dL9*j^r3WsJkH6GT7j(b0qm@fZ5(UoIaszFY{C6Z~9vvEJ}m zl~201Dzm4PBdCa%9$`tO1F7u`{%Vn!f}*FTqCTKFDOpPJ1^P5jJUfW;{HuYc$-HmT z65DH0J(G{`1hH24_T1hb@T0$?q#T}*aof8a^klXTGgSe#{hiJZxG{5v$ynd-GvxfZ z9VR5w=k?^S&Y)a6Ju0drg}mdPJ2A?fUUq_9669q*_eMIPGx0I(dbU)Y-!?RMeJ@WsJ+K0zVRVj zjQvyynY!{A@wEIZ4wI*-LoHFdno+~`1^E1+k=|`KkbNUims)R!b6{wdhb3z;PZ`fC zdR%xr_dnxv%A!|_jME{T%5w&WhVwZuV_bklKrqcd_pxxqQ2Z(wE?ODMJte)R4wH|X zEngjWJhtHQl@oitnqJ?3v@3;tE#juwyLgvG8IcrY?W|Ky$h1jl0b+t+(?1@-`= zGc0MBMVH4euGpT2Z;EYE6zw93!u+J!C~tqk(8PfG2gcB) zo)dEmSDT<7<)`U{cWS-UkDw?@9pJCO{&T1X;yLv4Wk1-yuK&nNeba$n7cQh;HVD76 z?qs7(2%2UjsDp~O#xtRIz>iW~c1P&#+S8%tjUbeoIp4LeP*m8l$>H(0lH0 zgnw!}vc7}5c2aTm|Lm9BABTtAI*(AupFW){f(^I$)z^kW7(xE_?NSjS#&}02Kqz=b zGyq96T4l!}t4y%>87#y*f?|37yvm1{@hz^+Ux7IYSuOWG!}m5V;?KGVey`UuiZ_1R z65$x8XsRUUi3k@nc}K z?P77;<6JWZhTVMiI@R30ExYVz08Jc$0Z{}rQBlI?zUO(;`2&L zs<$uq7Q1=_@Zh*|7 zTx1nY^jU3E{G3SSFgsD9JPyfQgBlb;%xOy+&hHOo%#&xHQt3JCQE8GUiW8wY-mL+U zK**E*6>$SKvfL0!-OWznI76leNhCketiyLZf^ITo7Av*7pc;Jr;a+Un#!OukbcL~) z5GJoO>I`uLab;Ndkk?cAk_E{0k%Q{cGOmOb4|6U|KZCnW;RRhgoO8~NVVv0*ikKax zDULG-@I8AHdQCqjjJ`6dwvnR(#%ob&;$(3yPTuc@w}S&r-|uHzW1v3HT}OMNE&eX? zCQ(4&e&;2naO(=64NWb(BspruZoWnOP13<+I+#0sEKw#5wpMbSaw8BeDtz3EfNJ3P z$d`q*ub+bgZU0qai;AmcU_(pdu+2ZnWeJt=($0jLz@Vf!RA;r&;lW?k7BZT61=~LAnbq z9`7f3A<@UfmK_hzFnMO;r}#`&L!!#{TsqPR$cUo}wYuLi5t>A^FsUV_+z`j~+V!ZD z{YVzLviDYv49Ds^PxU_&fnmI&P;{^obhvAD*%QV$;EnlBw9~kA^!OrPXL7%K$YCOU z#km(CusHxf<#SqUiQF|3$4~`T(*cCapiwUg(uH_#3w*9$)9kF+DcU-JF(K!juEch)p7aqssw z(}z~BK#cdK@Fv}%=M-h(y}Boi_xM#KaL6a?3SMzu?WJ)N(`89Wq9KscJDAJJZ#tEX ztH-;lG+erGV;R{rlQGSlpXkj9&c>V=E<@W_agRR?F~`p8U@<(08}pSWe;oa6GF6ZV z(1)-LA3oXcT(e_z-7ZLnk57F4`ZX$+<}0tD;L~mEj4Hnh-eu07p7|aotyU_3>j8>h z9{7Ixgz?nRGK}_cw;>nN?>3=0pNWfXV3fTbuJQ<)i6c`<%S(RheuL8VxhV0xS$lPc z)OkMCBO=C_5_3}S?4J6nO-R3%TO0G>&JFc4k+P$5uOMnD1!C#~_@HtW5DCPZs|I%&#%&23e0outjx7zdbe)ENVL1weuk{@MAlB3LK4)m1{?G?p$W&NoZ5K$5)-y zttB7@cu+#~wV~Iy_?IKO;Y5vYfi^?LBpyO*T<43eyHF^*AXay~=Cus>mIi4qLBN() zYdrE$fH4X&9)nvk(lEo8x6^T=mXKZkrUt5rJ-zYXNQ*OLnwIokSxOx}4i!b(OOJysI~)sxcVWWHWe; z-3BVl25imOppvE7bZWy=InXp=i#>|!qMjRtv&CvUkf^0`ZV~RkEC%P<=27U8TrD)# zEXc1$9C&R?!wJ(&VM(GXZ!FmV;TxaB)FIuTAxl-F}bv0%Px+(86(cBoe8H}C|Qeb z9sphKqdK%s@kkzkJbfwsfQtRd@MoNFQ$cc@M(;gyhx>y-Av4#|6F)|ig}duyZ8!^0 zQUmX@W83t%)m_2brNB||cY{43b7!hg?ZwT57t;7V&b7b1XyeOJL@V56Wuk$0rZgvz zL7HVU@#$6CWJA8Wi1F#UHa%(b%txCYaD#1y8*Jp1?*PVT17#K9D2ac>E`@Q&Qj{0v zKPjAIRp70&RtjbB+3z0-j(P@WzIG?TiZb^n{!6%Fqlqc1Y$YIB9JQzRSh{T`&o@ntef$RU^ zoeP`-0v3(sH1giQeMkE=y{f#2MiA5@#tb<_OAa7uEJ8x1N&m93^)0f&~t3xbMs5eM_`dI~)j|t*9*e<8a{q=S?63#r9 znM-7-8?xZ&x{fhzuuF5wnXzFZhDZ%M2crrrQv;lx*iuYS4Q6!8nu#;BAxNa#AQOs4 zaU_sYDcewd)_|g_fj)o?ZD7WdxyzZg_#ryLd^>4RB8UW-eCjiJ6kQ<#AuC&mI)Z_P ze!%w~do6Yzqm7^SfFXhMl_AZ7YbKZ3^t~<%BZ@k9j@Jv+0{d5ABB{|{xfVo>=>8p*A#sat}gjtbC;>b9I!F_owB{tZl!#@)Ux&F&iEM5UdF2OE9sUGf@1LKp&`x2V}2h{KvBu)k%oe_`j^#`$PlHxR=lCG zOD`G6oC=_*pzG+Un;%Ws!?iZ5>uMr2kPQYhCfs$ufv3U4joHuT+}=^AWi#N=sxA`Ck^1Nqr+fQ@`d1HxAKF5TwaBuC6& z1Wt;nhQGLMe4Z$S19kG942R%wcr!Oz#$+wtZt_n6;4;s1wvm?K@NC1~gr+FacwvCR z?_}X6BGd{&Z~QVoY>%1+#L$*RHG>xNa7h4Hma+Y6l#J~(zi^U@Ln=4J^3=4FL`z%z zgMdSEhGf5}e}>30P;=4l*e={LzoT66tE?q3Hzv+fi3w|d374C!g1o3Iq#2d=*l8^e z$o{l6&vkraVp2)XwqINtJf^qPJ!VihYZN4t&V~tU*jSfdNx5v$QnZKga?=7gSNqYp zda=)2k;vs$`%)eGdrj#7G~oRkXm3pY_3M{j(_-b$UAwYiZ9`ij9F4=&!mqDn2wYtd z<6zW1SGXxbJ+NFEnr0}HpjZV9Q+IDLG==sV2IaOZl~!ZX`KrMgF%2D>Z|nmk(s|uJ z4|Q{FbhML2JFC)MV^}>j0kGL+1EcC8sSau1KM?QrzXCjs9Cm(zraPk?fzLZ@k4RD- zb2p4HB^uF`>Ra0NVdLAbrarqB_!~KE(2Ccrjk&Ci6^7Yf76NS(MqwpdlgjPU)|6vp zBoY?pPQp7zCHUi^mO47sO!@(&&H-nOb}!)dIKz0+z{RItEqtQ4q^s+*-=$sM2jOx8 zEb|xAFXi_xF{nq?3Zl*kwFA9sY!p03N+*A&>49$~0fIpfygM)m=gK0zS`bFmNI&g5 zK)rsk4?=W3z%$r;wXXS5A+sMKmNTU+!aD7Om@HO=L)H?+ZAUeRpWkIF4~_9>s|ue9 zB2vZT9d7^z&}+pv!cepIY(mD&Pj}HponH5}?I z$YXLOo*F;J9B4gl8=TGSSr{cn*5uOa(J~7=O4jD8%WnjYgRDu7SGpU%f572V_4V#z z-^ z^0zMo4`@@)jk}%v)GPKVg?YLT$Iecc1oAwbua*TAAfWps$*Y-B7CG874l`akZ?^`8 z`l9>0b?6N~`AzSYhb@Z2SE2kiSe}vLrl6~@QM^WBeiA$IS;kev!|QzX6g7aKY$p!$ zDed~@AH2quz9n$t^0mb3e*pRWZGCWh0L%ZZnGVCYmSxPCX6w~5vqzvN_Qt`Nwz z%Qyxz5Dx9;%iOKHqEE+?1{WR=X5Nle-73?ozuK^v1`0BAgJT>b5vpp6cbre!bg;LP zi43M2v}2v7Rj`Aq>Aopnv*;>=SPjR*R{h$OnmE;O>)7F zTTE(ADLF0`ZW&9L&V#OR(p;h8(k^p+;BG9Fg0+!O5?gxe5;x~1UC_^K!6wr)&7UE_ z9g)13^d#<~ZGFF!QW9wQUtZoYN&7aVLx>hjI<}uDByrSM!txYEu)jToN6SnPk9Fz8 z$O*4{eGIVpTw6y86Fq)BPORn>dkC*$L~Al;t8i((OYk#f8olvYn_==(A#d|TffGMp zB!x(ttA9RTTNJ`S+TcxJY*Fa+(Vk(nu-R9dk<`4Yda*8cV&t6v?U++Ho_&=@ zH!HoH1#vrB3*CIQ_kMofZX^1Z^5X+7zw8Ak#-A)X_&?=Q8*PZ7_I6R6DLs*1hOH4_ zE8v#4KW})KVvlv{%FW3@cqo$|Q%9-7z27tXsIL1d&;2)5QQ~C|H5~Vq;(dnu`kv{j zR_{BasEZBL$gj$NJ|4|T1Ofw+aM=)OLn$c$&-ZQSbdi40gGlLKm_1IN2^`&Xmm$~H zLsdyyzZ%`uS?vH~=sh%-gYZy2OEE@Tqa}@MWW+U^kifADk>F?4KHyb8peo9HGw;rIM1Qz^Ena9vxMfPMttI?bX%@vl?WyMlXcQ#BD)`{oqYnUt4we0JCf2Ec;>_vU% z4-Mm*=VFYORs4A-De{(FRryOlB;G|_JnfC1kNYY`QMRTHZRs{7rt)fTGgIy=wyMAg z3P(49C{xH1t@!yk77SK8Ch`a02h>&t2=O)nwEM}O4hd2mI{>6=>Qy&7>WGUmN}eNp}Q z$g?B|z`r;1*Ht1Q3KyPtZ&8+ov0B6#p+K$;#vh*bn``UOuwTj_jJIS@o)1d1O3Ox1YUR4rW=bS@ zU9ucCvNM^-xm_wfU;LDT4oi!_cy>O9>gndy%*9_|fD*kcCCYbq=Cs`_ASt9Qwy7ae zREVmA6|$0%4-`(`Ip29Bj z3t7=qOVIeu8Y@Ag!X3|jD`r$C8sj3_5&D?>p7PZ%$5$!{1b{qbRoYsJ7r_!-Q+7qd z6MA_%k^w(wl_<%UaIp_-&^{Qk<`j~A z|CPq@AMh7aP5gm|{r)m|a{+XMV_r!NE0{H)Zn|^9Vf4WQfH^4oQ0r^{eeDDT0rJN* z9T?j8fwY{AG;M|3BLR7jKh|u25P+$YA(F&KB+2u4GSrd=Ko~j(HV6P?Aq;2-cNu*{ zL({+UG@#jgt@Iy<6{5D&Nh>^ozg?#bQ05{FxY`Q0BWwbP$uZAF#{7hfg-M`5PfaJXR^aQj zSJP-;+QIcA)b%S=SO)7++wXD{b{0AoSs66jx3-9c`}qP_>#pZHv&p8HJYVA;!zPCC zS()mX`XWE*hIA%h!4!p02qJy%^2bI+%J_y%-jk|8MVl*xAVixmLd&oBc*s}BgS6L3MyaAOiUeA`1<zSY3A+wGV_=I^o>H+aZl;mBpK*w+GaSJ9;12~dNpe$T{>0Zq49PC( zK;OLC65NswX}n{~$}`(sf%24E@Hju=N5O6#rKDLLbKvXy;{1t99+X$Q>Xb+l_O6zC z9q<|4l3PS#QK6{kglCZ#I_a|&nY3=_k->qW3AyK5;;8)_xd<*$18pIt7V4)>@H(dXK^ z2Mn;3FD)!mG;EiB4rrZZj|e%)CB-W$jPV7{<+yH^Bm*djgv1t4mFtt0*%lc|U^&y- zK#}h?Ae(mfzq+VX)xxo#pI8jQr{D5ugF^e@05*~on;kVVQe-P{6dZvu8m?Nr{p{-p z+KWI|OI!);c@_*Bh!>Z5hoeCrqD1?J?`JgTb6wv97>OUvMro=tD)8U|XU-E!Q?43a zq7=(8HL}fUOT-*+`CR){>|36opSS&XmEo1}eJzsDlf@b$UW@TY+wU4y^i&tVBL42_ z*G8I-O+k~ZN|$8U8kB?ha`h!XeWvxESr|gm5OidfK)^TkZw)>~2LN}P?VkXskoav8 zsF00CR`>cS0bNd<=-WXgie4@{h|7xE%Qwub+AmCpAJe`$tx?l0u60)X$>J*O;E0e7 zT%xkLAt5+@xY=Rs>+}nv#JkOUXgZ3#Jxeu$i&|A+bTPazJ& z&Er5Fb-Ld!;x=IubfTw~CaeHuN)}=}#ZEL%i15KrOAFN>6(zWIE46kj_AvWIL& zZ;Am+sWL|6b@&Tc8!BnJ=eq!xQGQd%e*0ymV$8QfYLXa(w8uWqKGO>SV zw=KgjvEz%a;^0(Li;^mn7_2E`Ls6XqD*D0ni5usFgU>4_O%JQ&)osUo3(!3!&kZF+ zO5fC@W?c&w@c zPYN-oYug%%W54NZeL7tLa%GJLfdvx?R`on$dV_t1*(xxAA^I%;^ zsBEJYn(ITo(t*E1L-b;JM}}|sNtD`v+|5r#shUH+YAQ-7@PsyAb(zRu4y=WAyDwT-03Z01Eo3 zJ2#?Pzihy0vz+w3!FDmr@=sy|pRXXnkDexpc?K)|~XG-aKg}7u7<3~TW zsON0h{?B&ogb<4fua+~FjUrE+sU_j0!63gp^ytF@X10I2mvmrNS5{R^W!F>?+iP+o z{(^AbOIfMo2s$Dm;&1`sBYe=m{Qy`bR`Yu&?l_&BY_Zyf)DF{L5lI;_H$X%aTUH~Z zq-1Wunirl`THSb#{jSfZ%@cM)Q*-Ngwt>NCQKS@lHg{J+w}XZ~ZD209Fn|s-_?#bk zYUz1uxZ$3>H$^=k2YfD;F(}y1pc=s~&u@#W>A8p40Qv_!9 zo1$ZwzI1YySy#Ju#OE_|IhyA8DKpS$nu-|41K{)g{L%{5GpS4=o9eGVOcB*Ek~{3Q z?Z&???~sb?f1dUE{}tQxe?RwsFZX}A+oRD@hpc1g53uI#cFhN_#B!}b#071ZtL58~ z?20@iji2;`GY3#vYOLWhDmv09^P2F4eQuHK$~{$z&$^>;wm!9IYA$Z&6|>}xPyObq zqq>lVe;P-NqQ;M|ymmo}D=hcLk_~Auu|MiEAhlDourd|Mr0_AXhjX{;<9>?i{*3TZ zaTSN9zd;s(2tgo%=2Ov6LVC*j!=HBl2^yo$-KVccR|RsV*So|3Ddq|QztQVn)%z_K zo39*Obla{2MZLmT>VcnWe0UC7SV2bon=!~90L(p1yYLFxz$eE}oLASr8c6!%NsAwn zFdmh^P9nC?nXDxjer2P-CyGJMxL|_*1Vwjg&UV>*&JjEBNCxK;M_7ov0;gA>$3)O7 z9bkr1f2?_VERRXdjdsqH+rnxVZ*IPxZbuN%hlww*VY=rSvg>p%e0d?<+rN0_)a}nc z8tu$!JY1w!1XIq1#e1;eup__VHzy)*z)l>k5)V_8S!E= z1FTpA2j=b^(h6Vc=T+QhUoCKQE>2g|yK3e5tPXxgPr0Z(5l8zj`~AND`dn!}GZWQ5 zfBzx<)kbbrH5j}PIb!9cD52RiWo>Qp6s%!F$U3;o_jI93XzL1cGnyD9jIUehI5I(ZC=#Mr-@ai zpj94?9sT`bs6gCL!qJeG{{G6bxi!Wsjr)j!O30Y}jugzw$+a`ZtRMhWJti10bNOv6 zKjqxMA+hH?d2WGhyJWI*%A?}O$1)|lU!K3c@_9`6HbG(K z6>uZ$TVw=FuLEcE@pTBL1U@Kv3ka=#zu+v7Ryjz<(U5<9U_PI|LRKt0&!VT Y?T4qakNNyw) => ({ +export const saveSettings = (settings: any, needsRestart = true) => ({ type: ReduxActionTypes.SAVE_ADMIN_SETTINGS, - payload: settings, + payload: { + settings, + needsRestart, + }, }); export const retryServerRestart = () => ({ diff --git a/app/client/src/ce/constants/ApiConstants.tsx b/app/client/src/ce/constants/ApiConstants.tsx index b6a266f7ff1d..587759b11a0c 100644 --- a/app/client/src/ce/constants/ApiConstants.tsx +++ b/app/client/src/ce/constants/ApiConstants.tsx @@ -8,6 +8,7 @@ export enum API_STATUS_CODES { RESOURCE_NOT_FOUND = 404, SERVER_ERROR = 502, SERVER_UNAVAILABLE = 503, + REQUEST_FORBIDDEN = 403, } export enum SERVER_ERROR_CODES { @@ -22,6 +23,7 @@ export enum ERROR_CODES { REQUEST_NOT_AUTHORISED = "REQUEST_NOT_AUTHORIZED", REQUEST_TIMEOUT = "REQUEST_TIMEOUT", FAILED_TO_CORRECT_BINDING = "FAILED_TO_CORRECT_BINDING", + REQUEST_FORBIDDEN = "REQUEST_FORBIDDEN", } export const OAuthURL = "/oauth2/authorization"; diff --git a/app/client/src/ce/constants/ReduxActionConstants.tsx b/app/client/src/ce/constants/ReduxActionConstants.tsx index 93d7fe00083c..85a7b15151b8 100644 --- a/app/client/src/ce/constants/ReduxActionConstants.tsx +++ b/app/client/src/ce/constants/ReduxActionConstants.tsx @@ -1,3 +1,5 @@ +import { ERROR_CODES } from "@appsmith/constants/ApiConstants"; +import { Workspace } from "@appsmith/constants/workspaceConstants"; import { ApplicationVersion } from "actions/applicationActions"; import { ApplicationPagePayload, @@ -7,8 +9,6 @@ import { LayoutOnLoadActionErrors, PageAction, } from "constants/AppsmithActionConstants/ActionConstants"; -import { Workspace } from "@appsmith/constants/workspaceConstants"; -import { ERROR_CODES } from "@appsmith/constants/ApiConstants"; import { AppLayoutConfig } from "reducers/entityReducers/pageListReducer"; import { WidgetCardProps, WidgetProps } from "widgets/BaseWidget"; @@ -125,7 +125,6 @@ export const ReduxActionTypes = { "SET_IS_PAGE_LEVEL_WEBSOCKET_CONNECTED", SET_SNIPING_MODE: "SET_SNIPING_MODE", RESET_SNIPING_MODE: "RESET_SNIPING_MODE", - HANDLE_PATH_UPDATED: "HANDLE_PATH_UPDATED", RESET_EDITOR_REQUEST: "RESET_EDITOR_REQUEST", RESET_EDITOR_SUCCESS: "RESET_EDITOR_SUCCESS", INITIALIZE_EDITOR: "INITIALIZE_EDITOR", @@ -162,7 +161,7 @@ export const ReduxActionTypes = { DEBUGGER_LOG: "DEBUGGER_LOG", DEBUGGER_LOG_INIT: "DEBUGGER_LOG_INIT", DEBUGGER_ERROR_ANALYTICS: "DEBUGGER_ERROR_ANALYTICS", - DEBUGGER_ADD_ERROR_LOG: "DEBUGGER_ADD_ERROR_LOG", + DEBUGGER_ADD_ERROR_LOGS: "DEBUGGER_ADD_ERROR_LOGS", DEBUGGER_DELETE_ERROR_LOG: "DEBUGGER_DELETE_ERROR_LOG", DEBUGGER_ADD_ERROR_LOG_INIT: "DEBUGGER_ADD_ERROR_LOG_INIT", DEBUGGER_DELETE_ERROR_LOG_INIT: "DEBUGGER_DELETE_ERROR_LOG_INIT", @@ -219,6 +218,8 @@ export const ReduxActionTypes = { UPDATE_ACTION_SUCCESS: "UPDATE_ACTION_SUCCESS", DELETE_ACTION_INIT: "DELETE_ACTION_INIT", SET_DATASOURCE_EDITOR_MODE: "SET_DATASOURCE_EDITOR_MODE", + SET_DATASOURCE_COLLAPSIBLE_STATE: "SET_DATASOURCE_COLLAPSIBLE_STATE", + SET_ALL_DATASOURCE_COLLAPSIBLE_STATE: "SET_ALL_DATASOURCE_COLLAPSIBLE_STATE", DELETE_ACTION_SUCCESS: "DELETE_ACTION_SUCCESS", SHOW_ACTION_MODAL: "SHOW_ACTION_MODAL", CANCEL_ACTION_MODAL: "CANCEL_ACTION_MODAL", @@ -237,9 +238,13 @@ export const ReduxActionTypes = { ADD_MOCK_DATASOURCES_SUCCESS: "ADD_MOCK_DATASOURCES_SUCCESS", SAVE_DATASOURCE_NAME: "SAVE_DATASOURCE_NAME", SAVE_DATASOURCE_NAME_SUCCESS: "SAVE_DATASOURCE_NAME_SUCCESS", + UPDATE_DATASOURCE_NAME_SUCCESS: "UPDATE_DATASOURCE_NAME_SUCCESS", + UPDATE_DATASOURCE_NAME: "UPDATE_DATASOURCE_NAME", CREATE_DATASOURCE_INIT: "CREATE_DATASOURCE_INIT", CREATE_DATASOURCE_SUCCESS: "CREATE_DATASOURCE_SUCCESS", CREATE_DATASOURCE_FROM_FORM_INIT: "CREATE_DATASOURCE_FROM_FORM_INIT", + CREATE_TEMP_DATASOURCE_FROM_FORM_SUCCESS: + "CREATE_TEMP_DATASOURCE_FROM_FORM_SUCCESS", UPDATE_DATASOURCE_INIT: "UPDATE_DATASOURCE_INIT", UPDATE_DATASOURCE_SUCCESS: "UPDATE_DATASOURCE_SUCCESS", CHANGE_DATASOURCE: "CHANGE_DATASOURCE", @@ -272,6 +277,7 @@ export const ReduxActionTypes = { CREATE_PAGE_SUCCESS: "CREATE_PAGE_SUCCESS", FETCH_PAGE_LIST_INIT: "FETCH_PAGE_LIST_INIT", FETCH_PAGE_LIST_SUCCESS: "FETCH_PAGE_LIST_SUCCESS", + UPDATE_PAGE_LIST: "UPDATE_PAGE_LIST", INITIALIZE_PAGE_VIEWER: "INITIALIZE_PAGE_VIEWER", INITIALIZE_PAGE_VIEWER_SUCCESS: "INITIALIZE_PAGE_VIEWER_SUCCESS", FETCH_APPLICATION_INIT: "FETCH_APPLICATION_INIT", @@ -461,6 +467,7 @@ export const ReduxActionTypes = { SET_APP_MODE: "SET_APP_MODE", TOGGLE_PROPERTY_PANE_WIDGET_NAME_EDIT: "TOGGLE_PROPERTY_PANE_WIDGET_NAME_EDIT", + SET_PROPERTY_PANE_WIDTH: "SET_PROPERTY_PANE_WIDTH", UPDATE_APP_PERSISTENT_STORE: "UPDATE_APP_PERSISTENT_STORE", UPDATE_APP_TRANSIENT_STORE: "UPDATE_APP_TRANSIENT_STORE", UPDATE_APP_STORE_EVALUATED: "UPDATE_APP_STORE_EVALUATED", @@ -483,6 +490,7 @@ export const ReduxActionTypes = { START_EVALUATION: "START_EVALUATION", CURRENT_APPLICATION_NAME_UPDATE: "CURRENT_APPLICATION_NAME_UPDATE", CURRENT_APPLICATION_LAYOUT_UPDATE: "CURRENT_APPLICATION_LAYOUT_UPDATE", + CURRENT_APPLICATION_ICON_UPDATE: "CURRENT_APPLICATION_ICON_UPDATE", FORK_APPLICATION_INIT: "FORK_APPLICATION_INIT", FORK_APPLICATION_SUCCESS: "FORK_APPLICATION_SUCCESS", IMPORT_APPLICATION_INIT: "IMPORT_APPLICATION_INIT", @@ -504,7 +512,6 @@ export const ReduxActionTypes = { "RESET_APPLICATION_WIDGET_STATE_REQUEST", SAAS_GET_OAUTH_ACCESS_TOKEN: "SAAS_GET_OAUTH_ACCESS_TOKEN", GET_OAUTH_ACCESS_TOKEN: "GET_OAUTH_ACCESS_TOKEN", - UPDATE_RECENT_ENTITY: "UPDATE_RECENT_ENTITY", RESTORE_RECENT_ENTITIES_REQUEST: "RESTORE_RECENT_ENTITIES_REQUEST", RESTORE_RECENT_ENTITIES_SUCCESS: "RESTORE_RECENT_ENTITIES_SUCCESS", SET_RECENT_ENTITIES: "SET_RECENT_ENTITIES", @@ -589,6 +596,7 @@ export const ReduxActionTypes = { SET_CANVAS_CARDS_STATE: "SET_CANVAS_TOP_SECTION_VISIBILITY", DELETE_CANVAS_CARDS_STATE: "DELETE_CANVAS_CARDS_STATE", UPDATE_EXPLORER_WIDTH: "UPDATE_EXPLORER_WIDTH", + UPDATE_PROPERTY_PANE_WIDTH: "UPDATE_PROPERTY_PANE_WIDTH", FIRST_TIME_USER_ONBOARDING_INIT: "FIRST_TIME_USER_ONBOARDING_INIT", SET_USER_ROLE_USECASE: "SET_USER_ROLE_USECASE", UPDATE_JS_ACTION_BODY: "UPDATE_JS_ACTION_BODY", @@ -672,11 +680,12 @@ export const ReduxActionTypes = { SHOW_TEMPLATES_MODAL: "SHOW_TEMPLATES_MODAL", GET_TEMPLATE_FILTERS_INIT: "GET_TEMPLATE_FILTERS_INIT", GET_TEMPLATE_FILTERS_SUCCESS: "GET_TEMPLATE_FILTERS_SUCCESS", - UPDATE_CUSTOM_SLUG_INIT: "UPDATE_CUSTOM_SLUG_INIT", - UPDATE_CUSTOM_SLUG_SUCCESS: "UPDATE_CUSTOM_SLUG_SUCCESS", INIT_TRIGGER_VALUES: "INIT_TRIGGER_VALUES", FETCH_TRIGGER_VALUES_INIT: "FETCH_TRIGGER_VALUES_INIT", FETCH_TRIGGER_VALUES_SUCCESS: "FETCH_TRIGGER_VALUES_SUCCESS", + SET_TRIGGER_VALUES_LOADING: "SET_TRIGGER_VALUES_LOADING", + OPEN_APP_SETTINGS_PANE: "OPEN_APP_SETTINGS_PANE", + CLOSE_APP_SETTINGS_PANE: "CLOSE_APP_SETTINGS_PANE", FETCH_CURRENT_TENANT_CONFIG: "FETCH_CURRENT_TENANT_CONFIG", FETCH_CURRENT_TENANT_CONFIG_SUCCESS: "FETCH_CURRENT_TENANT_CONFIG_SUCCESS", SET_FOCUS_HISTORY: "SET_FOCUS_HISTORY", @@ -684,18 +693,16 @@ export const ReduxActionTypes = { GENERATE_KEY_AND_SET_FOCUSABLE_CODE_EDITOR_FIELD: "GENERATE_KEY_AND_SET_FOCUSABLE_CODE_EDITOR_FIELD", SET_FOCUSABLE_PROPERTY_FIELD: "SET_FOCUSABLE_PROPERTY_FIELD", - GENERATE_KEY_AND_SET_FOCUSABLE_PROPERTY_FIELD: - "GENERATE_KEY_AND_SET_FOCUSABLE_PROPERTY_FIELD", ROUTE_CHANGED: "ROUTE_CHANGED", + PAGE_CHANGED: "PAGE_CHANGED", SET_API_PANE_CONFIG_SELECTED_TAB: "SET_API_PANE_CONFIG_SELECTED_TAB", SET_API_PANE_RESPONSE_SELECTED_TAB: "SET_API_PANE_RESPONSE_SELECTED_TAB", SET_API_PANE_RESPONSE_PANE_HEIGHT: "SET_API_PANE_RESPONSE_PANE_HEIGHT", - GENERATE_KEY_AND_SET_CODE_EDITOR_LAST_FOCUS: - "GENERATE_KEY_AND_SET_CODE_EDITOR_LAST_FOCUS", + SET_API_RIGHT_PANE_SELECTED_TAB: "SET_API_RIGHT_PANE_SELECTED_TAB", + SET_EDITOR_FIELD_FOCUS: "SET_EDITOR_FIELD_FOCUS", + SET_CODE_EDITOR_CURSOR: "SET_CODE_EDITOR_CURSOR", SET_CODE_EDITOR_CURSOR_HISTORY: "SET_CODE_EDITOR_CURSOR_HISTORY", SET_EVAL_POPUP_STATE: "SET_EVAL_POPUP_STATE", - GENERATE_KEY_AND_SET_EVAL_POPUP_STATE: - "GENERATE_KEY_AND_SET_EVAL_POPUP_STATE", APPEND_SELECTED_WIDGET_TO_URL: "APPEND_SELECTED_WIDGET_TO_URL", SET_ALL_PROPERTY_SECTION_STATE: "SET_ALL_PROPERTY_SECTION_STATE", SET_PROPERTY_SECTION_STATE: "SET_PROPERTY_SECTION_STATE", @@ -707,6 +714,20 @@ export const ReduxActionTypes = { SET_JS_PANE_CONFIG_SELECTED_TAB: "SET_JS_PANE_CONFIG_SELECTED_TAB", SET_JS_PANE_RESPONSE_SELECTED_TAB: "SET_JS_PANE_RESPONSE_SELECTED_TAB", SET_JS_PANE_RESPONSE_PANE_HEIGHT: "SET_JS_PANE_RESPONSE_PANE_HEIGHT", + SET_SELECTED_PANEL_PROPERTY: "SET_SELECTED_PANEL_PROPERTY", + UNSET_SELECTED_PANEL_PROPERTY: "UNSET_SELECTED_PANEL_PROPERTY", + SET_SELECTED_PANELS: "SET_SELECTED_PANELS", + SET_PANEL_SELECTED_PROPERTY_TAB_INDEX: + "SET_PANEL_SELECTED_PROPERTY_TAB_INDEX", + SET_PANEL_PROPERTY_SECTION_STATE: "SET_PANEL_PROPERTY_SECTION_STATE", + SET_WIDGET_SELECTED_PROPERTY_TAB_INDEX: + "SET_WIDGET_SELECTED_PROPERTY_TAB_INDEX", + SET_WIDGET_PROPERTY_SECTION_STATE: "SET_WIDGET_PROPERTY_SECTION_STATE", + SET_PANEL_PROPERTIES_STATE: "SET_PANEL_PROPERTIES_STATE", + SET_ENTITY_COLLAPSIBLE_STATE: "SET_ENTITY_COLLAPSIBLE_STATE", + SET_ALL_ENTITY_COLLAPSIBLE_STATE: "SET_ALL_ENTITY_COLLAPSIBLE_STATE", + SET_ALL_SUB_ENTITY_COLLAPSIBLE_STATE: "SET_ALL_SUB_ENTITY_COLLAPSIBLE_STATE", + SET_EXPLORER_SWITCH_INDEX: "SET_EXPLORER_SWITCH_INDEX", SET_AUTO_HEIGHT_LAYOUT_TREE: "SET_AUTO_HEIGHT_LAYOUT_TREE", UPDATE_MULTIPLE_WIDGET_PROPERTIES: "UPDATE_MULTIPLE_WIDGET_PROPERTIES", SET_CANVAS_LEVELS_MAP: "SET_CANVAS_LEVELS_MAP", @@ -714,14 +735,18 @@ export const ReduxActionTypes = { CHECK_CONTAINERS_FOR_AUTO_HEIGHT: "CHECK_CONTAINERS_FOR_AUTO_HEIGHT", UPDATE_WIDGET_AUTO_HEIGHT: "UPDATE_WIDGET_AUTO_HEIGHT", SET_LINT_ERRORS: "SET_LINT_ERRORS", + SET_AUTO_HEIGHT_WITH_LIMITS_CHANGING: "SET_AUTO_HEIGHT_WITH_LIMITS_CHANGING", PROCESS_AUTO_HEIGHT_UPDATES: "PROCESS_AUTO_HEIGHT_UPDATES", - AUTOLAYOUT_REORDER_WIDGETS: "AUTOLAYOUT_REORDER_WIDGETS", AUTOLAYOUT_ADD_NEW_WIDGETS: "AUTOLAYOUT_ADD_NEW_WIDGETS", REMOVE_CHILD_WRAPPERS: "REMOVE_CHILD_WRAPPERS", ADD_CHILD_WRAPPERS: "ADD_CHILD_WRAPPERS", UPDATE_FILL_CHILD_LAYER: "UPDATE_FILL_CHILD_LAYER", RECALCULATE_COLUMNS: "RECALCULATE_COLUMNS", + REMOVE_TEMP_DATASOURCE_SUCCESS: "REMOVE_TEMP_DATASOURCE_SUCCESS", + SET_DATASOURCE_SAVE_ACTION_FLAG: "SET_DATASOURCE_SAVE_ACTION_FLAG", + SET_DATASOURCE_SAVE_ACTION_FROM_POPUP_FLAG: + "SET_DATASOURCE_SAVE_ACTION_FROM_POPUP_FLAG", }; export type ReduxActionType = typeof ReduxActionTypes[keyof typeof ReduxActionTypes]; @@ -782,6 +807,7 @@ export const ReduxActionErrorTypes = { SEARCH_APIORPROVIDERS_ERROR: "SEARCH_APIORPROVIDERS_ERROR", UPDATE_DATASOURCE_ERROR: "UPDATE_DATASOURCE_ERROR", SAVE_DATASOURCE_NAME_ERROR: "SAVE_DATASOURCE_NAME_ERROR", + UPDATE_DATASOURCE_NAME_ERROR: "UPDATE_DATASOURCE_NAME_ERROR", CREATE_DATASOURCE_ERROR: "CREATE_DATASOURCE_ERROR", DELETE_DATASOURCE_ERROR: "DELETE_DATASOURCE_ERROR", FETCH_DATASOURCE_STRUCTURE_ERROR: "FETCH_DATASOURCE_STRUCTURE_ERROR", @@ -891,13 +917,11 @@ export const ReduxActionErrorTypes = { GET_DEFAULT_PLUGINS_ERROR: "GET_DEFAULT_PLUGINS_ERROR", GET_TEMPLATE_ERROR: "GET_TEMPLATE_ERROR", GET_TEMPLATE_FILTERS_ERROR: "GET_TEMPLATE_FILTERS_ERROR", - UPDATE_CUSTOM_SLUG_ERROR: "UPDATE_CUSTOM_SLUG_ERROR", FETCH_CURRENT_TENANT_CONFIG_ERROR: "FETCH_CURRENT_TENANT_CONFIG_ERROR", }; export const ReduxFormActionTypes = { VALUE_CHANGE: "@@redux-form/CHANGE", - UPDATE_FIELD_ERROR: "@@redux-form/UPDATE_SYNC_ERRORS", ARRAY_REMOVE: "@@redux-form/ARRAY_REMOVE", ARRAY_PUSH: "@@redux-form/ARRAY_PUSH", }; @@ -991,6 +1015,7 @@ export interface Page { isHidden?: boolean; slug: string; customSlug?: string; + userPermissions?: string[]; } export interface ClonePageSuccessPayload { diff --git a/app/client/src/ce/constants/messages.test.ts b/app/client/src/ce/constants/messages.test.ts index 12f7b54c518b..9338bee83c0c 100644 --- a/app/client/src/ce/constants/messages.test.ts +++ b/app/client/src/ce/constants/messages.test.ts @@ -464,7 +464,7 @@ describe("Audit logs messages", () => { const input = [INTRODUCING, EXCLUSIVE_TO_BUSINESS]; const expected = [ `Introducing XYZ`, - `The XYZ feature is exclusive to workspaces on the Enterprise Plan`, + `The XYZ feature is exclusive to workspaces on the Business Plan`, ]; const actual = input.map((f) => createMessage(f, "XYZ")); expect(actual).toEqual(expected); diff --git a/app/client/src/ce/constants/messages.ts b/app/client/src/ce/constants/messages.ts index cb3a51aa44ce..040431f3b46c 100644 --- a/app/client/src/ce/constants/messages.ts +++ b/app/client/src/ce/constants/messages.ts @@ -28,7 +28,7 @@ export const ERROR_EMPTY_APPLICATION_NAME = () => export const API_PATH_START_WITH_SLASH_ERROR = () => `Path cannot start with /`; export const FIELD_REQUIRED_ERROR = () => `This field is required`; export const INPUT_DEFAULT_TEXT_MAX_CHAR_ERROR = (max: number) => - `Default text length must be less than ${max} characters`; + `Default text length must be less than or equal to ${max} characters`; export const INPUT_TEXT_MAX_CHAR_ERROR = (max: number) => `Input text length must be less than ${max} characters`; export const INPUT_DEFAULT_TEXT_MAX_NUM_ERROR = () => @@ -50,11 +50,9 @@ export const ENTER_AUDIO_URL = () => `Please provide a valid url`; export const FORM_VALIDATION_EMPTY_PASSWORD = () => `Please enter the password`; export const FORM_VALIDATION_PASSWORD_RULE = () => - `Please provide a password between 6 and 42 characters`; + `Please provide a password between 6 and 256 characters`; export const FORM_VALIDATION_INVALID_PASSWORD = FORM_VALIDATION_PASSWORD_RULE; -export const LOGIN_PAGE_SUBTITLE = () => `Use your workspace email`; -export const LOGIN_PAGE_TITLE = () => `Sign in to your account`; export const LOGIN_PAGE_EMAIL_INPUT_LABEL = () => `Email`; export const LOGIN_PAGE_PASSWORD_INPUT_LABEL = () => `Password`; export const LOGIN_PAGE_EMAIL_INPUT_PLACEHOLDER = () => @@ -65,7 +63,9 @@ export const LOGIN_PAGE_INVALID_CREDS_ERROR = () => `It looks like you may have entered incorrect/invalid credentials. Please try again or reset password using the button below.`; export const LOGIN_PAGE_INVALID_CREDS_FORGOT_PASSWORD_LINK = () => `Reset Password`; -export const NEW_TO_APPSMITH = () => `New to Appsmith?`; +export const NEW_TO_APPSMITH = () => `Don't have an account?`; +export const LOGIN_PAGE_TITLE = () => `Sign in`; +export const LOGIN_PAGE_SUBTITLE = () => `Sign in to your account`; export const LOGIN_PAGE_LOGIN_BUTTON_TEXT = () => `sign in`; export const LOGIN_PAGE_FORGOT_PASSWORD_TEXT = () => `Forgot Password`; @@ -125,6 +125,8 @@ export const ERROR_0 = () => `We could not connect to our servers. Please check your network connection`; export const ERROR_401 = () => `We are unable to verify your identity. Please login again.`; +export const ERROR_403 = (entity: string, userEmail: string) => + `Sorry, but your account (${userEmail}) does not seem to have the required access to update this ${entity}. Please get in touch with your Appsmith admin to resolve this.`; export const PAGE_NOT_FOUND_ERROR = () => `The page you’re looking for either does not exist, or cannot be found`; export const INVALID_URL_ERROR = () => `Invalid URL`; @@ -335,7 +337,22 @@ export const WIDGET_BIND_HELP = () => export const BACK_TO_HOMEPAGE = () => "Go back to homepage"; +// error pages +export const PAGE_NOT_FOUND_TITLE = () => "404"; export const PAGE_NOT_FOUND = () => "Page not found"; +export const PAGE_SERVER_UNAVAILABLE_ERROR_CODE = () => "503"; +export const PAGE_SERVER_UNAVAILABLE_TITLE = () => + "Appsmith server is unavailable"; +export const PAGE_SERVER_UNAVAILABLE_DESCRIPTION = () => + "Please try again later"; +export const PAGE_SERVER_TIMEOUT_ERROR_CODE = () => "504"; +export const PAGE_SERVER_TIMEOUT_TITLE = () => + "Appsmith server is taking too long to respond"; +export const PAGE_SERVER_TIMEOUT_DESCRIPTION = () => + `Please retry after some time`; +export const PAGE_CLIENT_ERROR_TITLE = () => "Whoops something went wrong!"; +export const PAGE_CLIENT_ERROR_DESCRIPTION = () => + "This is embarrassing, please contact Appsmith support for help"; // comments export const POST = () => "Post"; @@ -1045,10 +1062,31 @@ export const INCIDENT_MANAGEMENT_DETAIL1 = () => "Go back in time from an incident to see who did what, correlate events with breaking changes, and run RCAs to remediate incidents for now and the future."; export const AVAILABLE_ON_BUSINESS = () => "Available on a business plan only"; export const EXCLUSIVE_TO_BUSINESS = (featureName: string) => - `The ${featureName} feature is exclusive to workspaces on the Enterprise Plan`; + `The ${featureName} feature is exclusive to workspaces on the Business Plan`; // Audit logs Upgrade page end // Audit logs end +// Access control upgrade page begin +export const GRANULAR_ACCESS_CONTROL_FOR_TEAMS = () => + "Granular Access Controls for teams"; +export const ACCESS_CONTROL_UPGRADE_PAGE_SUB_HEADING = () => + "Control view, create, edit, delete, share, and export permissions for all resources in your apps in a workspace. Manage permissions by attributes as granularly or broadly as you want. Use permissions and user groups to easily define access levels of new and existing users."; +export const SECURITY_APPS_LEAST_PRIVILEGE = () => + "Secure apps by the least privilege needed"; +export const SECURITY_APPS_LEAST_PRIVILEGE_DETAIL1 = () => + "Create roles by the least privilege needed as defaults, e.g.: View only, assign them to users in groups, e.g.: Marketing, and modify for special access, e.g.: Content creators_Execute queries"; +export const PREVENT_ACCIDENTAL_DAMAGE = () => + "Prevent accidental damage to data"; +export const PREVENT_ACCIDENTAL_DAMAGE_DETAIL1 = () => + `Assign edit and delete permissions to an entire group, then modify granularly so non-native users of your data don’t drop a table or bulk-delete streaming data records before you can say, “Retrieve”.`; +export const RESTRICT_PUBLIC_EXPOSURE = () => + "Restrict public exposure of sensitive data"; +export const RESTRICT_PUBLIC_EXPOSURE_DETAIL1 = () => + "Proactively disallow groups of non-admin or non-super-admin users from publicly sharing your app or exporting app data out of your environment, domain, and security perimeters."; +export const ACCESS_CONTROL_UPGRADE_PAGE_FOOTER = () => + "Unlock granular access controls along with audit logs and SSO for enhanced security and reliability with an upgrade to our Business edition."; +// Access control upgrade page end + // export const WELCOME_FORM_NON_SUPER_USER_ROLE_DROPDOWN = () => "Tell us more about what you do at work?"; @@ -1071,6 +1109,8 @@ export const CLOSE_ENTITY_EXPLORER_MESSAGE = () => `Close sidebar`; export const JS_TOGGLE_DISABLED_MESSAGE = "Clear the field to toggle back"; export const PROPERTY_PANE_EMPTY_SEARCH_RESULT_MESSAGE = "No Properties found based on your search"; +export const PROPERTY_SEARCH_INPUT_PLACEHOLDER = + "Search for controls, labels etc"; // API Pane export const API_PANE_NO_BODY = () => "This request does not have a body"; @@ -1100,6 +1140,8 @@ export const APP_THEME_BETA_CARD_CONTENT = () => export const UPGRADE_TO_EE = (authLabel: string) => `Hello, I would like to upgrade and start using ${authLabel} authentication.`; +export const UPGRADE_TO_EE_FEATURE = (feature: string) => + `Hello, I would like to upgrade and start using the ${feature} feature.`; export const UPGRADE_TO_EE_GENERIC = () => `Hello, I would like to upgrade`; export const ADMIN_AUTH_SETTINGS_TITLE = () => "Select Authentication Method"; export const ADMIN_AUTH_SETTINGS_SUBTITLE = () => @@ -1108,6 +1150,40 @@ export const DANGER_ZONE = () => "Danger Zone"; export const DISCONNECT_AUTH_METHOD = () => "Disconnect"; export const DISCONNECT_CONFIRMATION = () => "Are you sure?"; +// Branding +export const ADMIN_BRANDING_SETTINGS_TITLE = () => + "Custom branding for your workspaces"; +export const ADMIN_BRANDING_SETTINGS_SUBTITLE = () => + "Make your workspaces and apps look more yours in a few clicks as in the example below. Upload your logo and favicon, set your primary color, and preview the new look. To save a look you like, upgrade to our Business plan."; +export const ADMIN_BRANDING_COLOR_TOOLTIP = () => + `When you choose a primary color, we auto-magically fill in the secondary and accent colors. You can change them to get the look you want.`; +export const ADMIN_BRANDING_LOGO_SIZE_ERROR = () => + `Uploaded file must be less than 2MB`; +export const ADMIN_BRANDING_LOGO_DIMENSION_ERROR = () => + `Logo should be atleast 256px in height`; +export const ADMIN_BRANDING_LOGO_FORMAT_ERROR = () => + `Uploaded file must be in .SVG .PNG, and .JPG formats`; +export const ADMIN_BRANDING_LOGO_REQUIREMENT = () => + `.SVG, .PNG, or .JPG only • Max 2MB`; +export const ADMIN_BRANDING_FAVICON_DIMENSION_ERROR = () => + `Uploaded file must have a max size of 32X32 pixels`; +export const ADMIN_BRANDING_FAVICON_SIZE_ERROR = () => + `Uploaded file must be less than 2MB`; +export const ADMIN_BRANDING_FAVICON_FORMAT_ERROR = () => + `Uploaded file must be in .ICO, .PNG, and .JPG formats`; +export const ADMIN_BRANDING_FAVICON_REQUIREMENT = () => + `.ICO, .PNG, or .JPG only • Max 32X32`; +export const ADMIN_BRANDING_COLOR_TOOLTIP_PRIMARY = () => + `Used on buttons, links, and other interactive elements.`; +export const ADMIN_BRANDING_COLOR_TOOLTIP_BACKGROUND = () => + `Used as background color for the auth pages`; +export const ADMIN_BRANDING_COLOR_TOOLTIP_HOVER = () => + `Used as hover color for the button.`; +export const ADMIN_BRANDING_COLOR_TOOLTIP_FONT = () => + `Used as text color for the buttons.`; +export const ADMIN_BRANDING_COLOR_TOOLTIP_DISABLED = () => + `Used as background color for disabled buttons.`; + // Guided tour // -- STEPS --- export const STEP_ONE_TITLE = () => @@ -1185,6 +1261,7 @@ export const CONFIRM_CONTEXT_DELETING = () => "Deleting"; export const CONTEXT_NO_PAGE = () => "No pages"; export const CONTEXT_REFRESH = () => "Refresh"; export const CONTEXT_CLONE = () => "Clone"; +export const CONTEXT_SETTINGS = () => "Settings"; export const CONTEXT_SET_AS_HOME_PAGE = () => "Set as Home Page"; export const PAGE = () => "Page"; export const PAGES = () => "Pages"; @@ -1278,6 +1355,50 @@ export const GENERATE_PAGE_DESCRIPTION = () => export const ADD_PAGE_FROM_TEMPLATE = () => "Add Page From Template"; export const INVALID_URL = () => "Please enter a valid URL, for example, https://example.com"; +export const SAVE_OR_DISCARD_DATASOURCE_WARNING = () => + `Unsaved changes will be lost if you exit this page, save the changes before exiting.`; + +export const APP_SETTINGS_PANE_HEADER = () => "Settings"; +export const APP_SETTINGS_CLOSE_TOOLTIP = () => "Close settings panel"; + +export const GENERAL_SETTINGS_SECTION_HEADER = () => "General"; +export const GENERAL_SETTINGS_SECTION_CONTENT_HEADER = () => "General Settings"; +export const GENERAL_SETTINGS_SECTION_HEADER_DESC = () => + "App name, icon and share"; +export const GENERAL_SETTINGS_APP_NAME_LABEL = () => "App Name"; +export const GENERAL_SETTINGS_NAME_EMPTY_MESSAGE = () => + "App name cannot be empty"; +export const GENERAL_SETTINGS_NAME_SPECIAL_CHARACTER_ERROR = () => + "Only alphanumeric or '-()' are allowed"; +export const GENERAL_SETTINGS_APP_ICON_LABEL = () => "App Icon"; + +export const THEME_SETTINGS_SECTION_HEADER = () => "Theme"; +export const THEME_SETTINGS_SECTION_CONTENT_HEADER = () => "Theme Settings"; +export const THEME_SETTINGS_SECTION_HEADER_DESC = () => + "Set theme, color and font"; + +export const PAGE_SETTINGS_SECTION_HEADER = () => "Page settings"; +export const PAGE_SETTINGS_SECTION_CONTENT_HEADER = () => "Settings"; +export const PAGE_SETTINGS_PAGE_NAME_LABEL = () => "Page Name"; +export const PAGE_SETTINGS_NAME_EMPTY_MESSAGE = () => + "Page name cannot be empty"; +export const PAGE_SETTINGS_NAME_SPECIAL_CHARACTER_ERROR = () => + "Only alphanumeric or '-' are allowed"; +export const PAGE_SETTINGS_PAGE_URL_LABEL = () => "Change Page URL"; +export const PAGE_SETTINGS_PAGE_URL_VERSION_UPDATE_1 = () => "Please"; +export const PAGE_SETTINGS_PAGE_URL_VERSION_UPDATE_2 = () => "update"; +export const PAGE_SETTINGS_PAGE_URL_VERSION_UPDATE_3 = () => + "your app URL to new readable format to change this"; +export const PAGE_SETTINGS_SHOW_PAGE_NAV = () => "Show page navigation"; +export const PAGE_SETTINGS_SHOW_PAGE_NAV_TOOLTIP = () => + "Show or hide the page in the appsmith navbar in view mode"; +export const PAGE_SETTINGS_SET_AS_HOMEPAGE = () => "Set as home page"; +export const PAGE_SETTINGS_SET_AS_HOMEPAGE_TOOLTIP = () => + "This is the current home page, you can change this by setting another page as the home page"; +export const PAGE_SETTINGS_SET_AS_HOMEPAGE_TOOLTIP_NON_HOME_PAGE = () => + "Set this page as your home page. This will override your previously set home page."; +export const PAGE_SETTINGS_ACTION_NAME_CONFLICT_ERROR = (name: string) => + `${name} is already being used.`; // Alert options and labels for showMessage types export const ALERT_STYLE_OPTIONS = [ diff --git a/app/client/src/ce/pages/AdminSettings/LeftPane.tsx b/app/client/src/ce/pages/AdminSettings/LeftPane.tsx index bf87e38ce3f8..90507de367dd 100644 --- a/app/client/src/ce/pages/AdminSettings/LeftPane.tsx +++ b/app/client/src/ce/pages/AdminSettings/LeftPane.tsx @@ -10,22 +10,34 @@ import { createMessage } from "design-system/build/constants/messages"; import { USAGE_AND_BILLING } from "@appsmith/constants/messages"; import { useSelector } from "react-redux"; import { selectFeatureFlags } from "selectors/usersSelectors"; +import AnalyticsUtil from "utils/AnalyticsUtil"; export const Wrapper = styled.div` flex-basis: ${(props) => props.theme.homePage.leftPane.width + props.theme.homePage.leftPane.leftPadding}px; padding: 0 0 0 ${(props) => props.theme.homePage.leftPane.leftPadding}px; + overflow-y: auto; + border-right: 1px solid var(--appsmith-color-black-200); + flex-shrink: 0; + + &::-webkit-scrollbar { + display: none; + } `; -export const HeaderContainer = styled.div``; +export const HeaderContainer = styled.div` + border-top: 1px solid var(--appsmith-color-black-200); + padding: 20px 0; + margin: 0 12px; +`; export const StyledHeader = styled.div` font-size: 16px; height: 20px; line-height: 1.5; letter-spacing: -0.24px; - margin: 40px 16px 8px; + margin: 8px 16px 8px; color: var(--appsmith-color-black-900); font-weight: 500; `; @@ -34,11 +46,10 @@ export const CategoryList = styled.ul` margin: 0; padding: 0 0 0 16px; list-style-type: none; - width: 264px; `; export const CategoryItem = styled.li` - width: 80%; + width: 90%; `; export const StyledLink = styled(Link)<{ $active: boolean }>` @@ -128,25 +139,45 @@ export default function LeftPane() { const features = useSelector(selectFeatureFlags); const categories = getSettingsCategory(); const { category, selected: subCategory } = useParams() as any; + + function triggerAnalytics(source: string) { + AnalyticsUtil.logEvent("ADMIN_SETTINGS_CLICK", { + source, + }); + } + return ( Admin Settings + - - <> - - Enterprise - + + Business + {features.RBAC && ( + + +

+ +
+
Access Control
+ + + )} triggerAnalytics("AuditLogs")} to="/settings/audit-logs" >
- - - - - \ No newline at end of file + + + + Reset password for your Appsmith account + + + + + + + + + + + + + + + + + + + + +
+
+
+
+

+ +
+
+
+
+
+
+
+
+
+
+
+

Password reset link for Appsmith apps

+

Forgotten your password? No worries, we’ve got you covered. This link will expire in 48 hours.

+
+
+
Reset Password +
+
+
+ +
+
+
+
+

+ +
+
+
+
+
+
+
+
+
+
+

Appsmith is an open source framework that helps developer and companies to build powerful internal tools.

+
+
+ +
+
+ + diff --git a/app/server/appsmith-server/src/main/resources/email/inviteExistingUserToWorkspaceTemplate.html b/app/server/appsmith-server/src/main/resources/email/inviteExistingUserToWorkspaceTemplate.html deleted file mode 100644 index acde9c0443fa..000000000000 --- a/app/server/appsmith-server/src/main/resources/email/inviteExistingUserToWorkspaceTemplate.html +++ /dev/null @@ -1,301 +0,0 @@ - - - - - - - - - - - - - - -
-
- - - - - - -
- - - - - - -
- - - - - - -
- - - - - - - -
- - - - - - - - - - - - - -
- -
- - - - - - -
-
-
- You've been invited to collaborate. -
-
-
-
- {{Inviter_First_Name}} has invited you to collaborate on the workspace "{{inviter_org_name}}" in Appsmith. -
-
-
-
- - - - - - -
- - - - - - -
- Go To Appsmith -
-
- - - - - - -
-
-
- Cheers -
-
- Devs at Appsmith -
-
-
-
-
- -
-
-
-
-
- - - - \ No newline at end of file diff --git a/app/server/appsmith-server/src/main/resources/email/inviteUserCreatorTemplate.html b/app/server/appsmith-server/src/main/resources/email/inviteUserCreatorTemplate.html deleted file mode 100644 index a82be6dd72a0..000000000000 --- a/app/server/appsmith-server/src/main/resources/email/inviteUserCreatorTemplate.html +++ /dev/null @@ -1,301 +0,0 @@ - - - - - - - - - - - - - - -
-
- - - - - - -
- - - - - - -
- - - - - - -
- - - - - - - -
- - - - - - - - - - - - - -
- -
- - - - - - -
-
-
- You've been invited to collaborate. -
-
-
-
- {{Inviter_First_Name}} has invited you to collaborate on the workspace "{{inviter_org_name}}" in Appsmith. -
-
-
-
- - - - - - -
- - - - - - -
- Sign Up Now -
-
- - - - - - -
-
-
- Cheers -
-
- Devs at Appsmith -
-
-
-
-
- -
-
-
-
-
- - - - \ No newline at end of file diff --git a/app/server/appsmith-server/src/main/resources/email/inviteUserTemplate.html b/app/server/appsmith-server/src/main/resources/email/inviteUserTemplate.html new file mode 100644 index 000000000000..d4fd133ec4f0 --- /dev/null +++ b/app/server/appsmith-server/src/main/resources/email/inviteUserTemplate.html @@ -0,0 +1,123 @@ + + + + {{inviterFirstName}} has invited you to collaborate + + + + + + + + + + + + + + + + + + + + +
+
+
+
+

+ +
+
+
+
+
+
+
+
+
+
+
+

{{inviterFirstName}} has invited you to collaborate on the workspace "{{inviterWorkspaceName}}"

+

You have been invited to join the workspace "{{inviterWorkspaceName}}" in Appsmith. Accept the invite to create your account and get started.

+
+
+
{{primaryLinkText}} +
+
+
+ +
+
+
+
+

+ +
+
+
+
+
+
+
+
+
+
+

Appsmith is an open source framework that helps developer and companies to build powerful internal tools.

+
+
+ +
+
+ + diff --git a/app/server/appsmith-server/src/main/resources/email/updateRoleExistingUserTemplate.html b/app/server/appsmith-server/src/main/resources/email/updateRoleExistingUserTemplate.html deleted file mode 100644 index cd3974faaa94..000000000000 --- a/app/server/appsmith-server/src/main/resources/email/updateRoleExistingUserTemplate.html +++ /dev/null @@ -1,301 +0,0 @@ - - - - - - - - - - - - - - -
-
- - - - - - -
- - - - - - -
- - - - - - -
- - - - - - - -
- - - - - - - - - - - - - -
- -
- - - - - - -
-
-
- Your role has been updated. -
-
-
-
- {{Inviter_First_Name}} has updated your role in the workspace "{{inviter_org_name}}" to {{user_role_name}} in Appsmith. -
-
-
-
- - - - - - -
- - - - - - -
- Go To Appsmith -
-
- - - - - - -
-
-
- Cheers -
-
- Devs at Appsmith -
-
-
-
-
- -
-
-
-
-
- - - - \ No newline at end of file diff --git a/app/server/appsmith-server/src/main/resources/email/welcomeUserTemplate.html b/app/server/appsmith-server/src/main/resources/email/welcomeUserTemplate.html index fc932d5ed833..ff6e06779f7d 100644 --- a/app/server/appsmith-server/src/main/resources/email/welcomeUserTemplate.html +++ b/app/server/appsmith-server/src/main/resources/email/welcomeUserTemplate.html @@ -1,482 +1,302 @@ - - - - - - - - - - - - - - -

 

-

 

- - - - + + + + + - - - - - - - + + +
+
+ + + + +
+ + + + +
+ + +
+ + + + - - -
+ + + + + + - - -
+ + + Welcome to Appsmith! + + + + + + + + + + + - - - - - - - - - - - - -

 

- - - -
-
- - - - - - -
- - - - -
- - - - + +
- - - - - + +
- - - - - - - - - - - - - -
- - - -
- - - - - - -
-
-
- 👋 Hi -
-
-   -
-
-
-
-
- Welcome to appsmith! You're now part of a developer community using appsmith to save - - - hundreds of hours - building internal tools!  - - - 🎉 -
-
-
-
-
-
-
-   -
-
- If you'd like some help getting started, I'd be happy to get on a call & walk you through building your first app! - - Schedule a Call - -   - - 📅  -
-
-
-
-
-
-
-
-

 

-
-
-
-
-
-

- 🤪 - We're a fun, active group of developers and we'd love for you to join us on - - Discord - -

-
-
-
- - - - - - -
- - - - - - -
- Go to Appsmith -
-
- - - - - - -
-
-
- Cheers -
-
- Arpit Mohan -
-
-   -
- -
 
-
-
- + + + + + + + + + + +
+
+
+
+

+ +
+
+
+
+
+
+
+ + + +
+
+
+
+

Welcome to Appsmith

+

You're now part of a developer community using appsmith to save hundreds of hours building internal tools! 🎉 +

If you'd like some help getting started, I'd be happy to get on a call & walk you through building your first app! Schedule a Call 📅

+

We're a fun, active group of developers, and we'd love for you to join us on Discord

+
+
+
Go To Dashboard +
+
+
+ +
+
+
+
+

+ +
+
+
+
+
+
+
+
+
+
+

Appsmith is an open source framework that helps developer and companies to build powerful internal tools.

+
+
+ +
+
+ +
+ +
- - -
-
-
- - - +
+
+
+
+
+ diff --git a/app/server/appsmith-server/src/main/resources/features/init-flags.yml b/app/server/appsmith-server/src/main/resources/features/init-flags.yml index 3b92e881be86..9c01bc4d1466 100644 --- a/app/server/appsmith-server/src/main/resources/features/init-flags.yml +++ b/app/server/appsmith-server/src/main/resources/features/init-flags.yml @@ -70,3 +70,13 @@ ff4j: param: - name: emailDomains value: appsmith.com,moolya.com + + # Put EE flags below this line, to avoid conflicts. + - uid: RBAC + enable: true + description: Enable access control settings + flipstrategy: + class: com.appsmith.server.featureflags.strategies.EmailBasedRolloutStrategy + param: + - name: emailDomains + value: appsmith.com diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/helpers/DslUtilsTest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/helpers/DslUtilsTest.java new file mode 100644 index 000000000000..09969077d89c --- /dev/null +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/helpers/DslUtilsTest.java @@ -0,0 +1,94 @@ +package com.appsmith.server.helpers; + +import com.appsmith.external.models.MustacheBindingToken; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.node.TextNode; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.Set; + +class DslUtilsTest { + + @Test + void getMustacheValueSetFromSpecificDynamicBindingPath_withNullOrEmptyDsl_returnsEmptySet() { + Set tokensInNullDsl = DslUtils.getMustacheValueSetFromSpecificDynamicBindingPath(null, "irrelevantPath"); + Set tokensInEmptyDsl = DslUtils.getMustacheValueSetFromSpecificDynamicBindingPath(new TextNode(""), "irrelevantPath"); + + Assertions.assertThat(tokensInNullDsl).isEmpty(); + Assertions.assertThat(tokensInEmptyDsl).isEmpty(); + } + + @Test + void getMustacheValueSetFromSpecificDynamicBindingPath_withComplicatedPathAndMultipleBindings_parsesDslCorrectly() throws JsonProcessingException { + String fieldPath = "root.field.list[0].childField.anotherList.0.multidimensionalList[0][0]"; + String jsonString = "{ " + + "\"root\": { " + + " \"field\": { " + + " \"list\": [ " + + " { " + + " \"childField\": { " + + " \"anotherList\": [ " + + " { " + + " \"multidimensionalList\" : [ " + + " [\"{{ retrievedBinding1.text }} {{ retrievedBinding2.text }}\"]" + + " ] " + + " } " + + " ] " + + " } " + + " } " + + " ] " + + " } " + + " } " + + "}"; + + ObjectMapper mapper = new ObjectMapper(); + JsonNode dsl = mapper.readTree(jsonString); + + Set tokens = DslUtils.getMustacheValueSetFromSpecificDynamicBindingPath(dsl, fieldPath); + + Assertions.assertThat(tokens).containsExactlyInAnyOrder( + new MustacheBindingToken(" retrievedBinding1.text ", 2, false), + new MustacheBindingToken(" retrievedBinding2.text ", 31, false)); + } + + @Test + void replaceValuesInSpecificDynamicBindingPath_whenFieldPathNotFound() { + ObjectMapper mapper = new ObjectMapper(); + ObjectNode dsl = mapper.createObjectNode(); + dsl.put("fieldKey", "fieldValue"); + JsonNode replacedDsl = DslUtils.replaceValuesInSpecificDynamicBindingPath(dsl, "nonExistentPath", new HashMap<>()); + Assertions.assertThat(replacedDsl).isEqualTo(dsl); + } + + @Test + void replaceValuesInSpecificDynamicBindingPath_whenReplacementKeyNotFound() { + ObjectMapper mapper = new ObjectMapper(); + ObjectNode dsl = mapper.createObjectNode(); + dsl.put("existingPath", "fieldValue"); + HashMap replacementMap = new HashMap<>(); + replacementMap.put(new MustacheBindingToken("nonExistentBinding", 0, false), "newNonExistentBinding"); + JsonNode replacedDsl = DslUtils.replaceValuesInSpecificDynamicBindingPath(dsl, "existingPath", replacementMap); + ObjectNode newDsl = mapper.createObjectNode(); + newDsl.put("existingPath", "fieldValue"); + Assertions.assertThat(replacedDsl).isEqualTo(newDsl); + } + + @Test + void replaceValuesInSpecificDynamicBindingPath_withSuccessfulMultipleReplacements() { + ObjectMapper mapper = new ObjectMapper(); + ObjectNode dsl = mapper.createObjectNode(); + dsl.put("existingPath", "oldFieldValue1 oldFieldValue2"); + HashMap replacementMap = new HashMap<>(); + replacementMap.put(new MustacheBindingToken("oldFieldValue1", 0, false), "newFieldValue1"); + replacementMap.put(new MustacheBindingToken("oldFieldValue2", 15, false), "newFieldValue2"); + JsonNode replacedDsl = DslUtils.replaceValuesInSpecificDynamicBindingPath(dsl, "existingPath", replacementMap); + ObjectNode newDsl = mapper.createObjectNode(); + newDsl.put("existingPath", "newFieldValue1 newFieldValue2"); + Assertions.assertThat(replacedDsl).isEqualTo(dsl); + } +} \ No newline at end of file diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ActionCollectionServiceImplTest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ActionCollectionServiceImplTest.java index 72d58a7569d1..6054ff35823e 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ActionCollectionServiceImplTest.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ActionCollectionServiceImplTest.java @@ -18,6 +18,12 @@ import com.appsmith.server.exceptions.AppsmithException; import com.appsmith.server.helpers.ResponseUtils; import com.appsmith.server.repositories.ActionCollectionRepository; +import com.appsmith.server.solutions.ActionPermission; +import com.appsmith.server.solutions.ActionPermissionImpl; +import com.appsmith.server.solutions.ApplicationPermission; +import com.appsmith.server.solutions.ApplicationPermissionImpl; +import com.appsmith.server.solutions.PagePermission; +import com.appsmith.server.solutions.PagePermissionImpl; import com.appsmith.server.solutions.RefactoringSolution; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -91,11 +97,17 @@ public class ActionCollectionServiceImplTest { ResponseUtils responseUtils; @MockBean RefactoringSolution refactoringSolution; + ApplicationPermission applicationPermission; + PagePermission pagePermission; + ActionPermission actionPermission; private final File mockObjects = new File("src/test/resources/test_assets/ActionCollectionServiceTest/mockObjects.json"); @BeforeEach public void setUp() { + applicationPermission = new ApplicationPermissionImpl(); + pagePermission = new PagePermissionImpl(); + actionPermission = new ActionPermissionImpl(); actionCollectionService = new ActionCollectionServiceImpl( scheduler, validator, @@ -106,7 +118,9 @@ public void setUp() { newActionService, policyGenerator, applicationService, - responseUtils + responseUtils, + applicationPermission, + actionPermission ); layoutCollectionService = new LayoutCollectionServiceImpl( @@ -117,7 +131,9 @@ public void setUp() { newActionService, analyticsService, responseUtils, - actionCollectionRepository + actionCollectionRepository, + pagePermission, + actionPermission ); Mockito @@ -264,7 +280,7 @@ public void testCreateCollection_createActionFailure_returnsWithIncompleteCollec Mockito .when(layoutActionService.updatePageLayoutsByPageId(Mockito.anyString())) .thenAnswer(invocationOnMock -> { - return Mono.just(actionCollectionDTO.getPageId()); + return Mono.just(actionCollectionDTO.getPageId()); }); Mockito @@ -509,7 +525,7 @@ public void testUpdateUnpublishedActionCollection_withModifiedCollection_returns Mockito .when(layoutActionService.updatePageLayoutsByPageId(Mockito.anyString())) .thenAnswer(invocationOnMock -> { - return Mono.just(actionCollection.getUnpublishedCollection().getPageId()); + return Mono.just(actionCollection.getUnpublishedCollection().getPageId()); }); @@ -783,7 +799,7 @@ public void testRefactorCollectionName_withEmptyActions_returnsValidLayout() { jsonObject.put("key", "value"); layout.setDsl(jsonObject); Mockito - .when(refactoringSolution.refactorName(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())) + .when(refactoringSolution.refactorActionCollectionName(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.anyString())) .thenReturn(Mono.just(layout)); Mockito @@ -861,7 +877,7 @@ public void testRefactorCollectionName_withActions_returnsValidLayout() { layout.setActionUpdates(new ArrayList<>()); layout.setLayoutOnLoadActions(new ArrayList<>()); Mockito - .when(refactoringSolution.refactorName(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())) + .when(refactoringSolution.refactorActionCollectionName(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.anyString())) .thenReturn(Mono.just(layout)); Mockito diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ApplicationTemplateServiceTest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ApplicationTemplateServiceTest.java index 3b69f14337df..97d34ffe5224 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ApplicationTemplateServiceTest.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ApplicationTemplateServiceTest.java @@ -5,6 +5,7 @@ import com.appsmith.server.dtos.ApplicationTemplate; import com.appsmith.server.dtos.PageNameIdDTO; import com.appsmith.server.helpers.ResponseUtils; +import com.appsmith.server.solutions.ApplicationPermission; import com.appsmith.server.solutions.ImportExportApplicationService; import com.appsmith.server.solutions.ReleaseNotesService; import com.fasterxml.jackson.core.JsonProcessingException; @@ -58,6 +59,8 @@ public class ApplicationTemplateServiceTest { @MockBean private ResponseUtils responseUtils; + @MockBean + ApplicationPermission applicationPermission; private static MockWebServer mockCloudServices; @@ -80,7 +83,8 @@ public void initialize() { Mockito.when(cloudServicesConfig.getBaseUrl()).thenReturn(baseUrl); applicationTemplateService = new ApplicationTemplateServiceImpl( - cloudServicesConfig, releaseNotesService, importExportApplicationService, analyticsService, userDataService, applicationService, responseUtils + cloudServicesConfig, releaseNotesService, importExportApplicationService, analyticsService, + userDataService, applicationService, responseUtils, applicationPermission ); } diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/DatasourceContextServiceTest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/DatasourceContextServiceTest.java index 1e23162a4410..9d320a030544 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/DatasourceContextServiceTest.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/DatasourceContextServiceTest.java @@ -16,6 +16,7 @@ import com.appsmith.server.repositories.DatasourceRepository; import com.appsmith.server.repositories.NewActionRepository; import com.appsmith.server.repositories.WorkspaceRepository; +import com.appsmith.server.solutions.DatasourcePermission; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -29,8 +30,6 @@ import reactor.core.publisher.Mono; import reactor.test.StepVerifier; -import static com.appsmith.server.acl.AclPermission.EXECUTE_DATASOURCES; -import static com.appsmith.server.acl.AclPermission.MANAGE_DATASOURCES; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; @@ -68,6 +67,9 @@ public class DatasourceContextServiceTest { @Autowired WorkspaceService workspaceService; + @Autowired + DatasourcePermission datasourcePermission; + @MockBean PluginExecutorHelper pluginExecutorHelper; @@ -97,8 +99,8 @@ public void testDatasourceCache_afterDatasourceDeleted_doesNotReturnOldConnectio Mono> dsContextMono1 = datasourceContextService.getCachedDatasourceContextMono(datasource, spyMockPluginExecutor, monitor); - doReturn(Mono.just(datasource)).when(datasourceRepository).findById("id1", MANAGE_DATASOURCES); - doReturn(Mono.just(datasource)).when(datasourceRepository).findById("id1", EXECUTE_DATASOURCES); + doReturn(Mono.just(datasource)).when(datasourceRepository).findById("id1", datasourcePermission.getDeletePermission()); + doReturn(Mono.just(datasource)).when(datasourceRepository).findById("id1", datasourcePermission.getExecutePermission()); doReturn(Mono.just(new Plugin())).when(pluginService).findById("mockPlugin"); doReturn(Mono.just(0L)).when(newActionRepository).countByDatasourceId("id1"); doReturn(Mono.just(datasource)).when(datasourceRepository).archiveById("id1"); diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/DatasourceServiceTest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/DatasourceServiceTest.java index 5d0794acab56..5088621d0dbf 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/DatasourceServiceTest.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/DatasourceServiceTest.java @@ -392,7 +392,13 @@ public void createAndUpdateDatasourceValidDB() { assertThat(createdDatasource.getPluginId()).isEqualTo(datasource.getPluginId()); assertThat(createdDatasource.getName()).isEqualTo(datasource.getName()); assertThat(createdDatasource.getDatasourceConfiguration().getConnection().getSsl().getKeyFile().getName()).isEqualTo("ssl_key_file_id2"); - + assertThat(createdDatasource.getUserPermissions()).isNotEmpty(); + assertThat(createdDatasource.getUserPermissions()).containsAll( + Set.of( + READ_DATASOURCES.getValue(), EXECUTE_DATASOURCES.getValue(), + MANAGE_DATASOURCES.getValue(), DELETE_DATASOURCES.getValue() + ) + ); }) .verifyComplete(); } diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/LayoutServiceTest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/LayoutServiceTest.java index 0962c8f50cd3..33d0872d017a 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/LayoutServiceTest.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/LayoutServiceTest.java @@ -20,9 +20,12 @@ import com.appsmith.server.helpers.MockPluginExecutor; import com.appsmith.server.helpers.PluginExecutorHelper; import com.appsmith.server.repositories.PluginRepository; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import net.minidev.json.JSONArray; import net.minidev.json.JSONObject; +import org.assertj.core.api.Assertions; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -934,7 +937,7 @@ public void getActionsExecuteOnLoadWithAstLogic() { Mockito.when(astService.getPossibleReferencesFromDynamicBinding(List.of("\"anIgnoredAction.data:\" + aGetAction.data"), EVALUATION_VERSION)) .thenReturn(Flux.just(Tuples.of("\"anIgnoredAction.data:\" + aGetAction.data", new HashSet<>(Set.of("aGetAction.data"))))); - String bindingValue = "(function(ignoredAction1){\n" + + String bindingValue = "\n(function(ignoredAction1){\n" + "\tlet a = ignoredAction1.data\n" + "\tlet ignoredAction2 = { data: \"nothing\" }\n" + "\tlet b = ignoredAction2.data\n" + @@ -949,8 +952,8 @@ public void getActionsExecuteOnLoadWithAstLogic() { .thenReturn(Flux.just(Tuples.of("aPostActionWithAutoExec.data", new HashSet<>(Set.of("aPostActionWithAutoExec.data"))))); Mockito.when(astService.getPossibleReferencesFromDynamicBinding(List.of("aDBAction.data[0].irrelevant"), EVALUATION_VERSION)) .thenReturn(Flux.just(Tuples.of("aDBAction.data[0].irrelevant", new HashSet<>(Set.of("aDBAction.data[0].irrelevant"))))); - Mockito.when(astService.getPossibleReferencesFromDynamicBinding(List.of("anotherDBAction.data.optional"), EVALUATION_VERSION)) - .thenReturn(Flux.just(Tuples.of("anotherDBAction.data.optional", new HashSet<>(Set.of("anotherDBAction.data.optional"))))); + Mockito.when(astService.getPossibleReferencesFromDynamicBinding(List.of(" anotherDBAction.data.optional "), EVALUATION_VERSION)) + .thenReturn(Flux.just(Tuples.of(" anotherDBAction.data.optional ", new HashSet<>(Set.of("anotherDBAction.data.optional"))))); Mockito.when(astService.getPossibleReferencesFromDynamicBinding(List.of("aTableAction.data.child"), EVALUATION_VERSION)) .thenReturn(Flux.just(Tuples.of("aTableAction.data.child", new HashSet<>(Set.of("aTableAction.data.child"))))); Mockito.when(astService.getPossibleReferencesFromDynamicBinding(List.of("Collection.anAsyncCollectionActionWithoutCall.data"), EVALUATION_VERSION)) @@ -1202,8 +1205,15 @@ public void testIncorrectDynamicBindingPathInDsl() { .create(testMono) .expectErrorMatches(throwable -> { assertThat(throwable).isInstanceOf(AppsmithException.class); + ObjectMapper objectMapper = new ObjectMapper(); + Object oldParent = null; + try { + oldParent = objectMapper.readTree("{\"widgetName\":\"testWidget\",\"dynamicBindingPathList\":[{\"key\":\"dynamicGet_IncorrectKey\"}],\"widgetId\":\"id\",\"another\":\"Hello people of the {{input1.text}} planet!\",\"dynamicGet\":\"some dynamic {{aGetAction.data}}\",\"type\":\"test_type\",\"key\":\"value-updated\"}"); + } catch (JsonProcessingException e) { + Assertions.fail("Incorrect initialization of expected DSL"); + } assertThat(throwable.getMessage()).isEqualTo( - AppsmithError.INVALID_DYNAMIC_BINDING_REFERENCE.getMessage("test_type", "testWidget", "id", "dynamicGet_IncorrectKey", pageId, layoutId.get(), null) + AppsmithError.INVALID_DYNAMIC_BINDING_REFERENCE.getMessage("test_type", "testWidget", "id", "dynamicGet_IncorrectKey", pageId, layoutId.get(), oldParent, "dynamicGet_IncorrectKey", "New element is null") ); return true; }) diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/NewPageServiceTest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/NewPageServiceTest.java index 41197e0ff600..eb557ddc3c22 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/NewPageServiceTest.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/NewPageServiceTest.java @@ -2,7 +2,6 @@ import com.appsmith.external.models.DefaultResources; import com.appsmith.external.models.Policy; -import com.appsmith.server.acl.AclPermission; import com.appsmith.server.domains.Application; import com.appsmith.server.domains.ApplicationMode; import com.appsmith.server.domains.PermissionGroup; @@ -79,24 +78,63 @@ public void findApplicationPages_WhenApplicationIdPresent_ReturnsPages() { String randomId = UUID.randomUUID().toString(); Workspace workspace = new Workspace(); workspace.setName("org_" + randomId); - Mono applicationPagesDTOMono = workspaceService.create(workspace).flatMap(createdOrg -> { - Application application = new Application(); - application.setName("app_" + randomId); - return applicationPageService.createApplication(application, createdOrg.getId()); - }).flatMap(application -> { - PageDTO pageDTO = new PageDTO(); - pageDTO.setName("page_" + randomId); - pageDTO.setApplicationId(application.getId()); - return applicationPageService.createPage(pageDTO); - }).flatMap(pageDTO -> - newPageService.findApplicationPages(pageDTO.getApplicationId(), null, null, ApplicationMode.EDIT) - ); + Mono applicationPagesDTOMono = workspaceService.create(workspace) + .flatMap(createdOrg -> { + Application application = new Application(); + application.setName("app_" + randomId); + return applicationPageService.createApplication(application, createdOrg.getId()); + }) + .flatMap(application -> { + PageDTO pageDTO = new PageDTO(); + pageDTO.setName("page_" + randomId); + pageDTO.setApplicationId(application.getId()); + return applicationPageService.createPage(pageDTO); + }) + .flatMap(pageDTO -> + newPageService.findApplicationPages(pageDTO.getApplicationId(), null, null, ApplicationMode.EDIT) + ); StepVerifier.create(applicationPagesDTOMono).assertNext(applicationPagesDTO -> { - assertThat(applicationPagesDTO.getApplication()).isNotNull(); - assertThat(applicationPagesDTO.getApplication().getName()).isEqualTo("app_" + randomId); - assertThat(applicationPagesDTO.getPages()).isNotEmpty(); - }).verifyComplete(); + assertThat(applicationPagesDTO.getApplication()).isNotNull(); + assertThat(applicationPagesDTO.getApplication().getViewMode()).isFalse(); + assertThat(applicationPagesDTO.getApplication().getName()).isEqualTo("app_" + randomId); + assertThat(applicationPagesDTO.getPages()).isNotEmpty(); + }) + .verifyComplete(); + } + + @Test + @WithUserDetails("api_user") + public void findApplicationPagesInViewMode_WhenApplicationIdPresent_ReturnsViewMode() { + String randomId = UUID.randomUUID().toString(); + Workspace workspace = new Workspace(); + workspace.setName("org_" + randomId); + Mono applicationPagesDTOMono = workspaceService.create(workspace) + .flatMap(createdOrg -> { + Application application = new Application(); + application.setName("app_" + randomId); + return applicationPageService.createApplication(application, createdOrg.getId()); + }) + .flatMap(application -> { + PageDTO pageDTO = new PageDTO(); + pageDTO.setName("page_" + randomId); + pageDTO.setApplicationId(application.getId()); + Mono pageDTOMono = applicationPageService.createPage(pageDTO).cache(); + return pageDTOMono + .then(applicationPageService.publish(application.getId(), true)) + .then(pageDTOMono); + }) + .flatMap(pageDTO -> + newPageService.findApplicationPages(pageDTO.getApplicationId(), null, null, ApplicationMode.PUBLISHED) + ); + + StepVerifier.create(applicationPagesDTOMono).assertNext(applicationPagesDTO -> { + assertThat(applicationPagesDTO.getApplication()).isNotNull(); + assertThat(applicationPagesDTO.getApplication().getViewMode()).isTrue(); + assertThat(applicationPagesDTO.getApplication().getName()).isEqualTo("app_" + randomId); + assertThat(applicationPagesDTO.getPages()).isNotEmpty(); + }) + .verifyComplete(); } @Test @@ -105,24 +143,28 @@ public void findApplicationPages_WhenPageIdPresent_ReturnsPages() { String randomId = UUID.randomUUID().toString(); Workspace workspace = new Workspace(); workspace.setName("org_" + randomId); - Mono applicationPagesDTOMono = workspaceService.create(workspace).flatMap(createdWorkspace -> { - Application application = new Application(); - application.setName("app_" + randomId); - return applicationPageService.createApplication(application, createdWorkspace.getId()); - }).flatMap(application -> { - PageDTO pageDTO = new PageDTO(); - pageDTO.setName("page_" + randomId); - pageDTO.setApplicationId(application.getId()); - return applicationPageService.createPage(pageDTO); - }).flatMap(pageDTO -> - newPageService.findApplicationPages(null, pageDTO.getId(), null, ApplicationMode.EDIT) - ); + Mono applicationPagesDTOMono = workspaceService.create(workspace) + .flatMap(createdWorkspace -> { + Application application = new Application(); + application.setName("app_" + randomId); + return applicationPageService.createApplication(application, createdWorkspace.getId()); + }) + .flatMap(application -> { + PageDTO pageDTO = new PageDTO(); + pageDTO.setName("page_" + randomId); + pageDTO.setApplicationId(application.getId()); + return applicationPageService.createPage(pageDTO); + }) + .flatMap(pageDTO -> + newPageService.findApplicationPages(null, pageDTO.getId(), null, ApplicationMode.EDIT) + ); StepVerifier.create(applicationPagesDTOMono).assertNext(applicationPagesDTO -> { - assertThat(applicationPagesDTO.getApplication()).isNotNull(); - assertThat(applicationPagesDTO.getApplication().getName()).isEqualTo("app_" + randomId); - assertThat(applicationPagesDTO.getPages()).isNotEmpty(); - }).verifyComplete(); + assertThat(applicationPagesDTO.getApplication()).isNotNull(); + assertThat(applicationPagesDTO.getApplication().getName()).isEqualTo("app_" + randomId); + assertThat(applicationPagesDTO.getPages()).isNotEmpty(); + }) + .verifyComplete(); } } \ No newline at end of file diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/PageServiceTest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/PageServiceTest.java index 4650e748798f..82db939c26e4 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/PageServiceTest.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/PageServiceTest.java @@ -716,14 +716,18 @@ public void clonePage_whenPageCloned_defaultIdsRetained() { Plugin installed_plugin = pluginRepository.findByPackageName("installed-plugin").block(); datasource.setPluginId(installed_plugin.getId()); action.setDatasource(datasource); - action.setExecuteOnLoad(true); assert page != null; Layout layout = page.getLayouts().get(0); - JSONObject dsl = new JSONObject(Map.of("text", "{{ query1.data }}")); + JSONObject dsl = new JSONObject(Map.of("text", "{{ PageAction.data }}")); + dsl.put("widgetId", "firstWidget"); dsl.put("widgetName", "firstWidget"); + JSONArray temp = new JSONArray(); + temp.add(new JSONObject(Map.of("key", "text"))); + dsl.put("dynamicBindingPathList", temp); JSONObject dsl2 = new JSONObject(); + dsl2.put("widgetId", "Table1"); dsl2.put("widgetName", "Table1"); dsl2.put("type", "TABLE_WIDGET"); Map primaryColumns = new HashMap<>(); @@ -733,7 +737,7 @@ public void clonePage_whenPageCloned_defaultIdsRetained() { dsl2.put("primaryColumns", primaryColumns); final ArrayList objects = new ArrayList<>(); JSONArray temp2 = new JSONArray(); - temp2.addAll(List.of(new JSONObject(Map.of("key", "primaryColumns._id")))); + temp2.add(new JSONObject(Map.of("key", "primaryColumns._id"))); dsl2.put("dynamicBindingPathList", temp2); objects.add(dsl2); dsl.put("children", objects); @@ -743,10 +747,10 @@ public void clonePage_whenPageCloned_defaultIdsRetained() { action.setPageId(page.getId()); - final LayoutDTO layoutDTO = layoutActionService.updateLayout(page.getId(), page.getApplicationId(), layout.getId(), layout).block(); - layoutActionService.createSingleAction(action).block(); + final LayoutDTO layoutDTO = layoutActionService.updateLayout(page.getId(), page.getApplicationId(), layout.getId(), layout).block(); + // Save actionCollection ActionCollectionDTO actionCollectionDTO = new ActionCollectionDTO(); actionCollectionDTO.setName("testCollection1"); @@ -765,7 +769,6 @@ public void clonePage_whenPageCloned_defaultIdsRetained() { layoutCollectionService.createCollection(actionCollectionDTO).block(); - final Mono pageMono = applicationPageService.clonePageByDefaultPageIdAndBranch(page.getId(), branchName) .flatMap(pageDTO -> newPageService.findByBranchNameAndDefaultPageId(branchName, pageDTO.getId(), MANAGE_PAGES)) .cache(); @@ -863,8 +866,7 @@ public void clonePage_whenPageCloned_defaultIdsRetained() { assertThat(actionWithoutCollection.getUnpublishedAction().getDefaultResources().getPageId()).isEqualTo(clonedPage.getDefaultResources().getPageId()); // Confirm that executeOnLoad is cloned as well. - // TODO: Fix failing test. - //assertThat(actions.get(0).getUnpublishedAction().getExecuteOnLoad()).isTrue(); + assertThat(actions.stream().filter(clonedAction -> "PageAction".equals(clonedAction.getUnpublishedAction().getName())).findFirst().get().getUnpublishedAction().getExecuteOnLoad()).isTrue(); // Check if collections got copied too List collections = tuple.getT3(); diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/UserServiceTest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/UserServiceTest.java index fe79d234622a..1e894cc3c5a7 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/UserServiceTest.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/UserServiceTest.java @@ -120,9 +120,9 @@ public void checkEmailParamsForExistingUser() { String expectedUrl = inviteUrl + "/applications#" + workspace.getId(); Map params = userService.getEmailParams(workspace, inviter, inviteUrl, false); - assertEquals(expectedUrl, params.get("inviteUrl")); - assertEquals("inviterUserToApplication", params.get("Inviter_First_Name")); - assertEquals("UserServiceTest Update Org", params.get("inviter_org_name")); + assertEquals(expectedUrl, params.get("primaryLinkUrl")); + assertEquals("inviterUserToApplication", params.get("inviterFirstName")); + assertEquals("UserServiceTest Update Org", params.get("inviterWorkspaceName")); } @Test @@ -137,9 +137,9 @@ public void checkEmailParamsForNewUser() { String inviteUrl = "http://localhost:8080"; Map params = userService.getEmailParams(workspace, inviter, inviteUrl, true); - assertEquals(inviteUrl, params.get("inviteUrl")); - assertEquals("inviterUserToApplication", params.get("Inviter_First_Name")); - assertEquals("UserServiceTest Update Org", params.get("inviter_org_name")); + assertEquals(inviteUrl, params.get("primaryLinkUrl")); + assertEquals("inviterUserToApplication", params.get("inviterFirstName")); + assertEquals("UserServiceTest Update Org", params.get("inviterWorkspaceName")); } //Test the update workspace flow. diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ce/ActionServiceCE_Test.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ce/ActionServiceCE_Test.java index 90401df571e8..09b560eabb3f 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ce/ActionServiceCE_Test.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ce/ActionServiceCE_Test.java @@ -839,7 +839,7 @@ public void testActionExecuteErrorResponse() { Mockito.when(pluginExecutor.executeParameterized(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())).thenReturn(Mono.error(pluginException)); Mockito.when(pluginExecutor.datasourceCreate(Mockito.any())).thenReturn(Mono.empty()); - Mono executionResultMono = newActionService.executeAction(executeActionDTO); + Mono executionResultMono = newActionService.executeAction(executeActionDTO, null); StepVerifier.create(executionResultMono) .assertNext(result -> { @@ -889,7 +889,7 @@ public void testActionExecuteNullPaginationParameters() { Mockito.when(pluginExecutor.executeParameterized(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())).thenReturn(Mono.error(pluginException)); Mockito.when(pluginExecutor.datasourceCreate(Mockito.any())).thenReturn(Mono.empty()); - Mono executionResultMono = newActionService.executeAction(executeActionDTO); + Mono executionResultMono = newActionService.executeAction(executeActionDTO, null); StepVerifier.create(executionResultMono) .assertNext(result -> { @@ -933,7 +933,7 @@ public void testActionExecuteSecondaryStaleConnection() { .thenReturn(Mono.error(new StaleConnectionException())).thenReturn(Mono.error(new StaleConnectionException())); Mockito.when(pluginExecutor.datasourceCreate(Mockito.any())).thenReturn(Mono.empty()); - Mono executionResultMono = newActionService.executeAction(executeActionDTO); + Mono executionResultMono = newActionService.executeAction(executeActionDTO, null); StepVerifier.create(executionResultMono) .assertNext(result -> { @@ -977,7 +977,7 @@ public void testActionExecuteTimeout() { .thenAnswer(x -> Mono.delay(Duration.ofMillis(1000)).ofType(ActionExecutionResult.class)); Mockito.when(pluginExecutor.datasourceCreate(Mockito.any())).thenReturn(Mono.empty()); - Mono executionResultMono = newActionService.executeAction(executeActionDTO); + Mono executionResultMono = newActionService.executeAction(executeActionDTO, null); StepVerifier.create(executionResultMono) .assertNext(result -> { @@ -1079,7 +1079,7 @@ public void checkRecoveryFromStaleConnections() { executeActionDTO.setActionId(createdAction.getId()); executeActionDTO.setViewMode(false); - Mono actionExecutionResultMono = newActionService.executeAction(executeActionDTO); + Mono actionExecutionResultMono = newActionService.executeAction(executeActionDTO, null); StepVerifier.create(actionExecutionResultMono) .assertNext(result -> { @@ -1114,7 +1114,7 @@ private Mono executeAction(ExecuteActionDTO executeAction Mockito.when(pluginExecutor.executeParameterized(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())).thenReturn(Mono.just(mockResult)); Mockito.when(pluginExecutor.datasourceCreate(Mockito.any())).thenReturn(Mono.empty()); - Mono actionExecutionResultMono = newActionService.executeAction(executeActionDTO); + Mono actionExecutionResultMono = newActionService.executeAction(executeActionDTO, null); return actionExecutionResultMono; } @@ -1181,7 +1181,7 @@ public void executeActionWithExternalDatasource() { ExecuteActionDTO executeActionDTO = new ExecuteActionDTO(); executeActionDTO.setActionId(savedAction.getId()); executeActionDTO.setViewMode(false); - return newActionService.executeAction(executeActionDTO); + return newActionService.executeAction(executeActionDTO, null); }); diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ce/GitServiceCEImplTest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ce/GitServiceCEImplTest.java index e215d6bd97af..9ae7d83489aa 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ce/GitServiceCEImplTest.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ce/GitServiceCEImplTest.java @@ -17,7 +17,11 @@ import com.appsmith.server.services.SessionUserService; import com.appsmith.server.services.UserDataService; import com.appsmith.server.services.UserService; +import com.appsmith.server.solutions.ActionPermission; +import com.appsmith.server.solutions.ApplicationPermission; +import com.appsmith.server.solutions.DatasourcePermission; import com.appsmith.server.solutions.ImportExportApplicationService; +import com.appsmith.server.solutions.PagePermission; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -74,6 +78,14 @@ public class GitServiceCEImplTest { GitCloudServicesUtils gitCloudServicesUtils; @MockBean GitDeployKeysRepository gitDeployKeysRepository; + @MockBean + DatasourcePermission datasourcePermission; + @MockBean + ApplicationPermission applicationPermission; + @MockBean + PagePermission pagePermission; + @MockBean + ActionPermission actionPermission; @BeforeEach public void setup() { @@ -81,7 +93,8 @@ public void setup() { userService, userDataService, sessionUserService, applicationService, applicationPageService, newPageService, newActionService, actionCollectionService, gitFileUtils, importExportApplicationService, gitExecutor, responseUtils, emailConfig, analyticsService, gitCloudServicesUtils, gitDeployKeysRepository, - datasourceService, pluginService + datasourceService, pluginService, datasourcePermission, applicationPermission, pagePermission, + actionPermission ); } diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ce/NewActionServiceCEImplTest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ce/NewActionServiceCEImplTest.java index 00b19b4c541b..2070dd0d1bd8 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ce/NewActionServiceCEImplTest.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ce/NewActionServiceCEImplTest.java @@ -1,5 +1,6 @@ package com.appsmith.server.services.ce; +import com.appsmith.external.dtos.ExecuteActionDTO; import com.appsmith.external.models.ActionExecutionResult; import com.appsmith.external.models.Datasource; import com.appsmith.server.acl.PolicyGenerator; @@ -25,6 +26,10 @@ import com.appsmith.server.services.PermissionGroupService; import com.appsmith.server.services.PluginService; import com.appsmith.server.services.SessionUserService; +import com.appsmith.server.solutions.ActionPermission; +import com.appsmith.server.solutions.ApplicationPermission; +import com.appsmith.server.solutions.DatasourcePermission; +import com.appsmith.server.solutions.PagePermission; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -120,6 +125,15 @@ public class NewActionServiceCEImplTest { @MockBean NewActionRepository newActionRepository; + @MockBean + DatasourcePermission datasourcePermission; + @MockBean + ApplicationPermission applicationPermission; + @MockBean + PagePermission pagePermission; + @MockBean + ActionPermission actionPermission; + private BodyExtractor.Context context; private Map hints; @@ -145,7 +159,11 @@ public void setup() { authenticationValidator, configService, responseUtils, - permissionGroupService); + permissionGroupService, + datasourcePermission, + applicationPermission, + pagePermission, + actionPermission); } @BeforeEach @@ -181,7 +199,7 @@ public Map hints() { @Test public void testExecuteAction_withoutExecuteActionDTOPart_failsValidation() { - final Mono actionExecutionResultMono = newActionService.executeAction(Flux.empty(), null); + final Mono actionExecutionResultMono = newActionService.executeAction(Flux.empty(), null, null); StepVerifier .create(actionExecutionResultMono) @@ -202,7 +220,7 @@ public void testExecuteAction_withMalformedExecuteActionDTO_failsValidation() { final Flux partsFlux = BodyExtractors.toParts() .extract(mock, this.context); - final Mono actionExecutionResultMono = newActionService.executeAction(partsFlux, null); + final Mono actionExecutionResultMono = newActionService.executeAction(partsFlux, null, null); StepVerifier .create(actionExecutionResultMono) @@ -223,7 +241,7 @@ public void testExecuteAction_withoutActionId_failsValidation() { final Flux partsFlux = BodyExtractors.toParts() .extract(mock, this.context); - final Mono actionExecutionResultMono = newActionService.executeAction(partsFlux, null); + final Mono actionExecutionResultMono = newActionService.executeAction(partsFlux, null, null); StepVerifier .create(actionExecutionResultMono) @@ -313,7 +331,7 @@ public void testExecuteAPIWithUsualOrderingOfTheParts() { NewActionServiceCE newActionServiceSpy = spy(newActionService); - Mono actionExecutionResultMono = newActionServiceSpy.executeAction(partsFlux, null); + Mono actionExecutionResultMono = newActionServiceSpy.executeAction(partsFlux, null, null); ActionExecutionResult mockResult = new ActionExecutionResult(); mockResult.setIsExecutionSuccess(true); @@ -322,7 +340,7 @@ public void testExecuteAPIWithUsualOrderingOfTheParts() { NewAction newAction = new NewAction(); newAction.setId("63285a3388e48972c7519b18"); - doReturn(Mono.just(mockResult)).when(newActionServiceSpy).executeAction(any()); + doReturn(Mono.just(mockResult)).when(newActionServiceSpy).executeAction(any(), any()); doReturn(Mono.just(newAction)).when(newActionServiceSpy).findByBranchNameAndDefaultActionId(any(), any(), any()); @@ -363,7 +381,7 @@ public void testExecuteAPIWithParameterMapAsLastPart() { NewActionServiceCE newActionServiceSpy = spy(newActionService); - Mono actionExecutionResultMono = newActionServiceSpy.executeAction(partsFlux, null); + Mono actionExecutionResultMono = newActionServiceSpy.executeAction(partsFlux, null, null); ActionExecutionResult mockResult = new ActionExecutionResult(); mockResult.setIsExecutionSuccess(true); @@ -372,7 +390,7 @@ public void testExecuteAPIWithParameterMapAsLastPart() { NewAction newAction = new NewAction(); newAction.setId("63285a3388e48972c7519b18"); - doReturn(Mono.just(mockResult)).when(newActionServiceSpy).executeAction(any()); + doReturn(Mono.just(mockResult)).when(newActionServiceSpy).executeAction(any(), any()); doReturn(Mono.just(newAction)).when(newActionServiceSpy).findByBranchNameAndDefaultActionId(any(), any(), any()); diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/solutions/ApplicationFetcherUnitTest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/solutions/ApplicationFetcherUnitTest.java index aa41a5114ad2..a286bbd2f4ae 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/solutions/ApplicationFetcherUnitTest.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/solutions/ApplicationFetcherUnitTest.java @@ -77,6 +77,11 @@ public class ApplicationFetcherUnitTest { @MockBean UserWorkspaceService userWorkspaceService; + WorkspacePermission workspacePermission; + + ApplicationPermission applicationPermission; + PagePermission pagePermission; + User testUser; final static String defaultPageId = "defaultPageId"; @@ -84,6 +89,9 @@ public class ApplicationFetcherUnitTest { @BeforeEach public void setup() { + workspacePermission = new WorkspacePermissionImpl(); + applicationPermission = new ApplicationPermissionImpl(); + pagePermission = new PagePermissionImpl(); applicationFetcher = new ApplicationFetcherImpl(sessionUserService, userService, userDataService, @@ -92,7 +100,10 @@ public void setup() { releaseNotesService, responseUtils, newPageService, - userWorkspaceService); + userWorkspaceService, + workspacePermission, + applicationPermission, + pagePermission); } private List createDummyApplications(int orgCount, int appCount) { diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/solutions/EmailEventHandlerTest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/solutions/EmailEventHandlerTest.java index dbd47480caa3..fc0cc261397c 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/solutions/EmailEventHandlerTest.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/solutions/EmailEventHandlerTest.java @@ -53,6 +53,8 @@ public class EmailEventHandlerTest { private PolicyUtils policyUtils; @MockBean UserWorkspaceService userWorkspaceService; + @MockBean + ApplicationPermission applicationPermission; EmailEventHandler emailEventHandler; @@ -69,7 +71,8 @@ public class EmailEventHandlerTest { public void setUp() { emailEventHandler = new EmailEventHandlerImpl(applicationEventPublisher, emailSender, workspaceRepository, - applicationRepository, newPageRepository, policyUtils, emailConfig, userWorkspaceService); + applicationRepository, newPageRepository, policyUtils, emailConfig, userWorkspaceService, + applicationPermission); application = new Application(); application.setName("Test application for comment"); diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/solutions/EnvManagerTest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/solutions/EnvManagerTest.java index 0c990f89117c..d94fcb129515 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/solutions/EnvManagerTest.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/solutions/EnvManagerTest.java @@ -34,6 +34,7 @@ import java.io.File; import java.io.IOException; +import java.util.HashMap; import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; @@ -272,7 +273,7 @@ public void disallowedVariable() { ) )) .matches(value -> value instanceof AppsmithException - && AppsmithError.UNAUTHORIZED_ACCESS.equals(((AppsmithException) value).getError())); + && AppsmithError.GENERIC_BAD_REQUEST.equals(((AppsmithException) value).getError())); } @Test @@ -371,4 +372,26 @@ public void sendTestEmail_WhenUserNotSuperUser_ThrowsException() { .expectErrorMessage(AppsmithError.UNAUTHORIZED_ACCESS.getMessage()) .verify(); } + + @Test + public void setEnv_AndGetAll() { + EnvManager envManagerInner = Mockito.mock(EnvManagerImpl.class); + + Map envs = new HashMap<>(); + envs.put("APPSMITH_MONGODB_URI", "mongo-url"); + envs.put("APPSMITH_DISABLE_TELEMETRY", ""); + + Mockito.when(envManagerInner.getAll()).thenReturn(Mono.just(envs)); + Mockito.when(envManagerInner.getAllNonEmpty()).thenCallRealMethod(); + + + Mono> envMono = envManagerInner.getAllNonEmpty(); + + StepVerifier.create(envMono) + .assertNext(map -> { + assertThat(map).hasSize(1); + assertThat(map.containsKey("APPSMITH_DISABLE_TELEMETRY")).isFalse(); + }) + .verifyComplete(); + } } diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/solutions/ImportExportApplicationServiceTests.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/solutions/ImportExportApplicationServiceTests.java index 092027e460b2..473926ae3f3e 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/solutions/ImportExportApplicationServiceTests.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/solutions/ImportExportApplicationServiceTests.java @@ -8,7 +8,6 @@ import com.appsmith.external.models.InvisibleActionFields; import com.appsmith.external.models.Policy; import com.appsmith.external.models.Property; -import com.appsmith.server.acl.AclPermission; import com.appsmith.server.constants.FieldName; import com.appsmith.server.constants.SerialiseApplicationObjective; import com.appsmith.server.domains.ActionCollection; @@ -61,7 +60,6 @@ import net.minidev.json.JSONArray; import net.minidev.json.JSONObject; import org.apache.commons.lang.StringUtils; -import org.assertj.core.api.AbstractBigDecimalAssert; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Test; @@ -1473,7 +1471,7 @@ public void importUpdatedApplicationIntoWorkspaceFromFile_publicApplication_visi // Now add a page and export the same import it to the app // Check if the policies and visibility flag are not reset - Mono workspaceResponse = workspaceService.findById(workspaceId, AclPermission.READ_WORKSPACES); + Mono workspaceResponse = workspaceService.findById(workspaceId, READ_WORKSPACES); List permissionGroups = workspaceResponse .flatMapMany(savedWorkspace -> { @@ -3485,4 +3483,29 @@ public void mergeApplication_nonGitConnectedApplicationSelectedAllPages_selected }) .verifyComplete(); } + + @Test + @WithUserDetails(value = "api_user") + public void importApplication_invalidJson_createdAppIsDeleted() { + FilePart filePart = createFilePart("test_assets/ImportExportServiceTest/invalid-json-without-pages.json"); + + List applicationList = applicationService.findAllApplicationsByWorkspaceId(workspaceId).collectList().block(); + + Mono resultMono = importExportApplicationService.extractFileAndSaveApplication(workspaceId, filePart); + + StepVerifier + .create(resultMono) + .expectErrorMatches(throwable -> throwable instanceof AppsmithException && + throwable.getMessage().equals(AppsmithError.NO_RESOURCE_FOUND.getMessage(FieldName.PAGES, INVALID_JSON_FILE))) + .verify(); + + // Verify that the app card is not created + StepVerifier + .create(applicationService.findAllApplicationsByWorkspaceId(workspaceId).collectList()) + .assertNext(applications -> { + assertThat(applicationList.size()).isEqualTo(applications.size()); + }) + .verifyComplete(); + + } } diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/solutions/ImportExportApplicationServiceV2Tests.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/solutions/ImportExportApplicationServiceV2Tests.java new file mode 100644 index 000000000000..fe82381d6c13 --- /dev/null +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/solutions/ImportExportApplicationServiceV2Tests.java @@ -0,0 +1,3513 @@ +package com.appsmith.server.solutions; + +import com.appsmith.external.helpers.AppsmithBeanUtils; +import com.appsmith.external.models.ActionConfiguration; +import com.appsmith.external.models.DBAuth; +import com.appsmith.external.models.Datasource; +import com.appsmith.external.models.DatasourceConfiguration; +import com.appsmith.external.models.InvisibleActionFields; +import com.appsmith.external.models.Policy; +import com.appsmith.external.models.Property; +import com.appsmith.server.constants.FieldName; +import com.appsmith.server.constants.SerialiseApplicationObjective; +import com.appsmith.server.domains.ActionCollection; +import com.appsmith.server.domains.Application; +import com.appsmith.server.domains.ApplicationMode; +import com.appsmith.server.domains.ApplicationPage; +import com.appsmith.server.domains.GitApplicationMetadata; +import com.appsmith.server.domains.Layout; +import com.appsmith.server.domains.NewAction; +import com.appsmith.server.domains.NewPage; +import com.appsmith.server.domains.PermissionGroup; +import com.appsmith.server.domains.Plugin; +import com.appsmith.external.models.PluginType; +import com.appsmith.server.domains.Theme; +import com.appsmith.server.domains.Workspace; +import com.appsmith.server.dtos.ActionCollectionDTO; +import com.appsmith.external.models.ActionDTO; +import com.appsmith.server.dtos.ApplicationAccessDTO; +import com.appsmith.server.dtos.ApplicationImportDTO; +import com.appsmith.server.dtos.ApplicationJson; +import com.appsmith.server.dtos.ApplicationPagesDTO; +import com.appsmith.server.dtos.PageDTO; +import com.appsmith.server.dtos.PageNameIdDTO; +import com.appsmith.server.exceptions.AppsmithError; +import com.appsmith.server.exceptions.AppsmithException; +import com.appsmith.server.helpers.MockPluginExecutor; +import com.appsmith.server.helpers.PluginExecutorHelper; +import com.appsmith.server.migrations.ApplicationVersion; +import com.appsmith.server.migrations.JsonSchemaMigration; +import com.appsmith.server.migrations.JsonSchemaVersions; +import com.appsmith.server.repositories.ApplicationRepository; +import com.appsmith.server.repositories.PermissionGroupRepository; +import com.appsmith.server.repositories.PluginRepository; +import com.appsmith.server.repositories.ThemeRepository; +import com.appsmith.server.services.ActionCollectionService; +import com.appsmith.server.services.ApplicationPageService; +import com.appsmith.server.services.ApplicationService; +import com.appsmith.server.services.DatasourceService; +import com.appsmith.server.services.LayoutActionService; +import com.appsmith.server.services.LayoutCollectionService; +import com.appsmith.server.services.NewActionService; +import com.appsmith.server.services.NewPageService; +import com.appsmith.server.services.PermissionGroupService; +import com.appsmith.server.services.WorkspaceService; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.gson.Gson; +import lombok.extern.slf4j.Slf4j; +import net.minidev.json.JSONArray; +import net.minidev.json.JSONObject; +import org.apache.commons.lang.StringUtils; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.core.io.buffer.DataBufferUtils; +import org.springframework.core.io.buffer.DefaultDataBufferFactory; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.http.codec.multipart.FilePart; +import org.springframework.security.test.context.support.WithUserDetails; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.util.LinkedMultiValueMap; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; +import reactor.util.function.Tuple3; +import reactor.util.function.Tuple4; + +import java.time.Duration; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +import static com.appsmith.server.acl.AclPermission.MANAGE_ACTIONS; +import static com.appsmith.server.acl.AclPermission.MANAGE_APPLICATIONS; +import static com.appsmith.server.acl.AclPermission.MANAGE_DATASOURCES; +import static com.appsmith.server.acl.AclPermission.MANAGE_PAGES; +import static com.appsmith.server.acl.AclPermission.READ_ACTIONS; +import static com.appsmith.server.acl.AclPermission.READ_APPLICATIONS; +import static com.appsmith.server.acl.AclPermission.READ_PAGES; +import static com.appsmith.server.acl.AclPermission.READ_WORKSPACES; +import static com.appsmith.server.constants.FieldName.DEFAULT_PAGE_LAYOUT; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; + +@Slf4j +@ExtendWith(SpringExtension.class) +@SpringBootTest +@DirtiesContext +@TestMethodOrder(MethodOrderer.MethodName.class) +public class ImportExportApplicationServiceV2Tests { + + @Autowired + @Qualifier("importExportServiceCEImplV2") + ImportExportApplicationService importExportApplicationService; + + @Autowired + ApplicationPageService applicationPageService; + + @Autowired + PluginRepository pluginRepository; + + @Autowired + ApplicationRepository applicationRepository; + + @Autowired + DatasourceService datasourceService; + + @Autowired + NewPageService newPageService; + + @Autowired + NewActionService newActionService; + + @Autowired + WorkspaceService workspaceService; + + @Autowired + LayoutActionService layoutActionService; + + @Autowired + LayoutCollectionService layoutCollectionService; + + @Autowired + ActionCollectionService actionCollectionService; + + @MockBean + PluginExecutorHelper pluginExecutorHelper; + + @Autowired + ThemeRepository themeRepository; + + @Autowired + ApplicationService applicationService; + + @Autowired + PermissionGroupRepository permissionGroupRepository; + + @Autowired + PermissionGroupService permissionGroupService; + + private static final String INVALID_JSON_FILE = "invalid json file"; + private static Plugin installedPlugin; + private static String workspaceId; + private static String testAppId; + private static Datasource jsDatasource; + private static final Map datasourceMap = new HashMap<>(); + private static Plugin installedJsPlugin; + private static Boolean isSetupDone = false; + private static String exportWithConfigurationAppId; + + @BeforeEach + public void setup() { + Mockito + .when(pluginExecutorHelper.getPluginExecutor(Mockito.any())) + .thenReturn(Mono.just(new MockPluginExecutor())); + + if (Boolean.TRUE.equals(isSetupDone)) { + return; + } + installedPlugin = pluginRepository.findByPackageName("installed-plugin").block(); + Workspace workspace = new Workspace(); + workspace.setName("Import-Export-Test-Workspace"); + Workspace savedWorkspace = workspaceService.create(workspace).block(); + workspaceId = savedWorkspace.getId(); + + Application testApplication = new Application(); + testApplication.setName("Export-Application-Test-Application"); + testApplication.setWorkspaceId(workspaceId); + testApplication.setUpdatedAt(Instant.now()); + testApplication.setLastDeployedAt(Instant.now()); + testApplication.setModifiedBy("some-user"); + testApplication.setGitApplicationMetadata(new GitApplicationMetadata()); + + Application savedApplication = applicationPageService.createApplication(testApplication, workspaceId).block(); + testAppId = savedApplication.getId(); + + Datasource ds1 = new Datasource(); + ds1.setName("DS1"); + ds1.setWorkspaceId(workspaceId); + ds1.setPluginId(installedPlugin.getId()); + final DatasourceConfiguration datasourceConfiguration = new DatasourceConfiguration(); + datasourceConfiguration.setUrl("http://httpbin.org/get"); + datasourceConfiguration.setHeaders(List.of( + new Property("X-Answer", "42") + )); + ds1.setDatasourceConfiguration(datasourceConfiguration); + + Datasource ds2 = new Datasource(); + ds2.setName("DS2"); + ds2.setPluginId(installedPlugin.getId()); + ds2.setDatasourceConfiguration(new DatasourceConfiguration()); + ds2.setWorkspaceId(workspaceId); + DBAuth auth = new DBAuth(); + auth.setPassword("awesome-password"); + ds2.getDatasourceConfiguration().setAuthentication(auth); + + jsDatasource = new Datasource(); + jsDatasource.setName("Default JS datasource"); + jsDatasource.setWorkspaceId(workspaceId); + installedJsPlugin = pluginRepository.findByPackageName("installed-js-plugin").block(); + assert installedJsPlugin != null; + jsDatasource.setPluginId(installedJsPlugin.getId()); + + ds1 = datasourceService.create(ds1).block(); + ds2 = datasourceService.create(ds2).block(); + datasourceMap.put("DS1", ds1); + datasourceMap.put("DS2", ds2); + isSetupDone = true; + } + + private Flux getActionsInApplication(Application application) { + return newPageService + // fetch the unpublished pages + .findByApplicationId(application.getId(), READ_PAGES, false) + .flatMap(page -> newActionService.getUnpublishedActions(new LinkedMultiValueMap<>( + Map.of(FieldName.PAGE_ID, Collections.singletonList(page.getId()))), "")); + } + + private FilePart createFilePart(String filePath) { + FilePart filepart = Mockito.mock(FilePart.class, Mockito.RETURNS_DEEP_STUBS); + Flux dataBufferFlux = DataBufferUtils + .read( + new ClassPathResource(filePath), + new DefaultDataBufferFactory(), + 4096) + .cache(); + + Mockito.when(filepart.content()).thenReturn(dataBufferFlux); + Mockito.when(filepart.headers().getContentType()).thenReturn(MediaType.APPLICATION_JSON); + + return filepart; + + } + + private Mono createAppJson(String filePath) { + FilePart filePart = createFilePart(filePath); + + Mono stringifiedFile = DataBufferUtils.join(filePart.content()) + .map(dataBuffer -> { + byte[] data = new byte[dataBuffer.readableByteCount()]; + dataBuffer.read(data); + DataBufferUtils.release(dataBuffer); + return new String(data); + }); + + return stringifiedFile + .map(data -> { + Gson gson = new Gson(); + return gson.fromJson(data, ApplicationJson.class); + }) + .map(JsonSchemaMigration::migrateApplicationToLatestSchema); + } + + private Workspace createTemplateWorkspace() { + Workspace newWorkspace = new Workspace(); + newWorkspace.setName("Template Workspace"); + return workspaceService.create(newWorkspace).block(); + } + + @Test + @WithUserDetails(value = "api_user") + public void exportApplicationWithNullApplicationIdTest() { + Mono resultMono = importExportApplicationService.exportApplicationById(null, ""); + + StepVerifier + .create(resultMono) + .expectErrorMatches(throwable -> throwable instanceof AppsmithException && + throwable.getMessage().equals(AppsmithError.INVALID_PARAMETER.getMessage(FieldName.APPLICATION_ID))) + .verify(); + } + + @Test + @WithUserDetails(value = "api_user") + public void exportPublicApplicationTest() { + + Application application = new Application(); + application.setName("exportPublicApplicationTest-Test"); + + Application createdApplication = applicationPageService.createApplication(application, workspaceId).block(); + + Mono workspaceResponse = workspaceService.findById(workspaceId, READ_WORKSPACES); + + ApplicationAccessDTO applicationAccessDTO = new ApplicationAccessDTO(); + applicationAccessDTO.setPublicAccess(true); + + // Make the application public + applicationService.changeViewAccess(createdApplication.getId(), applicationAccessDTO).block(); + + Mono resultMono = + importExportApplicationService.exportApplicationById(createdApplication.getId(), ""); + + StepVerifier + .create(resultMono) + .assertNext(applicationJson -> { + Application exportedApplication = applicationJson.getExportedApplication(); + assertThat(exportedApplication).isNotNull(); + // Assert that the exported application is NOT public + assertThat(exportedApplication.getDefaultPermissionGroup()).isNull(); + assertThat(exportedApplication.getPolicies()).isNullOrEmpty(); + }) + .verifyComplete(); + } + + @Test + @WithUserDetails(value = "api_user") + public void exportApplication_withInvalidApplicationId_throwNoResourceFoundException() { + Mono resultMono = importExportApplicationService.exportApplicationById("invalidAppId", ""); + + StepVerifier + .create(resultMono) + .expectErrorMatches(throwable -> throwable instanceof AppsmithException && + throwable.getMessage().equals(AppsmithError.NO_RESOURCE_FOUND.getMessage(FieldName.APPLICATION_ID, "invalidAppId"))) + .verify(); + } + + @Test + @WithUserDetails(value = "api_user") + public void exportApplicationById_WhenContainsInternalFields_InternalFieldsNotExported() { + Mono resultMono = importExportApplicationService.exportApplicationById(testAppId, ""); + + StepVerifier + .create(resultMono) + .assertNext(applicationJson -> { + Application exportedApplication = applicationJson.getExportedApplication(); + assertThat(exportedApplication.getModifiedBy()).isNull(); + assertThat(exportedApplication.getLastUpdateTime()).isNull(); + assertThat(exportedApplication.getLastEditedAt()).isNull(); + assertThat(exportedApplication.getLastDeployedAt()).isNull(); + assertThat(exportedApplication.getGitApplicationMetadata()).isNull(); + assertThat(exportedApplication.getEditModeThemeId()).isNull(); + assertThat(exportedApplication.getPublishedModeThemeId()).isNull(); + }) + .verifyComplete(); + } + + @Test + @WithUserDetails(value = "api_user") + public void createExportAppJsonWithDatasourceButWithoutActionsTest() { + + Application testApplication = new Application(); + testApplication.setName("Another Export Application"); + + final Mono resultMono = workspaceService.getById(workspaceId) + .flatMap(workspace -> { + + final Datasource ds1 = datasourceMap.get("DS1"); + ds1.setWorkspaceId(workspace.getId()); + + final Datasource ds2 = datasourceMap.get("DS2"); + ds2.setWorkspaceId(workspace.getId()); + + return applicationPageService.createApplication(testApplication, workspaceId); + }) + .flatMap(application -> importExportApplicationService.exportApplicationById(application.getId(), "")); + + StepVerifier.create(resultMono) + .assertNext(applicationJson -> { + + assertThat(applicationJson.getPageList()).hasSize(1); + assertThat(applicationJson.getActionList()).isEmpty(); + assertThat(applicationJson.getDatasourceList()).isEmpty(); + }) + .verifyComplete(); + } + + @Test + @WithUserDetails(value = "api_user") + public void createExportAppJsonWithActionAndActionCollectionTest() { + + Workspace newWorkspace = new Workspace(); + newWorkspace.setName("template-org-with-ds"); + + Application testApplication = new Application(); + testApplication.setName("ApplicationWithActionCollectionAndDatasource"); + testApplication = applicationPageService.createApplication(testApplication, workspaceId).block(); + + assert testApplication != null; + final String appName = testApplication.getName(); + final Mono resultMono = Mono.zip( + Mono.just(testApplication), + newPageService.findPageById(testApplication.getPages().get(0).getId(), READ_PAGES, false) + ) + .flatMap(tuple -> { + Application testApp = tuple.getT1(); + PageDTO testPage = tuple.getT2(); + + Layout layout = testPage.getLayouts().get(0); + ObjectMapper objectMapper = new ObjectMapper(); + JSONObject dsl = new JSONObject(); + try { + dsl = new JSONObject(objectMapper.readValue(DEFAULT_PAGE_LAYOUT, new TypeReference>() { + })); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + + ArrayList children = (ArrayList) dsl.get("children"); + JSONObject testWidget = new JSONObject(); + testWidget.put("widgetName", "firstWidget"); + JSONArray temp = new JSONArray(); + temp.addAll(List.of(new JSONObject(Map.of("key", "testField")))); + testWidget.put("dynamicBindingPathList", temp); + testWidget.put("testField", "{{ validAction.data }}"); + children.add(testWidget); + + JSONObject tableWidget = new JSONObject(); + tableWidget.put("widgetName", "Table1"); + tableWidget.put("type", "TABLE_WIDGET"); + Map primaryColumns = new HashMap<>(); + JSONObject jsonObject = new JSONObject(Map.of("key", "value")); + primaryColumns.put("_id", "{{ PageAction.data }}"); + primaryColumns.put("_class", jsonObject); + tableWidget.put("primaryColumns", primaryColumns); + final ArrayList objects = new ArrayList<>(); + JSONArray temp2 = new JSONArray(); + temp2.addAll(List.of(new JSONObject(Map.of("key", "primaryColumns._id")))); + tableWidget.put("dynamicBindingPathList", temp2); + children.add(tableWidget); + + layout.setDsl(dsl); + layout.setPublishedDsl(dsl); + + ActionDTO action = new ActionDTO(); + action.setName("validAction"); + action.setPageId(testPage.getId()); + action.setExecuteOnLoad(true); + ActionConfiguration actionConfiguration = new ActionConfiguration(); + actionConfiguration.setHttpMethod(HttpMethod.GET); + action.setActionConfiguration(actionConfiguration); + action.setDatasource(datasourceMap.get("DS2")); + + ActionDTO action2 = new ActionDTO(); + action2.setName("validAction2"); + action2.setPageId(testPage.getId()); + action2.setExecuteOnLoad(true); + action2.setUserSetOnLoad(true); + ActionConfiguration actionConfiguration2 = new ActionConfiguration(); + actionConfiguration2.setHttpMethod(HttpMethod.GET); + action2.setActionConfiguration(actionConfiguration2); + action2.setDatasource(datasourceMap.get("DS2")); + + ActionCollectionDTO actionCollectionDTO1 = new ActionCollectionDTO(); + actionCollectionDTO1.setName("testCollection1"); + actionCollectionDTO1.setPageId(testPage.getId()); + actionCollectionDTO1.setApplicationId(testApp.getId()); + actionCollectionDTO1.setWorkspaceId(testApp.getWorkspaceId()); + actionCollectionDTO1.setPluginId(jsDatasource.getPluginId()); + ActionDTO action1 = new ActionDTO(); + action1.setName("testAction1"); + action1.setActionConfiguration(new ActionConfiguration()); + action1.getActionConfiguration().setBody("mockBody"); + actionCollectionDTO1.setActions(List.of(action1)); + actionCollectionDTO1.setPluginType(PluginType.JS); + + return layoutCollectionService.createCollection(actionCollectionDTO1) + .then(layoutActionService.createSingleAction(action)) + .then(layoutActionService.createSingleAction(action2)) + .then(layoutActionService.updateLayout(testPage.getId(), testPage.getApplicationId(), layout.getId(), layout)) + .then(importExportApplicationService.exportApplicationById(testApp.getId(), "")); + }) + .cache(); + + Mono> actionListMono = resultMono + .then(newActionService + .findAllByApplicationIdAndViewMode(testApplication.getId(), false, READ_ACTIONS, null).collectList()); + + Mono> collectionListMono = resultMono.then( + actionCollectionService + .findAllByApplicationIdAndViewMode(testApplication.getId(), false, READ_ACTIONS, null).collectList()); + + Mono> pageListMono = resultMono.then( + newPageService + .findNewPagesByApplicationId(testApplication.getId(), READ_PAGES).collectList()); + + StepVerifier + .create(Mono.zip(resultMono, actionListMono, collectionListMono, pageListMono)) + .assertNext(tuple -> { + + ApplicationJson applicationJson = tuple.getT1(); + List DBActions = tuple.getT2(); + List DBCollections = tuple.getT3(); + List DBPages = tuple.getT4(); + + Application exportedApp = applicationJson.getExportedApplication(); + List pageList = applicationJson.getPageList(); + List actionList = applicationJson.getActionList(); + List actionCollectionList = applicationJson.getActionCollectionList(); + List datasourceList = applicationJson.getDatasourceList(); + + List exportedCollectionIds = actionCollectionList.stream().map(ActionCollection::getId).collect(Collectors.toList()); + List exportedActionIds = actionList.stream().map(NewAction::getId).collect(Collectors.toList()); + List DBCollectionIds = DBCollections.stream().map(ActionCollection::getId).collect(Collectors.toList()); + List DBActionIds = DBActions.stream().map(NewAction::getId).collect(Collectors.toList()); + List DBOnLayoutLoadActionIds = new ArrayList<>(); + List exportedOnLayoutLoadActionIds = new ArrayList<>(); + + assertThat(DBPages).hasSize(1); + DBPages.forEach(newPage -> + newPage.getUnpublishedPage().getLayouts().forEach(layout -> { + if (layout.getLayoutOnLoadActions() != null) { + layout.getLayoutOnLoadActions().forEach(dslActionDTOSet -> { + dslActionDTOSet.forEach(actionDTO -> DBOnLayoutLoadActionIds.add(actionDTO.getId())); + }); + } + }) + ); + pageList.forEach(newPage -> + newPage.getUnpublishedPage().getLayouts().forEach(layout -> { + if (layout.getLayoutOnLoadActions() != null) { + layout.getLayoutOnLoadActions().forEach(dslActionDTOSet -> { + dslActionDTOSet.forEach(actionDTO -> exportedOnLayoutLoadActionIds.add(actionDTO.getId())); + }); + } + }) + ); + + NewPage defaultPage = pageList.get(0); + + // Check if the mongo escaped widget names are carried to exported file from DB + Layout pageLayout = DBPages.get(0).getUnpublishedPage().getLayouts().get(0); + Set mongoEscapedWidgets = pageLayout.getMongoEscapedWidgetNames(); + Set expectedMongoEscapedWidgets = Set.of("Table1"); + assertThat(mongoEscapedWidgets).isEqualTo(expectedMongoEscapedWidgets); + + pageLayout = pageList.get(0).getUnpublishedPage().getLayouts().get(0); + Set exportedMongoEscapedWidgets = pageLayout.getMongoEscapedWidgetNames(); + assertThat(exportedMongoEscapedWidgets).isEqualTo(expectedMongoEscapedWidgets); + + + assertThat(exportedApp.getName()).isEqualTo(appName); + assertThat(exportedApp.getWorkspaceId()).isNull(); + assertThat(exportedApp.getPages()).hasSize(1); + assertThat(exportedApp.getPages().get(0).getId()).isEqualTo(defaultPage.getUnpublishedPage().getName()); + + assertThat(exportedApp.getPolicies()).isNull(); + + assertThat(pageList).hasSize(1); + assertThat(defaultPage.getApplicationId()).isNull(); + assertThat(defaultPage.getUnpublishedPage().getLayouts().get(0).getDsl()).isNotNull(); + assertThat(defaultPage.getId()).isNull(); + assertThat(defaultPage.getPolicies()).isNull(); + + assertThat(actionList.isEmpty()).isFalse(); + assertThat(actionList).hasSize(3); + NewAction validAction = actionList.stream().filter(action -> action.getId().equals("Page1_validAction")).findFirst().get(); + assertThat(validAction.getApplicationId()).isNull(); + assertThat(validAction.getPluginId()).isEqualTo(installedPlugin.getPackageName()); + assertThat(validAction.getPluginType()).isEqualTo(PluginType.API); + assertThat(validAction.getWorkspaceId()).isNull(); + assertThat(validAction.getPolicies()).isNull(); + assertThat(validAction.getId()).isNotNull(); + ActionDTO unpublishedAction = validAction.getUnpublishedAction(); + assertThat(unpublishedAction.getPageId()).isEqualTo(defaultPage.getUnpublishedPage().getName()); + assertThat(unpublishedAction.getDatasource().getPluginId()).isEqualTo(installedPlugin.getPackageName()); + + NewAction testAction1 = actionList.stream().filter(action -> action.getUnpublishedAction().getName().equals("testAction1")).findFirst().get(); + assertThat(testAction1.getId()).isEqualTo("Page1_testCollection1.testAction1"); + + assertThat(actionCollectionList.isEmpty()).isFalse(); + assertThat(actionCollectionList).hasSize(1); + final ActionCollection actionCollection = actionCollectionList.get(0); + assertThat(actionCollection.getApplicationId()).isNull(); + assertThat(actionCollection.getWorkspaceId()).isNull(); + assertThat(actionCollection.getPolicies()).isNull(); + assertThat(actionCollection.getId()).isNotNull(); + assertThat(actionCollection.getUnpublishedCollection().getPluginType()).isEqualTo(PluginType.JS); + assertThat(actionCollection.getUnpublishedCollection().getPageId()) + .isEqualTo(defaultPage.getUnpublishedPage().getName()); + assertThat(actionCollection.getUnpublishedCollection().getPluginId()).isEqualTo(installedJsPlugin.getPackageName()); + + assertThat(datasourceList).hasSize(1); + Datasource datasource = datasourceList.get(0); + assertThat(datasource.getWorkspaceId()).isNull(); + assertThat(datasource.getId()).isNull(); + assertThat(datasource.getPluginId()).isEqualTo(installedPlugin.getPackageName()); + assertThat(datasource.getDatasourceConfiguration()).isNull(); + + assertThat(applicationJson.getInvisibleActionFields()).isNull(); + NewAction validAction2 = actionList.stream().filter(action -> action.getId().equals("Page1_validAction2")).findFirst().get(); + assertEquals(true, validAction2.getUnpublishedAction().getUserSetOnLoad()); + + assertThat(applicationJson.getUnpublishedLayoutmongoEscapedWidgets()).isNull(); + assertThat(applicationJson.getPublishedLayoutmongoEscapedWidgets()).isNull(); + assertThat(applicationJson.getEditModeTheme()).isNotNull(); + assertThat(applicationJson.getEditModeTheme().isSystemTheme()).isTrue(); + assertThat(applicationJson.getEditModeTheme().getName()).isEqualToIgnoringCase(Theme.DEFAULT_THEME_NAME); + + assertThat(applicationJson.getPublishedTheme()).isNotNull(); + assertThat(applicationJson.getPublishedTheme().isSystemTheme()).isTrue(); + assertThat(applicationJson.getPublishedTheme().getName()).isEqualToIgnoringCase(Theme.DEFAULT_THEME_NAME); + + assertThat(exportedCollectionIds).isNotEmpty(); + assertThat(exportedCollectionIds).doesNotContain(String.valueOf(DBCollectionIds)); + + assertThat(exportedActionIds).isNotEmpty(); + assertThat(exportedActionIds).doesNotContain(String.valueOf(DBActionIds)); + + assertThat(exportedOnLayoutLoadActionIds).isNotEmpty(); + assertThat(exportedOnLayoutLoadActionIds).doesNotContain(String.valueOf(DBOnLayoutLoadActionIds)); + }) + .verifyComplete(); + } + + @Test + @WithUserDetails(value = "api_user") + public void createExportAppJsonForGitTest() { + + StringBuilder pageName = new StringBuilder(); + final Mono resultMono = applicationRepository.findById(testAppId) + .flatMap(testApp -> { + final String pageId = testApp.getPages().get(0).getId(); + return Mono.zip( + Mono.just(testApp), + newPageService.findPageById(pageId, READ_PAGES, false) + ); + }) + .flatMap(tuple -> { + Datasource ds1 = datasourceMap.get("DS1"); + Application testApp = tuple.getT1(); + PageDTO testPage = tuple.getT2(); + pageName.append(testPage.getName()); + + Layout layout = testPage.getLayouts().get(0); + JSONObject dsl = new JSONObject(Map.of("text", "{{ query1.data }}")); + + layout.setDsl(dsl); + layout.setPublishedDsl(dsl); + + ActionDTO action = new ActionDTO(); + action.setName("validAction"); + action.setPageId(testPage.getId()); + action.setExecuteOnLoad(true); + ActionConfiguration actionConfiguration = new ActionConfiguration(); + actionConfiguration.setHttpMethod(HttpMethod.GET); + action.setActionConfiguration(actionConfiguration); + action.setDatasource(ds1); + + return layoutActionService.createAction(action) + .then(importExportApplicationService.exportApplicationById(testApp.getId(), SerialiseApplicationObjective.VERSION_CONTROL)); + }); + + StepVerifier + .create(resultMono) + .assertNext(applicationJson -> { + + Application exportedApp = applicationJson.getExportedApplication(); + List pageList = applicationJson.getPageList(); + List actionList = applicationJson.getActionList(); + List datasourceList = applicationJson.getDatasourceList(); + + NewPage newPage = pageList.get(0); + + assertThat(applicationJson.getServerSchemaVersion()).isEqualTo(JsonSchemaVersions.serverVersion); + assertThat(applicationJson.getClientSchemaVersion()).isEqualTo(JsonSchemaVersions.clientVersion); + + assertThat(exportedApp.getName()).isNotNull(); + assertThat(exportedApp.getWorkspaceId()).isNull(); + assertThat(exportedApp.getPages()).hasSize(1); + assertThat(exportedApp.getPages().get(0).getId()).isEqualTo(pageName.toString()); + assertThat(exportedApp.getGitApplicationMetadata()).isNull(); + + assertThat(exportedApp.getPolicies()).isNull(); + assertThat(exportedApp.getUserPermissions()).isNull(); + + assertThat(pageList).hasSize(1); + assertThat(newPage.getApplicationId()).isNull(); + assertThat(newPage.getUnpublishedPage().getLayouts().get(0).getDsl()).isNotNull(); + assertThat(newPage.getId()).isNull(); + assertThat(newPage.getPolicies()).isNull(); + + assertThat(actionList.isEmpty()).isFalse(); + NewAction validAction = actionList.get(0); + assertThat(validAction.getApplicationId()).isNull(); + assertThat(validAction.getPluginId()).isEqualTo(installedPlugin.getPackageName()); + assertThat(validAction.getPluginType()).isEqualTo(PluginType.API); + assertThat(validAction.getWorkspaceId()).isNull(); + assertThat(validAction.getPolicies()).isNull(); + assertThat(validAction.getId()).isNotNull(); + assertThat(validAction.getUnpublishedAction().getPageId()) + .isEqualTo(newPage.getUnpublishedPage().getName()); + + assertThat(datasourceList).hasSize(1); + Datasource datasource = datasourceList.get(0); + assertThat(datasource.getWorkspaceId()).isNull(); + assertThat(datasource.getId()).isNull(); + assertThat(datasource.getPluginId()).isEqualTo(installedPlugin.getPackageName()); + assertThat(datasource.getDatasourceConfiguration()).isNull(); + + assertThat(applicationJson.getUnpublishedLayoutmongoEscapedWidgets()).isNull(); + assertThat(applicationJson.getPublishedLayoutmongoEscapedWidgets()).isNull(); + }) + .verifyComplete(); + } + + @Test + @WithUserDetails(value = "api_user") + public void importApplicationFromInvalidFileTest() { + FilePart filepart = Mockito.mock(FilePart.class, Mockito.RETURNS_DEEP_STUBS); + Flux dataBufferFlux = DataBufferUtils + .read(new ClassPathResource("test_assets/WorkspaceServiceTest/my_workspace_logo.png"), new DefaultDataBufferFactory(), 4096) + .cache(); + + Mockito.when(filepart.content()).thenReturn(dataBufferFlux); + Mockito.when(filepart.headers().getContentType()).thenReturn(MediaType.IMAGE_PNG); + + Mono resultMono = importExportApplicationService.extractFileAndSaveApplication(workspaceId, filepart); + + StepVerifier + .create(resultMono) + .expectErrorMatches(error -> error instanceof AppsmithException) + .verify(); + } + + @Test + @WithUserDetails(value = "api_user") + public void importApplicationWithNullWorkspaceIdTest() { + FilePart filepart = Mockito.mock(FilePart.class, Mockito.RETURNS_DEEP_STUBS); + + Mono resultMono = importExportApplicationService + .extractFileAndSaveApplication(null, filepart); + + StepVerifier + .create(resultMono) + .expectErrorMatches(throwable -> throwable instanceof AppsmithException && + throwable.getMessage().equals(AppsmithError.INVALID_PARAMETER.getMessage(FieldName.WORKSPACE_ID))) + .verify(); + } + + @Test + @WithUserDetails(value = "api_user") + public void importApplicationFromInvalidJsonFileWithoutPagesTest() { + + FilePart filePart = createFilePart("test_assets/ImportExportServiceTest/invalid-json-without-pages.json"); + Mono resultMono = importExportApplicationService.extractFileAndSaveApplication(workspaceId, filePart); + + StepVerifier + .create(resultMono) + .expectErrorMatches(throwable -> throwable instanceof AppsmithException && + throwable.getMessage().equals(AppsmithError.NO_RESOURCE_FOUND.getMessage(FieldName.PAGES, INVALID_JSON_FILE))) + .verify(); + } + + @Test + @WithUserDetails(value = "api_user") + public void importApplicationFromInvalidJsonFileWithoutApplicationTest() { + + FilePart filePart = createFilePart("test_assets/ImportExportServiceTest/invalid-json-without-app.json"); + Mono resultMono = importExportApplicationService.extractFileAndSaveApplication(workspaceId, filePart); + + StepVerifier + .create(resultMono) + .expectErrorMatches(throwable -> throwable instanceof AppsmithException && + throwable.getMessage().equals(AppsmithError.NO_RESOURCE_FOUND.getMessage(FieldName.APPLICATION, INVALID_JSON_FILE))) + .verify(); + } + + @Test + @WithUserDetails(value = "api_user") + public void importApplicationFromValidJsonFileTest() { + + FilePart filePart = createFilePart("test_assets/ImportExportServiceTest/valid-application.json"); + + Workspace newWorkspace = new Workspace(); + newWorkspace.setName("Template Workspace"); + + Mono workspaceMono = workspaceService + .create(newWorkspace).cache(); + + final Mono resultMono = workspaceMono + .flatMap(workspace -> importExportApplicationService + .extractFileAndSaveApplication(workspace.getId(), filePart) + ); + + List permissionGroups = workspaceMono + .flatMapMany(savedWorkspace -> { + Set defaultPermissionGroups = savedWorkspace.getDefaultPermissionGroups(); + return permissionGroupRepository.findAllById(defaultPermissionGroups); + }) + .collectList() + .block(); + + PermissionGroup adminPermissionGroup = permissionGroups.stream() + .filter(permissionGroup -> permissionGroup.getName().startsWith(FieldName.ADMINISTRATOR)) + .findFirst().get(); + + PermissionGroup developerPermissionGroup = permissionGroups.stream() + .filter(permissionGroup -> permissionGroup.getName().startsWith(FieldName.DEVELOPER)) + .findFirst().get(); + + PermissionGroup viewerPermissionGroup = permissionGroups.stream() + .filter(permissionGroup -> permissionGroup.getName().startsWith(FieldName.VIEWER)) + .findFirst().get(); + + Policy manageAppPolicy = Policy.builder().permission(MANAGE_APPLICATIONS.getValue()) + .permissionGroups(Set.of(adminPermissionGroup.getId(), developerPermissionGroup.getId())) + .build(); + Policy readAppPolicy = Policy.builder().permission(READ_APPLICATIONS.getValue()) + .permissionGroups(Set.of(adminPermissionGroup.getId(), developerPermissionGroup.getId(), + viewerPermissionGroup.getId())) + .build(); + + StepVerifier + .create(resultMono + .flatMap(applicationImportDTO -> { + Application application = applicationImportDTO.getApplication(); + return Mono.zip( + Mono.just(applicationImportDTO), + datasourceService.findAllByWorkspaceId(application.getWorkspaceId(), MANAGE_DATASOURCES).collectList(), + newActionService.findAllByApplicationIdAndViewMode(application.getId(), false, READ_ACTIONS, null).collectList(), + newPageService.findByApplicationId(application.getId(), MANAGE_PAGES, false).collectList(), + actionCollectionService.findAllByApplicationIdAndViewMode(application.getId(), false, MANAGE_ACTIONS, null).collectList() + ); + })) + .assertNext(tuple -> { + final Application application = tuple.getT1().getApplication(); + final List unConfiguredDatasourceList = tuple.getT1().getUnConfiguredDatasourceList(); + final boolean isPartialImport = tuple.getT1().getIsPartialImport(); + final List datasourceList = tuple.getT2(); + final List actionList = tuple.getT3(); + final List pageList = tuple.getT4(); + final List actionCollectionList = tuple.getT5(); + + assertThat(application.getName()).isEqualTo("valid_application"); + assertThat(application.getWorkspaceId()).isNotNull(); + assertThat(application.getPages()).hasSize(2); + assertThat(application.getPolicies()).containsAll(Set.of(manageAppPolicy, readAppPolicy)); + assertThat(application.getPublishedPages()).hasSize(1); + assertThat(application.getModifiedBy()).isEqualTo("api_user"); + assertThat(application.getUpdatedAt()).isNotNull(); + assertThat(application.getEditModeThemeId()).isNotNull(); + assertThat(application.getPublishedModeThemeId()).isNotNull(); + assertThat(isPartialImport).isEqualTo(Boolean.TRUE); + assertThat(unConfiguredDatasourceList).isNotNull(); + + assertThat(datasourceList).isNotEmpty(); + datasourceList.forEach(datasource -> { + assertThat(datasource.getWorkspaceId()).isEqualTo(application.getWorkspaceId()); + assertThat(datasource.getDatasourceConfiguration()).isNotNull(); + }); + + List collectionIdInAction = new ArrayList<>(); + assertThat(actionList).isNotEmpty(); + actionList.forEach(newAction -> { + ActionDTO actionDTO = newAction.getUnpublishedAction(); + assertThat(actionDTO.getPageId()).isNotEqualTo(pageList.get(0).getName()); + if (StringUtils.equals(actionDTO.getName(), "api_wo_auth")) { + ActionDTO publishedAction = newAction.getPublishedAction(); + assertThat(publishedAction).isNotNull(); + assertThat(publishedAction.getActionConfiguration()).isNotNull(); + // Test the fallback page ID from the unpublishedAction is copied to published version when + // published version does not have pageId + assertThat(actionDTO.getPageId()).isEqualTo(publishedAction.getPageId()); + } + if (!StringUtils.isEmpty(actionDTO.getCollectionId())) { + collectionIdInAction.add(actionDTO.getCollectionId()); + } + }); + + assertThat(actionCollectionList).isNotEmpty(); + actionCollectionList.forEach(actionCollection -> { + assertThat(actionCollection.getUnpublishedCollection().getPageId()).isNotEqualTo(pageList.get(0).getName()); + if (StringUtils.equals(actionCollection.getUnpublishedCollection().getName(), "JSObject2")) { + // Check if this action collection is not attached to any action + assertThat(collectionIdInAction).doesNotContain(actionCollection.getId()); + } else { + assertThat(collectionIdInAction).contains(actionCollection.getId()); + } + }); + + assertThat(pageList).hasSize(2); + + ApplicationPage defaultAppPage = application.getPages() + .stream() + .filter(ApplicationPage::getIsDefault) + .findFirst() + .orElse(null); + assertThat(defaultAppPage).isNotNull(); + + PageDTO defaultPageDTO = pageList.stream() + .filter(pageDTO -> pageDTO.getId().equals(defaultAppPage.getId())).findFirst().orElse(null); + + assertThat(defaultPageDTO).isNotNull(); + assertThat(defaultPageDTO.getLayouts().get(0).getLayoutOnLoadActions()).isNotEmpty(); + }) + .verifyComplete(); + } + + @Test + @WithUserDetails(value = "api_user") + public void importFromValidJson_cancelledMidway_importSuccess() { + + FilePart filePart = createFilePart("test_assets/ImportExportServiceTest/valid-application.json"); + + Workspace newWorkspace = new Workspace(); + newWorkspace.setName("Midway cancel import app workspace"); + newWorkspace = workspaceService.create(newWorkspace).block(); + + importExportApplicationService + .extractFileAndSaveApplication(newWorkspace.getId(), filePart) + .timeout(Duration.ofMillis(10)) + .subscribe(); + + // Wait for import to complete + Mono importedAppFromDbMono = Mono.just(newWorkspace) + .flatMap(workspace -> { + try { + // Before fetching the imported application, sleep for 5 seconds to ensure that the import completes + Thread.sleep(5000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return applicationRepository.findByWorkspaceId(workspace.getId(), READ_APPLICATIONS) + .next(); + }); + + StepVerifier.create(importedAppFromDbMono) + .assertNext(application -> { + assertThat(application.getId()).isNotEmpty(); + }) + .verifyComplete(); + } + + @Test + @WithUserDetails(value = "api_user") + public void importApplicationInWorkspace_WhenCustomizedThemes_ThemesCreated() { + FilePart filePart = createFilePart( + "test_assets/ImportExportServiceTest/valid-application-with-custom-themes.json" + ); + + Workspace newWorkspace = new Workspace(); + newWorkspace.setName("Import theme test org"); + + final Mono resultMono = workspaceService + .create(newWorkspace) + .flatMap(workspace -> importExportApplicationService + .extractFileAndSaveApplication(workspace.getId(), filePart) + ); + + StepVerifier + .create(resultMono + .flatMap(applicationImportDTO -> Mono.zip( + Mono.just(applicationImportDTO), + themeRepository.findById(applicationImportDTO.getApplication().getEditModeThemeId()), + themeRepository.findById(applicationImportDTO.getApplication().getPublishedModeThemeId()) + ))) + .assertNext(tuple -> { + final Application application = tuple.getT1().getApplication(); + Theme editTheme = tuple.getT2(); + Theme publishedTheme = tuple.getT3(); + + assertThat(editTheme.isSystemTheme()).isFalse(); + assertThat(editTheme.getDisplayName()).isEqualTo("Custom edit theme"); + assertThat(editTheme.getWorkspaceId()).isNull(); + assertThat(editTheme.getApplicationId()).isNull(); + + assertThat(publishedTheme.isSystemTheme()).isFalse(); + assertThat(publishedTheme.getDisplayName()).isEqualTo("Custom published theme"); + assertThat(publishedTheme.getWorkspaceId()).isNullOrEmpty(); + assertThat(publishedTheme.getApplicationId()).isNullOrEmpty(); + }) + .verifyComplete(); + } + + @Test + @WithUserDetails(value = "api_user") + public void importApplication_withoutActionCollection_succeedsWithoutError() { + + FilePart filePart = createFilePart("test_assets/ImportExportServiceTest/valid-application-without-action-collection.json"); + + Workspace newWorkspace = new Workspace(); + newWorkspace.setName("Template Workspace"); + + Mono workspaceMono = workspaceService + .create(newWorkspace).cache(); + + final Mono resultMono = workspaceMono + .flatMap(workspace -> importExportApplicationService + .extractFileAndSaveApplication(workspace.getId(), filePart) + ); + + List permissionGroups = workspaceMono + .flatMapMany(savedWorkspace -> { + Set defaultPermissionGroups = savedWorkspace.getDefaultPermissionGroups(); + return permissionGroupRepository.findAllById(defaultPermissionGroups); + }) + .collectList() + .block(); + + PermissionGroup adminPermissionGroup = permissionGroups.stream() + .filter(permissionGroup -> permissionGroup.getName().startsWith(FieldName.ADMINISTRATOR)) + .findFirst().get(); + + PermissionGroup developerPermissionGroup = permissionGroups.stream() + .filter(permissionGroup -> permissionGroup.getName().startsWith(FieldName.DEVELOPER)) + .findFirst().get(); + + PermissionGroup viewerPermissionGroup = permissionGroups.stream() + .filter(permissionGroup -> permissionGroup.getName().startsWith(FieldName.VIEWER)) + .findFirst().get(); + + Policy manageAppPolicy = Policy.builder().permission(MANAGE_APPLICATIONS.getValue()) + .permissionGroups(Set.of(adminPermissionGroup.getId(), developerPermissionGroup.getId())) + .build(); + Policy readAppPolicy = Policy.builder().permission(READ_APPLICATIONS.getValue()) + .permissionGroups(Set.of(adminPermissionGroup.getId(), developerPermissionGroup.getId(), + viewerPermissionGroup.getId())) + .build(); + + StepVerifier + .create(resultMono + .flatMap(applicationImportDTO -> Mono.zip( + Mono.just(applicationImportDTO), + datasourceService.findAllByWorkspaceId(applicationImportDTO.getApplication().getWorkspaceId(), MANAGE_DATASOURCES).collectList(), + getActionsInApplication(applicationImportDTO.getApplication()).collectList(), + newPageService.findByApplicationId(applicationImportDTO.getApplication().getId(), MANAGE_PAGES, false).collectList(), + actionCollectionService.findAllByApplicationIdAndViewMode(applicationImportDTO.getApplication().getId(), false + , MANAGE_ACTIONS, null).collectList() + ))) + .assertNext(tuple -> { + final Application application = tuple.getT1().getApplication(); + final List datasourceList = tuple.getT2(); + final List actionDTOS = tuple.getT3(); + final List pageList = tuple.getT4(); + final List actionCollectionList = tuple.getT5(); + + assertThat(application.getName()).isEqualTo("valid_application"); + assertThat(application.getWorkspaceId()).isNotNull(); + assertThat(application.getPages()).hasSize(2); + assertThat(application.getPolicies()).containsAll(Set.of(manageAppPolicy, readAppPolicy)); + assertThat(application.getPublishedPages()).hasSize(1); + assertThat(application.getModifiedBy()).isEqualTo("api_user"); + assertThat(application.getUpdatedAt()).isNotNull(); + + assertThat(datasourceList).isNotEmpty(); + datasourceList.forEach(datasource -> { + assertThat(datasource.getWorkspaceId()).isEqualTo(application.getWorkspaceId()); + assertThat(datasource.getDatasourceConfiguration()).isNotNull(); + }); + + assertThat(actionDTOS).isNotEmpty(); + actionDTOS.forEach(actionDTO -> { + assertThat(actionDTO.getPageId()).isNotEqualTo(pageList.get(0).getName()); + + }); + + assertThat(actionCollectionList).isEmpty(); + + assertThat(pageList).hasSize(2); + + ApplicationPage defaultAppPage = application.getPages() + .stream() + .filter(ApplicationPage::getIsDefault) + .findFirst() + .orElse(null); + assertThat(defaultAppPage).isNotNull(); + + PageDTO defaultPageDTO = pageList.stream() + .filter(pageDTO -> pageDTO.getId().equals(defaultAppPage.getId())).findFirst().orElse(null); + + assertThat(defaultPageDTO).isNotNull(); + assertThat(defaultPageDTO.getLayouts().get(0).getLayoutOnLoadActions()).isNotEmpty(); + }) + .verifyComplete(); + } + + @Test + @WithUserDetails(value = "api_user") + public void importApplication_WithoutThemes_LegacyThemesAssigned() { + FilePart filePart = createFilePart("test_assets/ImportExportServiceTest/valid-application-without-theme.json"); + + Workspace newWorkspace = new Workspace(); + newWorkspace.setName("Template Workspace"); + + final Mono resultMono = workspaceService.create(newWorkspace) + .flatMap(workspace -> importExportApplicationService + .extractFileAndSaveApplication(workspace.getId(), filePart) + ); + + StepVerifier + .create(resultMono) + .assertNext(applicationImportDTO -> { + assertThat(applicationImportDTO.getApplication().getEditModeThemeId()).isNotEmpty(); + assertThat(applicationImportDTO.getApplication().getPublishedModeThemeId()).isNotEmpty(); + }) + .verifyComplete(); + } + + @Test + @WithUserDetails(value = "api_user") + public void importApplication_withoutPageIdInActionCollection_succeeds() { + + FilePart filePart = createFilePart("test_assets/ImportExportServiceTest/invalid-application-without-pageId-action-collection.json"); + + Workspace newWorkspace = new Workspace(); + newWorkspace.setName("Template Workspace"); + + final Mono resultMono = workspaceService + .create(newWorkspace) + .flatMap(workspace -> importExportApplicationService + .extractFileAndSaveApplication(workspace.getId(), filePart) + ); + + StepVerifier + .create(resultMono + .flatMap(applicationImportDTO -> Mono.zip( + Mono.just(applicationImportDTO), + datasourceService.findAllByWorkspaceId(applicationImportDTO.getApplication().getWorkspaceId(), MANAGE_DATASOURCES).collectList(), + getActionsInApplication(applicationImportDTO.getApplication()).collectList(), + newPageService.findByApplicationId(applicationImportDTO.getApplication().getId(), MANAGE_PAGES, false).collectList(), + actionCollectionService + .findAllByApplicationIdAndViewMode(applicationImportDTO.getApplication().getId(), false, MANAGE_ACTIONS, null).collectList() + ))) + .assertNext(tuple -> { + final Application application = tuple.getT1().getApplication(); + final List datasourceList = tuple.getT2(); + final List actionDTOS = tuple.getT3(); + final List pageList = tuple.getT4(); + final List actionCollectionList = tuple.getT5(); + + + assertThat(datasourceList).isNotEmpty(); + + assertThat(actionDTOS).hasSize(1); + actionDTOS.forEach(actionDTO -> { + assertThat(actionDTO.getPageId()).isNotEqualTo(pageList.get(0).getName()); + + }); + + assertThat(actionCollectionList).isEmpty(); + }) + .verifyComplete(); + } + + @Test + @WithUserDetails(value = "api_user") + public void exportImportApplication_importWithBranchName_updateApplicationResourcesWithBranch() { + Application testApplication = new Application(); + testApplication.setName("Export-Import-Update-Branch_Test-App"); + testApplication.setWorkspaceId(workspaceId); + testApplication.setUpdatedAt(Instant.now()); + testApplication.setLastDeployedAt(Instant.now()); + testApplication.setModifiedBy("some-user"); + testApplication.setGitApplicationMetadata(new GitApplicationMetadata()); + GitApplicationMetadata gitData = new GitApplicationMetadata(); + gitData.setBranchName("testBranch"); + testApplication.setGitApplicationMetadata(gitData); + + Application savedApplication = applicationPageService.createApplication(testApplication, workspaceId) + .flatMap(application1 -> { + application1.getGitApplicationMetadata().setDefaultApplicationId(application1.getId()); + return applicationService.save(application1); + }).block(); + + Mono result = newPageService.findNewPagesByApplicationId(savedApplication.getId(), READ_PAGES).collectList() + .flatMap(newPages -> { + NewPage newPage = newPages.get(0); + + ActionDTO action = new ActionDTO(); + action.setName("validAction"); + action.setPageId(newPage.getId()); + action.setExecuteOnLoad(true); + ActionConfiguration actionConfiguration = new ActionConfiguration(); + actionConfiguration.setHttpMethod(HttpMethod.GET); + action.setActionConfiguration(actionConfiguration); + action.setDatasource(datasourceMap.get("DS1")); + return layoutActionService.createAction(action) + .flatMap(createdAction -> newActionService.findById(createdAction.getId(), READ_ACTIONS)); + }) + .then(importExportApplicationService.exportApplicationById(savedApplication.getId(), SerialiseApplicationObjective.VERSION_CONTROL) + .flatMap(applicationJson -> importExportApplicationService.importApplicationInWorkspace(workspaceId, applicationJson, savedApplication.getId(), gitData.getBranchName()))) + .cache(); + + Mono> updatedPagesMono = result.then(newPageService.findNewPagesByApplicationId(savedApplication.getId(), READ_PAGES).collectList()); + + Mono> updatedActionsMono = result.then(newActionService.findAllByApplicationIdAndViewMode(savedApplication.getId(), false, READ_PAGES, null).collectList()); + + StepVerifier + .create(Mono.zip(result, updatedPagesMono, updatedActionsMono)) + .assertNext(tuple -> { + Application application = tuple.getT1(); + List pageList = tuple.getT2(); + List actionList = tuple.getT3(); + + final String branchName = application.getGitApplicationMetadata().getBranchName(); + pageList.forEach(page -> { + assertThat(page.getDefaultResources()).isNotNull(); + assertThat(page.getDefaultResources().getBranchName()).isEqualTo(branchName); + }); + + actionList.forEach(action -> { + assertThat(action.getDefaultResources()).isNotNull(); + assertThat(action.getDefaultResources().getBranchName()).isEqualTo(branchName); + }); + }) + .verifyComplete(); + } + + @Test + @WithUserDetails(value = "api_user") + public void importApplication_incompatibleJsonFile_throwException() { + FilePart filePart = createFilePart("test_assets/ImportExportServiceTest/incompatible_version.json"); + Mono resultMono = importExportApplicationService.extractFileAndSaveApplication(workspaceId, filePart); + + StepVerifier + .create(resultMono) + .expectErrorMatches(throwable -> throwable instanceof AppsmithException && + throwable.getMessage().equals(AppsmithError.INCOMPATIBLE_IMPORTED_JSON.getMessage())) + .verify(); + } + + @Test + @WithUserDetails(value = "api_user") + public void importApplication_withUnConfiguredDatasources_Success() { + FilePart filePart = createFilePart("test_assets/ImportExportServiceTest/valid-application-with-un-configured-datasource.json"); + + Workspace newWorkspace = new Workspace(); + newWorkspace.setName("Template Workspace"); + + Mono workspaceMono = workspaceService + .create(newWorkspace).cache(); + + final Mono resultMono = workspaceMono + .flatMap(workspace -> importExportApplicationService + .extractFileAndSaveApplication(workspace.getId(), filePart) + ); + + List permissionGroups = workspaceMono + .flatMapMany(savedWorkspace -> { + Set defaultPermissionGroups = savedWorkspace.getDefaultPermissionGroups(); + return permissionGroupRepository.findAllById(defaultPermissionGroups); + }) + .collectList() + .block(); + + PermissionGroup adminPermissionGroup = permissionGroups.stream() + .filter(permissionGroup -> permissionGroup.getName().startsWith(FieldName.ADMINISTRATOR)) + .findFirst().get(); + + PermissionGroup developerPermissionGroup = permissionGroups.stream() + .filter(permissionGroup -> permissionGroup.getName().startsWith(FieldName.DEVELOPER)) + .findFirst().get(); + + PermissionGroup viewerPermissionGroup = permissionGroups.stream() + .filter(permissionGroup -> permissionGroup.getName().startsWith(FieldName.VIEWER)) + .findFirst().get(); + + Policy manageAppPolicy = Policy.builder().permission(MANAGE_APPLICATIONS.getValue()) + .permissionGroups(Set.of(adminPermissionGroup.getId(), developerPermissionGroup.getId())) + .build(); + Policy readAppPolicy = Policy.builder().permission(READ_APPLICATIONS.getValue()) + .permissionGroups(Set.of(adminPermissionGroup.getId(), developerPermissionGroup.getId(), + viewerPermissionGroup.getId())) + .build(); + + StepVerifier + .create(resultMono + .flatMap(applicationImportDTO -> { + Application application = applicationImportDTO.getApplication(); + return Mono.zip( + Mono.just(applicationImportDTO), + datasourceService.findAllByWorkspaceId(application.getWorkspaceId(), MANAGE_DATASOURCES).collectList(), + newActionService.findAllByApplicationIdAndViewMode(application.getId(), false, READ_ACTIONS, null).collectList(), + newPageService.findByApplicationId(application.getId(), MANAGE_PAGES, false).collectList(), + actionCollectionService.findAllByApplicationIdAndViewMode(application.getId(), false, MANAGE_ACTIONS, null).collectList() + ); + })) + .assertNext(tuple -> { + final Application application = tuple.getT1().getApplication(); + final List unConfiguredDatasourceList = tuple.getT1().getUnConfiguredDatasourceList(); + final boolean isPartialImport = tuple.getT1().getIsPartialImport(); + final List datasourceList = tuple.getT2(); + final List actionList = tuple.getT3(); + final List pageList = tuple.getT4(); + final List actionCollectionList = tuple.getT5(); + + assertThat(application.getName()).isEqualTo("importExportTest"); + assertThat(application.getWorkspaceId()).isNotNull(); + assertThat(application.getPages()).hasSize(1); + assertThat(application.getPolicies()).containsAll(Set.of(manageAppPolicy, readAppPolicy)); + assertThat(application.getPublishedPages()).hasSize(1); + assertThat(application.getModifiedBy()).isEqualTo("api_user"); + assertThat(application.getUpdatedAt()).isNotNull(); + assertThat(application.getEditModeThemeId()).isNotNull(); + assertThat(application.getPublishedModeThemeId()).isNotNull(); + assertThat(isPartialImport).isEqualTo(Boolean.TRUE); + assertThat(unConfiguredDatasourceList.size()).isNotEqualTo(0); + + assertThat(datasourceList).isNotEmpty(); + List datasourceNames = unConfiguredDatasourceList.stream().map(Datasource::getName).collect(Collectors.toList()); + assertThat(datasourceNames).contains("mongoDatasource", "postgresTest"); + + List collectionIdInAction = new ArrayList<>(); + assertThat(actionList).isNotEmpty(); + actionList.forEach(newAction -> { + ActionDTO actionDTO = newAction.getUnpublishedAction(); + assertThat(actionDTO.getPageId()).isNotEqualTo(pageList.get(0).getName()); + if (!StringUtils.isEmpty(actionDTO.getCollectionId())) { + collectionIdInAction.add(actionDTO.getCollectionId()); + } + }); + + assertThat(actionCollectionList).isEmpty(); + + assertThat(pageList).hasSize(1); + + ApplicationPage defaultAppPage = application.getPages() + .stream() + .filter(ApplicationPage::getIsDefault) + .findFirst() + .orElse(null); + assertThat(defaultAppPage).isNotNull(); + + PageDTO defaultPageDTO = pageList.stream() + .filter(pageDTO -> pageDTO.getId().equals(defaultAppPage.getId())).findFirst().orElse(null); + + assertThat(defaultPageDTO).isNotNull(); + }) + .verifyComplete(); + } + + public void importApplicationIntoWorkspace_pageRemovedAndUpdatedDefaultPageNameInBranchApplication_Success() { + Application testApplication = new Application(); + testApplication.setName("importApplicationIntoWorkspace_pageRemovedInBranchApplication_Success"); + testApplication.setWorkspaceId(workspaceId); + testApplication.setUpdatedAt(Instant.now()); + testApplication.setLastDeployedAt(Instant.now()); + testApplication.setModifiedBy("some-user"); + testApplication.setGitApplicationMetadata(new GitApplicationMetadata()); + GitApplicationMetadata gitData = new GitApplicationMetadata(); + gitData.setBranchName("master"); + testApplication.setGitApplicationMetadata(gitData); + + Application application = applicationPageService.createApplication(testApplication, workspaceId) + .flatMap(application1 -> { + application1.getGitApplicationMetadata().setDefaultApplicationId(application1.getId()); + return applicationService.save(application1); + }).block(); + String gitSyncIdBeforeImport = newPageService.findById(application.getPages().get(0).getId(), MANAGE_PAGES).block().getGitSyncId(); + + PageDTO page = new PageDTO(); + page.setName("Page 2"); + page.setApplicationId(application.getId()); + PageDTO savedPage = applicationPageService.createPage(page).block(); + + assert application.getId() != null; + Set applicationPageIdsBeforeImport = Objects.requireNonNull(applicationRepository.findById(application.getId()).block()) + .getPages() + .stream() + .map(ApplicationPage::getId) + .collect(Collectors.toSet()); + + ApplicationJson applicationJson = createAppJson("test_assets/ImportExportServiceTest/valid-application-with-page-removed.json").block(); + applicationJson.getPageList().get(0).setGitSyncId(gitSyncIdBeforeImport); + + Application importedApplication = importExportApplicationService.importApplicationInWorkspace(workspaceId, applicationJson, application.getId(), "master").block(); + + assert importedApplication != null; + Mono> pageList = Flux.fromIterable( + importedApplication + .getPages() + .stream() + .map(ApplicationPage::getId) + .collect(Collectors.toList()) + ).flatMap(s -> newPageService.findById(s, MANAGE_PAGES)).collectList(); + + StepVerifier + .create(pageList) + .assertNext(newPages -> { + // Check before import we had both the pages + assertThat(applicationPageIdsBeforeImport).hasSize(2); + assertThat(applicationPageIdsBeforeImport).contains(savedPage.getId()); + + assertThat(newPages.size()).isEqualTo(1); + assertThat(importedApplication.getPages().size()).isEqualTo(1); + assertThat(importedApplication.getPages().get(0).getId()).isEqualTo(newPages.get(0).getId()); + assertThat(newPages.get(0).getPublishedPage().getName()).isEqualTo("importedPage"); + assertThat(newPages.get(0).getGitSyncId()).isEqualTo(gitSyncIdBeforeImport); + + }) + .verifyComplete(); + } + + @Test + @WithUserDetails(value = "api_user") + public void importApplicationIntoWorkspace_pageAddedInBranchApplication_Success() { + Application testApplication = new Application(); + testApplication.setName("importApplicationIntoWorkspace_pageAddedInBranchApplication_Success"); + testApplication.setWorkspaceId(workspaceId); + testApplication.setUpdatedAt(Instant.now()); + testApplication.setLastDeployedAt(Instant.now()); + testApplication.setModifiedBy("some-user"); + testApplication.setGitApplicationMetadata(new GitApplicationMetadata()); + GitApplicationMetadata gitData = new GitApplicationMetadata(); + gitData.setBranchName("master"); + testApplication.setGitApplicationMetadata(gitData); + + Application application = applicationPageService.createApplication(testApplication, workspaceId) + .flatMap(application1 -> { + application1.getGitApplicationMetadata().setDefaultApplicationId(application1.getId()); + return applicationService.save(application1); + }).block(); + + String gitSyncIdBeforeImport = newPageService.findById(application.getPages().get(0).getId(), MANAGE_PAGES).block().getGitSyncId(); + + assert application.getId() != null; + Set applicationPageIdsBeforeImport = Objects.requireNonNull(applicationRepository.findById(application.getId()).block()) + .getPages() + .stream() + .map(ApplicationPage::getId) + .collect(Collectors.toSet()); + + ApplicationJson applicationJson = createAppJson("test_assets/ImportExportServiceTest/valid-application-with-page-added.json").block(); + applicationJson.getPageList().get(0).setGitSyncId(gitSyncIdBeforeImport); + + Application applicationMono = importExportApplicationService.importApplicationInWorkspace(workspaceId, applicationJson, application.getId(), "master").block(); + + Mono> pageList = Flux.fromIterable( + applicationMono.getPages() + .stream() + .map(ApplicationPage::getId) + .collect(Collectors.toList()) + ).flatMap(s -> newPageService.findById(s, MANAGE_PAGES)).collectList(); + + StepVerifier + .create(pageList) + .assertNext(newPages -> { + // Check before import we had both the pages + assertThat(applicationPageIdsBeforeImport).hasSize(1); + assertThat(newPages.size()).isEqualTo(3); + List pageNames = newPages.stream().map(newPage -> newPage.getUnpublishedPage().getName()).collect(Collectors.toList()); + assertThat(pageNames).contains("Page1"); + assertThat(pageNames).contains("Page2"); + assertThat(pageNames).contains("Page3"); + }) + .verifyComplete(); + + } + + @Test + @WithUserDetails(value = "api_user") + public void importUpdatedApplicationIntoWorkspaceFromFile_publicApplication_visibilityFlagNotReset() { + // Create a application and make it public + // Now add a page and export the same import it to the app + // Check if the policies and visibility flag are not reset + + Mono workspaceResponse = workspaceService.findById(workspaceId, READ_WORKSPACES); + + List permissionGroups = workspaceResponse + .flatMapMany(savedWorkspace -> { + Set defaultPermissionGroups = savedWorkspace.getDefaultPermissionGroups(); + return permissionGroupRepository.findAllById(defaultPermissionGroups); + }) + .collectList() + .block(); + + PermissionGroup adminPermissionGroup = permissionGroups.stream() + .filter(permissionGroup -> permissionGroup.getName().startsWith(FieldName.ADMINISTRATOR)) + .findFirst().get(); + + PermissionGroup developerPermissionGroup = permissionGroups.stream() + .filter(permissionGroup -> permissionGroup.getName().startsWith(FieldName.DEVELOPER)) + .findFirst().get(); + + PermissionGroup viewerPermissionGroup = permissionGroups.stream() + .filter(permissionGroup -> permissionGroup.getName().startsWith(FieldName.VIEWER)) + .findFirst().get(); + + Application testApplication = new Application(); + testApplication.setName("importUpdatedApplicationIntoWorkspaceFromFile_publicApplication_visibilityFlagNotReset"); + testApplication.setWorkspaceId(workspaceId); + testApplication.setUpdatedAt(Instant.now()); + testApplication.setLastDeployedAt(Instant.now()); + testApplication.setModifiedBy("some-user"); + testApplication.setGitApplicationMetadata(new GitApplicationMetadata()); + GitApplicationMetadata gitData = new GitApplicationMetadata(); + gitData.setBranchName("master"); + testApplication.setGitApplicationMetadata(gitData); + + Application application = applicationPageService.createApplication(testApplication, workspaceId) + .flatMap(application1 -> { + application1.getGitApplicationMetadata().setDefaultApplicationId(application1.getId()); + return applicationService.save(application1); + }).block(); + ApplicationAccessDTO applicationAccessDTO = new ApplicationAccessDTO(); + applicationAccessDTO.setPublicAccess(true); + Application newApplication = applicationService.changeViewAccess(application.getId(), "master", applicationAccessDTO).block(); + + PermissionGroup anonymousPermissionGroup = permissionGroupService.getPublicPermissionGroup().block(); + + Policy manageAppPolicy = Policy.builder().permission(MANAGE_APPLICATIONS.getValue()) + .permissionGroups(Set.of(adminPermissionGroup.getId(), developerPermissionGroup.getId())) + .build(); + Policy readAppPolicy = Policy.builder().permission(READ_APPLICATIONS.getValue()) + .permissionGroups(Set.of(adminPermissionGroup.getId(), developerPermissionGroup.getId(), + viewerPermissionGroup.getId(), anonymousPermissionGroup.getId())) + .build(); + + Mono applicationMono = importExportApplicationService.exportApplicationById(application.getId(), "master") + .flatMap(applicationJson -> importExportApplicationService.importApplicationInWorkspace(workspaceId, applicationJson, application.getId(), "master")); + + StepVerifier + .create(applicationMono) + .assertNext(application1 -> { + assertThat(application1.getIsPublic()).isEqualTo(Boolean.TRUE); + assertThat(application1.getPolicies()).containsAll(Set.of(manageAppPolicy, readAppPolicy)); + }) + .verifyComplete(); + } + + /** + * Testcase for checking the discard changes flow for following events: + * 1. Import application in org + * 2. Add new page to the imported application + * 3. User tries to import application from same application json file + * 4. Added page will be removed + */ + @Test + @WithUserDetails(value = "api_user") + public void discardChange_addNewPageAfterImport_addedPageRemoved() { + + /* + 1. Import application + 2. Add single page to imported app + 3. Import the application from same JSON with applicationId + 4. Added page should be deleted from DB + */ + Mono applicationJsonMono = createAppJson("test_assets/ImportExportServiceTest/valid-application.json"); + String workspaceId = createTemplateWorkspace().getId(); + final Mono resultMonoWithoutDiscardOperation = applicationJsonMono + .flatMap(applicationJson -> { + applicationJson.getExportedApplication().setName("discard-change-page-added"); + return importExportApplicationService.importApplicationInWorkspace(workspaceId, applicationJson); + }) + .flatMap(application -> { + PageDTO page = new PageDTO(); + page.setName("discard-page-test"); + page.setApplicationId(application.getId()); + return applicationPageService.createPage(page); + }) + .flatMap(page -> applicationRepository.findById(page.getApplicationId())) + .cache(); + + StepVerifier + .create(resultMonoWithoutDiscardOperation + .flatMap(application -> Mono.zip( + Mono.just(application), + newPageService.findByApplicationId(application.getId(), MANAGE_PAGES, false).collectList() + ))) + .assertNext(tuple -> { + final Application application = tuple.getT1(); + final List pageList = tuple.getT2(); + + assertThat(application.getName()).isEqualTo("discard-change-page-added"); + assertThat(application.getWorkspaceId()).isNotNull(); + assertThat(application.getPages()).hasSize(3); + assertThat(application.getPublishedPages()).hasSize(1); + assertThat(application.getModifiedBy()).isEqualTo("api_user"); + assertThat(application.getUpdatedAt()).isNotNull(); + assertThat(application.getEditModeThemeId()).isNotNull(); + assertThat(application.getPublishedModeThemeId()).isNotNull(); + + assertThat(pageList).hasSize(3); + + ApplicationPage defaultAppPage = application.getPages() + .stream() + .filter(ApplicationPage::getIsDefault) + .findFirst() + .orElse(null); + assertThat(defaultAppPage).isNotNull(); + + PageDTO defaultPageDTO = pageList.stream() + .filter(pageDTO -> pageDTO.getId().equals(defaultAppPage.getId())).findFirst().orElse(null); + + assertThat(defaultPageDTO).isNotNull(); + assertThat(defaultPageDTO.getLayouts().get(0).getLayoutOnLoadActions()).isNotEmpty(); + + List pageNames = new ArrayList<>(); + pageList.forEach(page -> pageNames.add(page.getName())); + assertThat(pageNames).contains("discard-page-test"); + }) + .verifyComplete(); + + // Import the same application again to find if the added page is deleted + final Mono resultMonoWithDiscardOperation = resultMonoWithoutDiscardOperation + .flatMap(importedApplication -> + applicationJsonMono + .flatMap(applicationJson -> + { + importedApplication.setGitApplicationMetadata(new GitApplicationMetadata()); + importedApplication.getGitApplicationMetadata().setDefaultApplicationId(importedApplication.getId()); + return applicationService.save(importedApplication) + .then(importExportApplicationService.importApplicationInWorkspace( + importedApplication.getWorkspaceId(), + applicationJson, + importedApplication.getId(), + "main") + ); + } + ) + ); + + StepVerifier + .create(resultMonoWithDiscardOperation + .flatMap(application -> Mono.zip( + Mono.just(application), + newPageService.findByApplicationId(application.getId(), MANAGE_PAGES, false).collectList() + ))) + .assertNext(tuple -> { + final Application application = tuple.getT1(); + final List pageList = tuple.getT2(); + + assertThat(application.getPages()).hasSize(2); + assertThat(application.getPublishedPages()).hasSize(1); + + assertThat(pageList).hasSize(2); + + List pageNames = new ArrayList<>(); + pageList.forEach(page -> pageNames.add(page.getName())); + assertThat(pageNames).doesNotContain("discard-page-test"); + }) + .verifyComplete(); + } + + /** + * Testcase for checking the discard changes flow for following events: + * 1. Import application in org + * 2. Add new action to the imported application + * 3. User tries to import application from same application json file + * 4. Added action will be removed + */ + @Test + @WithUserDetails(value = "api_user") + public void discardChange_addNewActionAfterImport_addedActionRemoved() { + + Mono applicationJsonMono = createAppJson("test_assets/ImportExportServiceTest/valid-application.json"); + String workspaceId = createTemplateWorkspace().getId(); + + final Mono resultMonoWithoutDiscardOperation = applicationJsonMono + .flatMap(applicationJson -> { + applicationJson.getExportedApplication().setName("discard-change-action-added"); + return importExportApplicationService.importApplicationInWorkspace(workspaceId, applicationJson); + }) + .flatMap(application -> { + ActionDTO action = new ActionDTO(); + ActionConfiguration actionConfiguration = new ActionConfiguration(); + actionConfiguration.setHttpMethod(HttpMethod.GET); + action.setActionConfiguration(actionConfiguration); + action.setDatasource(datasourceMap.get("DS1")); + action.setName("discard-action-test"); + action.setPageId(application.getPages().get(0).getId()); + return layoutActionService.createAction(action); + }) + .flatMap(actionDTO -> newActionService.getById(actionDTO.getId())) + .flatMap(newAction -> applicationRepository.findById(newAction.getApplicationId())) + .cache(); + + StepVerifier + .create(resultMonoWithoutDiscardOperation + .flatMap(application -> Mono.zip( + Mono.just(application), + getActionsInApplication(application).collectList() + ))) + .assertNext(tuple -> { + final Application application = tuple.getT1(); + final List actionList = tuple.getT2(); + + assertThat(application.getName()).isEqualTo("discard-change-action-added"); + assertThat(application.getWorkspaceId()).isNotNull(); + + + List actionNames = new ArrayList<>(); + actionList.forEach(actionDTO -> actionNames.add(actionDTO.getName())); + assertThat(actionNames).contains("discard-action-test"); + }) + .verifyComplete(); + + // Import the same application again + final Mono resultMonoWithDiscardOperation = resultMonoWithoutDiscardOperation + .flatMap(importedApplication -> + applicationJsonMono + .flatMap(applicationJson -> { + importedApplication.setGitApplicationMetadata(new GitApplicationMetadata()); + importedApplication.getGitApplicationMetadata().setDefaultApplicationId(importedApplication.getId()); + return applicationService.save(importedApplication) + .then(importExportApplicationService.importApplicationInWorkspace( + importedApplication.getWorkspaceId(), + applicationJson, + importedApplication.getId(), + "main") + ); + } + ) + ); + + StepVerifier + .create(resultMonoWithDiscardOperation + .flatMap(application -> Mono.zip( + Mono.just(application), + getActionsInApplication(application).collectList() + ))) + .assertNext(tuple -> { + final Application application = tuple.getT1(); + final List actionList = tuple.getT2(); + + assertThat(application.getWorkspaceId()).isNotNull(); + + List actionNames = new ArrayList<>(); + actionList.forEach(actionDTO -> actionNames.add(actionDTO.getName())); + assertThat(actionNames).doesNotContain("discard-action-test"); + }) + .verifyComplete(); + } + + /** + * Testcase for checking the discard changes flow for following events: + * 1. Import application in org + * 2. Add actionCollection to the imported application + * 3. User tries to import application from same application json file + * 4. Added actionCollection will be removed + */ + @Test + @WithUserDetails(value = "api_user") + public void discardChange_addNewActionCollectionAfterImport_addedActionCollectionRemoved() { + + Mono applicationJsonMono = createAppJson("test_assets/ImportExportServiceTest/valid-application-without-action-collection.json"); + String workspaceId = createTemplateWorkspace().getId(); + final Mono resultMonoWithoutDiscardOperation = applicationJsonMono + .flatMap(applicationJson -> { + applicationJson.getExportedApplication().setName("discard-change-collection-added"); + return importExportApplicationService.importApplicationInWorkspace(workspaceId, applicationJson); + }) + .flatMap(application -> { + ActionCollectionDTO actionCollectionDTO1 = new ActionCollectionDTO(); + actionCollectionDTO1.setName("discard-action-collection-test"); + actionCollectionDTO1.setPageId(application.getPages().get(0).getId()); + actionCollectionDTO1.setApplicationId(application.getId()); + actionCollectionDTO1.setWorkspaceId(application.getWorkspaceId()); + actionCollectionDTO1.setPluginId(jsDatasource.getPluginId()); + ActionDTO action1 = new ActionDTO(); + action1.setName("discard-action-collection-test-action"); + action1.setActionConfiguration(new ActionConfiguration()); + action1.getActionConfiguration().setBody("mockBody"); + actionCollectionDTO1.setActions(List.of(action1)); + actionCollectionDTO1.setPluginType(PluginType.JS); + + return layoutCollectionService.createCollection(actionCollectionDTO1); + }) + .flatMap(actionCollectionDTO -> actionCollectionService.getById(actionCollectionDTO.getId())) + .flatMap(actionCollection -> applicationRepository.findById(actionCollection.getApplicationId())) + .cache(); + + StepVerifier + .create(resultMonoWithoutDiscardOperation + .flatMap(application -> Mono.zip( + Mono.just(application), + actionCollectionService.findAllByApplicationIdAndViewMode(application.getId(), false, READ_ACTIONS, null).collectList(), + getActionsInApplication(application).collectList() + ))) + .assertNext(tuple -> { + final Application application = tuple.getT1(); + final List actionCollectionList = tuple.getT2(); + final List actionList = tuple.getT3(); + + assertThat(application.getName()).isEqualTo("discard-change-collection-added"); + assertThat(application.getWorkspaceId()).isNotNull(); + + List actionCollectionNames = new ArrayList<>(); + actionCollectionList.forEach(actionCollection -> actionCollectionNames.add(actionCollection.getUnpublishedCollection().getName())); + assertThat(actionCollectionNames).contains("discard-action-collection-test"); + + List actionNames = new ArrayList<>(); + actionList.forEach(actionDTO -> actionNames.add(actionDTO.getName())); + assertThat(actionNames).contains("discard-action-collection-test-action"); + }) + .verifyComplete(); + + // Import the same application again + final Mono resultMonoWithDiscardOperation = resultMonoWithoutDiscardOperation + .flatMap(importedApplication -> + applicationJsonMono + .flatMap(applicationJson -> + { + importedApplication.setGitApplicationMetadata(new GitApplicationMetadata()); + importedApplication.getGitApplicationMetadata().setDefaultApplicationId(importedApplication.getId()); + return applicationService.save(importedApplication) + .then(importExportApplicationService.importApplicationInWorkspace( + importedApplication.getWorkspaceId(), + applicationJson, + importedApplication.getId(), + "main") + ); + } + ) + ); + + StepVerifier + .create(resultMonoWithDiscardOperation + .flatMap(application -> Mono.zip( + Mono.just(application), + actionCollectionService.findAllByApplicationIdAndViewMode(application.getId(), false, READ_ACTIONS, null).collectList(), + getActionsInApplication(application).collectList() + ))) + .assertNext(tuple -> { + final Application application = tuple.getT1(); + final List actionCollectionList = tuple.getT2(); + final List actionList = tuple.getT3(); + + assertThat(application.getWorkspaceId()).isNotNull(); + + List actionCollectionNames = new ArrayList<>(); + actionCollectionList.forEach(actionCollection -> actionCollectionNames.add(actionCollection.getUnpublishedCollection().getName())); + assertThat(actionCollectionNames).doesNotContain("discard-action-collection-test"); + + List actionNames = new ArrayList<>(); + actionList.forEach(actionDTO -> actionNames.add(actionDTO.getName())); + assertThat(actionNames).doesNotContain("discard-action-collection-test-action"); + }) + .verifyComplete(); + } + + /** + * Testcase for checking the discard changes flow for following events: + * 1. Import application in org + * 2. Remove existing page from imported application + * 3. Import application from same application json file + * 4. Removed page will be restored + */ + @Test + @WithUserDetails(value = "api_user") + public void discardChange_removeNewPageAfterImport_removedPageRestored() { + + Mono applicationJsonMono = createAppJson("test_assets/ImportExportServiceTest/valid-application.json"); + String workspaceId = createTemplateWorkspace().getId(); + final Mono resultMonoWithoutDiscardOperation = applicationJsonMono + .flatMap(applicationJson -> { + applicationJson.getExportedApplication().setName("discard-change-page-removed"); + return importExportApplicationService.importApplicationInWorkspace(workspaceId, applicationJson); + }) + .flatMap(application -> { + Optional applicationPage = application + .getPages() + .stream() + .filter(page -> !page.isDefault()) + .findFirst(); + return applicationPageService.deleteUnpublishedPage(applicationPage.get().getId()); + }) + .flatMap(page -> applicationRepository.findById(page.getApplicationId())) + .cache(); + + StepVerifier + .create(resultMonoWithoutDiscardOperation + .flatMap(application -> Mono.zip( + Mono.just(application), + newPageService.findByApplicationId(application.getId(), MANAGE_PAGES, false).collectList() + ))) + .assertNext(tuple -> { + final Application application = tuple.getT1(); + final List pageList = tuple.getT2(); + + assertThat(application.getName()).isEqualTo("discard-change-page-removed"); + assertThat(application.getWorkspaceId()).isNotNull(); + assertThat(application.getPages()).hasSize(1); + + assertThat(pageList).hasSize(1); + }) + .verifyComplete(); + + // Import the same application again + final Mono resultMonoWithDiscardOperation = resultMonoWithoutDiscardOperation + .flatMap(importedApplication -> + applicationJsonMono + .flatMap(applicationJson -> + { + importedApplication.setGitApplicationMetadata(new GitApplicationMetadata()); + importedApplication.getGitApplicationMetadata().setDefaultApplicationId(importedApplication.getId()); + return applicationService.save(importedApplication) + .then(importExportApplicationService.importApplicationInWorkspace( + importedApplication.getWorkspaceId(), + applicationJson, + importedApplication.getId(), + "main") + ); + } + ) + ); + + StepVerifier + .create(resultMonoWithDiscardOperation + .flatMap(application -> Mono.zip( + Mono.just(application), + newPageService.findByApplicationId(application.getId(), MANAGE_PAGES, false).collectList() + ))) + .assertNext(tuple -> { + final Application application = tuple.getT1(); + final List pageList = tuple.getT2(); + + assertThat(application.getPages()).hasSize(2); + assertThat(application.getPublishedPages()).hasSize(1); + + assertThat(pageList).hasSize(2); + }) + .verifyComplete(); + } + + /** + * Testcase for checking the discard changes flow for following events: + * 1. Import application in org + * 2. Remove existing action from imported application + * 3. Import application from same application json file + * 4. Removed action will be restored + */ + @Test + @WithUserDetails(value = "api_user") + public void discardChange_removeNewActionAfterImport_removedActionRestored() { + + Mono applicationJsonMono = createAppJson("test_assets/ImportExportServiceTest/valid-application.json"); + String workspaceId = createTemplateWorkspace().getId(); + final String[] deletedActionName = new String[1]; + final Mono resultMonoWithoutDiscardOperation = applicationJsonMono + .flatMap(applicationJson -> { + applicationJson.getExportedApplication().setName("discard-change-action-removed"); + return importExportApplicationService.importApplicationInWorkspace(workspaceId, applicationJson); + }) + .flatMap(application -> { + return getActionsInApplication(application) + .next() + .flatMap(actionDTO -> { + deletedActionName[0] = actionDTO.getName(); + return newActionService.deleteUnpublishedAction(actionDTO.getId()); + }) + .then(applicationPageService.publish(application.getId(), true)); + }) + .cache(); + + StepVerifier + .create(resultMonoWithoutDiscardOperation + .flatMap(application -> Mono.zip( + Mono.just(application), + getActionsInApplication(application).collectList() + ))) + .assertNext(tuple -> { + final Application application = tuple.getT1(); + final List actionList = tuple.getT2(); + + assertThat(application.getName()).isEqualTo("discard-change-action-removed"); + assertThat(application.getWorkspaceId()).isNotNull(); + + List actionNames = new ArrayList<>(); + actionList.forEach(actionDTO -> actionNames.add(actionDTO.getName())); + assertThat(actionNames).doesNotContain(deletedActionName[0]); + }) + .verifyComplete(); + + // Import the same application again + final Mono resultMonoWithDiscardOperation = resultMonoWithoutDiscardOperation + .flatMap(importedApplication -> + applicationJsonMono + .flatMap(applicationJson -> + { + importedApplication.setGitApplicationMetadata(new GitApplicationMetadata()); + importedApplication.getGitApplicationMetadata().setDefaultApplicationId(importedApplication.getId()); + return applicationService.save(importedApplication) + .then(importExportApplicationService.importApplicationInWorkspace( + importedApplication.getWorkspaceId(), + applicationJson, + importedApplication.getId(), + "main") + ); + } + ) + ); + + StepVerifier + .create(resultMonoWithDiscardOperation + .flatMap(application -> Mono.zip( + Mono.just(application), + getActionsInApplication(application).collectList() + ))) + .assertNext(tuple -> { + final Application application = tuple.getT1(); + final List actionList = tuple.getT2(); + + assertThat(application.getWorkspaceId()).isNotNull(); + + List actionNames = new ArrayList<>(); + actionList.forEach(actionDTO -> actionNames.add(actionDTO.getName())); + assertThat(actionNames).contains(deletedActionName[0]); + }) + .verifyComplete(); + } + + /** + * Testcase for checking the discard changes flow for following events: + * 1. Import application in org + * 2. Remove existing actionCollection from imported application + * 3. Import application from same application json file + * 4. Removed actionCollection along-with actions will be restored + */ + @Test + @WithUserDetails(value = "api_user") + public void discardChange_removeNewActionCollection_removedActionCollectionRestored() { + + Mono applicationJsonMono = createAppJson("test_assets/ImportExportServiceTest/valid-application.json"); + String workspaceId = createTemplateWorkspace().getId(); + final String[] deletedActionCollectionNames = new String[1]; + final Mono resultMonoWithoutDiscardOperation = applicationJsonMono + .flatMap(applicationJson -> { + applicationJson.getExportedApplication().setName("discard-change-collection-removed"); + return importExportApplicationService.importApplicationInWorkspace(workspaceId, applicationJson); + }) + .flatMap(application -> { + return actionCollectionService.findAllByApplicationIdAndViewMode(application.getId(), false, READ_ACTIONS, null) + .next() + .flatMap(actionCollection -> { + deletedActionCollectionNames[0] = actionCollection.getUnpublishedCollection().getName(); + return actionCollectionService.deleteUnpublishedActionCollection(actionCollection.getId()); + }) + .then(applicationPageService.publish(application.getId(), true)); + }) + .cache(); + + StepVerifier + .create(resultMonoWithoutDiscardOperation + .flatMap(application -> Mono.zip( + Mono.just(application), + actionCollectionService.findAllByApplicationIdAndViewMode(application.getId(), false, READ_ACTIONS, null).collectList() + ))) + .assertNext(tuple -> { + final Application application = tuple.getT1(); + final List actionCollectionList = tuple.getT2(); + + assertThat(application.getName()).isEqualTo("discard-change-collection-removed"); + assertThat(application.getWorkspaceId()).isNotNull(); + + List actionCollectionNames = new ArrayList<>(); + actionCollectionList.forEach(actionCollection -> actionCollectionNames.add(actionCollection.getUnpublishedCollection().getName())); + assertThat(actionCollectionNames).doesNotContain(deletedActionCollectionNames); + }) + .verifyComplete(); + + // Import the same application again + final Mono resultMonoWithDiscardOperation = resultMonoWithoutDiscardOperation + .flatMap(importedApplication -> + applicationJsonMono + .flatMap(applicationJson -> + { + importedApplication.setGitApplicationMetadata(new GitApplicationMetadata()); + importedApplication.getGitApplicationMetadata().setDefaultApplicationId(importedApplication.getId()); + return applicationService.save(importedApplication) + .then(importExportApplicationService.importApplicationInWorkspace( + importedApplication.getWorkspaceId(), + applicationJson, + importedApplication.getId(), + "main") + ); + } + ) + ); + + StepVerifier + .create(resultMonoWithDiscardOperation + .flatMap(application -> Mono.zip( + Mono.just(application), + actionCollectionService.findAllByApplicationIdAndViewMode(application.getId(), false, READ_ACTIONS, null).collectList() + ))) + .assertNext(tuple -> { + final Application application = tuple.getT1(); + final List actionCollectionList = tuple.getT2(); + + assertThat(application.getWorkspaceId()).isNotNull(); + + List actionCollectionNames = new ArrayList<>(); + actionCollectionList.forEach(actionCollection -> actionCollectionNames.add(actionCollection.getUnpublishedCollection().getName())); + assertThat(actionCollectionNames).contains(deletedActionCollectionNames); + }) + .verifyComplete(); + } + + @Test + @WithUserDetails(value = "api_user") + public void applySchemaMigration_jsonFileWithFirstVersion_migratedToLatestVersionSuccess() { + FilePart filePart = createFilePart("test_assets/ImportExportServiceTest/file-with-v1.json"); + + Mono stringifiedFile = DataBufferUtils.join(filePart.content()) + .map(dataBuffer -> { + byte[] data = new byte[dataBuffer.readableByteCount()]; + dataBuffer.read(data); + DataBufferUtils.release(dataBuffer); + return new String(data); + }); + Mono v1ApplicationMono = stringifiedFile + .map(data -> { + Gson gson = new Gson(); + return gson.fromJson(data, ApplicationJson.class); + }).cache(); + + Mono migratedApplicationMono = v1ApplicationMono + .map(applicationJson -> { + ApplicationJson applicationJson1 = new ApplicationJson(); + AppsmithBeanUtils.copyNestedNonNullProperties(applicationJson, applicationJson1); + return JsonSchemaMigration.migrateApplicationToLatestSchema(applicationJson1); + }); + + StepVerifier + .create(Mono.zip(v1ApplicationMono, migratedApplicationMono)) + .assertNext(tuple -> { + ApplicationJson v1ApplicationJson = tuple.getT1(); + ApplicationJson latestApplicationJson = tuple.getT2(); + + assertThat(v1ApplicationJson.getServerSchemaVersion()).isEqualTo(1); + assertThat(v1ApplicationJson.getClientSchemaVersion()).isEqualTo(1); + + assertThat(latestApplicationJson.getServerSchemaVersion()).isEqualTo(JsonSchemaVersions.serverVersion); + assertThat(latestApplicationJson.getClientSchemaVersion()).isEqualTo(JsonSchemaVersions.clientVersion); + }) + .verifyComplete(); + } + + /** + * Testcase to check if the application is exported with the datasource configuration object if this setting is + * enabled from application object + * This can be enabled with exportWithConfiguration: true + */ + @Test + @WithUserDetails(value = "api_user") + public void exportApplication_withDatasourceConfig_exportedWithDecryptedFields() { + Workspace newWorkspace = new Workspace(); + newWorkspace.setName("template-org-with-ds"); + + Application testApplication = new Application(); + testApplication.setName("exportApplication_withCredentialsForSampleApps_SuccessWithDecryptFields"); + testApplication.setExportWithConfiguration(true); + testApplication = applicationPageService.createApplication(testApplication, workspaceId).block(); + assert testApplication != null; + exportWithConfigurationAppId = testApplication.getId(); + ApplicationAccessDTO accessDTO = new ApplicationAccessDTO(); + accessDTO.setPublicAccess(true); + applicationService.changeViewAccess(exportWithConfigurationAppId, accessDTO).block(); + final String appName = testApplication.getName(); + final Mono resultMono = Mono.zip( + Mono.just(testApplication), + newPageService.findPageById(testApplication.getPages().get(0).getId(), READ_PAGES, false) + ) + .flatMap(tuple -> { + Application testApp = tuple.getT1(); + PageDTO testPage = tuple.getT2(); + + Layout layout = testPage.getLayouts().get(0); + ObjectMapper objectMapper = new ObjectMapper(); + JSONObject dsl = new JSONObject(); + try { + dsl = new JSONObject(objectMapper.readValue(DEFAULT_PAGE_LAYOUT, new TypeReference>() { + })); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + + ArrayList children = (ArrayList) dsl.get("children"); + JSONObject testWidget = new JSONObject(); + testWidget.put("widgetName", "firstWidget"); + JSONArray temp = new JSONArray(); + temp.addAll(List.of(new JSONObject(Map.of("key", "testField")))); + testWidget.put("dynamicBindingPathList", temp); + testWidget.put("testField", "{{ validAction.data }}"); + children.add(testWidget); + + layout.setDsl(dsl); + layout.setPublishedDsl(dsl); + + ActionDTO action = new ActionDTO(); + action.setName("validAction"); + action.setPageId(testPage.getId()); + action.setExecuteOnLoad(true); + ActionConfiguration actionConfiguration = new ActionConfiguration(); + actionConfiguration.setHttpMethod(HttpMethod.GET); + action.setActionConfiguration(actionConfiguration); + action.setDatasource(datasourceMap.get("DS2")); + + ActionDTO action2 = new ActionDTO(); + action2.setName("validAction2"); + action2.setPageId(testPage.getId()); + action2.setExecuteOnLoad(true); + action2.setUserSetOnLoad(true); + ActionConfiguration actionConfiguration2 = new ActionConfiguration(); + actionConfiguration2.setHttpMethod(HttpMethod.GET); + action2.setActionConfiguration(actionConfiguration2); + action2.setDatasource(datasourceMap.get("DS2")); + + ActionCollectionDTO actionCollectionDTO1 = new ActionCollectionDTO(); + actionCollectionDTO1.setName("testCollection1"); + actionCollectionDTO1.setPageId(testPage.getId()); + actionCollectionDTO1.setApplicationId(testApp.getId()); + actionCollectionDTO1.setWorkspaceId(testApp.getWorkspaceId()); + actionCollectionDTO1.setPluginId(jsDatasource.getPluginId()); + ActionDTO action1 = new ActionDTO(); + action1.setName("testAction1"); + action1.setActionConfiguration(new ActionConfiguration()); + action1.getActionConfiguration().setBody("mockBody"); + actionCollectionDTO1.setActions(List.of(action1)); + actionCollectionDTO1.setPluginType(PluginType.JS); + + return layoutCollectionService.createCollection(actionCollectionDTO1) + .then(layoutActionService.createSingleAction(action)) + .then(layoutActionService.createSingleAction(action2)) + .then(layoutActionService.updateLayout(testPage.getId(), testPage.getApplicationId(), layout.getId(), layout)) + .then(importExportApplicationService.exportApplicationById(testApp.getId(), "")); + }) + .cache(); + + Mono> actionListMono = resultMono + .then(newActionService + .findAllByApplicationIdAndViewMode(testApplication.getId(), false, READ_ACTIONS, null).collectList()); + + Mono> collectionListMono = resultMono.then( + actionCollectionService + .findAllByApplicationIdAndViewMode(testApplication.getId(), false, READ_ACTIONS, null).collectList()); + + Mono> pageListMono = resultMono.then( + newPageService + .findNewPagesByApplicationId(testApplication.getId(), READ_PAGES).collectList()); + + StepVerifier + .create(Mono.zip(resultMono, actionListMono, collectionListMono, pageListMono)) + .assertNext(tuple -> { + + ApplicationJson applicationJson = tuple.getT1(); + List DBActions = tuple.getT2(); + List DBCollections = tuple.getT3(); + List DBPages = tuple.getT4(); + + Application exportedApp = applicationJson.getExportedApplication(); + List pageList = applicationJson.getPageList(); + List actionList = applicationJson.getActionList(); + List actionCollectionList = applicationJson.getActionCollectionList(); + List datasourceList = applicationJson.getDatasourceList(); + + List exportedCollectionIds = actionCollectionList.stream().map(ActionCollection::getId).collect(Collectors.toList()); + List exportedActionIds = actionList.stream().map(NewAction::getId).collect(Collectors.toList()); + List DBCollectionIds = DBCollections.stream().map(ActionCollection::getId).collect(Collectors.toList()); + List DBActionIds = DBActions.stream().map(NewAction::getId).collect(Collectors.toList()); + List DBOnLayoutLoadActionIds = new ArrayList<>(); + List exportedOnLayoutLoadActionIds = new ArrayList<>(); + + DBPages.forEach(newPage -> + newPage.getUnpublishedPage().getLayouts().forEach(layout -> { + if (layout.getLayoutOnLoadActions() != null) { + layout.getLayoutOnLoadActions().forEach(dslActionDTOSet -> { + dslActionDTOSet.forEach(actionDTO -> DBOnLayoutLoadActionIds.add(actionDTO.getId())); + }); + } + }) + ); + + pageList.forEach(newPage -> + newPage.getUnpublishedPage().getLayouts().forEach(layout -> { + if (layout.getLayoutOnLoadActions() != null) { + layout.getLayoutOnLoadActions().forEach(dslActionDTOSet -> { + dslActionDTOSet.forEach(actionDTO -> exportedOnLayoutLoadActionIds.add(actionDTO.getId())); + }); + } + }) + ); + + NewPage defaultPage = pageList.get(0); + + assertThat(exportedApp.getName()).isEqualTo(appName); + assertThat(exportedApp.getWorkspaceId()).isNull(); + assertThat(exportedApp.getPages()).hasSize(1); + ApplicationPage page = exportedApp.getPages().get(0); + + assertThat(page.getId()).isEqualTo(defaultPage.getUnpublishedPage().getName()); + assertThat(page.getIsDefault()).isTrue(); + assertThat(page.getDefaultPageId()).isNull(); + + assertThat(exportedApp.getPolicies()).isNull(); + + assertThat(pageList).hasSize(1); + assertThat(defaultPage.getApplicationId()).isNull(); + assertThat(defaultPage.getUnpublishedPage().getLayouts().get(0).getDsl()).isNotNull(); + assertThat(defaultPage.getId()).isNull(); + assertThat(defaultPage.getPolicies()).isNull(); + + assertThat(actionList.isEmpty()).isFalse(); + assertThat(actionList).hasSize(3); + NewAction validAction = actionList.stream().filter(action -> action.getId().equals("Page1_validAction")).findFirst().get(); + assertThat(validAction.getApplicationId()).isNull(); + assertThat(validAction.getPluginId()).isEqualTo(installedPlugin.getPackageName()); + assertThat(validAction.getPluginType()).isEqualTo(PluginType.API); + assertThat(validAction.getWorkspaceId()).isNull(); + assertThat(validAction.getPolicies()).isNull(); + assertThat(validAction.getId()).isNotNull(); + ActionDTO unpublishedAction = validAction.getUnpublishedAction(); + assertThat(unpublishedAction.getPageId()).isEqualTo(defaultPage.getUnpublishedPage().getName()); + assertThat(unpublishedAction.getDatasource().getPluginId()).isEqualTo(installedPlugin.getPackageName()); + + NewAction testAction1 = actionList.stream().filter(action -> action.getUnpublishedAction().getName().equals("testAction1")).findFirst().get(); + assertThat(testAction1.getId()).isEqualTo("Page1_testCollection1.testAction1"); + + assertThat(actionCollectionList.isEmpty()).isFalse(); + assertThat(actionCollectionList).hasSize(1); + final ActionCollection actionCollection = actionCollectionList.get(0); + assertThat(actionCollection.getApplicationId()).isNull(); + assertThat(actionCollection.getWorkspaceId()).isNull(); + assertThat(actionCollection.getPolicies()).isNull(); + assertThat(actionCollection.getId()).isNotNull(); + assertThat(actionCollection.getUnpublishedCollection().getPluginType()).isEqualTo(PluginType.JS); + assertThat(actionCollection.getUnpublishedCollection().getPageId()) + .isEqualTo(defaultPage.getUnpublishedPage().getName()); + assertThat(actionCollection.getUnpublishedCollection().getPluginId()).isEqualTo(installedJsPlugin.getPackageName()); + + assertThat(datasourceList).hasSize(1); + Datasource datasource = datasourceList.get(0); + assertThat(datasource.getWorkspaceId()).isNull(); + assertThat(datasource.getId()).isNull(); + assertThat(datasource.getPluginId()).isEqualTo(installedPlugin.getPackageName()); + assertThat(datasource.getDatasourceConfiguration()).isNotNull(); + + final Map invisibleActionFields = applicationJson.getInvisibleActionFields(); + + assertThat(invisibleActionFields).isNull(); + for (NewAction newAction : actionList) { + if (newAction.getId().equals("Page1_validAction2")) { + assertEquals(true, newAction.getUnpublishedAction().getUserSetOnLoad()); + } else { + assertEquals(false, newAction.getUnpublishedAction().getUserSetOnLoad()); + } + } + + assertThat(applicationJson.getUnpublishedLayoutmongoEscapedWidgets()).isNull(); + assertThat(applicationJson.getPublishedLayoutmongoEscapedWidgets()).isNull(); + assertThat(applicationJson.getEditModeTheme()).isNotNull(); + assertThat(applicationJson.getEditModeTheme().isSystemTheme()).isTrue(); + assertThat(applicationJson.getEditModeTheme().getName()).isEqualToIgnoringCase(Theme.DEFAULT_THEME_NAME); + + assertThat(applicationJson.getPublishedTheme()).isNotNull(); + assertThat(applicationJson.getPublishedTheme().isSystemTheme()).isTrue(); + assertThat(applicationJson.getPublishedTheme().getName()).isEqualToIgnoringCase(Theme.DEFAULT_THEME_NAME); + + assertThat(exportedCollectionIds).isNotEmpty(); + assertThat(exportedCollectionIds).doesNotContain(String.valueOf(DBCollectionIds)); + + assertThat(exportedActionIds).isNotEmpty(); + assertThat(exportedActionIds).doesNotContain(String.valueOf(DBActionIds)); + + assertThat(exportedOnLayoutLoadActionIds).isNotEmpty(); + assertThat(exportedOnLayoutLoadActionIds).doesNotContain(String.valueOf(DBOnLayoutLoadActionIds)); + + assertThat(applicationJson.getDecryptedFields()).isNotNull(); + }) + .verifyComplete(); + } + + /** + * Test to check if the application can be exported with read only access if this is sample application + */ + @Test + @WithUserDetails(value = "usertest@usertest.com") + public void exportApplication_withReadOnlyAccess_exportedWithDecryptedFields() { + Mono exportApplicationMono = importExportApplicationService + .exportApplicationById(exportWithConfigurationAppId, SerialiseApplicationObjective.SHARE); + + StepVerifier + .create(exportApplicationMono) + .assertNext(applicationJson -> { + assertThat(applicationJson.getExportedApplication()).isNotNull(); + assertThat(applicationJson.getDecryptedFields()).isNotNull(); + }) + .verifyComplete(); + } + + @Test + @WithUserDetails(value = "api_user") + public void importApplication_datasourceWithSameNameAndDifferentPlugin_importedWithValidActionsAndSuffixedDatasource() { + + ApplicationJson applicationJson = createAppJson("test_assets/ImportExportServiceTest/valid-application.json").block(); + + Workspace testWorkspace = new Workspace(); + testWorkspace.setName("Duplicate datasource with different plugin org"); + testWorkspace = workspaceService.create(testWorkspace).block(); + + Datasource testDatasource = new Datasource(); + // Chose any plugin except for mongo, as json static file has mongo plugin for datasource + Plugin postgreSQLPlugin = pluginRepository.findByName("PostgreSQL").block(); + testDatasource.setPluginId(postgreSQLPlugin.getId()); + testDatasource.setWorkspaceId(testWorkspace.getId()); + final String datasourceName = applicationJson.getDatasourceList().get(0).getName(); + testDatasource.setName(datasourceName); + datasourceService.create(testDatasource).block(); + + final Mono resultMono = importExportApplicationService.importApplicationInWorkspace(testWorkspace.getId(), applicationJson); + + StepVerifier + .create(resultMono + .flatMap(application -> Mono.zip( + Mono.just(application), + datasourceService.findAllByWorkspaceId(application.getWorkspaceId(), MANAGE_DATASOURCES).collectList(), + newActionService.findAllByApplicationIdAndViewMode(application.getId(), false, READ_ACTIONS, null).collectList() + ))) + .assertNext(tuple -> { + final Application application = tuple.getT1(); + final List datasourceList = tuple.getT2(); + final List actionList = tuple.getT3(); + + assertThat(application.getName()).isEqualTo("valid_application"); + + List datasourceNameList = new ArrayList<>(); + assertThat(datasourceList).isNotEmpty(); + datasourceList.forEach(datasource -> { + assertThat(datasource.getWorkspaceId()).isEqualTo(application.getWorkspaceId()); + datasourceNameList.add(datasource.getName()); + }); + // Check if both suffixed and newly imported datasource are present + assertThat(datasourceNameList).contains(datasourceName, datasourceName + " #1"); + + assertThat(actionList).isNotEmpty(); + actionList.forEach(newAction -> { + ActionDTO actionDTO = newAction.getUnpublishedAction(); + assertThat(actionDTO.getDatasource()).isNotNull(); + }); + }) + .verifyComplete(); + } + + @Test + @WithUserDetails(value = "api_user") + public void importApplication_datasourceWithSameNameAndPlugin_importedWithValidActionsWithoutSuffixedDatasource() { + + ApplicationJson applicationJson = createAppJson("test_assets/ImportExportServiceTest/valid-application.json").block(); + + Workspace testWorkspace = new Workspace(); + testWorkspace.setName("Duplicate datasource with same plugin org"); + testWorkspace = workspaceService.create(testWorkspace).block(); + + Datasource testDatasource = new Datasource(); + // Chose plugin same as mongo, as json static file has mongo plugin for datasource + Plugin postgreSQLPlugin = pluginRepository.findByName("MongoDB").block(); + testDatasource.setPluginId(postgreSQLPlugin.getId()); + testDatasource.setWorkspaceId(testWorkspace.getId()); + final String datasourceName = applicationJson.getDatasourceList().get(0).getName(); + testDatasource.setName(datasourceName); + datasourceService.create(testDatasource).block(); + + final Mono resultMono = importExportApplicationService.importApplicationInWorkspace(testWorkspace.getId(), applicationJson); + + StepVerifier + .create(resultMono + .flatMap(application -> Mono.zip( + Mono.just(application), + datasourceService.findAllByWorkspaceId(application.getWorkspaceId(), MANAGE_DATASOURCES).collectList(), + newActionService.findAllByApplicationIdAndViewMode(application.getId(), false, READ_ACTIONS, null).collectList() + ))) + .assertNext(tuple -> { + final Application application = tuple.getT1(); + final List datasourceList = tuple.getT2(); + final List actionList = tuple.getT3(); + + assertThat(application.getName()).isEqualTo("valid_application"); + + List datasourceNameList = new ArrayList<>(); + assertThat(datasourceList).isNotEmpty(); + datasourceList.forEach(datasource -> { + assertThat(datasource.getWorkspaceId()).isEqualTo(application.getWorkspaceId()); + datasourceNameList.add(datasource.getName()); + }); + // Check that there are no datasources are created with suffix names as datasource's are of same plugin + assertThat(datasourceNameList).contains(datasourceName); + + assertThat(actionList).isNotEmpty(); + actionList.forEach(newAction -> { + ActionDTO actionDTO = newAction.getUnpublishedAction(); + assertThat(actionDTO.getDatasource()).isNotNull(); + }); + }) + .verifyComplete(); + } + + @Test + @WithUserDetails(value = "api_user") + public void exportAndImportApplication_withMultiplePagesOrderSameInDeployAndEditMode_PagesOrderIsMaintainedInEditAndViewMode() { + Workspace newWorkspace = new Workspace(); + newWorkspace.setName("template-org-with-ds"); + + Application testApplication = new Application(); + testApplication.setName("exportAndImportApplication_withMultiplePagesOrderSameInDeployAndEditMode_PagesOrderIsMaintainedInEditAndViewMode"); + testApplication.setExportWithConfiguration(true); + testApplication = applicationPageService.createApplication(testApplication, workspaceId).block(); + assert testApplication != null; + + PageDTO testPage1 = new PageDTO(); + testPage1.setName("testPage1"); + testPage1.setApplicationId(testApplication.getId()); + testPage1 = applicationPageService.createPage(testPage1).block(); + + PageDTO testPage2 = new PageDTO(); + testPage2.setName("testPage2"); + testPage2.setApplicationId(testApplication.getId()); + testPage2 = applicationPageService.createPage(testPage2).block(); + + // Set order for the newly created pages + applicationPageService.reorderPage(testApplication.getId(), testPage1.getId(), 0, null).block(); + applicationPageService.reorderPage(testApplication.getId(), testPage2.getId(), 1, null).block(); + // Deploy the current application + applicationPageService.publish(testApplication.getId(), true).block(); + + Mono applicationJsonMono = importExportApplicationService.exportApplicationById(testApplication.getId(), "").cache(); + + StepVerifier + .create(applicationJsonMono) + .assertNext(applicationJson -> { + assertThat(applicationJson.getPageOrder()).isNull(); + assertThat(applicationJson.getPublishedPageOrder()).isNull(); + List pageList = applicationJson.getExportedApplication().getPages() + .stream() + .map(ApplicationPage::getId) + .collect(Collectors.toList()); + + assertThat(pageList.get(0)).isEqualTo("testPage1"); + assertThat(pageList.get(1)).isEqualTo("testPage2"); + assertThat(pageList.get(2)).isEqualTo("Page1"); + + List publishedPageList = applicationJson.getExportedApplication().getPublishedPages() + .stream() + .map(ApplicationPage::getId) + .collect(Collectors.toList()); + + assertThat(publishedPageList.get(0)).isEqualTo("testPage1"); + assertThat(publishedPageList.get(1)).isEqualTo("testPage2"); + assertThat(publishedPageList.get(2)).isEqualTo("Page1"); + }) + .verifyComplete(); + + ApplicationJson applicationJson = applicationJsonMono.block(); + Application application = importExportApplicationService.importApplicationInWorkspace(workspaceId, applicationJson).block(); + + // Get the unpublished pages and verify the order + List pageDTOS = application.getPages(); + Mono newPageMono1 = newPageService.findById(pageDTOS.get(0).getId(), MANAGE_PAGES); + Mono newPageMono2 = newPageService.findById(pageDTOS.get(1).getId(), MANAGE_PAGES); + Mono newPageMono3 = newPageService.findById(pageDTOS.get(2).getId(), MANAGE_PAGES); + + StepVerifier + .create(Mono.zip(newPageMono1, newPageMono2, newPageMono3)) + .assertNext(objects -> { + NewPage newPage1 = objects.getT1(); + NewPage newPage2 = objects.getT2(); + NewPage newPage3 = objects.getT3(); + assertThat(newPage1.getUnpublishedPage().getName()).isEqualTo("testPage1"); + assertThat(newPage2.getUnpublishedPage().getName()).isEqualTo("testPage2"); + assertThat(newPage3.getUnpublishedPage().getName()).isEqualTo("Page1"); + + assertThat(newPage1.getId()).isEqualTo(pageDTOS.get(0).getId()); + assertThat(newPage2.getId()).isEqualTo(pageDTOS.get(1).getId()); + assertThat(newPage3.getId()).isEqualTo(pageDTOS.get(2).getId()); + }) + .verifyComplete(); + + // Get the published pages + List publishedPageDTOs = application.getPublishedPages(); + Mono newPublishedPageMono1 = newPageService.findById(publishedPageDTOs.get(0).getId(), MANAGE_PAGES); + Mono newPublishedPageMono2 = newPageService.findById(publishedPageDTOs.get(1).getId(), MANAGE_PAGES); + Mono newPublishedPageMono3 = newPageService.findById(publishedPageDTOs.get(2).getId(), MANAGE_PAGES); + + StepVerifier + .create(Mono.zip(newPublishedPageMono1, newPublishedPageMono2, newPublishedPageMono3)) + .assertNext(objects -> { + NewPage newPage1 = objects.getT1(); + NewPage newPage2 = objects.getT2(); + NewPage newPage3 = objects.getT3(); + assertThat(newPage1.getPublishedPage().getName()).isEqualTo("testPage1"); + assertThat(newPage2.getPublishedPage().getName()).isEqualTo("testPage2"); + assertThat(newPage3.getPublishedPage().getName()).isEqualTo("Page1"); + + assertThat(newPage1.getId()).isEqualTo(publishedPageDTOs.get(0).getId()); + assertThat(newPage2.getId()).isEqualTo(publishedPageDTOs.get(1).getId()); + assertThat(newPage3.getId()).isEqualTo(publishedPageDTOs.get(2).getId()); + }) + .verifyComplete(); + + } + + + @Test + @WithUserDetails(value = "api_user") + public void exportAndImportApplication_withMultiplePagesOrderDifferentInDeployAndEditMode_PagesOrderIsMaintainedInEditAndViewMode() { + Workspace newWorkspace = new Workspace(); + newWorkspace.setName("template-org-with-ds"); + + Application testApplication = new Application(); + testApplication.setName("exportAndImportApplication_withMultiplePagesOrderDifferentInDeployAndEditMode_PagesOrderIsMaintainedInEditAndViewMode"); + testApplication.setExportWithConfiguration(true); + testApplication = applicationPageService.createApplication(testApplication, workspaceId).block(); + assert testApplication != null; + + PageDTO testPage1 = new PageDTO(); + testPage1.setName("testPage1"); + testPage1.setApplicationId(testApplication.getId()); + testPage1 = applicationPageService.createPage(testPage1).block(); + + PageDTO testPage2 = new PageDTO(); + testPage2.setName("testPage2"); + testPage2.setApplicationId(testApplication.getId()); + testPage2 = applicationPageService.createPage(testPage2).block(); + + // Deploy the current application so that edit and view mode will have different page order + applicationPageService.publish(testApplication.getId(), true).block(); + + // Set order for the newly created pages + applicationPageService.reorderPage(testApplication.getId(), testPage1.getId(), 0, null).block(); + applicationPageService.reorderPage(testApplication.getId(), testPage2.getId(), 1, null).block(); + + Mono applicationJsonMono = importExportApplicationService.exportApplicationById(testApplication.getId(), "").cache(); + + StepVerifier + .create(applicationJsonMono) + .assertNext(applicationJson -> { + Application exportedApplication = applicationJson.getExportedApplication(); + exportedApplication.setViewMode(false); + List pageOrder = exportedApplication.getPages() + .stream() + .map(ApplicationPage::getId) + .collect(Collectors.toList()); + assertThat(pageOrder.get(0)).isEqualTo("testPage1"); + assertThat(pageOrder.get(1)).isEqualTo("testPage2"); + assertThat(pageOrder.get(2)).isEqualTo("Page1"); + + pageOrder.clear(); + pageOrder = exportedApplication.getPublishedPages() + .stream() + .map(ApplicationPage::getId) + .collect(Collectors.toList()); + assertThat(pageOrder.get(0)).isEqualTo("Page1"); + assertThat(pageOrder.get(1)).isEqualTo("testPage1"); + assertThat(pageOrder.get(2)).isEqualTo("testPage2"); + }) + .verifyComplete(); + + ApplicationJson applicationJson = applicationJsonMono.block(); + Application application = importExportApplicationService.importApplicationInWorkspace(workspaceId, applicationJson).block(); + + // Get the unpublished pages and verify the order + application.setViewMode(false); + List pageDTOS = application.getPages(); + Mono newPageMono1 = newPageService.findById(pageDTOS.get(0).getId(), MANAGE_PAGES); + Mono newPageMono2 = newPageService.findById(pageDTOS.get(1).getId(), MANAGE_PAGES); + Mono newPageMono3 = newPageService.findById(pageDTOS.get(2).getId(), MANAGE_PAGES); + + StepVerifier + .create(Mono.zip(newPageMono1, newPageMono2, newPageMono3)) + .assertNext(objects -> { + NewPage newPage1 = objects.getT1(); + NewPage newPage2 = objects.getT2(); + NewPage newPage3 = objects.getT3(); + assertThat(newPage1.getUnpublishedPage().getName()).isEqualTo("testPage1"); + assertThat(newPage2.getUnpublishedPage().getName()).isEqualTo("testPage2"); + assertThat(newPage3.getUnpublishedPage().getName()).isEqualTo("Page1"); + + assertThat(newPage1.getId()).isEqualTo(pageDTOS.get(0).getId()); + assertThat(newPage2.getId()).isEqualTo(pageDTOS.get(1).getId()); + assertThat(newPage3.getId()).isEqualTo(pageDTOS.get(2).getId()); + }) + .verifyComplete(); + + // Get the published pages + List publishedPageDTOs = application.getPublishedPages(); + Mono newPublishedPageMono1 = newPageService.findById(publishedPageDTOs.get(0).getId(), MANAGE_PAGES); + Mono newPublishedPageMono2 = newPageService.findById(publishedPageDTOs.get(1).getId(), MANAGE_PAGES); + Mono newPublishedPageMono3 = newPageService.findById(publishedPageDTOs.get(2).getId(), MANAGE_PAGES); + + StepVerifier + .create(Mono.zip(newPublishedPageMono1, newPublishedPageMono2, newPublishedPageMono3)) + .assertNext(objects -> { + NewPage newPage1 = objects.getT1(); + NewPage newPage2 = objects.getT2(); + NewPage newPage3 = objects.getT3(); + assertThat(newPage1.getPublishedPage().getName()).isEqualTo("Page1"); + assertThat(newPage2.getPublishedPage().getName()).isEqualTo("testPage1"); + assertThat(newPage3.getPublishedPage().getName()).isEqualTo("testPage2"); + + assertThat(newPage1.getId()).isEqualTo(publishedPageDTOs.get(0).getId()); + assertThat(newPage2.getId()).isEqualTo(publishedPageDTOs.get(1).getId()); + assertThat(newPage3.getId()).isEqualTo(publishedPageDTOs.get(2).getId()); + }) + .verifyComplete(); + + } + + private ApplicationJson createApplicationJSON(List pageNames) { + ApplicationJson applicationJson = new ApplicationJson(); + + // set the application data + Application application = new Application(); + application.setName("Template Application"); + application.setSlug("template-application"); + application.setForkingEnabled(true); + application.setIsPublic(true); + application.setApplicationVersion(ApplicationVersion.LATEST_VERSION); + applicationJson.setExportedApplication(application); + + Datasource sampleDatasource = new Datasource(); + sampleDatasource.setName("SampleDS"); + sampleDatasource.setPluginId("restapi-plugin"); + + applicationJson.setDatasourceList(List.of(sampleDatasource)); + + // add pages and actions + List newPageList = new ArrayList<>(pageNames.size()); + List actionList = new ArrayList<>(); + List actionCollectionList = new ArrayList<>(); + + for (String pageName : pageNames) { + NewPage newPage = new NewPage(); + newPage.setUnpublishedPage(new PageDTO()); + newPage.getUnpublishedPage().setName(pageName); + newPage.getUnpublishedPage().setLayouts(List.of()); + newPageList.add(newPage); + + NewAction action = new NewAction(); + action.setId(pageName + "_SampleQuery"); + action.setPluginType(PluginType.API); + action.setPluginId("restapi-plugin"); + action.setUnpublishedAction(new ActionDTO()); + action.getUnpublishedAction().setName("SampleQuery"); + action.getUnpublishedAction().setPageId(pageName); + action.getUnpublishedAction().setDatasource(new Datasource()); + action.getUnpublishedAction().getDatasource().setId("SampleDS"); + action.getUnpublishedAction().getDatasource().setPluginId("restapi-plugin"); + actionList.add(action); + + ActionCollection actionCollection = new ActionCollection(); + actionCollection.setId(pageName + "_SampleJS"); + actionCollection.setUnpublishedCollection(new ActionCollectionDTO()); + actionCollection.getUnpublishedCollection().setName("SampleJS"); + actionCollection.getUnpublishedCollection().setPageId(pageName); + actionCollection.getUnpublishedCollection().setPluginId("js-plugin"); + actionCollection.getUnpublishedCollection().setPluginType(PluginType.JS); + actionCollection.getUnpublishedCollection().setBody("export default {\\n\\t\\n}"); + actionCollectionList.add(actionCollection); + } + + applicationJson.setPageList(newPageList); + applicationJson.setActionList(actionList); + applicationJson.setActionCollectionList(actionCollectionList); + return applicationJson; + } + + @Test + @WithUserDetails("api_user") + public void mergeApplicationJsonWithApplication_WhenPageNameConflicts_PageNamesRenamed() { + String uniqueString = UUID.randomUUID().toString(); + + Application destApplication = new Application(); + destApplication.setName("App_" + uniqueString); + destApplication.setSlug("my-slug"); + destApplication.setIsPublic(false); + destApplication.setForkingEnabled(false); + Mono createAppAndPageMono = applicationPageService.createApplication(destApplication, workspaceId) + .flatMap(application -> { + PageDTO pageDTO = new PageDTO(); + pageDTO.setName("Home"); + pageDTO.setApplicationId(application.getId()); + return applicationPageService.createPage(pageDTO).thenReturn(application); + }); + + // let's create an ApplicationJSON which we'll merge with application created by createAppAndPageMono + ApplicationJson applicationJson = createApplicationJSON(List.of("Home", "About")); + + Mono, List>> tuple2Mono = createAppAndPageMono.flatMap(application -> + // merge the application json with the application we've created + importExportApplicationService.mergeApplicationJsonWithApplication(application.getWorkspaceId(), application.getId(), null, applicationJson, null) + .thenReturn(application) + ).flatMap(application -> + // fetch the application pages, this should contain pages from application json + Mono.zip( + newPageService.findApplicationPages(application.getId(), null, null, ApplicationMode.EDIT), + newActionService.findAllByApplicationIdAndViewMode(application.getId(), false, MANAGE_ACTIONS, null).collectList(), + actionCollectionService.findAllByApplicationIdAndViewMode(application.getId(), false, MANAGE_ACTIONS, null).collectList() + ) + ); + + StepVerifier.create(tuple2Mono).assertNext(objects -> { + ApplicationPagesDTO applicationPagesDTO = objects.getT1(); + List newActionList = objects.getT2(); + List actionCollectionList = objects.getT3(); + + assertThat(applicationPagesDTO.getApplication().getName()).isEqualTo(destApplication.getName()); + assertThat(applicationPagesDTO.getApplication().getSlug()).isEqualTo(destApplication.getSlug()); + assertThat(applicationPagesDTO.getApplication().getIsPublic()).isFalse(); + assertThat(applicationPagesDTO.getApplication().getForkingEnabled()).isFalse(); + assertThat(applicationPagesDTO.getPages().size()).isEqualTo(4); + List pageNames = applicationPagesDTO.getPages().stream() + .map(PageNameIdDTO::getName) + .collect(Collectors.toList()); + assertThat(pageNames).contains("Home", "Home2", "About"); + assertThat(newActionList.size()).isEqualTo(2); // we imported two pages and each page has one action + assertThat(actionCollectionList.size()).isEqualTo(2); // we imported two pages and each page has one Collection + }).verifyComplete(); + } + + @Test + @WithUserDetails("api_user") + public void mergeApplicationJsonWithApplication_WhenPageListIProvided_OnlyListedPagesAreMerged() { + String uniqueString = UUID.randomUUID().toString(); + + Application destApplication = new Application(); + destApplication.setName("App_" + uniqueString); + Mono createAppAndPageMono = applicationPageService.createApplication(destApplication, workspaceId) + .flatMap(application -> { + PageDTO pageDTO = new PageDTO(); + pageDTO.setName("Home"); + pageDTO.setApplicationId(application.getId()); + return applicationPageService.createPage(pageDTO).thenReturn(application); + }); + + // let's create an ApplicationJSON which we'll merge with application created by createAppAndPageMono + ApplicationJson applicationJson = createApplicationJSON(List.of("Profile", "About", "Contact US")); + + Mono applicationPagesDTOMono = createAppAndPageMono.flatMap(application -> + // merge the application json with the application we've created + importExportApplicationService.mergeApplicationJsonWithApplication(application.getWorkspaceId(), application.getId(), null, applicationJson, List.of("About", "Contact US")) + .thenReturn(application) + ).flatMap(application -> + // fetch the application pages, this should contain pages from application json + newPageService.findApplicationPages(application.getId(), null, null, ApplicationMode.EDIT) + ); + + StepVerifier.create(applicationPagesDTOMono).assertNext(applicationPagesDTO -> { + assertThat(applicationPagesDTO.getPages().size()).isEqualTo(4); + List pageNames = applicationPagesDTO.getPages().stream() + .map(PageNameIdDTO::getName) + .collect(Collectors.toList()); + assertThat(pageNames).contains("Home", "About", "Contact US"); + assertThat(pageNames).doesNotContain("Profile"); + }).verifyComplete(); + } + + @Test + @WithUserDetails(value = "api_user") + public void exportApplicationById_WhenThemeDoesNotExist_ExportedWithDefaultTheme() { + Theme customTheme = new Theme(); + customTheme.setName("my-custom-theme"); + + String randomId = UUID.randomUUID().toString(); + Application testApplication = new Application(); + testApplication.setName("Application_" + randomId); + Mono exportedAppJson = applicationPageService.createApplication(testApplication, workspaceId) + .flatMap(application -> { + application.setEditModeThemeId("invalid-theme-id"); + application.setPublishedModeThemeId("invalid-theme-id"); + String branchName = null; + return applicationService.save(application) + .then(importExportApplicationService.exportApplicationById(application.getId(), branchName)); + }); + + StepVerifier.create(exportedAppJson).assertNext(applicationJson -> { + assertThat(applicationJson.getEditModeTheme().getName()).isEqualToIgnoringCase(Theme.DEFAULT_THEME_NAME); + assertThat(applicationJson.getPublishedTheme().getName()).isEqualToIgnoringCase(Theme.DEFAULT_THEME_NAME); + }).verifyComplete(); + } + + @Test + @WithUserDetails(value = "api_user") + public void importApplication_invalidPluginReferenceForDatasource_throwException() { + + Workspace newWorkspace = new Workspace(); + newWorkspace.setName("Template Workspace"); + + ApplicationJson appJson = createAppJson("test_assets/ImportExportServiceTest/valid-application.json").block(); + assert appJson != null; + final String randomId = UUID.randomUUID().toString(); + appJson.getDatasourceList().get(0).setPluginId(randomId); + final Mono resultMono = workspaceService + .create(newWorkspace) + .flatMap(workspace -> importExportApplicationService.importApplicationInWorkspace(workspace.getId(), appJson)); + + StepVerifier + .create(resultMono) + .expectErrorMatches(throwable -> throwable instanceof AppsmithException && + throwable.getMessage().equals(AppsmithError.UNKNOWN_PLUGIN_REFERENCE.getMessage(randomId))) + .verify(); + } + + @Test + @WithUserDetails(value = "api_user") + public void importApplication_importSameApplicationTwice_applicationImportedLaterWithSuffixCount() { + + Mono applicationJsonMono = createAppJson("test_assets/ImportExportServiceTest/valid-application-without-action-collection.json"); + + Workspace newWorkspace = new Workspace(); + newWorkspace.setName("Template Workspace"); + + Mono createWorkspaceMono = workspaceService.create(newWorkspace).cache(); + final Mono importApplicationMono = createWorkspaceMono + .zipWith(applicationJsonMono) + .flatMap(tuple -> { + Workspace workspace = tuple.getT1(); + ApplicationJson applicationJson = tuple.getT2(); + return importExportApplicationService + .importApplicationInWorkspace(workspace.getId(), applicationJson); + }); + + StepVerifier + .create(importApplicationMono.zipWhen(application -> importApplicationMono)) + .assertNext(tuple -> { + Application firstImportedApplication = tuple.getT1(); + Application secondImportedApplication = tuple.getT2(); + assertThat(firstImportedApplication.getName()).isEqualTo("valid_application"); + assertThat(secondImportedApplication.getName()).isEqualTo("valid_application (1)"); + assertThat(firstImportedApplication.getWorkspaceId()).isEqualTo(secondImportedApplication.getWorkspaceId()); + assertThat(firstImportedApplication.getWorkspaceId()).isNotNull(); + }) + .verifyComplete(); + } + + @Test + @WithUserDetails(value = "api_user") + public void mergeApplication_existingApplication_pageAddedSuccessfully() { + + //Create application + Application application = new Application(); + application.setName("mergeApplication_existingApplication_pageAddedSuccessfully"); + application.setWorkspaceId(workspaceId); + application = applicationPageService.createApplication(application).block(); + + Mono applicationJson = createAppJson("test_assets/ImportExportServiceTest/valid-application.json"); + + Application finalApplication = application; + Mono, List, List>> importedApplication = applicationJson + .flatMap(applicationJson1 -> importExportApplicationService.mergeApplicationJsonWithApplication( + workspaceId, + finalApplication.getId(), + null, + applicationJson1, + new ArrayList<>()) + ) + .flatMap(application1 -> { + Mono> pageList = newPageService.findNewPagesByApplicationId(application1.getId(), MANAGE_PAGES).collectList(); + Mono> actionList = newActionService.findAllByApplicationIdAndViewMode(application1.getId(), false, MANAGE_ACTIONS, null).collectList(); + Mono> actionCollectionList = actionCollectionService.findAllByApplicationIdAndViewMode(application1.getId(), false, MANAGE_ACTIONS, null).collectList(); + return Mono.zip(Mono.just(application1), pageList, actionList, actionCollectionList); + }); + + + StepVerifier + .create(importedApplication) + .assertNext(tuple -> { + Application application1 = tuple.getT1(); + List pageList = tuple.getT2(); + List actionList = tuple.getT3(); + List actionCollectionList = tuple.getT4(); + + assertThat(application1.getId()).isEqualTo(finalApplication.getId()); + assertThat(finalApplication.getPages().size()).isLessThan(application1.getPages().size()); + assertThat(finalApplication.getPages().size()).isEqualTo(application1.getPublishedPages().size()); + + // Verify the pages after merging the template + pageList.forEach(newPage -> { + assertThat(newPage.getUnpublishedPage().getName()).containsAnyOf("Page1", "Page12", "Page2"); + assertThat(newPage.getGitSyncId()).isNotNull(); + }); + + NewPage page = pageList.stream().filter(newPage -> newPage.getUnpublishedPage().getName().equals("Page12")).collect(Collectors.toList()).get(0); + // Verify the actions after merging the template + actionList.forEach(newAction -> { + assertThat(newAction.getUnpublishedAction().getName()).containsAnyOf("api_wo_auth", "get_users", "run"); + assertThat(newAction.getUnpublishedAction().getPageId()).isEqualTo(page.getId()); + }); + + // Verify the actionCollections after merging the template + actionCollectionList.forEach(newAction -> { + assertThat(newAction.getUnpublishedCollection().getName()).containsAnyOf("JSObject1", "JSObject2"); + assertThat(newAction.getUnpublishedCollection().getPageId()).isEqualTo(page.getId()); + }); + }) + .verifyComplete(); + + } + + @Test + @WithUserDetails(value = "api_user") + public void mergeApplication_gitConnectedApplication_pageAddedSuccessfully() { + + //Create application connected to git + Application testApplication = new Application(); + testApplication.setName("mergeApplication_gitConnectedApplication_pageAddedSuccessfully"); + testApplication.setWorkspaceId(workspaceId); + testApplication.setUpdatedAt(Instant.now()); + testApplication.setLastDeployedAt(Instant.now()); + testApplication.setModifiedBy("some-user"); + testApplication.setGitApplicationMetadata(new GitApplicationMetadata()); + GitApplicationMetadata gitData = new GitApplicationMetadata(); + gitData.setBranchName("master"); + gitData.setDefaultBranchName("master"); + testApplication.setGitApplicationMetadata(gitData); + + Application application = applicationPageService.createApplication(testApplication, workspaceId) + .flatMap(application1 -> { + application1.getGitApplicationMetadata().setDefaultApplicationId(application1.getId()); + return applicationService.save(application1); + }).block(); + + Mono applicationJson = createAppJson("test_assets/ImportExportServiceTest/valid-application.json"); + + Application finalApplication = application; + Mono, List, List>> importedApplication = applicationJson + .flatMap(applicationJson1 -> importExportApplicationService.mergeApplicationJsonWithApplication( + workspaceId, + finalApplication.getId(), + "master", + applicationJson1, + new ArrayList<>()) + ) + .flatMap(application1 -> { + Mono> pageList = newPageService.findNewPagesByApplicationId(application1.getId(), MANAGE_PAGES).collectList(); + Mono> actionList = newActionService.findAllByApplicationIdAndViewMode(application1.getId(), false, MANAGE_ACTIONS, null).collectList(); + Mono> actionCollectionList = actionCollectionService.findAllByApplicationIdAndViewMode(application1.getId(), false, MANAGE_ACTIONS, null).collectList(); + return Mono.zip(Mono.just(application1), pageList, actionList, actionCollectionList); + }); + + StepVerifier + .create(importedApplication) + .assertNext(tuple -> { + Application application1 = tuple.getT1(); + List pageList = tuple.getT2(); + List actionList = tuple.getT3(); + List actionCollectionList = tuple.getT4(); + + assertThat(application1.getId()).isEqualTo(finalApplication.getId()); + assertThat(finalApplication.getPages().size()).isLessThan(application1.getPages().size()); + assertThat(finalApplication.getPages().size()).isEqualTo(application1.getPublishedPages().size()); + + // Verify the pages after merging the template + pageList.forEach(newPage -> { + assertThat(newPage.getUnpublishedPage().getName()).containsAnyOf("Page1", "Page12", "Page2"); + assertThat(newPage.getGitSyncId()).isNotNull(); + }); + + NewPage page = pageList.stream().filter(newPage -> newPage.getUnpublishedPage().getName().equals("Page12")).collect(Collectors.toList()).get(0); + // Verify the actions after merging the template + actionList.forEach(newAction -> { + assertThat(newAction.getUnpublishedAction().getName()).containsAnyOf("api_wo_auth", "get_users", "run"); + assertThat(newAction.getUnpublishedAction().getPageId()).isEqualTo(page.getId()); + }); + + // Verify the actionCollections after merging the template + actionCollectionList.forEach(newAction -> { + assertThat(newAction.getUnpublishedCollection().getName()).containsAnyOf("JSObject1", "JSObject2"); + assertThat(newAction.getUnpublishedCollection().getPageId()).isEqualTo(page.getId()); + }); + }) + .verifyComplete(); + + } + + @Test + @WithUserDetails(value = "api_user") + public void mergeApplication_gitConnectedApplicationChildBranch_pageAddedSuccessfully() { + + //Create application connected to git + Application testApplication = new Application(); + testApplication.setName("mergeApplication_gitConnectedApplicationChildBranch_pageAddedSuccessfully"); + testApplication.setWorkspaceId(workspaceId); + testApplication.setUpdatedAt(Instant.now()); + testApplication.setLastDeployedAt(Instant.now()); + testApplication.setModifiedBy("some-user"); + testApplication.setGitApplicationMetadata(new GitApplicationMetadata()); + GitApplicationMetadata gitData = new GitApplicationMetadata(); + gitData.setBranchName("master"); + gitData.setDefaultBranchName("master"); + testApplication.setGitApplicationMetadata(gitData); + + Application application = applicationPageService.createApplication(testApplication, workspaceId) + .flatMap(application1 -> { + application1.getGitApplicationMetadata().setDefaultApplicationId(application1.getId()); + return applicationService.save(application1); + }).block(); + + // Create branch for the application + testApplication = new Application(); + testApplication.setName("mergeApplication_gitConnectedApplicationChildBranch_pageAddedSuccessfully1"); + testApplication.setWorkspaceId(workspaceId); + testApplication.setUpdatedAt(Instant.now()); + testApplication.setLastDeployedAt(Instant.now()); + testApplication.setModifiedBy("some-user"); + testApplication.setGitApplicationMetadata(new GitApplicationMetadata()); + GitApplicationMetadata gitData1 = new GitApplicationMetadata(); + gitData1.setBranchName("feature"); + gitData1.setDefaultBranchName("master"); + testApplication.setGitApplicationMetadata(gitData1); + + Application branchApp = applicationPageService.createApplication(testApplication, workspaceId) + .flatMap(application2 -> { + application2.getGitApplicationMetadata().setDefaultApplicationId(application.getId()); + return applicationService.save(application2); + }).block(); + + Mono applicationJson = createAppJson("test_assets/ImportExportServiceTest/valid-application.json"); + + Application finalApplication = application; + Mono, List, List>> importedApplication = applicationJson + .flatMap(applicationJson1 -> importExportApplicationService.mergeApplicationJsonWithApplication( + workspaceId, + branchApp.getId(), + "feature", + applicationJson1, + new ArrayList<>()) + ) + .flatMap(application2 -> { + Mono> pageList = newPageService.findNewPagesByApplicationId(branchApp.getId(), MANAGE_PAGES).collectList(); + Mono> actionList = newActionService.findAllByApplicationIdAndViewMode(branchApp.getId(), false, MANAGE_ACTIONS, null).collectList(); + Mono> actionCollectionList = actionCollectionService.findAllByApplicationIdAndViewMode(branchApp.getId(), false, MANAGE_ACTIONS, null).collectList(); + return Mono.zip(Mono.just(application2), pageList, actionList, actionCollectionList); + }); + + StepVerifier + .create(importedApplication) + .assertNext(tuple -> { + Application application3 = tuple.getT1(); + List pageList = tuple.getT2(); + List actionList = tuple.getT3(); + List actionCollectionList = tuple.getT4(); + + assertThat(application3.getId()).isNotEqualTo(finalApplication.getId()); + assertThat(finalApplication.getPages().size()).isLessThan(application3.getPages().size()); + assertThat(finalApplication.getPages().size()).isEqualTo(application3.getPublishedPages().size()); + + // Verify the pages after merging the template + pageList.forEach(newPage -> { + assertThat(newPage.getUnpublishedPage().getName()).containsAnyOf("Page1", "Page12", "Page2"); + assertThat(newPage.getGitSyncId()).isNotNull(); + }); + + NewPage page = pageList.stream().filter(newPage -> newPage.getUnpublishedPage().getName().equals("Page12")).collect(Collectors.toList()).get(0); + // Verify the actions after merging the template + actionList.forEach(newAction -> { + assertThat(newAction.getUnpublishedAction().getName()).containsAnyOf("api_wo_auth", "get_users", "run"); + assertThat(newAction.getUnpublishedAction().getPageId()).isEqualTo(page.getId()); + }); + + // Verify the actionCollections after merging the template + actionCollectionList.forEach(newAction -> { + assertThat(newAction.getUnpublishedCollection().getName()).containsAnyOf("JSObject1", "JSObject2"); + assertThat(newAction.getUnpublishedCollection().getPageId()).isEqualTo(page.getId()); + }); + }) + .verifyComplete(); + + } + + @Test + @WithUserDetails(value = "api_user") + public void mergeApplication_gitConnectedApplicationSelectedSpecificPages_selectedPageAddedSuccessfully() { + //Create application connected to git + Application testApplication = new Application(); + testApplication.setName("mergeApplication_gitConnectedApplicationSelectedSpecificPages_selectedPageAddedSuccessfully"); + testApplication.setWorkspaceId(workspaceId); + testApplication.setUpdatedAt(Instant.now()); + testApplication.setLastDeployedAt(Instant.now()); + testApplication.setModifiedBy("some-user"); + testApplication.setGitApplicationMetadata(new GitApplicationMetadata()); + GitApplicationMetadata gitData = new GitApplicationMetadata(); + gitData.setBranchName("master"); + gitData.setDefaultBranchName("master"); + testApplication.setGitApplicationMetadata(gitData); + + Application application = applicationPageService.createApplication(testApplication, workspaceId) + .flatMap(application1 -> { + application1.getGitApplicationMetadata().setDefaultApplicationId(application1.getId()); + return applicationService.save(application1); + }).block(); + + // Create branch for the application + testApplication = new Application(); + testApplication.setName("mergeApplication_gitConnectedApplicationSelectedSpecificPages_selectedPageAddedSuccessfully1"); + testApplication.setWorkspaceId(workspaceId); + testApplication.setUpdatedAt(Instant.now()); + testApplication.setLastDeployedAt(Instant.now()); + testApplication.setModifiedBy("some-user"); + testApplication.setGitApplicationMetadata(new GitApplicationMetadata()); + GitApplicationMetadata gitData1 = new GitApplicationMetadata(); + gitData1.setBranchName("feature"); + gitData1.setDefaultBranchName("master"); + testApplication.setGitApplicationMetadata(gitData1); + + Application branchApp = applicationPageService.createApplication(testApplication, workspaceId) + .flatMap(application2 -> { + application2.getGitApplicationMetadata().setDefaultApplicationId(application.getId()); + return applicationService.save(application2); + }).block(); + + Mono applicationJson = createAppJson("test_assets/ImportExportServiceTest/valid-application.json"); + + Application finalApplication = application; + Mono, List, List>> importedApplication = applicationJson + .flatMap(applicationJson1 -> importExportApplicationService.mergeApplicationJsonWithApplication( + workspaceId, + branchApp.getId(), + "feature", + applicationJson1, + List.of("Page1")) + ) + .flatMap(application2 -> { + Mono> pageList = newPageService.findNewPagesByApplicationId(branchApp.getId(), MANAGE_PAGES).collectList(); + Mono> actionList = newActionService.findAllByApplicationIdAndViewMode(branchApp.getId(), false, MANAGE_ACTIONS, null).collectList(); + Mono> actionCollectionList = actionCollectionService.findAllByApplicationIdAndViewMode(branchApp.getId(), false, MANAGE_ACTIONS, null).collectList(); + return Mono.zip(Mono.just(application2), pageList, actionList, actionCollectionList); + }); + + StepVerifier + .create(importedApplication) + .assertNext(tuple -> { + Application application3 = tuple.getT1(); + List pageList = tuple.getT2(); + List actionList = tuple.getT3(); + List actionCollectionList = tuple.getT4(); + + assertThat(application3.getId()).isNotEqualTo(finalApplication.getId()); + assertThat(finalApplication.getPages().size()).isLessThan(application3.getPages().size()); + assertThat(finalApplication.getPages().size()).isEqualTo(application3.getPublishedPages().size()); + + // Verify the pages after merging the template + pageList.forEach(newPage -> { + assertThat(newPage.getUnpublishedPage().getName()).containsAnyOf("Page1", "Page12"); + assertThat(newPage.getGitSyncId()).isNotNull(); + }); + + NewPage page = pageList.stream().filter(newPage -> newPage.getUnpublishedPage().getName().equals("Page12")).collect(Collectors.toList()).get(0); + // Verify the actions after merging the template + actionList.forEach(newAction -> { + assertThat(newAction.getUnpublishedAction().getName()).containsAnyOf("api_wo_auth", "get_users", "run"); + assertThat(newAction.getUnpublishedAction().getPageId()).isEqualTo(page.getId()); + }); + + // Verify the actionCollections after merging the template + actionCollectionList.forEach(newAction -> { + assertThat(newAction.getUnpublishedCollection().getName()).containsAnyOf("JSObject1", "JSObject2"); + assertThat(newAction.getUnpublishedCollection().getPageId()).isEqualTo(page.getId()); + }); + }) + .verifyComplete(); + } + + @Test + @WithUserDetails(value = "api_user") + public void mergeApplication_gitConnectedApplicationSelectedAllPages_selectedPageAddedSuccessfully() { + //Create application connected to git + Application testApplication = new Application(); + testApplication.setName("mergeApplication_gitConnectedApplicationSelectedAllPages_selectedPageAddedSuccessfully"); + testApplication.setWorkspaceId(workspaceId); + testApplication.setUpdatedAt(Instant.now()); + testApplication.setLastDeployedAt(Instant.now()); + testApplication.setModifiedBy("some-user"); + testApplication.setGitApplicationMetadata(new GitApplicationMetadata()); + GitApplicationMetadata gitData = new GitApplicationMetadata(); + gitData.setBranchName("master"); + gitData.setDefaultBranchName("master"); + testApplication.setGitApplicationMetadata(gitData); + + Application application = applicationPageService.createApplication(testApplication, workspaceId) + .flatMap(application1 -> { + application1.getGitApplicationMetadata().setDefaultApplicationId(application1.getId()); + return applicationService.save(application1); + }).block(); + + // Create branch for the application + testApplication = new Application(); + testApplication.setName("mergeApplication_gitConnectedApplicationSelectedAllPages_selectedPageAddedSuccessfully1"); + testApplication.setWorkspaceId(workspaceId); + testApplication.setUpdatedAt(Instant.now()); + testApplication.setLastDeployedAt(Instant.now()); + testApplication.setModifiedBy("some-user"); + testApplication.setGitApplicationMetadata(new GitApplicationMetadata()); + GitApplicationMetadata gitData1 = new GitApplicationMetadata(); + gitData1.setBranchName("feature"); + gitData1.setDefaultBranchName("master"); + testApplication.setGitApplicationMetadata(gitData1); + + Application branchApp = applicationPageService.createApplication(testApplication, workspaceId) + .flatMap(application2 -> { + application2.getGitApplicationMetadata().setDefaultApplicationId(application.getId()); + return applicationService.save(application2); + }).block(); + + Mono applicationJson = createAppJson("test_assets/ImportExportServiceTest/valid-application.json"); + + Application finalApplication = application; + Mono, List, List>> importedApplication = applicationJson + .flatMap(applicationJson1 -> importExportApplicationService.mergeApplicationJsonWithApplication( + workspaceId, + branchApp.getId(), + "feature", + applicationJson1, + List.of("Page1", "Page2")) + ) + .flatMap(application2 -> { + Mono> pageList = newPageService.findNewPagesByApplicationId(branchApp.getId(), MANAGE_PAGES).collectList(); + Mono> actionList = newActionService.findAllByApplicationIdAndViewMode(branchApp.getId(), false, MANAGE_ACTIONS, null).collectList(); + Mono> actionCollectionList = actionCollectionService.findAllByApplicationIdAndViewMode(branchApp.getId(), false, MANAGE_ACTIONS, null).collectList(); + return Mono.zip(Mono.just(application2), pageList, actionList, actionCollectionList); + }); + + StepVerifier + .create(importedApplication) + .assertNext(tuple -> { + Application application3 = tuple.getT1(); + List pageList = tuple.getT2(); + List actionList = tuple.getT3(); + List actionCollectionList = tuple.getT4(); + + assertThat(application3.getId()).isNotEqualTo(finalApplication.getId()); + assertThat(finalApplication.getPages().size()).isLessThan(application3.getPages().size()); + assertThat(finalApplication.getPages().size()).isEqualTo(application3.getPublishedPages().size()); + + // Verify the pages after merging the template + pageList.forEach(newPage -> { + assertThat(newPage.getUnpublishedPage().getName()).containsAnyOf("Page1", "Page12", "Page2"); + assertThat(newPage.getGitSyncId()).isNotNull(); + }); + + NewPage page = pageList.stream().filter(newPage -> newPage.getUnpublishedPage().getName().equals("Page12")).collect(Collectors.toList()).get(0); + // Verify the actions after merging the template + actionList.forEach(newAction -> { + assertThat(newAction.getUnpublishedAction().getName()).containsAnyOf("api_wo_auth", "get_users", "run"); + assertThat(newAction.getUnpublishedAction().getPageId()).isEqualTo(page.getId()); + }); + + // Verify the actionCollections after merging the template + actionCollectionList.forEach(newAction -> { + assertThat(newAction.getUnpublishedCollection().getName()).containsAnyOf("JSObject1", "JSObject2"); + assertThat(newAction.getUnpublishedCollection().getPageId()).isEqualTo(page.getId()); + }); + }) + .verifyComplete(); + } + + @Test + @WithUserDetails(value = "api_user") + public void mergeApplication_nonGitConnectedApplicationSelectedSpecificPages_selectedPageAddedSuccessfully() { + //Create application + Application application = new Application(); + application.setName("mergeApplication_nonGitConnectedApplicationSelectedSpecificPages_selectedPageAddedSuccessfully"); + application.setWorkspaceId(workspaceId); + application = applicationPageService.createApplication(application).block(); + + Mono applicationJson = createAppJson("test_assets/ImportExportServiceTest/valid-application.json"); + + Application finalApplication = application; + Mono, List, List>> importedApplication = applicationJson + .flatMap(applicationJson1 -> importExportApplicationService.mergeApplicationJsonWithApplication( + workspaceId, + finalApplication.getId(), + null, + applicationJson1, + List.of("Page1")) + ) + .flatMap(application1 -> { + Mono> pageList = newPageService.findNewPagesByApplicationId(application1.getId(), MANAGE_PAGES).collectList(); + Mono> actionList = newActionService.findAllByApplicationIdAndViewMode(application1.getId(), false, MANAGE_ACTIONS, null).collectList(); + Mono> actionCollectionList = actionCollectionService.findAllByApplicationIdAndViewMode(application1.getId(), false, MANAGE_ACTIONS, null).collectList(); + return Mono.zip(Mono.just(application1), pageList, actionList, actionCollectionList); + }); + + + StepVerifier + .create(importedApplication) + .assertNext(tuple -> { + Application application1 = tuple.getT1(); + List pageList = tuple.getT2(); + List actionList = tuple.getT3(); + List actionCollectionList = tuple.getT4(); + + assertThat(application1.getId()).isEqualTo(finalApplication.getId()); + assertThat(finalApplication.getPages().size()).isLessThan(application1.getPages().size()); + assertThat(finalApplication.getPages().size()).isEqualTo(application1.getPublishedPages().size()); + + // Verify the pages after merging the template + pageList.forEach(newPage -> { + assertThat(newPage.getUnpublishedPage().getName()).containsAnyOf("Page1", "Page12"); + assertThat(newPage.getGitSyncId()).isNotNull(); + }); + + NewPage page = pageList.stream().filter(newPage -> newPage.getUnpublishedPage().getName().equals("Page12")).collect(Collectors.toList()).get(0); + // Verify the actions after merging the template + actionList.forEach(newAction -> { + assertThat(newAction.getUnpublishedAction().getName()).containsAnyOf("api_wo_auth", "get_users", "run"); + assertThat(newAction.getUnpublishedAction().getPageId()).isEqualTo(page.getId()); + }); + + // Verify the actionCollections after merging the template + actionCollectionList.forEach(newAction -> { + assertThat(newAction.getUnpublishedCollection().getName()).containsAnyOf("JSObject1", "JSObject2"); + assertThat(newAction.getUnpublishedCollection().getPageId()).isEqualTo(page.getId()); + }); + }) + .verifyComplete(); + } + + @Test + @WithUserDetails(value = "api_user") + public void mergeApplication_nonGitConnectedApplicationSelectedAllPages_selectedPageAddedSuccessfully() { + //Create application + Application application = new Application(); + application.setName("mergeApplication_nonGitConnectedApplicationSelectedAllPages_selectedPageAddedSuccessfully"); + application.setWorkspaceId(workspaceId); + application = applicationPageService.createApplication(application).block(); + + Mono applicationJson = createAppJson("test_assets/ImportExportServiceTest/valid-application.json"); + + Application finalApplication = application; + Mono, List, List>> importedApplication = applicationJson + .flatMap(applicationJson1 -> importExportApplicationService.mergeApplicationJsonWithApplication( + workspaceId, + finalApplication.getId(), + null, + applicationJson1, + List.of("Page1", "Page2")) + ) + .flatMap(application1 -> { + Mono> pageList = newPageService.findNewPagesByApplicationId(application1.getId(), MANAGE_PAGES).collectList(); + Mono> actionList = newActionService.findAllByApplicationIdAndViewMode(application1.getId(), false, MANAGE_ACTIONS, null).collectList(); + Mono> actionCollectionList = actionCollectionService.findAllByApplicationIdAndViewMode(application1.getId(), false, MANAGE_ACTIONS, null).collectList(); + return Mono.zip(Mono.just(application1), pageList, actionList, actionCollectionList); + }); + + + StepVerifier + .create(importedApplication) + .assertNext(tuple -> { + Application application1 = tuple.getT1(); + List pageList = tuple.getT2(); + List actionList = tuple.getT3(); + List actionCollectionList = tuple.getT4(); + + assertThat(application1.getId()).isEqualTo(finalApplication.getId()); + assertThat(finalApplication.getPages().size()).isLessThan(application1.getPages().size()); + assertThat(finalApplication.getPages().size()).isEqualTo(application1.getPublishedPages().size()); + + // Verify the pages after merging the template + pageList.forEach(newPage -> { + assertThat(newPage.getUnpublishedPage().getName()).containsAnyOf("Page1", "Page12", "Page2"); + assertThat(newPage.getGitSyncId()).isNotNull(); + }); + + NewPage page = pageList.stream().filter(newPage -> newPage.getUnpublishedPage().getName().equals("Page12")).collect(Collectors.toList()).get(0); + // Verify the actions after merging the template + actionList.forEach(newAction -> { + assertThat(newAction.getUnpublishedAction().getName()).containsAnyOf("api_wo_auth", "get_users", "run"); + assertThat(newAction.getUnpublishedAction().getPageId()).isEqualTo(page.getId()); + }); + + // Verify the actionCollections after merging the template + actionCollectionList.forEach(newAction -> { + assertThat(newAction.getUnpublishedCollection().getName()).containsAnyOf("JSObject1", "JSObject2"); + assertThat(newAction.getUnpublishedCollection().getPageId()).isEqualTo(page.getId()); + }); + }) + .verifyComplete(); + } + + @Test + @WithUserDetails(value = "api_user") + public void importApplication_invalidJson_createdAppIsDeleted() { + FilePart filePart = createFilePart("test_assets/ImportExportServiceTest/invalid-json-without-pages.json"); + + List applicationList = applicationService.findAllApplicationsByWorkspaceId(workspaceId).collectList().block(); + + Mono resultMono = importExportApplicationService.extractFileAndSaveApplication(workspaceId, filePart); + + StepVerifier + .create(resultMono) + .expectErrorMatches(throwable -> throwable instanceof AppsmithException && + throwable.getMessage().equals(AppsmithError.NO_RESOURCE_FOUND.getMessage(FieldName.PAGES, INVALID_JSON_FILE))) + .verify(); + + // Verify that the app card is not created + StepVerifier + .create(applicationService.findAllApplicationsByWorkspaceId(workspaceId).collectList()) + .assertNext(applications -> { + assertThat(applicationList.size()).isEqualTo(applications.size()); + }) + .verifyComplete(); + + } +} diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/solutions/ce/RefactoringSolutionCEImplTest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/solutions/ce/RefactoringSolutionCEImplTest.java index 56b136158012..0ac3511d8f83 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/solutions/ce/RefactoringSolutionCEImplTest.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/solutions/ce/RefactoringSolutionCEImplTest.java @@ -3,11 +3,17 @@ import com.appsmith.server.configurations.InstanceConfig; import com.appsmith.server.helpers.ResponseUtils; import com.appsmith.server.services.ActionCollectionService; +import com.appsmith.server.services.AnalyticsService; import com.appsmith.server.services.ApplicationService; import com.appsmith.server.services.AstService; import com.appsmith.server.services.LayoutActionService; import com.appsmith.server.services.NewActionService; import com.appsmith.server.services.NewPageService; +import com.appsmith.server.services.SessionUserService; +import com.appsmith.server.solutions.ActionPermission; +import com.appsmith.server.solutions.ActionPermissionImpl; +import com.appsmith.server.solutions.PagePermission; +import com.appsmith.server.solutions.PagePermissionImpl; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; @@ -48,6 +54,13 @@ class RefactoringSolutionCEImplTest { private AstService astService; @MockBean private InstanceConfig instanceConfig; + @MockBean + private AnalyticsService analyticsService; + @MockBean + private SessionUserService sessionUserService; + + PagePermission pagePermission; + ActionPermission actionPermission; ObjectMapper mapper = new ObjectMapper(); @@ -56,6 +69,8 @@ class RefactoringSolutionCEImplTest { @BeforeEach public void setUp() { + pagePermission = new PagePermissionImpl(); + actionPermission = new ActionPermissionImpl(); refactoringSolutionCE = new RefactoringSolutionCEImpl(objectMapper, newPageService, newActionService, @@ -64,7 +79,11 @@ public void setUp() { layoutActionService, applicationService, astService, - instanceConfig); + instanceConfig, + analyticsService, + sessionUserService, + pagePermission, + actionPermission); } @Test diff --git a/app/shared/ast/index.ts b/app/shared/ast/index.ts index 4fb4231cbe63..410670d4394f 100644 --- a/app/shared/ast/index.ts +++ b/app/shared/ast/index.ts @@ -21,14 +21,15 @@ import { import { ECMA_VERSION, SourceType, NodeTypes } from "./src/constants"; // JSObjects -import { parseJSObjectWithAST } from "./src/jsObject"; +import { parseJSObjectWithAST, JsObjectProperty } from "./src/jsObject"; -// types or intefaces should be exported with type keyword, while enums can be exported like normal functions +// types or interfaces should be exported with type keyword, while enums can be exported like normal functions export type { ObjectExpression, PropertyNode, MemberExpressionData, IdentifierInfo, + JsObjectProperty, }; export { diff --git a/app/shared/ast/src/index.ts b/app/shared/ast/src/index.ts index eeba8fcda334..73fce5f40dab 100644 --- a/app/shared/ast/src/index.ts +++ b/app/shared/ast/src/index.ts @@ -41,6 +41,13 @@ interface IdentifierNode extends Node { name: string; } +//Using this to handle the Variable property refactor +interface RefactorIdentifierNode extends Node { + type: NodeTypes.Identifier; + name: string; + property?: IdentifierNode; +} + // doc: https://github.com/estree/estree/blob/master/es5.md#variabledeclarator interface VariableDeclaratorNode extends Node { type: NodeTypes.VariableDeclarator; @@ -270,50 +277,81 @@ export const entityRefactorFromCode = ( evaluationVersion: number, invalidIdentifiers?: Record ): EntityRefactorResponse => { + //Sanitizing leads to removal of special charater. + //Hence we are not sanatizing the script. Fix(#18492) //If script is a JSObject then replace export default to decalartion. if (isJSObject) script = jsObjectToCode(script); let ast: Node = { end: 0, start: 0, type: "" }; //Copy of script to refactor let refactorScript = script; //Difference in length of oldName and newName - let nameLengthDiff: number = newName.length - oldName.length; + const nameLengthDiff: number = newName.length - oldName.length; //Offset index used for deciding location of oldName. let refactorOffset: number = 0; //Count of refactors on the script let refactorCount: number = 0; try { - const sanitizedScript = sanitizeScript(script, evaluationVersion); - ast = getAST(sanitizedScript); + ast = getAST(script); let { references, functionalParams, variableDeclarations, identifierList, }: NodeList = ancestorWalk(ast); - let identifierArray = Array.from(identifierList) as Array; - Array.from(references).forEach((reference, index) => { + const identifierArray = Array.from( + identifierList + ) as Array; + //To handle if oldName has property ("JSObject.myfunc") + const oldNameArr = oldName.split("."); + const referencesArr = Array.from(references).filter((reference) => { + // To remove references derived from declared variables and function params, + // We extract the topLevelIdentifier Eg. Api1.name => Api1 const topLevelIdentifier = toPath(reference)[0]; - let shouldUpdateNode = !( + return !( functionalParams.has(topLevelIdentifier) || variableDeclarations.has(topLevelIdentifier) || has(invalidIdentifiers, topLevelIdentifier) ); - //check if node should be updated - if (shouldUpdateNode && identifierArray[index].name === oldName) { - //Replace the oldName by newName - //Get start index from node and get subarray from index 0 till start - //Append above with new name - //Append substring from end index from the node till end of string - //Offset variable is used to alter the position based on `refactorOffset` - refactorScript = - refactorScript.substring( - 0, - identifierArray[index].start + refactorOffset - ) + - newName + - refactorScript.substring(identifierArray[index].end + refactorOffset); - refactorOffset += nameLengthDiff; - ++refactorCount; + }); + //Traverse through all identifiers in the script + identifierArray.forEach((identifier) => { + if (identifier.name === oldNameArr[0]) { + let index = 0; + while (index < referencesArr.length) { + if (identifier.name === referencesArr[index].split(".")[0]) { + //Replace the oldName by newName + //Get start index from node and get subarray from index 0 till start + //Append above with new name + //Append substring from end index from the node till end of string + //Offset variable is used to alter the position based on `refactorOffset` + //In case of nested JS action get end postion fro the property. + ///Default end index + let endIndex = identifier.end; + const propertyNode = identifier.property; + //Flag variable : true if property should be updated + //false if property should not be updated + let propertyCondFlag = + oldNameArr.length > 1 && + propertyNode && + oldNameArr[1] === propertyNode.name; + //Condition to validate if Identifier || Property should be updated?? + if (oldNameArr.length === 1 || propertyCondFlag) { + //Condition to extend end index in case of property match + if (propertyCondFlag && propertyNode) { + endIndex = propertyNode.end; + } + refactorScript = + refactorScript.substring(0, identifier.start + refactorOffset) + + newName + + refactorScript.substring(endIndex + refactorOffset); + refactorOffset += nameLengthDiff; + ++refactorCount; + //We are only looking for one match in refrence for the identifier name. + break; + } + } + index++; + } } }); //If script is a JSObject then revert decalartion to export default. @@ -507,8 +545,8 @@ export const extractInvalidTopLevelMemberExpressionsFromCode = ( }; const ancestorWalk = (ast: Node): NodeList => { - //List of all Identifier nodes - const identifierList = new Array(); + //List of all Identifier nodes with their property(if exists). + const identifierList = new Array(); // List of all references found const references = new Set(); // List of variables declared within the script. All identifiers and member expressions derived from declared variables will be removed @@ -554,7 +592,15 @@ const ancestorWalk = (ast: Node): NodeList => { break; } } - identifierList.push(node as IdentifierNode); + //If parent is a Member expression then attach property to the Node. + //else push Identifier Node. + const parentNode = ancestors[ancestors.length - 2]; + if (isMemberExpressionNode(parentNode)) { + identifierList.push({ + ...(node as IdentifierNode), + property: parentNode.property as IdentifierNode, + }); + } else identifierList.push(node as RefactorIdentifierNode); if (isIdentifierNode(candidateTopLevelNode)) { // If the node is an Identifier, just save that references.add(candidateTopLevelNode.name); diff --git a/app/shared/ast/src/jsObject/index.ts b/app/shared/ast/src/jsObject/index.ts index b3a4feff74f7..bb3c9627285c 100644 --- a/app/shared/ast/src/jsObject/index.ts +++ b/app/shared/ast/src/jsObject/index.ts @@ -11,7 +11,7 @@ import { functionParam, } from "../index"; -type JsObjectProperty = { +export type JsObjectProperty = { key: string; value: string; type: string; diff --git a/app/util/plugin-generation/package-lock.json b/app/util/plugin-generation/package-lock.json index 51a21ba6013b..418c18af58bf 100644 --- a/app/util/plugin-generation/package-lock.json +++ b/app/util/plugin-generation/package-lock.json @@ -434,9 +434,9 @@ } }, "decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==" }, "defaults": { "version": "1.0.3", @@ -1426,9 +1426,9 @@ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" }, "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "requires": { "brace-expansion": "^1.1.7" } diff --git a/contributions/AppsmithWidgetDevelopmentGuide.md b/contributions/AppsmithWidgetDevelopmentGuide.md index 0b00ba38c227..376fcd1a2a7a 100644 --- a/contributions/AppsmithWidgetDevelopmentGuide.md +++ b/contributions/AppsmithWidgetDevelopmentGuide.md @@ -353,7 +353,7 @@ Properties can be required to be validated when an Appsmith developer is allowed - `expected` (required for `ValidationTypes.FUNCTION`, [CodeEditorExpected](https://github.com/appsmithorg/appsmith/blob/e772fd4ff96accfb94818fa9f0b58dc6851a1cf0/app/client/src/components/editorComponents/CodeEditor/index.tsx#L107)): A structure that describes the expected type, example and autocomplete data type. - `type`(required, string): The type of the property to be shown to the Appsmith developer - `example`(required, [ExpectedValueExample](https://github.com/appsmithorg/appsmith/blob/e772fd4ff96accfb94818fa9f0b58dc6851a1cf0/app/client/src/utils/validation/common.ts#L16)): An example of the value expected for the property. - - `autocompleteDataType`(required, [AutocompleteDataType](https://github.com/appsmithorg/appsmith/blob/e772fd4ff96accfb94818fa9f0b58dc6851a1cf0/app/client/src/utils/autocomplete/TernServer.ts#L64)): Describes how the auto-complete feature for this property should work. + - `autocompleteDataType`(required, [AutocompleteDataType](https://github.com/appsmithorg/appsmith/blob/e772fd4ff96accfb94818fa9f0b58dc6851a1cf0/app/client/src/utils/autocomplete/CodemirrorTernService.ts#L64)): Describes how the auto-complete feature for this property should work. - `strict` (optional, boolean): If set to `true`, values in `ValidationTypes.TEXT`, will not be cast to a string before validating. - `ignoreCase` (optional, boolean): If set to `true`, keys will be matched while ignoring case in `allowedKeys` parameter of the `ValidationTypes.OBJECT`. - `ValidationTypes.FUNCTION` is meant to be used sparingly, and as an escape hatch when the other `ValidationTypes` do not fit the requirements of validation. diff --git a/contributions/CustomJsLibrary.md b/contributions/CustomJsLibrary.md index 4c9c340dc822..313ed1e527c3 100644 --- a/contributions/CustomJsLibrary.md +++ b/contributions/CustomJsLibrary.md @@ -23,7 +23,7 @@ const extraLibraries = [ }, ] ``` -3. To make this show up in autocomplete of appsmith we will need to add this to the Tern server file `app/client/src/utils/autocomplete/TernServer.ts` +3. To make this show up in autocomplete of appsmith we will need to add this to the Tern server file `app/client/src/utils/autocomplete/CodemirrorTernService.ts` - Add a new def file for your library usages [ref](https://ternjs.net/doc/manual.html#typedef) under `app/client/src/constants/defs/` - Import that file in the Tern Server file and add the def in the `DEFS` array right at the top of file ``` diff --git a/contributions/docs/CONTRIBUTING.md b/contributions/docs/CONTRIBUTING.md deleted file mode 100644 index de28bbabbe05..000000000000 --- a/contributions/docs/CONTRIBUTING.md +++ /dev/null @@ -1,36 +0,0 @@ -# Contributing to Appsmith Documentation - -Thank you for your interest in Appsmith and taking the time out to contribute to our documentation. 🙌 -Feel free to propose changes to this document in a pull request. - -Appsmith uses Gitbook for [documentation](https://docs.appsmith.com). The docs are backed by the [appsmith-docs](https://github.com/appsmithorg/appsmith-docs) repository - -## Suggesting Improvements - -If you feel parts of our documentation can be improved or have incorrect information, you can open a new issue using our [Documentation Template](https://github.com/appsmithorg/appsmith/issues/new?assignees=Nikhil-Nandagopal&labels=Documentation&template=---documentation-improvement.md&title=%5BDocs%5D+) - -## Contributing - -Our [good first issues](https://github.com/appsmithorg/appsmith/issues?q=is%3Aissue+is%3Aopen+label%3A%22Good+First+Issue%22+label%3A%22Documentation%22+no%3Aassignee) or [Documentation issues](https://github.com/appsmithorg/appsmith-docs/issues) list is the best place to begin contributing - -### Updating the docs - -Before raising a pull request, ensure you have raised a corresponding issue and discussed it with a maintainer. This gives your pull request the highest chance of getting merged quickly. - -1. Fork the [appsmith-docs](https://github.com/appsmithorg/appsmith-docs) repo and branch out from the default branch. -2. If a new release is being created, contact a maintainer to update the default branch to mirror the new release version. -3. Read our [guidelines](#guidelines) for the section you wish to update -4. Add / Update the relevant files and commit them with a clear commit message -5. If you are creating a new page, don't forget to add an entry in the SUMMARY.md file of the main repo -6. Create a pull request in your fork to the default branch in the appsmithorg/appsmith-docs base repository -7. Link the issue of the base repository in your Pull request description. [Guide](https://docs.github.com/en/free-pro-team@latest/github/managing-your-work-on-github/linking-a-pull-request-to-an-issue) - -## Guidelines - -To maintain consistency, we have a set structure for the different types of documentation pages on appsmith. Refer to the following when updating the docs - -- [Documenting Widgets](Widgets.md) -- [Documenting Functions](InternalFunctions.md) -- [Documenting DB Integrations](DB%20Integrations.md) -- [Adding Guides](adding_guides.md) -- [Uploading Assets](UploadingAssets.md) diff --git a/contributions/docs/DB Integrations.md b/contributions/docs/DB Integrations.md deleted file mode 100644 index 2356d02a5e0b..000000000000 --- a/contributions/docs/DB Integrations.md +++ /dev/null @@ -1,21 +0,0 @@ -# DB Integration -Create a appsmith-docs/datasource-reference/.md file. -Follow the [asset-upload](./UploadingAssets.md) guidelines to upload and use an asset in docs. -Update the SUMMARY.md file with the link to the new document - -## Action docs template -Copy paste this template -``` ---- -description: >- - Integration Description ---- - -# Connection Settings - -# Querying the database - -# Using Queries in applications - -``` - diff --git a/contributions/docs/GlobalFunctions.md b/contributions/docs/GlobalFunctions.md index bdd083d45103..a9b1788c87f3 100644 --- a/contributions/docs/GlobalFunctions.md +++ b/contributions/docs/GlobalFunctions.md @@ -1,3 +1,5 @@ +# Global Function docs + Global functions in Appsmith are available through the right-hand pane and in the JS editor. They allow users to perform different tasks throughout the Appsmith application. #### Here are some pull requests you can use as an example: diff --git a/contributions/docs/TestAutomation.md b/contributions/docs/TestAutomation.md index 10187dd7b45e..fa347d3db688 100644 --- a/contributions/docs/TestAutomation.md +++ b/contributions/docs/TestAutomation.md @@ -1,6 +1,6 @@ # How can I contribute to the Cypress Test Suite? -1. Follow the setup document to set up [Appsmith locally](contributions/ClientSetup.md) and go through [the docs](https://docs.appsmith.com). The docs are backed by the [appsmith-docs](https://github.com/appsmithorg/appsmith-docs) repository. +1. Follow the setup document to set up [Appsmith locally](/contributions/ClientSetup.md) and go through [the docs](https://docs.appsmith.com). The docs are backed by the [appsmith-docs](https://github.com/appsmithorg/appsmith-docs) repository. 1. Once we have the setup in place, all dependencies will be available locally for test execution. diff --git a/contributions/docs/UploadingAssets.md b/contributions/docs/UploadingAssets.md deleted file mode 100644 index af087786d02f..000000000000 --- a/contributions/docs/UploadingAssets.md +++ /dev/null @@ -1,20 +0,0 @@ -# Uploading and using assets -Upload images & gifs to the `appsmith-docs/.gitbook/assets/` folder. -Asset files cannot be larger than 25mb. -Refer to the assets using a relative path from the root folder Ex. `.gitbook/assets/asset.jpg` - -## Embedding Images & Gifs -Images are gifs can be embedded into a page using the following syntax -``` -![Click to expand](/.gitbook/assets/asset-name.png) -``` -The images should be focused on the content and not include unnecessary parts of the UI ex. header / empty canvas etc. -Gifs can be recorded using the a screen recorder like loom and converted to a gif using gifski. Gifs should be of 26 - 30 fps and be of high quality. If you do not have access to a good gif converter, please upload the video as is and raise a PR. We will be happy to help with Gif creation! - -## Embedding Videos -Videos must be uploaded to the appsmith youtube channel. Contact nikhil@appsmith.com to have your video uploaded. -Videos must be of very high quality and abide by our [Code of Conduct](/CODE_OF_CONDUCT.md) -Videos can be embedded inside pages using the following syntax -``` -{% embed url="https://www.youtube.com/watch?v=mzqK0QIZRLs&feature=youtu.be" %} -``` diff --git a/contributions/docs/Widgets.md b/contributions/docs/Widgets.md deleted file mode 100644 index 5dc862ffee76..000000000000 --- a/contributions/docs/Widgets.md +++ /dev/null @@ -1,30 +0,0 @@ -# Widget docs -1. Create a appsmith-docs/widget-reference/.md file. -2. Follow the [asset-upload](UploadingAssets.md) guidelines to upload and use an asset in the docs. - -## Widget docs template -Copy paste this template -``` - -# WidgetName - - - -## Image/gif of the widget on the canvas with the icon of the widget in the sidebar - -## 2 different usages of the widget with API / Query data - -## Properties -| Property | Description | -| :------------ | :-------------------- | -| **property1** | Property1 description | -| **property2** | Property2 description | -| **property3** | Property3 description | - -| Action | Description | -| :---------- | :------------------ | -| **action1** | action1 description | -| **action2** | action2 description | -| **action3** | action3 description | -``` - diff --git a/contributions/docs/adding_guides.md b/contributions/docs/adding_guides.md deleted file mode 100644 index 7a4bd14a348f..000000000000 --- a/contributions/docs/adding_guides.md +++ /dev/null @@ -1,20 +0,0 @@ -## Adding a Guide - -1. Create an **appsmith-docs/how-to-guide/.md** file. Please Use valid markdown for all the content. -2. Follow the [asset-upload](https://github.com/appsmithorg/appsmith/blob/release/contributions/docs/UploadingAssets.md) guidelines to upload and use an asset in the docs. - -## Guide Template - -Start your guide using this template. Paste this template at the beginning of your document. - -``` ---- -description: >- - <> ---- - -# Name of the topic - -``` - -Refer to this [Guide](https://github.com/appsmithorg/appsmith-docs/blob/v1.3/how-to-guides/embed-appsmith-into-existing-application.md) as an example. diff --git a/deploy/docker/entrypoint.sh b/deploy/docker/entrypoint.sh index f0e8abe0ebba..6ba07cf3688a 100755 --- a/deploy/docker/entrypoint.sh +++ b/deploy/docker/entrypoint.sh @@ -172,9 +172,10 @@ init_replica_set() { if [[ ${mongo_state: -1} -eq 1 ]]; then echo "Mongodb cloud Replica Set is enabled" else - echo -e "\033[0;31m********************************************************************\033[0m" - echo -e "\033[0;31m* MongoDB Replica Set is not enabled *\033[0m" - echo -e "\033[0;31m********************************************************************\033[0m" + echo -e "\033[0;31m*************************************************************************************************************\033[0m" + echo -e "\033[0;31m* MongoDB Replica Set is not enabled *\033[0m" + echo -e "\033[0;31m* Please ensure the credentials provided for MongoDB, has `readWrite` and `clusterMonitor` roles. *\033[0m" + echo -e "\033[0;31m*************************************************************************************************************\033[0m" exit 1 fi fi diff --git a/deploy/docker/utils/package-lock.json b/deploy/docker/utils/package-lock.json index 51610f76f42d..1f7506dda28d 100644 --- a/deploy/docker/utils/package-lock.json +++ b/deploy/docker/utils/package-lock.json @@ -2618,9 +2618,9 @@ } }, "node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -5351,9 +5351,9 @@ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" }, "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "requires": { "brace-expansion": "^1.1.7" } diff --git a/deploy/helm/templates/ingress.yaml b/deploy/helm/templates/ingress.yaml index fcd113d1fef3..537a7771013b 100644 --- a/deploy/helm/templates/ingress.yaml +++ b/deploy/helm/templates/ingress.yaml @@ -44,6 +44,7 @@ spec: secretName: {{ .secretName }} {{- end }} {{- end }} + ingressClassName: {{ .Values.ingress.className }} rules: {{- if not .Values.ingress.hosts }} - host: diff --git a/static/images/how-it-works.svg b/static/images/how-it-works.svg new file mode 100644 index 000000000000..45d559d14f2a --- /dev/null +++ b/static/images/how-it-works.svg @@ -0,0 +1 @@ + \ No newline at end of file From d6759097421a6bffd5b34d05fa45b17cbab07ed4 Mon Sep 17 00:00:00 2001 From: Preet Date: Fri, 16 Dec 2022 09:39:46 -0500 Subject: [PATCH 287/708] enforce drag changes to all widgets on screen --- .../appsmith/autoLayout/FlexBoxComponent.tsx | 9 ++++++--- .../designSystems/appsmith/autoLayout/FlexComponent.tsx | 2 +- .../common/CanvasArenas/hooks/useAutoLayoutHighlights.ts | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx index 574592c9dccd..797f6f8d2496 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx @@ -65,6 +65,7 @@ export const FlexContainer = styled.div<{ leaveSpaceForWidgetName: boolean; isMobile?: boolean; isMainContainer: boolean; + isDragging: boolean; }>` display: ${({ useAutoLayout }) => (useAutoLayout ? "flex" : "block")}; flex-direction: ${({ direction }) => @@ -81,8 +82,8 @@ export const FlexContainer = styled.div<{ overflow-y: ${({ isMainContainer, isMobile }) => isMainContainer || isMobile ? "auto" : "hidden"}; - padding: ${({ leaveSpaceForWidgetName }) => - leaveSpaceForWidgetName ? "4px 4px 22px 4px;" : "4px;"}; + padding: ${({ isDragging, leaveSpaceForWidgetName }) => + !isDragging && leaveSpaceForWidgetName ? "4px 4px 22px 4px;" : "4px;"}; `; export const DEFAULT_HIGHLIGHT_SIZE = 4; @@ -130,7 +131,8 @@ function FlexBoxComponent(props: FlexBoxProps) { ? dragDetails?.draggingGroupCenter?.widgetId : ""; - const isDragging = useSelector(isCurrentCanvasDragging(props.widgetId)); + // const isDragging = useSelector(isCurrentCanvasDragging(props.widgetId)); + const isDragging: boolean = dragDetails?.draggedOn !== undefined; const renderChildren = () => { if (!props.children) return null; @@ -444,6 +446,7 @@ function FlexBoxComponent(props: FlexBoxProps) { 0 + (siblingCount > 0 ? (DEFAULT_MARGIN * siblingCount + 2) / siblingCount : 0) : props.componentWidth; diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index 9938fb640776..8d99990f3a58 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -195,7 +195,7 @@ export const useAutoLayoutHighlights = ({ ? highlight.alignment === FlexLayerAlignment.Start ? containerDimensions.width : 0 - : blocksToDraw[0].width; + : containerDimensions?.width / 3; highlight.height = rect.height; (highlight.el as HTMLElement).style.width = `${highlight.width}px`; highlights[index] = highlight; From 1ded2504fe132e4b95e61852ad79dd18237c309b Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Mon, 19 Dec 2022 11:22:36 +0530 Subject: [PATCH 288/708] fixing merge changes. --- app/client/src/widgets/ButtonGroupWidget/widget/index.tsx | 1 - app/client/src/widgets/CameraWidget/widget/index.tsx | 1 - app/client/src/widgets/IframeWidget/widget/index.tsx | 1 - app/client/src/widgets/ImageWidget/widget/index.tsx | 1 - 4 files changed, 4 deletions(-) diff --git a/app/client/src/widgets/ButtonGroupWidget/widget/index.tsx b/app/client/src/widgets/ButtonGroupWidget/widget/index.tsx index 935ca16bf9d8..555c08493bcb 100644 --- a/app/client/src/widgets/ButtonGroupWidget/widget/index.tsx +++ b/app/client/src/widgets/ButtonGroupWidget/widget/index.tsx @@ -20,7 +20,6 @@ import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; import { MinimumPopupRows } from "widgets/constants"; import ButtonGroupComponent from "../component"; import { getStylesheetValue } from "./helpers"; -import { Stylesheet } from "entities/AppTheming"; class ButtonGroupWidget extends BaseWidget< ButtonGroupWidgetProps, diff --git a/app/client/src/widgets/CameraWidget/widget/index.tsx b/app/client/src/widgets/CameraWidget/widget/index.tsx index 4f12373944e7..f8ecd5979d95 100644 --- a/app/client/src/widgets/CameraWidget/widget/index.tsx +++ b/app/client/src/widgets/CameraWidget/widget/index.tsx @@ -20,7 +20,6 @@ import { CameraModeTypes, MediaCaptureStatusTypes, } from "../constants"; -import { Stylesheet } from "entities/AppTheming"; class CameraWidget extends BaseWidget { static getPropertyPaneContentConfig() { diff --git a/app/client/src/widgets/IframeWidget/widget/index.tsx b/app/client/src/widgets/IframeWidget/widget/index.tsx index 7addb3ff336d..b45d81a2e7c7 100644 --- a/app/client/src/widgets/IframeWidget/widget/index.tsx +++ b/app/client/src/widgets/IframeWidget/widget/index.tsx @@ -10,7 +10,6 @@ import { import BaseWidget, { WidgetState } from "widgets/BaseWidget"; import IframeComponent from "../component"; import { IframeWidgetProps } from "../constants"; -import { Stylesheet } from "entities/AppTheming"; class IframeWidget extends BaseWidget { static getPropertyPaneContentConfig() { diff --git a/app/client/src/widgets/ImageWidget/widget/index.tsx b/app/client/src/widgets/ImageWidget/widget/index.tsx index 9de0add078d2..7d4e1660c23f 100644 --- a/app/client/src/widgets/ImageWidget/widget/index.tsx +++ b/app/client/src/widgets/ImageWidget/widget/index.tsx @@ -12,7 +12,6 @@ import { generateVerticalAlignmentConfig, } from "utils/layoutPropertiesUtils"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; -import { Stylesheet } from "entities/AppTheming"; class ImageWidget extends BaseWidget { constructor(props: ImageWidgetProps) { From 638eff52de413f888dad33c6727e30f055141bed Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Mon, 19 Dec 2022 14:35:25 +0530 Subject: [PATCH 289/708] fixing another layout issue. --- app/client/src/utils/hooks/useDynamicAppLayout.tsx | 9 --------- 1 file changed, 9 deletions(-) diff --git a/app/client/src/utils/hooks/useDynamicAppLayout.tsx b/app/client/src/utils/hooks/useDynamicAppLayout.tsx index bdadecdf0b21..2076599678b3 100644 --- a/app/client/src/utils/hooks/useDynamicAppLayout.tsx +++ b/app/client/src/utils/hooks/useDynamicAppLayout.tsx @@ -105,15 +105,6 @@ export const useDynamicAppLayout = () => { calculatedWidth -= APP_SETTINGS_PANE_WIDTH; } - // if explorer is closed or its preview mode, we don't need to subtract the EE width - if ( - isExplorerPinned === true && - !isPreviewMode && - appMode === APP_MODE.EDIT - ) { - calculatedWidth -= explorerWidth; - } - // if explorer is closed or its preview mode, we don't need to subtract the EE width if ( isExplorerPinned === true && From 14cbd7ddf07cf9f38b4cc7abe7d6152a57110b08 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Mon, 19 Dec 2022 14:44:37 +0530 Subject: [PATCH 290/708] fixing drop target changes. --- .../components/editorComponents/DropTargetComponent.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/client/src/components/editorComponents/DropTargetComponent.tsx b/app/client/src/components/editorComponents/DropTargetComponent.tsx index 931d5f61353a..c59ed3fb721e 100644 --- a/app/client/src/components/editorComponents/DropTargetComponent.tsx +++ b/app/client/src/components/editorComponents/DropTargetComponent.tsx @@ -199,7 +199,7 @@ export function DropTargetComponent(props: DropTargetComponentProps) { if (dropTargetRef.current) { const height = getDropTargetHeight( canDropTargetExtend, - isMobile, + isMobile && props.widgetId !== MAIN_CONTAINER_WIDGET_ID, isPreviewMode, rowRef.current, props.snapRowSpace, @@ -262,7 +262,7 @@ export function DropTargetComponent(props: DropTargetComponentProps) { const height = getDropTargetHeight( canDropTargetExtend, - isMobile, + isMobile && props.widgetId !== MAIN_CONTAINER_WIDGET_ID, isPreviewMode, rowRef.current, props.snapRowSpace, @@ -289,7 +289,8 @@ export function DropTargetComponent(props: DropTargetComponentProps) { ((isDragging && draggedOn === props.widgetId) || isResizing || isAutoHeightWithLimitsChanging) && - !isPreviewMode; + !isPreviewMode && + !props.useAutoLayout; const dropTargetRef = useRef(null); From 77b283e68a0d83efb1ab2a020c752490bbf03c7a Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Wed, 21 Dec 2022 11:10:09 +0530 Subject: [PATCH 291/708] Enable container extension(temp fix) --- .../CanvasSagas/AutoLayoutDraggingSagas.ts | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts b/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts index ee99a3c4598a..449da709c868 100644 --- a/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts +++ b/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts @@ -1,4 +1,3 @@ -import { isArray } from "lodash"; import { updateAndSaveLayout, WidgetAddChild } from "actions/pageActions"; import { ReduxAction, @@ -9,20 +8,22 @@ import { LayoutDirection, ResponsiveBehavior, } from "components/constants"; -import { HighlightInfo } from "pages/common/CanvasArenas/hooks/useAutoLayoutHighlights"; -import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; -import { all, call, put, select, takeLatest } from "redux-saga/effects"; -import log from "loglevel"; -import { getWidgets } from "sagas/selectors"; import { FlexLayer, LayerChild, } from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; -import { getUpdateDslAfterCreatingChild } from "sagas/WidgetAdditionSagas"; +import { GridDefaults } from "constants/WidgetConstants"; +import { isArray } from "lodash"; +import log from "loglevel"; +import { HighlightInfo } from "pages/common/CanvasArenas/hooks/useAutoLayoutHighlights"; +import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; +import { all, call, put, select, takeLatest } from "redux-saga/effects"; import { updateFlexChildColumns, updateSizeOfAllChildren, } from "sagas/AutoLayoutUtils"; +import { getWidgets } from "sagas/selectors"; +import { getUpdateDslAfterCreatingChild } from "sagas/WidgetAdditionSagas"; function* addWidgetAndReorderSaga( actionPayload: ReduxAction<{ @@ -214,6 +215,16 @@ function* reorderAutolayoutChildren(params: { ...newItems.slice(pos), ], }; + const parentWidget = allWidgets[allWidgets[parentId].parentId || "0"]; + const isAutoLayoutContainerCanvas = parentWidget.type === "CONTAINER_WIDGET"; + if (isAutoLayoutContainerCanvas) { + const height = + allWidgets[parentId].bottomRow / GridDefaults.DEFAULT_GRID_ROW_HEIGHT; + updatedWidgets[parentWidget.widgetId] = { + ...updatedWidgets[parentWidget.widgetId], + bottomRow: parentWidget.topRow + height, + }; + } return updatedWidgets; } From c71fe4dfda4a1c1b8e5820bebdb52383d1d047a8 Mon Sep 17 00:00:00 2001 From: Preet Date: Fri, 23 Dec 2022 23:15:13 -0500 Subject: [PATCH 292/708] wip --- .../appsmith/autoLayout/AutoLayoutLayer.tsx | 1 - .../appsmith/autoLayout/FlexBoxComponent.tsx | 322 ++---------------- .../appsmith/autoLayout/FlexComponent.tsx | 34 +- .../hooks/useAutoLayoutHighlights.ts | 37 +- .../CanvasArenas/hooks/useCanvasDragging.ts | 21 +- .../CanvasSagas/AutoLayoutDraggingSagas.ts | 13 +- .../src/utils/autoLayout/highlightUtils.ts | 312 +++++++++++++++++ 7 files changed, 428 insertions(+), 312 deletions(-) create mode 100644 app/client/src/utils/autoLayout/highlightUtils.ts diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx index ad772870bcb0..5ee553f616f6 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx @@ -20,7 +20,6 @@ export interface AutoLayoutLayerProps { widgetId: string; isMobile?: boolean; isCurrentCanvasDragging: boolean; - currentChildCount: number; wrapStart: boolean; wrapCenter: boolean; wrapEnd: boolean; diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx index 797f6f8d2496..28ddd5674791 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx @@ -11,7 +11,6 @@ import { import { APP_MODE } from "entities/App"; import { useSelector } from "react-redux"; import { getWidgets } from "sagas/selectors"; -import { isCurrentCanvasDragging } from "selectors/autoLayoutSelectors"; import { getAppMode } from "selectors/entitiesSelector"; import { getIsMobile } from "selectors/mainCanvasSelectors"; import AutoLayoutLayer from "./AutoLayoutLayer"; @@ -36,27 +35,6 @@ export interface FlexLayer { hasFillChild?: boolean; } -interface DropPositionProps { - isVertical: boolean; - alignment: FlexLayerAlignment; - layerIndex: number; - childIndex: number; - widgetId: string; - isNewLayer: boolean; - rowIndex: number; -} - -interface NewLayerProps { - alignment: FlexLayerAlignment; - childCount: number; - layerIndex: number; - isDragging: boolean; - isNewLayer: boolean; - isVertical: boolean; - map: { [key: string]: any }; - widgetId: string; -} - export const FlexContainer = styled.div<{ useAutoLayout?: boolean; direction?: LayoutDirection; @@ -88,29 +66,6 @@ export const FlexContainer = styled.div<{ export const DEFAULT_HIGHLIGHT_SIZE = 4; -export const DropPosition = styled.div<{ - isDragging: boolean; - isNewLayer: boolean; - isVertical: boolean; -}>` - width: ${({ isVertical }) => - isVertical ? `${DEFAULT_HIGHLIGHT_SIZE}px` : "calc(33% - 4px)"}; - height: ${({ isNewLayer, isVertical }) => - isVertical && !isNewLayer ? "auto" : `${DEFAULT_HIGHLIGHT_SIZE}px`}; - background-color: rgba(223, 158, 206, 0.6); - margin: ${({ isVertical }) => (isVertical ? "2px 4px" : "2px")}; - display: ${({ isDragging }) => (isDragging ? "block" : "none")}; - align-self: stretch; - opacity: 0; -`; - -export const NewLayerStyled = styled.div<{ - isDragging: boolean; -}>` - width: 100%; - height: ${({ isDragging }) => (isDragging ? "6px" : "0px")}; -`; - function FlexBoxComponent(props: FlexBoxProps) { // TODO: set isMobile as a prop at the top level const isMobile = useSelector(getIsMobile); @@ -123,13 +78,6 @@ function FlexBoxComponent(props: FlexBoxProps) { const { dragDetails } = useSelector( (state: AppState) => state.ui.widgetDragResize, ); - const draggedOn: string | undefined = dragDetails - ? dragDetails?.draggedOn - : undefined; - - const draggedWidget = dragDetails - ? dragDetails?.draggingGroupCenter?.widgetId - : ""; // const isDragging = useSelector(isCurrentCanvasDragging(props.widgetId)); const isDragging: boolean = dragDetails?.draggedOn !== undefined; @@ -138,15 +86,7 @@ function FlexBoxComponent(props: FlexBoxProps) { if (!props.children) return null; if (!props.useAutoLayout) return props.children; if (direction === LayoutDirection.Horizontal) { - return addDropPositions({ - arr: props.children as any, - childCount: 0, - layerIndex: 0, - alignment: FlexLayerAlignment.Start, - isVertical: true, - isNewLayer: true, - addInitialHighlight: true, - }); + return props.children; } /** @@ -175,192 +115,32 @@ function FlexBoxComponent(props: FlexBoxProps) { }); } - function DropPositionComponent(props: DropPositionProps) { - return ( - - ); - } - - function NewLayerComponent(props: NewLayerProps): JSX.Element { - const { childCount, isDragging, layerIndex, map, widgetId } = props; - - const { element: verticalHighlights } = processIndividualLayer( - { children: [], hasFillChild: false }, - childCount, - layerIndex, - map, - true, - ); - - return ( - - {verticalHighlights} - - ); - } - - const getDropPositionKey = ( - index: number, - alignment: FlexLayerAlignment, - layerIndex: number, - isVertical: boolean, - ): string => - `drop-layer-${props.widgetId}-${layerIndex}-${alignment}-${index}-${ - isVertical ? "vertical" : "horizontal" - }-${Math.random()}`; - - const addDropPositions = (data: { - arr: any[]; - childCount: number; - layerIndex: number; - alignment: FlexLayerAlignment; - isVertical: boolean; - isNewLayer: boolean; - addInitialHighlight: boolean; - }): any[] => { - const { - addInitialHighlight, - alignment, - arr, - childCount, - isNewLayer, - isVertical, - layerIndex, - } = data; - const res = addInitialHighlight - ? [ - , - ] - : []; - let count = 0; - if (arr) { - for (const item of arr) { - const widgetId = item - ? (item as JSX.Element)?.props.widgetId - : undefined; - if (draggedWidget && widgetId && draggedWidget === widgetId) continue; - count += 1; - res.push(item); - res.push( - , - ); - } + function processLayers(map: { [key: string]: any }) { + const layers = []; + let index = 0; + for (const layer of props.flexLayers) { + layers.push(processIndividualLayer(layer, map, index)); + index += 1; } - return res; - }; + return layers; + } function getColumns(id: string, isMobile: boolean): number { const widget = allWidgets[id]; if (!widget) return 0; return isMobile && widget.mobileRightColumn - ? widget.mobileRightColumn - : widget.rightColumn; - } - - function processLayers(map: { [key: string]: any }) { - const layers = []; - let childCount = 0; - let layerIndex = 0; - for (const layer of props.flexLayers) { - const isEmpty = - layer?.children?.filter( - (child: LayerChild) => child.id !== draggedWidget, - ).length === 0; - - !isEmpty && - layers.push( - , - ); - - const { count, element } = processIndividualLayer( - layer, - childCount, - layerIndex, - map, - ); - childCount += count; - if (!isEmpty) { - layerIndex += 1; - layers.push(element); - } - } - layers.push( - , - ); - return layers; + ? widget.mobileRightColumn - widget.leftColumn + : widget.rightColumn - widget.leftColumn; } function processIndividualLayer( layer: FlexLayer, - childCount: number, - index: number, map: { [key: string]: any }, - isNewLayer = false, + index: number, ) { const { children } = layer; - let count = 0; - let start = [], + const start = [], center = [], end = []; let startColumns = 0, @@ -368,7 +148,6 @@ function FlexBoxComponent(props: FlexBoxProps) { endColumns = 0; for (const child of children) { - count += 1; const widget = map[child.id]; if (child.align === "end") { @@ -382,64 +161,25 @@ function FlexBoxComponent(props: FlexBoxProps) { startColumns += getColumns(child.id, isMobile); } } - /** - * Add drop positions - */ - const startLength = start.length, - centerLength = center.length; - start = addDropPositions({ - arr: start, - childCount, - layerIndex: index, - alignment: FlexLayerAlignment.Start, - isVertical: !isNewLayer, - isNewLayer, - addInitialHighlight: !( - start.length === 0 && centerColumns + endColumns > 60 - ), - }); - center = addDropPositions({ - arr: center, - childCount: childCount + startLength, - layerIndex: index, - alignment: FlexLayerAlignment.Center, - isVertical: !isNewLayer, - isNewLayer, - addInitialHighlight: !(startColumns > 25 || endColumns > 25), - }); - end = addDropPositions({ - arr: end, - childCount: childCount + startLength + centerLength, - layerIndex: index, - alignment: FlexLayerAlignment.End, - isVertical: !isNewLayer, - isNewLayer, - addInitialHighlight: !(centerColumns + startColumns > 60), - }); - - return { - element: ( - 64} - wrapEnd={endColumns > 64} - wrapLayer={startColumns + centerColumns + endColumns > 64} - wrapStart={startColumns > 64} - /> - ), - count, - }; + return ( + 64} + wrapEnd={endColumns > 64} + wrapLayer={startColumns + centerColumns + endColumns > 64} + wrapStart={startColumns > 64} + /> + ); } return ( diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx index 25e8224d5e1e..27e44ce6ab05 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx @@ -1,4 +1,10 @@ -import React, { ReactNode, useCallback } from "react"; +import React, { + ReactNode, + useCallback, + useEffect, + useRef, + useState, +} from "react"; import styled from "styled-components"; import { AppState } from "ce/reducers"; @@ -50,6 +56,7 @@ const FlexWidget = styled.div<{ isAffectedByDrag: boolean; parentId?: string; flexVerticalAlignment: FlexVerticalAlignment; + ref: any; }>` position: relative; z-index: ${({ zIndex }) => zIndex}; @@ -74,6 +81,8 @@ const FlexWidget = styled.div<{ const DEFAULT_MARGIN = 16; export function FlexComponent(props: AutoLayoutProps) { + const ref = useRef(); + const [observer, setObserver] = useState(null); const isMobile = useSelector(getIsMobile); const isSnipingMode = useSelector(snipingModeSelector); const clickToSelectWidget = useClickToSelectWidget(props.widgetId); @@ -101,6 +110,28 @@ export function FlexComponent(props: AutoLayoutProps) { const stopEventPropagation = (e: any) => { !isSnipingMode && e.stopPropagation(); }; + + const callback = (mutations: any, observer: any) => { + console.log("#### mutations", mutations); + if (mutations.length) + console.log("#### mutations[0].target", mutations[0].target.offsetTop); + }; + + useEffect(() => { + const observer = new MutationObserver(callback); + setObserver(observer); + return () => observer.disconnect(); + }, []); + + useEffect(() => { + if (!ref.current || !observer) return; + return; + observer.observe(ref.current, { + attributes: true, + childList: false, + subtree: false, + }); + }, [observer, ref.current]); /** * In a vertical stack, * Fill widgets grow / shrink to take up all the available space. @@ -150,6 +181,7 @@ export function FlexComponent(props: AutoLayoutProps) { onClickCapture={onClickFn} padding={WIDGET_PADDING} parentId={props.parentId} + ref={ref} zIndex={zIndex} zIndexOnHover={onHoverZIndex} > diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index 582cea0fee43..f82d1cceaf6f 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -6,6 +6,7 @@ import { import { useSelector } from "react-redux"; import { ReflowDirection } from "reflow/reflowTypes"; import { getWidgets } from "sagas/selectors"; +import { deriveHighlightsFromLayers } from "utils/autoLayout/highlightUtils"; import WidgetFactory from "utils/WidgetFactory"; import { WidgetDraggingBlock } from "./useBlocksToBeDraggedOnCanvas"; @@ -25,7 +26,8 @@ export interface HighlightInfo { width: number; // width of the highlight. height: number; // height of the highlight. isVertical: boolean; // determines if the highlight is vertical or horizontal. - el: Element; // dom node of the highlight. + el?: Element; // dom node of the highlight. + canvasId: string; // widgetId of the canvas to which the highlight belongs. } export interface AutoLayoutHighlightProps { @@ -131,6 +133,7 @@ export const useAutoLayoutHighlights = ({ height: rect.height, isVertical: false, el, + canvasId: "0", }, ); if (!highlight.isVertical) newLayers[highlights.length] = highlight.posY; @@ -239,15 +242,21 @@ export const useAutoLayoutHighlights = ({ moveDirection: ReflowDirection, // acceleration: number, ): HighlightInfo | undefined => { - if (!highlights) return; - updateHighlights(moveDirection); - - const highlight: HighlightInfo = getHighlightPayload(e, moveDirection); + highlights = allWidgets[canvasId].highlights; + if (!highlights) + highlights = deriveHighlightsFromLayers(allWidgets, canvasId); - if (!highlight) return; + if (!highlights) return; + // updateHighlights(moveDirection); - updateSelection(highlight); + const highlight: HighlightInfo | undefined = getHighlightPayload( + e, + moveDirection, + ); + // updateSelection(highlight); + console.log("#### selection", highlight); + lastActiveHighlight = highlight; return highlight; }; @@ -255,7 +264,7 @@ export const useAutoLayoutHighlights = ({ e: any, moveDirection?: ReflowDirection, val?: XYCord, - ): HighlightInfo => { + ): HighlightInfo | undefined => { let base: HighlightInfo[] = []; if (!highlights || !highlights.length) highlights = getDropPositions(); base = highlights; @@ -267,8 +276,8 @@ export const useAutoLayoutHighlights = ({ let filteredHighlights: HighlightInfo[] = []; filteredHighlights = getViableDropPositions(base, pos, moveDirection); - - const arr = filteredHighlights.sort((a, b) => { + if (!filteredHighlights || !filteredHighlights?.length) return; + const arr = [...filteredHighlights]?.sort((a, b) => { return ( calculateDistance( a, @@ -284,7 +293,7 @@ export const useAutoLayoutHighlights = ({ ) ); }); - toggleHighlightVisibility(base, arr[0]); + // toggleHighlightVisibility(base, arr[0]); // console.log("#### arr", arr, base, moveDirection); return arr[0]; }; @@ -392,7 +401,11 @@ export const useAutoLayoutHighlights = ({ const getDropInfo = (val: XYCord): HighlightInfo | undefined => { if (lastActiveHighlight) return lastActiveHighlight; - const payload: HighlightInfo = getHighlightPayload(null, undefined, val); + const payload: HighlightInfo | undefined = getHighlightPayload( + null, + undefined, + val, + ); if (!payload) return; lastActiveHighlight = payload; return payload; diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index aaf2ea93f030..f6bb1987d74c 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -601,15 +601,18 @@ export const useCanvasDragging = ( } else if (!isUpdatingRows) { currentDirection.current = getMouseMoveDirection(e); triggerReflow(e, firstMove); + let highlight: HighlightInfo | undefined; if ( useAutoLayout && isCurrentDraggedCanvas && currentDirection.current !== ReflowDirection.UNSET - ) - debounce(() => { - highlightDropPosition(e, currentDirection.current); - }, 100)(); - renderBlocks(); + ) { + // debounce(() => { + // highlightDropPosition(e, currentDirection.current); + // }, 100)(); + highlight = highlightDropPosition(e, currentDirection.current); + } + renderBlocks(highlight); } scrollObj.lastMouseMoveEvent = { offsetX: e.offsetX, @@ -677,7 +680,7 @@ export const useCanvasDragging = ( }, ); - const renderBlocks = () => { + const renderBlocks = (highlight?: HighlightInfo | undefined) => { if ( slidingArenaRef.current && isCurrentDraggedCanvas && @@ -699,6 +702,12 @@ export const useCanvasDragging = ( drawBlockOnCanvas(each); }); } + if (highlight) { + canvasCtx.fillStyle = "rgba(196, 139, 181, 1)"; + const { height, posX, posY, width } = highlight; + canvasCtx.fillRect(posX, posY, width, height); + canvasCtx.save(); + } canvasCtx.restore(); } }; diff --git a/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts b/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts index 449da709c868..8d82c8ed484b 100644 --- a/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts +++ b/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts @@ -24,6 +24,7 @@ import { } from "sagas/AutoLayoutUtils"; import { getWidgets } from "sagas/selectors"; import { getUpdateDslAfterCreatingChild } from "sagas/WidgetAdditionSagas"; +import { deriveHighlightsFromLayers } from "utils/autoLayout/highlightUtils"; function* addWidgetAndReorderSaga( actionPayload: ReduxAction<{ @@ -225,7 +226,17 @@ function* reorderAutolayoutChildren(params: { bottomRow: parentWidget.topRow + height, }; } - + const highlights: HighlightInfo[] = deriveHighlightsFromLayers( + updatedWidgets, + parentId, + ); + updatedWidgets = { + ...updatedWidgets, + [parentId]: { + ...updatedWidgets[parentId], + highlights, + }, + }; return updatedWidgets; } diff --git a/app/client/src/utils/autoLayout/highlightUtils.ts b/app/client/src/utils/autoLayout/highlightUtils.ts new file mode 100644 index 000000000000..d8050ce9dee0 --- /dev/null +++ b/app/client/src/utils/autoLayout/highlightUtils.ts @@ -0,0 +1,312 @@ +import { FlexLayerAlignment } from "components/constants"; +import { + DEFAULT_HIGHLIGHT_SIZE, + FlexLayer, + LayerChild, +} from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; +import { HighlightInfo } from "pages/common/CanvasArenas/hooks/useAutoLayoutHighlights"; +import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; + +/** + * @param allWidgets : CanvasWidgetsReduxState + * @param canvasId : string + * @param draggedWidgets : string[] + * @returns widgets: CanvasWidgetsReduxState + * + * This function is used to derive highlights from flex layers and stored in widget dsl. + */ +export function deriveHighlightsFromLayers( + allWidgets: CanvasWidgetsReduxState, + canvasId: string, + draggedWidgets: string[] = [], +): HighlightInfo[] { + const widgets = { ...allWidgets }; + try { + const canvas = widgets[canvasId]; + if (!canvas) return []; + + const columns: number = canvas.rightColumn - canvas.leftColumn; + const columnSpace: number = + canvas.parentColumnSpace === 1 && canvas.parentId + ? widgets[canvas.parentId].parentColumnSpace + : canvas.parentColumnSpace; + const canvasWidth: number = columns * columnSpace; + + const layers: FlexLayer[] = canvas.flexLayers; + const highlights: HighlightInfo[] = []; + let childCount = 0; + let layerIndex = 0; + // TODO: remove offsetTop and use child positions after widget positioning on grid is solved. + let offsetTop = 0; // used to calculate distance of a highlight from parents's top. + for (const layer of layers) { + /** + * If the layer is empty, after discounting the dragged widgets, + * then don't process it for vertical highlights. + */ + // const isEmpty = + // layer?.children?.filter( + // (child: LayerChild) => draggedWidgets.indexOf(child.id) === -1, + // ).length === 0; + const tallestChild = layer.children?.reduce((acc, child) => { + const widget = widgets[child.id]; + return Math.max( + acc, + (widget.bottomRow - widget.topRow) * widget.parentRowSpace, + ); + }, 0); + + /** + * Add a layer of horizontal highlights before each flex layer + * to account for new vertical drop positions. + */ + highlights.push( + ...generateHorizontalHighlights( + childCount, + layerIndex, + offsetTop, + canvasWidth, + canvasId, + ), + ); + const payload: VerticalHighlightsPayload = generateVerticalHighlights({ + widgets, + layer, + childCount, + layerIndex, + height: tallestChild, + offsetTop, + canvasWidth, + canvasId, + }); + + highlights.push(...payload.highlights); + childCount += payload.childCount; + offsetTop += tallestChild || 0; + layerIndex += 1; + } + // Add a layer of horizontal highlights for the empty space at the bottom of a stack. + highlights.push( + ...generateHorizontalHighlights( + childCount, + layerIndex, + offsetTop, + canvasWidth, + canvasId, + ), + ); + return highlights; + } catch (e) { + console.error(e); + return []; + } +} +interface VerticalHighlightsPayload { + childCount: number; + highlights: HighlightInfo[]; +} + +function generateVerticalHighlights(data: { + widgets: CanvasWidgetsReduxState; + layer: FlexLayer; + childCount: number; + layerIndex: number; + height: number; + offsetTop: number; + canvasWidth: number; + canvasId: string; +}): VerticalHighlightsPayload { + const { + canvasId, + canvasWidth, + childCount, + height, + layer, + layerIndex, + offsetTop, + widgets, + } = data; + const { children } = layer; + + let count = 0; + const startChildren = [], + centerChildren = [], + endChildren = []; + let startColumns = 0, + endColumns = 0; + + for (const child of children) { + const widget = widgets[child.id]; + if (!widget) continue; + count += 1; + + if (child.align === FlexLayerAlignment.End) { + endChildren.push(widget); + endColumns += widget.rightColumn - widget.leftColumn; + } else if (child.align === FlexLayerAlignment.Center) { + centerChildren.push(widget); + } else { + startChildren.push(widget); + startColumns += widget.rightColumn - widget.leftColumn; + } + } + + return { + highlights: [ + ...generateHighlightsForSubWrapper({ + arr: startChildren, + childCount, + layerIndex, + alignment: FlexLayerAlignment.Start, + height, + offsetTop, + canvasId, + parentColumnSpace: widgets[canvasId].parentColumnSpace, + canvasWidth, + }), + ...generateHighlightsForSubWrapper({ + arr: centerChildren, + childCount: childCount + startChildren.length, + layerIndex, + alignment: FlexLayerAlignment.Center, + height, + offsetTop, + canvasId, + parentColumnSpace: widgets[canvasId].parentColumnSpace, + canvasWidth, + avoidInitialHighlight: startColumns > 25 || endColumns > 25, + }), + ...generateHighlightsForSubWrapper({ + arr: endChildren, + childCount: childCount + startChildren.length + centerChildren.length, + layerIndex, + alignment: FlexLayerAlignment.End, + height, + offsetTop, + canvasId, + parentColumnSpace: widgets[canvasId].parentColumnSpace, + canvasWidth, + }), + ], + childCount: count, + }; +} + +function generateHighlightsForSubWrapper(data: { + arr: any[]; + childCount: number; + layerIndex: number; + alignment: FlexLayerAlignment; + height: number; + offsetTop: number; + canvasId: string; + parentColumnSpace: number; + canvasWidth: number; + avoidInitialHighlight?: boolean; +}): HighlightInfo[] { + const { + alignment, + arr, + avoidInitialHighlight, + canvasId, + canvasWidth, + childCount, + height, + layerIndex, + offsetTop, + parentColumnSpace, + } = data; + const res: HighlightInfo[] = []; + let count = 0; + let lastWidgetWidth = 0; + for (const child of arr) { + const { leftColumn, rightColumn, widgetId } = child; + const el = document.querySelector(`.auto-layout-child-${widgetId}`); + if (!el) continue; + // TODO: remove boundingClientRect after widget positioning on grid is solved. + const { x } = el.getBoundingClientRect(); + res.push({ + isNewLayer: false, + index: count + childCount, + layerIndex, + rowIndex: count, + alignment, + posX: x, + posY: offsetTop, + width: DEFAULT_HIGHLIGHT_SIZE, + height, + isVertical: true, + canvasId, + }); + count += 1; + lastWidgetWidth = (rightColumn - leftColumn) * parentColumnSpace; + } + if (!avoidInitialHighlight) + res.push({ + isNewLayer: false, + index: count + childCount, + layerIndex, + rowIndex: count, + alignment, + posX: getPositionForInitialHighlight( + res, + alignment, + lastWidgetWidth, + canvasWidth, + ), + posY: offsetTop, + width: DEFAULT_HIGHLIGHT_SIZE, + height, + isVertical: true, + canvasId, + }); + return res; +} + +function getPositionForInitialHighlight( + highlights: HighlightInfo[], + alignment: FlexLayerAlignment, + width: number, + containerWidth: number, +): number { + if (alignment === FlexLayerAlignment.End) { + return containerWidth - 2; + // return highlights[highlights.length - 1].posX - width; + } else if (alignment === FlexLayerAlignment.Center) { + if (!highlights.length) return containerWidth / 2; + return highlights[highlights.length - 1].posX + width; + } else { + if (!highlights.length) return 2; + return highlights[highlights.length - 1].posX + width; + } +} + +function generateHorizontalHighlights( + childIndex: number, + layerIndex: number, + offsetTop: number, + containerWidth: number, + canvasId: string, +): HighlightInfo[] { + const width = containerWidth / 3; + const arr: HighlightInfo[] = []; + [ + FlexLayerAlignment.Start, + FlexLayerAlignment.Center, + FlexLayerAlignment.End, + ].forEach((alignment, index) => { + arr.push({ + isNewLayer: true, + index: childIndex, + layerIndex, + rowIndex: 0, + alignment, + posX: width * index, + posY: offsetTop, + width, + height: DEFAULT_HIGHLIGHT_SIZE, + isVertical: false, + canvasId, + }); + }); + return arr; +} From a5acd7744eeab917a6b947ad7e57624e364ae8f0 Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 26 Dec 2022 09:30:20 -0500 Subject: [PATCH 293/708] wip --- .../appsmith/autoLayout/FlexComponent.tsx | 19 ++++--- .../hooks/useAutoLayoutHighlights.ts | 54 +------------------ .../src/resizable/resizenreflow/index.tsx | 2 +- .../CanvasSagas/AutoLayoutDraggingSagas.ts | 22 ++++---- .../src/utils/autoLayout/highlightUtils.ts | 1 - 5 files changed, 23 insertions(+), 75 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx index 27e44ce6ab05..18ecfdb10aac 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx @@ -52,8 +52,6 @@ const FlexWidget = styled.div<{ padding: number; zIndex: number; zIndexOnHover: number; - dragMargin: number; - isAffectedByDrag: boolean; parentId?: string; flexVerticalAlignment: FlexVerticalAlignment; ref: any; @@ -66,8 +64,7 @@ const FlexWidget = styled.div<{ isMobile ? "auto" : Math.floor(componentHeight) + "px"}; min-height: 30px; - padding: ${({ isAffectedByDrag, padding }) => - isAffectedByDrag ? 0 : padding + "px"}; + padding: ${({ padding }) => padding + "px"}; flex-grow: ${({ isFillWidget }) => (isFillWidget ? "1" : "0")}; align-self: ${({ flexVerticalAlignment }) => flexVerticalAlignment}; @@ -75,7 +72,6 @@ const FlexWidget = styled.div<{ &:hover { z-index: ${({ zIndexOnHover }) => zIndexOnHover} !important; } - margin-top: ${({ isAffectedByDrag }) => (isAffectedByDrag ? "4px" : "0px")}; `; const DEFAULT_MARGIN = 16; @@ -114,7 +110,12 @@ export function FlexComponent(props: AutoLayoutProps) { const callback = (mutations: any, observer: any) => { console.log("#### mutations", mutations); if (mutations.length) - console.log("#### mutations[0].target", mutations[0].target.offsetTop); + console.log( + "#### mutations[0].target", + mutations[0].target.classList[3], + mutations[0].target.offsetTop, + observer, + ); }; useEffect(() => { @@ -125,7 +126,7 @@ export function FlexComponent(props: AutoLayoutProps) { useEffect(() => { if (!ref.current || !observer) return; - return; + // return; observer.observe(ref.current, { attributes: true, childList: false, @@ -170,11 +171,9 @@ export function FlexComponent(props: AutoLayoutProps) { { - const highlight = highlights[index]; - if (!highlight || !highlight.el) return highlight; - const rect: DOMRect = highlight.el.getBoundingClientRect(); - - highlight.posX = rect.x - containerDimensions.left; - highlight.posY = rect.y - containerDimensions.top; - highlight.width = isFillWidget - ? highlight.alignment === FlexLayerAlignment.Start - ? containerDimensions.width - : 0 - : containerDimensions?.width / 3; - highlight.height = rect.height; - (highlight.el as HTMLElement).style.width = `${highlight.width}px`; - highlights[index] = highlight; - - return highlight; - }; - - const updateHighlights = (moveDirection?: ReflowDirection) => { - if (!highlights?.length || !moveDirection) return; - highlights.map((highlight: HighlightInfo, index: number) => { - let updatedHighlight: HighlightInfo = highlight; - if (highlight.isNewLayer || !highlight.height) - updatedHighlight = updateHighlight(index); - return updatedHighlight; - }); - }; - - const toggleHighlightVisibility = ( - arr: HighlightInfo[], - selected: HighlightInfo, - ): void => { - arr.forEach((each: HighlightInfo) => { - const el = each.el as HTMLElement; - // if (!isVerticalStack) el.style.opacity = "1"; - el.style.opacity = selected === each ? "1" : "0"; - }); - }; - - const updateSelection = (highlight: HighlightInfo): void => { - if (lastActiveHighlight) { - const lastEl = lastActiveHighlight?.el as HTMLElement; - if (lastEl) lastEl.style.backgroundColor = "rgba(223, 158, 206, 0.6)"; - } - const el = highlight.el as HTMLElement; - if (el) el.style.backgroundColor = "rgba(196, 139, 181, 1)"; - lastActiveHighlight = highlight; - }; - const highlightDropPosition = ( e: any, moveDirection: ReflowDirection, @@ -255,7 +205,7 @@ export const useAutoLayoutHighlights = ({ ); // updateSelection(highlight); - console.log("#### selection", highlight); + // console.log("#### selection", highlight); lastActiveHighlight = highlight; return highlight; }; diff --git a/app/client/src/resizable/resizenreflow/index.tsx b/app/client/src/resizable/resizenreflow/index.tsx index d8d33ec4f164..7c173be00c12 100644 --- a/app/client/src/resizable/resizenreflow/index.tsx +++ b/app/client/src/resizable/resizenreflow/index.tsx @@ -540,7 +540,7 @@ export function ReflowResizable(props: ResizableProps) { }} immediate={newDimensions.reset ? true : false} to={{ - width: props.isAffectedByDrag ? "auto" : widgetWidth, + width: widgetWidth, height: props.isMobile ? "auto" : widgetHeight, transform: `translate3d(${newDimensions.x}px,${newDimensions.y}px,0)`, }} diff --git a/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts b/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts index 8d82c8ed484b..cf59a23f64bc 100644 --- a/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts +++ b/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts @@ -226,17 +226,17 @@ function* reorderAutolayoutChildren(params: { bottomRow: parentWidget.topRow + height, }; } - const highlights: HighlightInfo[] = deriveHighlightsFromLayers( - updatedWidgets, - parentId, - ); - updatedWidgets = { - ...updatedWidgets, - [parentId]: { - ...updatedWidgets[parentId], - highlights, - }, - }; + // const highlights: HighlightInfo[] = deriveHighlightsFromLayers( + // updatedWidgets, + // parentId, + // ); + // updatedWidgets = { + // ...updatedWidgets, + // [parentId]: { + // ...updatedWidgets[parentId], + // highlights, + // }, + // }; return updatedWidgets; } diff --git a/app/client/src/utils/autoLayout/highlightUtils.ts b/app/client/src/utils/autoLayout/highlightUtils.ts index d8050ce9dee0..7ab0db232c6c 100644 --- a/app/client/src/utils/autoLayout/highlightUtils.ts +++ b/app/client/src/utils/autoLayout/highlightUtils.ts @@ -2,7 +2,6 @@ import { FlexLayerAlignment } from "components/constants"; import { DEFAULT_HIGHLIGHT_SIZE, FlexLayer, - LayerChild, } from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; import { HighlightInfo } from "pages/common/CanvasArenas/hooks/useAutoLayoutHighlights"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; From e55ee027ffa502e868f079f02325ebc19be5b835 Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 26 Dec 2022 09:59:19 -0500 Subject: [PATCH 294/708] merge fix --- app/client/src/pages/Editor/MainContainer.tsx | 1 + app/client/src/sagas/WidgetOperationSagas.tsx | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/client/src/pages/Editor/MainContainer.tsx b/app/client/src/pages/Editor/MainContainer.tsx index 1e867c4dfb61..e35760313ba2 100644 --- a/app/client/src/pages/Editor/MainContainer.tsx +++ b/app/client/src/pages/Editor/MainContainer.tsx @@ -17,6 +17,7 @@ import { getExplorerWidth } from "selectors/explorerSelector"; import BottomBar from "./BottomBar"; import WidgetsEditor from "./WidgetsEditor"; import EditorsRouter from "./routes"; +import styled from "styled-components"; const SentryRoute = Sentry.withSentryRouting(Route); diff --git a/app/client/src/sagas/WidgetOperationSagas.tsx b/app/client/src/sagas/WidgetOperationSagas.tsx index ef8a90343f32..bd8e76dda814 100644 --- a/app/client/src/sagas/WidgetOperationSagas.tsx +++ b/app/client/src/sagas/WidgetOperationSagas.tsx @@ -77,7 +77,6 @@ import { } from "entities/Widget/utils"; import { getDataTree } from "selectors/dataTreeSelectors"; import { ColumnProperties } from "widgets/TableWidget/component/Constants"; -import { getAllPaths } from "workers/Evaluation/evaluationUtils"; import { validateProperty } from "./EvaluationsSaga"; import { generateAutoHeightLayoutTreeAction } from "actions/autoHeightActions"; @@ -156,6 +155,7 @@ import { WIDGET_PASTE_PADDING, } from "./WidgetOperationUtils"; import { widgetSelectionSagas } from "./WidgetSelectionSagas"; +import { getAllPaths } from "ce/workers/Evaluation/evaluationUtils"; export function* updateAllChildCanvasHeights( currentContainerLikeWidgetId: string, From fcaa223b812d87cc154be70218a21dc663425582 Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 26 Dec 2022 13:13:37 -0500 Subject: [PATCH 295/708] calculate positions for hug widgets --- app/client/src/sagas/AutoLayoutUtils.ts | 156 ++++++++++++++++++ .../CanvasSagas/AutoLayoutDraggingSagas.ts | 9 +- 2 files changed, 164 insertions(+), 1 deletion(-) diff --git a/app/client/src/sagas/AutoLayoutUtils.ts b/app/client/src/sagas/AutoLayoutUtils.ts index 7dca477f6bb4..eb65219e8849 100644 --- a/app/client/src/sagas/AutoLayoutUtils.ts +++ b/app/client/src/sagas/AutoLayoutUtils.ts @@ -315,3 +315,159 @@ function checkIsNotVerticalStack(widget: any): boolean { widget.positioning !== Positioning.Vertical ); } + +/** + * Calculate widget position on canvas. + */ +export function updateWidgetPositions( + allWidgets: CanvasWidgetsReduxState, + parentId: string, +): CanvasWidgetsReduxState { + let widgets = { ...allWidgets }; + try { + const parent = widgets[parentId]; + if (!parent || !parent.flexLayers || !parent.flexLayers?.length) + return widgets; + + let height = 0; + for (const layer of parent.flexLayers) { + const payload: { + height: number; + widgets: CanvasWidgetsReduxState; + } = calculateWidgetPositions(widgets, layer, height); + widgets = payload.widgets; + height += payload.height; + } + return widgets; + } catch (e) { + console.error(e); + return widgets; + } +} + +function calculateWidgetPositions( + allWidgets: CanvasWidgetsReduxState, + layer: FlexLayer, + topRow: number, +): { height: number; widgets: CanvasWidgetsReduxState } { + let widgets = { ...allWidgets }; + + const startChildren = [], + centerChildren = [], + endChildren = []; + let startColumns = 0, + centerColumns = 0, + endColumns = 0; + let startSize = 0, + centerSize = 0, + endSize = 0; + // Calculate the number of columns occupied by each alignment. + for (const child of layer.children) { + const widget = widgets[child.id]; + if (child.align === FlexLayerAlignment.Start) { + startChildren.push(widget); + startColumns += widget.rightColumn - widget.leftColumn; + } else if (child.align === FlexLayerAlignment.Center) { + centerChildren.push(widget); + centerColumns += widget.rightColumn - widget.leftColumn; + } else if (child.align === FlexLayerAlignment.End) { + endChildren.push(widget); + endColumns += widget.rightColumn - widget.leftColumn; + } + } + + const arr: { alignment: FlexLayerAlignment; columns: number }[] = [ + { alignment: FlexLayerAlignment.Start, columns: startColumns }, + { alignment: FlexLayerAlignment.Center, columns: centerColumns }, + { alignment: FlexLayerAlignment.End, columns: endColumns }, + ].sort((a, b) => b.columns - a.columns); + + const sizes: { + alignment: FlexLayerAlignment; + columns: number; + }[] = getAlignmentSizes(arr, 64, []); + + for (const each of sizes) { + if (each.alignment === FlexLayerAlignment.Start) { + startSize = each.columns; + } else if (each.alignment === FlexLayerAlignment.Center) { + centerSize = each.columns; + } else if (each.alignment === FlexLayerAlignment.End) { + endSize = each.columns; + } + } + + let maxHeight = 0; + // Calculate positions for start aligned children. + let rightColumn = 0; + for (const widget of startChildren) { + const height = widget.bottomRow - widget.topRow; + const width = widget.rightColumn - widget.leftColumn; + maxHeight = Math.max(maxHeight, height); + widgets = { + ...widgets, + [widget.widgetId]: { + ...widget, + leftColumn: rightColumn, + rightColumn: rightColumn + width, + topRow, + bottomRow: topRow + height, + }, + }; + rightColumn = widget.rightColumn; + } + + [ + { children: startChildren, leftColumn: 0 }, + { + children: centerChildren, + leftColumn: startSize + centerSize / 2 - centerColumns / 2, + }, + { + children: endChildren, + leftColumn: startSize + centerSize + endSize - endColumns, + }, + ].forEach((each) => { + let left = each.leftColumn; + for (const widget of each.children) { + const height = widget.bottomRow - widget.topRow; + const width = widget.rightColumn - widget.leftColumn; + maxHeight = Math.max(maxHeight, height); + widgets = { + ...widgets, + [widget.widgetId]: { + ...widget, + leftColumn: left, + rightColumn: left + width, + topRow, + bottomRow: topRow + height, + }, + }; + left += width; + } + }); + + return { height: maxHeight, widgets }; +} + +function getAlignmentSizes( + arr: { alignment: FlexLayerAlignment; columns: number }[], + space: number, + sizes: { alignment: FlexLayerAlignment; columns: number }[] = [], +): { alignment: FlexLayerAlignment; columns: number }[] { + if (arr.length === 0) return sizes; + if (arr[0].columns > space / arr.length) { + sizes.push(arr[0]); + arr.shift(); + return getAlignmentSizes( + arr, + space - sizes[sizes.length - 1].columns, + sizes, + ); + } else { + for (let i = 0; i < arr.length; i++) { + sizes.push({ ...arr[i], columns: space / arr.length }); + } + } + return sizes; +} diff --git a/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts b/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts index 449da709c868..7553400f3413 100644 --- a/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts +++ b/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts @@ -21,6 +21,7 @@ import { all, call, put, select, takeLatest } from "redux-saga/effects"; import { updateFlexChildColumns, updateSizeOfAllChildren, + updateWidgetPositions, } from "sagas/AutoLayoutUtils"; import { getWidgets } from "sagas/selectors"; import { getUpdateDslAfterCreatingChild } from "sagas/WidgetAdditionSagas"; @@ -34,6 +35,7 @@ function* addWidgetAndReorderSaga( }>, ) { const start = performance.now(); + console.log("#### action payload", actionPayload); const { direction, dropPayload, newWidget, parentId } = actionPayload.payload; const { alignment, index, isNewLayer, layerIndex, rowIndex } = dropPayload; try { @@ -226,7 +228,12 @@ function* reorderAutolayoutChildren(params: { }; } - return updatedWidgets; + const widgetsAfterPositionUpdate = updateWidgetPositions( + updatedWidgets, + parentId, + ); + console.log("#### updatedWidgets", widgetsAfterPositionUpdate); + return widgetsAfterPositionUpdate; } /** From 9d892e3b2731a8e60746ac32a650c459b1c89ee6 Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 26 Dec 2022 13:43:56 -0500 Subject: [PATCH 296/708] calculate positions for fill widgets --- app/client/src/sagas/AutoLayoutUtils.ts | 51 ++++++++++--------- .../CanvasSagas/AutoLayoutDraggingSagas.ts | 1 - 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/app/client/src/sagas/AutoLayoutUtils.ts b/app/client/src/sagas/AutoLayoutUtils.ts index eb65219e8849..304581678862 100644 --- a/app/client/src/sagas/AutoLayoutUtils.ts +++ b/app/client/src/sagas/AutoLayoutUtils.ts @@ -354,25 +354,42 @@ function calculateWidgetPositions( const startChildren = [], centerChildren = [], - endChildren = []; + endChildren = [], + fillChildren = []; let startColumns = 0, centerColumns = 0, endColumns = 0; let startSize = 0, centerSize = 0, endSize = 0; - // Calculate the number of columns occupied by each alignment. + + // Calculate the number of columns occupied by hug widgets in each alignment. for (const child of layer.children) { const widget = widgets[child.id]; + const isFillWidget = widget.responsiveBehavior === ResponsiveBehavior.Fill; + if (isFillWidget) fillChildren.push(child); if (child.align === FlexLayerAlignment.Start) { startChildren.push(widget); - startColumns += widget.rightColumn - widget.leftColumn; + if (!isFillWidget) startColumns += widget.rightColumn - widget.leftColumn; } else if (child.align === FlexLayerAlignment.Center) { centerChildren.push(widget); - centerColumns += widget.rightColumn - widget.leftColumn; + if (!isFillWidget) + centerColumns += widget.rightColumn - widget.leftColumn; } else if (child.align === FlexLayerAlignment.End) { endChildren.push(widget); - endColumns += widget.rightColumn - widget.leftColumn; + if (!isFillWidget) endColumns += widget.rightColumn - widget.leftColumn; + } + } + + const availableColumns = 64 - startColumns - centerColumns - endColumns; + const fillWidgetLength = availableColumns / fillChildren.length; + for (const child of fillChildren) { + if (child.align === FlexLayerAlignment.Start) { + startColumns += fillWidgetLength; + } else if (child.align === FlexLayerAlignment.Center) { + centerColumns += fillWidgetLength; + } else if (child.align === FlexLayerAlignment.End) { + endColumns += fillWidgetLength; } } @@ -398,25 +415,6 @@ function calculateWidgetPositions( } let maxHeight = 0; - // Calculate positions for start aligned children. - let rightColumn = 0; - for (const widget of startChildren) { - const height = widget.bottomRow - widget.topRow; - const width = widget.rightColumn - widget.leftColumn; - maxHeight = Math.max(maxHeight, height); - widgets = { - ...widgets, - [widget.widgetId]: { - ...widget, - leftColumn: rightColumn, - rightColumn: rightColumn + width, - topRow, - bottomRow: topRow + height, - }, - }; - rightColumn = widget.rightColumn; - } - [ { children: startChildren, leftColumn: 0 }, { @@ -431,7 +429,10 @@ function calculateWidgetPositions( let left = each.leftColumn; for (const widget of each.children) { const height = widget.bottomRow - widget.topRow; - const width = widget.rightColumn - widget.leftColumn; + const width = + widget.responsiveBehavior === ResponsiveBehavior.Fill + ? fillWidgetLength + : widget.rightColumn - widget.leftColumn; maxHeight = Math.max(maxHeight, height); widgets = { ...widgets, diff --git a/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts b/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts index 7553400f3413..5ba9bef970a8 100644 --- a/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts +++ b/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts @@ -35,7 +35,6 @@ function* addWidgetAndReorderSaga( }>, ) { const start = performance.now(); - console.log("#### action payload", actionPayload); const { direction, dropPayload, newWidget, parentId } = actionPayload.payload; const { alignment, index, isNewLayer, layerIndex, rowIndex } = dropPayload; try { From 801b766ef58493a25d1406522a063cccb7acd097 Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 27 Dec 2022 09:22:46 -0500 Subject: [PATCH 297/708] update size and positions on reorder --- .../appsmith/autoLayout/AutoLayoutLayer.tsx | 2 - .../appsmith/autoLayout/FlexBoxComponent.tsx | 4 +- .../appsmith/autoLayout/FlexComponent.tsx | 75 +-------------- .../hooks/useAutoLayoutHighlights.ts | 17 +++- .../CanvasSagas/AutoLayoutDraggingSagas.ts | 21 +++-- .../src/utils/autoLayout/highlightUtils.ts | 92 ++++++++++++++----- 6 files changed, 99 insertions(+), 112 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx index 5ee553f616f6..4ca922787adf 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx @@ -2,7 +2,6 @@ import React, { ReactNode } from "react"; import styled from "styled-components"; import { FlexDirection, LayoutDirection } from "components/constants"; -import { DRAG_MARGIN } from "widgets/constants"; /** * 1. Given a direction if should employ flex in perpendicular direction. @@ -38,7 +37,6 @@ const LayoutLayerContainer = styled.div<{ flex-wrap: ${({ wrap }) => (wrap ? "wrap" : "nowrap")}; width: 100%; - margin-top: ${DRAG_MARGIN}px; `; const SubWrapper = styled.div<{ diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx index 28ddd5674791..66649ee9be26 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx @@ -60,8 +60,8 @@ export const FlexContainer = styled.div<{ overflow-y: ${({ isMainContainer, isMobile }) => isMainContainer || isMobile ? "auto" : "hidden"}; - padding: ${({ isDragging, leaveSpaceForWidgetName }) => - !isDragging && leaveSpaceForWidgetName ? "4px 4px 22px 4px;" : "4px;"}; + padding: ${({ leaveSpaceForWidgetName }) => + leaveSpaceForWidgetName ? "4px 4px 22px 4px;" : "0px;"}; `; export const DEFAULT_HIGHLIGHT_SIZE = 4; diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx index 18ecfdb10aac..4cee1e4f039e 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx @@ -1,31 +1,21 @@ -import React, { - ReactNode, - useCallback, - useEffect, - useRef, - useState, -} from "react"; +import React, { ReactNode, useCallback } from "react"; import styled from "styled-components"; -import { AppState } from "ce/reducers"; import { FlexVerticalAlignment, LayoutDirection, ResponsiveBehavior, } from "components/constants"; import { - MAIN_CONTAINER_WIDGET_ID, WidgetType, widgetTypeClassname, WIDGET_PADDING, } from "constants/WidgetConstants"; import { useSelector } from "react-redux"; -import { getSiblingCount } from "selectors/autoLayoutSelectors"; import { snipingModeSelector } from "selectors/editorSelectors"; import { getIsMobile } from "selectors/mainCanvasSelectors"; import { useClickToSelectWidget } from "utils/hooks/useClickToSelectWidget"; import { usePositionedContainerZIndex } from "utils/hooks/usePositionedContainerZIndex"; -import { DRAG_MARGIN } from "widgets/constants"; import { checkIsDropTarget } from "../PositionedContainer"; export type AutoLayoutProps = { @@ -54,7 +44,6 @@ const FlexWidget = styled.div<{ zIndexOnHover: number; parentId?: string; flexVerticalAlignment: FlexVerticalAlignment; - ref: any; }>` position: relative; z-index: ${({ zIndex }) => zIndex}; @@ -74,11 +63,7 @@ const FlexWidget = styled.div<{ } `; -const DEFAULT_MARGIN = 16; - export function FlexComponent(props: AutoLayoutProps) { - const ref = useRef(); - const [observer, setObserver] = useState(null); const isMobile = useSelector(getIsMobile); const isSnipingMode = useSelector(snipingModeSelector); const clickToSelectWidget = useClickToSelectWidget(props.widgetId); @@ -86,15 +71,6 @@ export function FlexComponent(props: AutoLayoutProps) { clickToSelectWidget(props.widgetId); }, [props.widgetId, clickToSelectWidget]); - const { dragDetails } = useSelector( - (state: AppState) => state.ui.widgetDragResize, - ); - const isDragging: boolean = dragDetails?.draggedOn !== undefined; - - const siblingCount = useSelector( - getSiblingCount(props.widgetId, props.parentId || MAIN_CONTAINER_WIDGET_ID), - ); - const isDropTarget = checkIsDropTarget(props.widgetType); const { onHoverZIndex, zIndex } = usePositionedContainerZIndex( isDropTarget, @@ -107,32 +83,6 @@ export function FlexComponent(props: AutoLayoutProps) { !isSnipingMode && e.stopPropagation(); }; - const callback = (mutations: any, observer: any) => { - console.log("#### mutations", mutations); - if (mutations.length) - console.log( - "#### mutations[0].target", - mutations[0].target.classList[3], - mutations[0].target.offsetTop, - observer, - ); - }; - - useEffect(() => { - const observer = new MutationObserver(callback); - setObserver(observer); - return () => observer.disconnect(); - }, []); - - useEffect(() => { - if (!ref.current || !observer) return; - // return; - observer.observe(ref.current, { - attributes: true, - childList: false, - subtree: false, - }); - }, [observer, ref.current]); /** * In a vertical stack, * Fill widgets grow / shrink to take up all the available space. @@ -145,28 +95,6 @@ export function FlexComponent(props: AutoLayoutProps) { props.widgetId } ${widgetTypeClassname(props.widgetType)}`; - const dragMargin = - props.parentId === MAIN_CONTAINER_WIDGET_ID - ? DEFAULT_MARGIN - : Math.max(props.parentColumnSpace, DRAG_MARGIN); - - const isAffectedByDrag: boolean = isDragging; - // TODO: Simplify this logic. - /** - * resize logic: - * if isAffectedByDrag - * newWidth = width - drag margin - - * (decrease in parent width) / # of siblings - - * width of the DropPosition introduced due to the widget. - */ - const resizedWidth: number = isAffectedByDrag - ? props.componentWidth - - dragMargin - - (siblingCount > 0 - ? (DEFAULT_MARGIN * siblingCount + 2) / siblingCount - : 0) - : props.componentWidth; - return ( diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index fe9c7171d2fd..9ccbb60a8265 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -6,6 +6,7 @@ import { import { useSelector } from "react-redux"; import { ReflowDirection } from "reflow/reflowTypes"; import { getWidgets } from "sagas/selectors"; +import { getCanvasWidth } from "selectors/editorSelectors"; import { deriveHighlightsFromLayers } from "utils/autoLayout/highlightUtils"; import WidgetFactory from "utils/WidgetFactory"; import { WidgetDraggingBlock } from "./useBlocksToBeDraggedOnCanvas"; @@ -54,6 +55,7 @@ export const useAutoLayoutHighlights = ({ useAutoLayout, }: AutoLayoutHighlightProps) => { const allWidgets = useSelector(getWidgets); + const canvasWidth: number = useSelector(getCanvasWidth); let highlights: HighlightInfo[] = []; let newLayers: { [key: string]: number } = {}; let lastActiveHighlight: HighlightInfo | undefined; @@ -177,7 +179,11 @@ export const useAutoLayoutHighlights = ({ */ if (!updateContainerDimensions()) return []; isFillWidget = checkForFillWidget(); - highlights = deriveHighlightsFromLayers(allWidgets, canvasId); + highlights = deriveHighlightsFromLayers( + allWidgets, + canvasId, + canvasWidth, + ); } // console.log("#### highlights", highlights); return highlights; @@ -192,10 +198,13 @@ export const useAutoLayoutHighlights = ({ moveDirection: ReflowDirection, // acceleration: number, ): HighlightInfo | undefined => { - highlights = allWidgets[canvasId].highlights; if (!highlights) - highlights = deriveHighlightsFromLayers(allWidgets, canvasId); - + highlights = deriveHighlightsFromLayers( + allWidgets, + canvasId, + canvasWidth, + ); + // console.log("#### highlights", highlights); if (!highlights) return; // updateHighlights(moveDirection); diff --git a/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts b/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts index 73986379d8c9..7df023f6da3f 100644 --- a/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts +++ b/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts @@ -25,7 +25,6 @@ import { } from "sagas/AutoLayoutUtils"; import { getWidgets } from "sagas/selectors"; import { getUpdateDslAfterCreatingChild } from "sagas/WidgetAdditionSagas"; -import { deriveHighlightsFromLayers } from "utils/autoLayout/highlightUtils"; function* addWidgetAndReorderSaga( actionPayload: ReduxAction<{ @@ -232,7 +231,7 @@ function* reorderAutolayoutChildren(params: { updatedWidgets, parentId, ); - console.log("#### updatedWidgets", widgetsAfterPositionUpdate); + return widgetsAfterPositionUpdate; } @@ -255,13 +254,14 @@ function updateRelationships( const orphans = movedWidgets.filter( (item) => widgets[item].parentId !== parentId, ); - let prevParentId: string | undefined; + const prevParents: string[] = []; if (orphans && orphans.length) { //parent has changed orphans.forEach((item) => { // remove from previous parent - prevParentId = widgets[item].parentId; + const prevParentId = widgets[item].parentId; if (prevParentId !== undefined) { + prevParents.push(prevParentId); const prevParent = Object.assign({}, widgets[prevParentId]); if (prevParent.children && isArray(prevParent.children)) { const updatedPrevParent = { @@ -284,8 +284,15 @@ function updateRelationships( }; }); } - if (prevParentId) { - return updateSizeOfAllChildren(widgets, prevParentId); + if (prevParents.length) { + for (const id of prevParents) { + const updatedWidgets = updateSizeOfAllChildren(widgets, id); + const updatedWidgetsAfterPositionCalculation = updateWidgetPositions( + updatedWidgets, + id, + ); + return updatedWidgetsAfterPositionCalculation; + } } return widgets; } @@ -325,7 +332,7 @@ function updateExistingLayer( rowIndex: number, ): CanvasWidgetsReduxState { try { - const widgets: CanvasWidgetsReduxState = Object.assign({}, allWidgets); + const widgets: CanvasWidgetsReduxState = { ...allWidgets }; const canvas = widgets[parentId]; if (!canvas || !newLayer) return widgets; diff --git a/app/client/src/utils/autoLayout/highlightUtils.ts b/app/client/src/utils/autoLayout/highlightUtils.ts index 7ab0db232c6c..7fb384ead379 100644 --- a/app/client/src/utils/autoLayout/highlightUtils.ts +++ b/app/client/src/utils/autoLayout/highlightUtils.ts @@ -3,9 +3,17 @@ import { DEFAULT_HIGHLIGHT_SIZE, FlexLayer, } from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; +import { + CONTAINER_GRID_PADDING, + GridDefaults, + MAIN_CONTAINER_WIDGET_ID, + WIDGET_PADDING, +} from "constants/WidgetConstants"; import { HighlightInfo } from "pages/common/CanvasArenas/hooks/useAutoLayoutHighlights"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; +const HORIZONTAL_HIGHLIGHT_MARGIN = 4; + /** * @param allWidgets : CanvasWidgetsReduxState * @param canvasId : string @@ -17,6 +25,7 @@ import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsRe export function deriveHighlightsFromLayers( allWidgets: CanvasWidgetsReduxState, canvasId: string, + mainCanvasWidth = 0, draggedWidgets: string[] = [], ): HighlightInfo[] { const widgets = { ...allWidgets }; @@ -25,18 +34,50 @@ export function deriveHighlightsFromLayers( if (!canvas) return []; const columns: number = canvas.rightColumn - canvas.leftColumn; + + let padding = (CONTAINER_GRID_PADDING + WIDGET_PADDING) * 2; + if ( + canvas.widgetId === MAIN_CONTAINER_WIDGET_ID || + canvas.type === "CONTAINER_WIDGET" + ) { + //For MainContainer and any Container Widget padding doesn't exist coz there is already container padding. + padding = CONTAINER_GRID_PADDING * 2; + } + if (canvas.noPad) { + // Widgets like ListWidget choose to have no container padding so will only have widget padding + padding = WIDGET_PADDING * 2; + } const columnSpace: number = - canvas.parentColumnSpace === 1 && canvas.parentId - ? widgets[canvas.parentId].parentColumnSpace + canvas.parentColumnSpace === 1 + ? canvas.parentId + ? (widgets[canvas.parentId].parentColumnSpace * columns - + (padding || 0)) / + GridDefaults.DEFAULT_GRID_COLUMNS + : mainCanvasWidth / GridDefaults.DEFAULT_GRID_COLUMNS : canvas.parentColumnSpace; - const canvasWidth: number = columns * columnSpace; + // const columnSpace = canvasWidth / GridDefaults.DEFAULT_GRID_COLUMNS; + const canvasWidth: number = + canvasId === MAIN_CONTAINER_WIDGET_ID + ? mainCanvasWidth + : columns * columnSpace - padding; + + console.log( + "#### canvasWidth: " + canvasWidth, + "columnSpace: " + columnSpace, + "padding", + padding, + canvas.leftColumn, + canvas.rightColumn, + widgets, + columns * columnSpace, + ); - const layers: FlexLayer[] = canvas.flexLayers; + const layers: FlexLayer[] = canvas.flexLayers || []; const highlights: HighlightInfo[] = []; let childCount = 0; let layerIndex = 0; // TODO: remove offsetTop and use child positions after widget positioning on grid is solved. - let offsetTop = 0; // used to calculate distance of a highlight from parents's top. + let offsetTop = HORIZONTAL_HIGHLIGHT_MARGIN; // used to calculate distance of a highlight from parents's top. for (const layer of layers) { /** * If the layer is empty, after discounting the dragged widgets, @@ -76,11 +117,12 @@ export function deriveHighlightsFromLayers( offsetTop, canvasWidth, canvasId, + columnSpace, }); highlights.push(...payload.highlights); childCount += payload.childCount; - offsetTop += tallestChild || 0; + offsetTop += (tallestChild || 0) + HORIZONTAL_HIGHLIGHT_MARGIN; layerIndex += 1; } // Add a layer of horizontal highlights for the empty space at the bottom of a stack. @@ -113,11 +155,13 @@ function generateVerticalHighlights(data: { offsetTop: number; canvasWidth: number; canvasId: string; + columnSpace: number; }): VerticalHighlightsPayload { const { canvasId, canvasWidth, childCount, + columnSpace, height, layer, layerIndex, @@ -159,7 +203,8 @@ function generateVerticalHighlights(data: { height, offsetTop, canvasId, - parentColumnSpace: widgets[canvasId].parentColumnSpace, + parentColumnSpace: columnSpace, + parentRowSpace: widgets[canvasId].parentRowSpace, canvasWidth, }), ...generateHighlightsForSubWrapper({ @@ -170,7 +215,8 @@ function generateVerticalHighlights(data: { height, offsetTop, canvasId, - parentColumnSpace: widgets[canvasId].parentColumnSpace, + parentColumnSpace: columnSpace, + parentRowSpace: widgets[canvasId].parentRowSpace, canvasWidth, avoidInitialHighlight: startColumns > 25 || endColumns > 25, }), @@ -182,7 +228,8 @@ function generateVerticalHighlights(data: { height, offsetTop, canvasId, - parentColumnSpace: widgets[canvasId].parentColumnSpace, + parentColumnSpace: columnSpace, + parentRowSpace: widgets[canvasId].parentRowSpace, canvasWidth, }), ], @@ -199,6 +246,7 @@ function generateHighlightsForSubWrapper(data: { offsetTop: number; canvasId: string; parentColumnSpace: number; + parentRowSpace: number; canvasWidth: number; avoidInitialHighlight?: boolean; }): HighlightInfo[] { @@ -216,20 +264,15 @@ function generateHighlightsForSubWrapper(data: { } = data; const res: HighlightInfo[] = []; let count = 0; - let lastWidgetWidth = 0; for (const child of arr) { - const { leftColumn, rightColumn, widgetId } = child; - const el = document.querySelector(`.auto-layout-child-${widgetId}`); - if (!el) continue; - // TODO: remove boundingClientRect after widget positioning on grid is solved. - const { x } = el.getBoundingClientRect(); + const { leftColumn } = child; res.push({ isNewLayer: false, index: count + childCount, layerIndex, rowIndex: count, alignment, - posX: x, + posX: leftColumn * parentColumnSpace, posY: offsetTop, width: DEFAULT_HIGHLIGHT_SIZE, height, @@ -237,8 +280,8 @@ function generateHighlightsForSubWrapper(data: { canvasId, }); count += 1; - lastWidgetWidth = (rightColumn - leftColumn) * parentColumnSpace; } + if (!avoidInitialHighlight) res.push({ isNewLayer: false, @@ -249,8 +292,11 @@ function generateHighlightsForSubWrapper(data: { posX: getPositionForInitialHighlight( res, alignment, - lastWidgetWidth, + arr && arr.length + ? arr[arr.length - 1].rightColumn * parentColumnSpace + : 0, canvasWidth, + parentColumnSpace, ), posY: offsetTop, width: DEFAULT_HIGHLIGHT_SIZE, @@ -264,18 +310,18 @@ function generateHighlightsForSubWrapper(data: { function getPositionForInitialHighlight( highlights: HighlightInfo[], alignment: FlexLayerAlignment, - width: number, + posX: number, containerWidth: number, + parentColumnSpace: number, ): number { if (alignment === FlexLayerAlignment.End) { - return containerWidth - 2; - // return highlights[highlights.length - 1].posX - width; + return 64 * parentColumnSpace; } else if (alignment === FlexLayerAlignment.Center) { if (!highlights.length) return containerWidth / 2; - return highlights[highlights.length - 1].posX + width; + return posX; } else { if (!highlights.length) return 2; - return highlights[highlights.length - 1].posX + width; + return posX; } } From 6286d082f4c0705fbe89728c664659620fa46c6a Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 27 Dec 2022 09:30:29 -0500 Subject: [PATCH 298/708] fix vertical highlight posY --- app/client/src/utils/autoLayout/highlightUtils.ts | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/app/client/src/utils/autoLayout/highlightUtils.ts b/app/client/src/utils/autoLayout/highlightUtils.ts index 7fb384ead379..4a320da59eba 100644 --- a/app/client/src/utils/autoLayout/highlightUtils.ts +++ b/app/client/src/utils/autoLayout/highlightUtils.ts @@ -61,17 +61,6 @@ export function deriveHighlightsFromLayers( ? mainCanvasWidth : columns * columnSpace - padding; - console.log( - "#### canvasWidth: " + canvasWidth, - "columnSpace: " + columnSpace, - "padding", - padding, - canvas.leftColumn, - canvas.rightColumn, - widgets, - columns * columnSpace, - ); - const layers: FlexLayer[] = canvas.flexLayers || []; const highlights: HighlightInfo[] = []; let childCount = 0; @@ -122,7 +111,7 @@ export function deriveHighlightsFromLayers( highlights.push(...payload.highlights); childCount += payload.childCount; - offsetTop += (tallestChild || 0) + HORIZONTAL_HIGHLIGHT_MARGIN; + offsetTop += tallestChild || 0; layerIndex += 1; } // Add a layer of horizontal highlights for the empty space at the bottom of a stack. From aa5072dea15c7bad1e0af15903ca2c2a0674c404 Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 27 Dec 2022 10:35:53 -0500 Subject: [PATCH 299/708] update positions on delete --- app/client/src/sagas/AutoLayoutUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/client/src/sagas/AutoLayoutUtils.ts b/app/client/src/sagas/AutoLayoutUtils.ts index 304581678862..ac1efe9e6ede 100644 --- a/app/client/src/sagas/AutoLayoutUtils.ts +++ b/app/client/src/sagas/AutoLayoutUtils.ts @@ -114,7 +114,7 @@ export function* updateFlexLayersOnDelete( widgets[parentId] = parent; if (layerIndex === -1) return widgets; - return updateFlexChildColumns(widgets, layerIndex, parentId); + return updateWidgetPositions(widgets, parentId); } // TODO: refactor these implementations export function updateFillChildStatus( From 7c6b5639aba3421d26c68025447f35b2f52fa23a Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 27 Dec 2022 11:20:54 -0500 Subject: [PATCH 300/708] discard empty layers during drag --- .../appsmith/autoLayout/FlexComponent.tsx | 74 ++++++------ .../hooks/useAutoLayoutHighlights.ts | 4 +- .../src/utils/autoLayout/highlightUtils.ts | 114 ++++++++++-------- 3 files changed, 106 insertions(+), 86 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx index 4cee1e4f039e..75486aceae48 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx @@ -1,4 +1,4 @@ -import React, { ReactNode, useCallback } from "react"; +import React, { CSSProperties, ReactNode, useCallback, useMemo } from "react"; import styled from "styled-components"; import { @@ -17,6 +17,8 @@ import { getIsMobile } from "selectors/mainCanvasSelectors"; import { useClickToSelectWidget } from "utils/hooks/useClickToSelectWidget"; import { usePositionedContainerZIndex } from "utils/hooks/usePositionedContainerZIndex"; import { checkIsDropTarget } from "../PositionedContainer"; +import { isWidgetSelected } from "selectors/widgetSelectors"; +import { AppState } from "ce/reducers"; export type AutoLayoutProps = { children: ReactNode; @@ -33,39 +35,18 @@ export type AutoLayoutProps = { parentColumnSpace: number; flexVerticalAlignment: FlexVerticalAlignment; }; -// TODO: create a memoized style object for the div instead. -const FlexWidget = styled.div<{ - componentHeight: number; - componentWidth: number; - isMobile: boolean; - isFillWidget: boolean; - padding: number; - zIndex: number; - zIndexOnHover: number; - parentId?: string; - flexVerticalAlignment: FlexVerticalAlignment; -}>` - position: relative; - z-index: ${({ zIndex }) => zIndex}; - - width: ${({ componentWidth }) => `${Math.floor(componentWidth)}px`}; - height: ${({ componentHeight, isMobile }) => - isMobile ? "auto" : Math.floor(componentHeight) + "px"}; - min-height: 30px; - padding: ${({ padding }) => padding + "px"}; - - flex-grow: ${({ isFillWidget }) => (isFillWidget ? "1" : "0")}; - align-self: ${({ flexVerticalAlignment }) => flexVerticalAlignment}; - - &:hover { - z-index: ${({ zIndexOnHover }) => zIndexOnHover} !important; - } +const FlexWidget = styled.div` + position: "relative"; `; export function FlexComponent(props: AutoLayoutProps) { const isMobile = useSelector(getIsMobile); const isSnipingMode = useSelector(snipingModeSelector); + const isSelected = useSelector(isWidgetSelected(props.widgetId)); + const isDragging = useSelector( + (state: AppState) => state.ui.widgetDragResize.isDragging, + ); const clickToSelectWidget = useClickToSelectWidget(props.widgetId); const onClickFn = useCallback(() => { clickToSelectWidget(props.widgetId); @@ -95,21 +76,38 @@ export function FlexComponent(props: AutoLayoutProps) { props.widgetId } ${widgetTypeClassname(props.widgetType)}`; + const flexComponentStyle: CSSProperties = useMemo(() => { + return { + display: isSelected && isDragging ? "none" : "block", + zIndex, + width: `${Math.floor(props.componentWidth)}px`, + height: isMobile ? "auto" : Math.floor(props.componentHeight) + "px", + minHeight: "30px", + padding: WIDGET_PADDING + "px", + flexGrow: isFillWidget ? 1 : 0, + alignSelf: props.flexVerticalAlignment, + "&:hover": { + zIndex: onHoverZIndex + " !important", + }, + }; + }, [ + isDragging, + isFillWidget, + isMobile, + isSelected, + props.componentWidth, + props.componentHeight, + props.flexVerticalAlignment, + zIndex, + onHoverZIndex, + ]); + return ( {props.children} diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index 9ccbb60a8265..63aefeeb6b96 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -183,6 +183,7 @@ export const useAutoLayoutHighlights = ({ allWidgets, canvasId, canvasWidth, + blocksToDraw.map((block) => block?.widgetId), ); } // console.log("#### highlights", highlights); @@ -203,8 +204,9 @@ export const useAutoLayoutHighlights = ({ allWidgets, canvasId, canvasWidth, + blocksToDraw.map((block) => block?.widgetId), ); - // console.log("#### highlights", highlights); + console.log("#### highlights", highlights); if (!highlights) return; // updateHighlights(moveDirection); diff --git a/app/client/src/utils/autoLayout/highlightUtils.ts b/app/client/src/utils/autoLayout/highlightUtils.ts index 4a320da59eba..249f93484898 100644 --- a/app/client/src/utils/autoLayout/highlightUtils.ts +++ b/app/client/src/utils/autoLayout/highlightUtils.ts @@ -2,6 +2,7 @@ import { FlexLayerAlignment } from "components/constants"; import { DEFAULT_HIGHLIGHT_SIZE, FlexLayer, + LayerChild, } from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; import { CONTAINER_GRID_PADDING, @@ -33,33 +34,11 @@ export function deriveHighlightsFromLayers( const canvas = widgets[canvasId]; if (!canvas) return []; - const columns: number = canvas.rightColumn - canvas.leftColumn; - - let padding = (CONTAINER_GRID_PADDING + WIDGET_PADDING) * 2; - if ( - canvas.widgetId === MAIN_CONTAINER_WIDGET_ID || - canvas.type === "CONTAINER_WIDGET" - ) { - //For MainContainer and any Container Widget padding doesn't exist coz there is already container padding. - padding = CONTAINER_GRID_PADDING * 2; - } - if (canvas.noPad) { - // Widgets like ListWidget choose to have no container padding so will only have widget padding - padding = WIDGET_PADDING * 2; - } - const columnSpace: number = - canvas.parentColumnSpace === 1 - ? canvas.parentId - ? (widgets[canvas.parentId].parentColumnSpace * columns - - (padding || 0)) / - GridDefaults.DEFAULT_GRID_COLUMNS - : mainCanvasWidth / GridDefaults.DEFAULT_GRID_COLUMNS - : canvas.parentColumnSpace; - // const columnSpace = canvasWidth / GridDefaults.DEFAULT_GRID_COLUMNS; - const canvasWidth: number = - canvasId === MAIN_CONTAINER_WIDGET_ID - ? mainCanvasWidth - : columns * columnSpace - padding; + const { canvasWidth, columnSpace } = getCanvasDimensions( + canvas, + widgets, + mainCanvasWidth, + ); const layers: FlexLayer[] = canvas.flexLayers || []; const highlights: HighlightInfo[] = []; @@ -72,10 +51,10 @@ export function deriveHighlightsFromLayers( * If the layer is empty, after discounting the dragged widgets, * then don't process it for vertical highlights. */ - // const isEmpty = - // layer?.children?.filter( - // (child: LayerChild) => draggedWidgets.indexOf(child.id) === -1, - // ).length === 0; + const isEmpty: boolean = + layer?.children?.filter( + (child: LayerChild) => draggedWidgets.indexOf(child.id) === -1, + ).length === 0; const tallestChild = layer.children?.reduce((acc, child) => { const widget = widgets[child.id]; return Math.max( @@ -84,19 +63,6 @@ export function deriveHighlightsFromLayers( ); }, 0); - /** - * Add a layer of horizontal highlights before each flex layer - * to account for new vertical drop positions. - */ - highlights.push( - ...generateHorizontalHighlights( - childCount, - layerIndex, - offsetTop, - canvasWidth, - canvasId, - ), - ); const payload: VerticalHighlightsPayload = generateVerticalHighlights({ widgets, layer, @@ -107,12 +73,29 @@ export function deriveHighlightsFromLayers( canvasWidth, canvasId, columnSpace, + draggedWidgets, }); - highlights.push(...payload.highlights); + if (!isEmpty) { + /** + * Add a layer of horizontal highlights before each flex layer + * to account for new vertical drop positions. + */ + highlights.push( + ...generateHorizontalHighlights( + childCount, + layerIndex, + offsetTop, + canvasWidth, + canvasId, + ), + ); + + highlights.push(...payload.highlights); + offsetTop += tallestChild || 0; + layerIndex += 1; + } childCount += payload.childCount; - offsetTop += tallestChild || 0; - layerIndex += 1; } // Add a layer of horizontal highlights for the empty space at the bottom of a stack. highlights.push( @@ -145,12 +128,14 @@ function generateVerticalHighlights(data: { canvasWidth: number; canvasId: string; columnSpace: number; + draggedWidgets: string[]; }): VerticalHighlightsPayload { const { canvasId, canvasWidth, childCount, columnSpace, + draggedWidgets, height, layer, layerIndex, @@ -344,3 +329,38 @@ function generateHorizontalHighlights( }); return arr; } + +function getCanvasDimensions( + canvas: any, + widgets: CanvasWidgetsReduxState, + mainCanvasWidth: number, +): { canvasWidth: number; columnSpace: number } { + const columns: number = canvas.rightColumn - canvas.leftColumn; + + let padding = (CONTAINER_GRID_PADDING + WIDGET_PADDING) * 2; + if ( + canvas.widgetId === MAIN_CONTAINER_WIDGET_ID || + canvas.type === "CONTAINER_WIDGET" + ) { + //For MainContainer and any Container Widget padding doesn't exist coz there is already container padding. + padding = CONTAINER_GRID_PADDING * 2; + } + if (canvas.noPad) { + // Widgets like ListWidget choose to have no container padding so will only have widget padding + padding = WIDGET_PADDING * 2; + } + const columnSpace: number = + canvas.parentColumnSpace === 1 + ? canvas.parentId + ? (widgets[canvas.parentId].parentColumnSpace * columns - + (padding || 0)) / + GridDefaults.DEFAULT_GRID_COLUMNS + : mainCanvasWidth / GridDefaults.DEFAULT_GRID_COLUMNS + : canvas.parentColumnSpace; + // const columnSpace = canvasWidth / GridDefaults.DEFAULT_GRID_COLUMNS; + const canvasWidth: number = + canvas.widgetId === MAIN_CONTAINER_WIDGET_ID + ? mainCanvasWidth + : columns * columnSpace - padding; + return { canvasWidth, columnSpace }; +} From cbb85f13dfa786d8868e535fc9145f36c7b0e5d9 Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 27 Dec 2022 11:24:38 -0500 Subject: [PATCH 301/708] discard dragged widgets for vertical highlight calculations --- .../pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts | 2 +- app/client/src/utils/autoLayout/highlightUtils.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index 63aefeeb6b96..c1ff3dffc784 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -206,7 +206,7 @@ export const useAutoLayoutHighlights = ({ canvasWidth, blocksToDraw.map((block) => block?.widgetId), ); - console.log("#### highlights", highlights); + // console.log("#### highlights", highlights); if (!highlights) return; // updateHighlights(moveDirection); diff --git a/app/client/src/utils/autoLayout/highlightUtils.ts b/app/client/src/utils/autoLayout/highlightUtils.ts index 249f93484898..769237ded756 100644 --- a/app/client/src/utils/autoLayout/highlightUtils.ts +++ b/app/client/src/utils/autoLayout/highlightUtils.ts @@ -155,7 +155,7 @@ function generateVerticalHighlights(data: { const widget = widgets[child.id]; if (!widget) continue; count += 1; - + if (draggedWidgets.indexOf(child.id) > -1) continue; if (child.align === FlexLayerAlignment.End) { endChildren.push(widget); endColumns += widget.rightColumn - widget.leftColumn; From a705f382fe56746bf9a7f11d5b3c2bb62cfa9331 Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 27 Dec 2022 11:27:04 -0500 Subject: [PATCH 302/708] fix widgetName placement --- .../designSystems/appsmith/autoLayout/FlexComponent.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx index 75486aceae48..0030675596aa 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx @@ -37,7 +37,7 @@ export type AutoLayoutProps = { }; const FlexWidget = styled.div` - position: "relative"; + position: relative; `; export function FlexComponent(props: AutoLayoutProps) { From c7447c615d4317a0c7671910d52ce3c680c11bac Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 27 Dec 2022 11:33:38 -0500 Subject: [PATCH 303/708] update position on resize --- .../designSystems/appsmith/autoLayout/FlexComponent.tsx | 2 +- app/client/src/sagas/WidgetOperationSagas.tsx | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx index 0030675596aa..1368ca115d6f 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx @@ -78,7 +78,7 @@ export function FlexComponent(props: AutoLayoutProps) { const flexComponentStyle: CSSProperties = useMemo(() => { return { - display: isSelected && isDragging ? "none" : "block", + display: isSelected && isDragging ? "none" : "flex", zIndex, width: `${Math.floor(props.componentWidth)}px`, height: isMobile ? "auto" : Math.floor(props.componentHeight) + "px", diff --git a/app/client/src/sagas/WidgetOperationSagas.tsx b/app/client/src/sagas/WidgetOperationSagas.tsx index bd8e76dda814..b8a57e30f267 100644 --- a/app/client/src/sagas/WidgetOperationSagas.tsx +++ b/app/client/src/sagas/WidgetOperationSagas.tsx @@ -113,7 +113,7 @@ import { collisionCheckPostReflow, getBottomRowAfterReflow, } from "utils/reflowHookUtils"; -import { updateChildrenSize } from "./AutoLayoutUtils"; +import { updateWidgetPositions } from "./AutoLayoutUtils"; import { getCanvasSizeAfterWidgetMove } from "./CanvasSagas/DraggingCanvasSagas"; import widgetAdditionSagas from "./WidgetAdditionSagas"; import { traverseTreeAndExecuteBlueprintChildOperations } from "./WidgetBlueprintSagas"; @@ -255,10 +255,9 @@ export function* resizeSaga(resizeAction: ReduxAction) { bottomRow: updatedCanvasBottomRow, }; } - const updatedWidgetsAfterResizing = updateChildrenSize( + const updatedWidgetsAfterResizing = updateWidgetPositions( movedWidgets, parentId, - widgetId, ); log.debug("resize computations took", performance.now() - start, "ms"); yield put(stopReflowAction()); From e5a6f2cd50290d6ea706b423ebd42e931c6e9ae0 Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 27 Dec 2022 11:56:45 -0500 Subject: [PATCH 304/708] fix end highlight positioning --- app/client/src/utils/autoLayout/highlightUtils.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/client/src/utils/autoLayout/highlightUtils.ts b/app/client/src/utils/autoLayout/highlightUtils.ts index 769237ded756..f3fc604582ca 100644 --- a/app/client/src/utils/autoLayout/highlightUtils.ts +++ b/app/client/src/utils/autoLayout/highlightUtils.ts @@ -271,6 +271,7 @@ function generateHighlightsForSubWrapper(data: { : 0, canvasWidth, parentColumnSpace, + canvasId, ), posY: offsetTop, width: DEFAULT_HIGHLIGHT_SIZE, @@ -287,9 +288,12 @@ function getPositionForInitialHighlight( posX: number, containerWidth: number, parentColumnSpace: number, + canvasId: string, ): number { if (alignment === FlexLayerAlignment.End) { - return 64 * parentColumnSpace; + return ( + 64 * parentColumnSpace - (canvasId === MAIN_CONTAINER_WIDGET_ID ? 6 : 0) + ); } else if (alignment === FlexLayerAlignment.Center) { if (!highlights.length) return containerWidth / 2; return posX; From bc18a80b0fc3c213c0ef7f98edef3ff6a7c9b667 Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 27 Dec 2022 12:38:39 -0500 Subject: [PATCH 305/708] fix highlight width for fill widget --- .../hooks/useAutoLayoutHighlights.ts | 49 ++----------------- .../src/utils/autoLayout/highlightUtils.ts | 16 +++++- 2 files changed, 17 insertions(+), 48 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index c1ff3dffc784..01e6609f86a0 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -100,51 +100,6 @@ export const useAutoLayoutHighlights = ({ newLayers = {}; }; - const getDropPositions = () => { - const els = document.querySelectorAll(`.t--drop-position-${canvasId}`); - const highlights: HighlightInfo[] = []; - - for (const el of els) { - const rect: DOMRect = el.getBoundingClientRect(); - const classList = Array.from(el.classList); - - const highlight: HighlightInfo = classList.reduce( - (acc: HighlightInfo, curr) => { - if (curr.indexOf("alignment") > -1) - acc.alignment = curr.split("-")[1] as FlexLayerAlignment; - else if (curr.indexOf("layer-index") > -1) - acc.layerIndex = parseInt(curr.split("layer-index-")[1]); - else if (curr.indexOf("child-index") > -1) - acc.index = parseInt(curr.split("child-index-")[1]); - else if (curr.indexOf("row-index") > -1) - acc.rowIndex = parseInt(curr.split("row-index-")[1]); - else if (curr.indexOf("isNewLayer") > -1) acc.isNewLayer = true; - else if (curr.indexOf("isVertical") > -1) acc.isVertical = true; - - return acc; - }, - { - isNewLayer: false, - index: 0, - layerIndex: 0, - rowIndex: 0, - alignment: FlexLayerAlignment.Start, - posX: rect.x - containerDimensions.left, - posY: rect.y - containerDimensions?.top, - width: rect.width, - height: rect.height, - isVertical: false, - el, - canvasId: "0", - }, - ); - if (!highlight.isVertical) newLayers[highlights.length] = highlight.posY; - highlights.push(highlight); - } - - return highlights; - }; - const checkForFillWidget = (): boolean => { let flag = false; if (!blocksToDraw?.length) return flag; @@ -184,6 +139,7 @@ export const useAutoLayoutHighlights = ({ canvasId, canvasWidth, blocksToDraw.map((block) => block?.widgetId), + isFillWidget, ); } // console.log("#### highlights", highlights); @@ -205,6 +161,7 @@ export const useAutoLayoutHighlights = ({ canvasId, canvasWidth, blocksToDraw.map((block) => block?.widgetId), + isFillWidget, ); // console.log("#### highlights", highlights); if (!highlights) return; @@ -254,7 +211,7 @@ export const useAutoLayoutHighlights = ({ ) ); }); - // toggleHighlightVisibility(base, arr[0]); + // console.log("#### arr", arr, base, moveDirection); return arr[0]; }; diff --git a/app/client/src/utils/autoLayout/highlightUtils.ts b/app/client/src/utils/autoLayout/highlightUtils.ts index f3fc604582ca..1ab41216db36 100644 --- a/app/client/src/utils/autoLayout/highlightUtils.ts +++ b/app/client/src/utils/autoLayout/highlightUtils.ts @@ -28,6 +28,7 @@ export function deriveHighlightsFromLayers( canvasId: string, mainCanvasWidth = 0, draggedWidgets: string[] = [], + hasFillWidget = false, ): HighlightInfo[] { const widgets = { ...allWidgets }; try { @@ -88,6 +89,7 @@ export function deriveHighlightsFromLayers( offsetTop, canvasWidth, canvasId, + hasFillWidget, ), ); @@ -105,6 +107,7 @@ export function deriveHighlightsFromLayers( offsetTop, canvasWidth, canvasId, + hasFillWidget, ), ); return highlights; @@ -309,6 +312,7 @@ function generateHorizontalHighlights( offsetTop: number, containerWidth: number, canvasId: string, + hasFillWidget: boolean, ): HighlightInfo[] { const width = containerWidth / 3; const arr: HighlightInfo[] = []; @@ -323,9 +327,17 @@ function generateHorizontalHighlights( layerIndex, rowIndex: 0, alignment, - posX: width * index, + posX: hasFillWidget + ? alignment === FlexLayerAlignment.Start + ? 0 + : containerWidth + : width * index, posY: offsetTop, - width, + width: hasFillWidget + ? alignment === FlexLayerAlignment.Start + ? containerWidth + : 0 + : width, height: DEFAULT_HIGHLIGHT_SIZE, isVertical: false, canvasId, From 03e5d4d3a2875c988163d68ac579467e0c7a0103 Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 27 Dec 2022 12:40:14 -0500 Subject: [PATCH 306/708] minor update --- .../common/CanvasArenas/hooks/useAutoLayoutHighlights.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index 01e6609f86a0..124c3eb20d7a 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -184,7 +184,14 @@ export const useAutoLayoutHighlights = ({ val?: XYCord, ): HighlightInfo | undefined => { let base: HighlightInfo[] = []; - if (!highlights || !highlights.length) highlights = getDropPositions(); + if (!highlights || !highlights.length) + highlights = deriveHighlightsFromLayers( + allWidgets, + canvasId, + canvasWidth, + blocksToDraw.map((block) => block?.widgetId), + isFillWidget, + ); base = highlights; const pos: XYCord = { From 404d5c058ed6a147ad991b9992ac5f88c0a7f3c1 Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 27 Dec 2022 20:12:05 -0500 Subject: [PATCH 307/708] move position utils to a separate file --- app/client/src/sagas/AutoLayoutUtils.ts | 158 +---------------- .../CanvasSagas/AutoLayoutDraggingSagas.ts | 2 +- app/client/src/sagas/WidgetOperationSagas.tsx | 2 +- .../src/utils/autoLayout/positionUtils.ts | 161 ++++++++++++++++++ 4 files changed, 164 insertions(+), 159 deletions(-) create mode 100644 app/client/src/utils/autoLayout/positionUtils.ts diff --git a/app/client/src/sagas/AutoLayoutUtils.ts b/app/client/src/sagas/AutoLayoutUtils.ts index ac1efe9e6ede..cae584e5c53d 100644 --- a/app/client/src/sagas/AutoLayoutUtils.ts +++ b/app/client/src/sagas/AutoLayoutUtils.ts @@ -8,6 +8,7 @@ import { LayerChild, } from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; +import { updateWidgetPositions } from "utils/autoLayout/positionUtils"; function getCanvas(widgets: CanvasWidgetsReduxState, containerId: string) { const container = widgets[containerId]; @@ -315,160 +316,3 @@ function checkIsNotVerticalStack(widget: any): boolean { widget.positioning !== Positioning.Vertical ); } - -/** - * Calculate widget position on canvas. - */ -export function updateWidgetPositions( - allWidgets: CanvasWidgetsReduxState, - parentId: string, -): CanvasWidgetsReduxState { - let widgets = { ...allWidgets }; - try { - const parent = widgets[parentId]; - if (!parent || !parent.flexLayers || !parent.flexLayers?.length) - return widgets; - - let height = 0; - for (const layer of parent.flexLayers) { - const payload: { - height: number; - widgets: CanvasWidgetsReduxState; - } = calculateWidgetPositions(widgets, layer, height); - widgets = payload.widgets; - height += payload.height; - } - return widgets; - } catch (e) { - console.error(e); - return widgets; - } -} - -function calculateWidgetPositions( - allWidgets: CanvasWidgetsReduxState, - layer: FlexLayer, - topRow: number, -): { height: number; widgets: CanvasWidgetsReduxState } { - let widgets = { ...allWidgets }; - - const startChildren = [], - centerChildren = [], - endChildren = [], - fillChildren = []; - let startColumns = 0, - centerColumns = 0, - endColumns = 0; - let startSize = 0, - centerSize = 0, - endSize = 0; - - // Calculate the number of columns occupied by hug widgets in each alignment. - for (const child of layer.children) { - const widget = widgets[child.id]; - const isFillWidget = widget.responsiveBehavior === ResponsiveBehavior.Fill; - if (isFillWidget) fillChildren.push(child); - if (child.align === FlexLayerAlignment.Start) { - startChildren.push(widget); - if (!isFillWidget) startColumns += widget.rightColumn - widget.leftColumn; - } else if (child.align === FlexLayerAlignment.Center) { - centerChildren.push(widget); - if (!isFillWidget) - centerColumns += widget.rightColumn - widget.leftColumn; - } else if (child.align === FlexLayerAlignment.End) { - endChildren.push(widget); - if (!isFillWidget) endColumns += widget.rightColumn - widget.leftColumn; - } - } - - const availableColumns = 64 - startColumns - centerColumns - endColumns; - const fillWidgetLength = availableColumns / fillChildren.length; - for (const child of fillChildren) { - if (child.align === FlexLayerAlignment.Start) { - startColumns += fillWidgetLength; - } else if (child.align === FlexLayerAlignment.Center) { - centerColumns += fillWidgetLength; - } else if (child.align === FlexLayerAlignment.End) { - endColumns += fillWidgetLength; - } - } - - const arr: { alignment: FlexLayerAlignment; columns: number }[] = [ - { alignment: FlexLayerAlignment.Start, columns: startColumns }, - { alignment: FlexLayerAlignment.Center, columns: centerColumns }, - { alignment: FlexLayerAlignment.End, columns: endColumns }, - ].sort((a, b) => b.columns - a.columns); - - const sizes: { - alignment: FlexLayerAlignment; - columns: number; - }[] = getAlignmentSizes(arr, 64, []); - - for (const each of sizes) { - if (each.alignment === FlexLayerAlignment.Start) { - startSize = each.columns; - } else if (each.alignment === FlexLayerAlignment.Center) { - centerSize = each.columns; - } else if (each.alignment === FlexLayerAlignment.End) { - endSize = each.columns; - } - } - - let maxHeight = 0; - [ - { children: startChildren, leftColumn: 0 }, - { - children: centerChildren, - leftColumn: startSize + centerSize / 2 - centerColumns / 2, - }, - { - children: endChildren, - leftColumn: startSize + centerSize + endSize - endColumns, - }, - ].forEach((each) => { - let left = each.leftColumn; - for (const widget of each.children) { - const height = widget.bottomRow - widget.topRow; - const width = - widget.responsiveBehavior === ResponsiveBehavior.Fill - ? fillWidgetLength - : widget.rightColumn - widget.leftColumn; - maxHeight = Math.max(maxHeight, height); - widgets = { - ...widgets, - [widget.widgetId]: { - ...widget, - leftColumn: left, - rightColumn: left + width, - topRow, - bottomRow: topRow + height, - }, - }; - left += width; - } - }); - - return { height: maxHeight, widgets }; -} - -function getAlignmentSizes( - arr: { alignment: FlexLayerAlignment; columns: number }[], - space: number, - sizes: { alignment: FlexLayerAlignment; columns: number }[] = [], -): { alignment: FlexLayerAlignment; columns: number }[] { - if (arr.length === 0) return sizes; - if (arr[0].columns > space / arr.length) { - sizes.push(arr[0]); - arr.shift(); - return getAlignmentSizes( - arr, - space - sizes[sizes.length - 1].columns, - sizes, - ); - } else { - for (let i = 0; i < arr.length; i++) { - sizes.push({ ...arr[i], columns: space / arr.length }); - } - } - return sizes; -} diff --git a/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts b/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts index 7df023f6da3f..f2f12dda9a05 100644 --- a/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts +++ b/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts @@ -21,10 +21,10 @@ import { all, call, put, select, takeLatest } from "redux-saga/effects"; import { updateFlexChildColumns, updateSizeOfAllChildren, - updateWidgetPositions, } from "sagas/AutoLayoutUtils"; import { getWidgets } from "sagas/selectors"; import { getUpdateDslAfterCreatingChild } from "sagas/WidgetAdditionSagas"; +import { updateWidgetPositions } from "utils/autoLayout/positionUtils"; function* addWidgetAndReorderSaga( actionPayload: ReduxAction<{ diff --git a/app/client/src/sagas/WidgetOperationSagas.tsx b/app/client/src/sagas/WidgetOperationSagas.tsx index b8a57e30f267..3e515b92a8ad 100644 --- a/app/client/src/sagas/WidgetOperationSagas.tsx +++ b/app/client/src/sagas/WidgetOperationSagas.tsx @@ -113,7 +113,6 @@ import { collisionCheckPostReflow, getBottomRowAfterReflow, } from "utils/reflowHookUtils"; -import { updateWidgetPositions } from "./AutoLayoutUtils"; import { getCanvasSizeAfterWidgetMove } from "./CanvasSagas/DraggingCanvasSagas"; import widgetAdditionSagas from "./WidgetAdditionSagas"; import { traverseTreeAndExecuteBlueprintChildOperations } from "./WidgetBlueprintSagas"; @@ -156,6 +155,7 @@ import { } from "./WidgetOperationUtils"; import { widgetSelectionSagas } from "./WidgetSelectionSagas"; import { getAllPaths } from "ce/workers/Evaluation/evaluationUtils"; +import { updateWidgetPositions } from "utils/autoLayout/positionUtils"; export function* updateAllChildCanvasHeights( currentContainerLikeWidgetId: string, diff --git a/app/client/src/utils/autoLayout/positionUtils.ts b/app/client/src/utils/autoLayout/positionUtils.ts new file mode 100644 index 000000000000..cfe2d64b7cb8 --- /dev/null +++ b/app/client/src/utils/autoLayout/positionUtils.ts @@ -0,0 +1,161 @@ +import { FlexLayerAlignment, ResponsiveBehavior } from "components/constants"; +import { FlexLayer } from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; +import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; + +/** + * Calculate widget position on canvas. + */ +export function updateWidgetPositions( + allWidgets: CanvasWidgetsReduxState, + parentId: string, + isMobile?: boolean, +): CanvasWidgetsReduxState { + let widgets = { ...allWidgets }; + try { + const parent = widgets[parentId]; + if (!parent || !parent.flexLayers || !parent.flexLayers?.length) + return widgets; + + let height = 0; + for (const layer of parent.flexLayers) { + const payload: { + height: number; + widgets: CanvasWidgetsReduxState; + } = calculateWidgetPositions(widgets, layer, height); + widgets = payload.widgets; + height += payload.height; + } + return widgets; + } catch (e) { + console.error(e); + return widgets; + } +} + +function calculateWidgetPositions( + allWidgets: CanvasWidgetsReduxState, + layer: FlexLayer, + topRow: number, +): { height: number; widgets: CanvasWidgetsReduxState } { + let widgets = { ...allWidgets }; + + const startChildren = [], + centerChildren = [], + endChildren = [], + fillChildren = []; + let startColumns = 0, + centerColumns = 0, + endColumns = 0; + let startSize = 0, + centerSize = 0, + endSize = 0; + + // Calculate the number of columns occupied by hug widgets in each alignment. + for (const child of layer.children) { + const widget = widgets[child.id]; + const isFillWidget = widget.responsiveBehavior === ResponsiveBehavior.Fill; + if (isFillWidget) fillChildren.push(child); + if (child.align === FlexLayerAlignment.Start) { + startChildren.push(widget); + if (!isFillWidget) startColumns += widget.rightColumn - widget.leftColumn; + } else if (child.align === FlexLayerAlignment.Center) { + centerChildren.push(widget); + if (!isFillWidget) + centerColumns += widget.rightColumn - widget.leftColumn; + } else if (child.align === FlexLayerAlignment.End) { + endChildren.push(widget); + if (!isFillWidget) endColumns += widget.rightColumn - widget.leftColumn; + } + } + + const availableColumns = 64 - startColumns - centerColumns - endColumns; + const fillWidgetLength = availableColumns / fillChildren.length; + for (const child of fillChildren) { + if (child.align === FlexLayerAlignment.Start) { + startColumns += fillWidgetLength; + } else if (child.align === FlexLayerAlignment.Center) { + centerColumns += fillWidgetLength; + } else if (child.align === FlexLayerAlignment.End) { + endColumns += fillWidgetLength; + } + } + + const arr: { alignment: FlexLayerAlignment; columns: number }[] = [ + { alignment: FlexLayerAlignment.Start, columns: startColumns }, + { alignment: FlexLayerAlignment.Center, columns: centerColumns }, + { alignment: FlexLayerAlignment.End, columns: endColumns }, + ].sort((a, b) => b.columns - a.columns); + + const sizes: { + alignment: FlexLayerAlignment; + columns: number; + }[] = getAlignmentSizes(arr, 64, []); + + for (const each of sizes) { + if (each.alignment === FlexLayerAlignment.Start) { + startSize = each.columns; + } else if (each.alignment === FlexLayerAlignment.Center) { + centerSize = each.columns; + } else if (each.alignment === FlexLayerAlignment.End) { + endSize = each.columns; + } + } + + let maxHeight = 0; + [ + { children: startChildren, leftColumn: 0 }, + { + children: centerChildren, + leftColumn: startSize + centerSize / 2 - centerColumns / 2, + }, + { + children: endChildren, + leftColumn: startSize + centerSize + endSize - endColumns, + }, + ].forEach((each) => { + let left = each.leftColumn; + for (const widget of each.children) { + const height = widget.bottomRow - widget.topRow; + const width = + widget.responsiveBehavior === ResponsiveBehavior.Fill + ? fillWidgetLength + : widget.rightColumn - widget.leftColumn; + maxHeight = Math.max(maxHeight, height); + widgets = { + ...widgets, + [widget.widgetId]: { + ...widget, + leftColumn: left, + rightColumn: left + width, + topRow, + bottomRow: topRow + height, + }, + }; + left += width; + } + }); + + return { height: maxHeight, widgets }; +} + +function getAlignmentSizes( + arr: { alignment: FlexLayerAlignment; columns: number }[], + space: number, + sizes: { alignment: FlexLayerAlignment; columns: number }[] = [], +): { alignment: FlexLayerAlignment; columns: number }[] { + if (arr.length === 0) return sizes; + if (arr[0].columns > space / arr.length) { + sizes.push(arr[0]); + arr.shift(); + return getAlignmentSizes( + arr, + space - sizes[sizes.length - 1].columns, + sizes, + ); + } else { + for (let i = 0; i < arr.length; i++) { + sizes.push({ ...arr[i], columns: space / arr.length }); + } + } + return sizes; +} From b5d9d2fe36a80931875ed9a08ac51a2cf5feae60 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Wed, 28 Dec 2022 15:30:57 +0530 Subject: [PATCH 308/708] fixing ui glitch bug. --- app/client/src/pages/common/CanvasArenas/StickyCanvasArena.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/client/src/pages/common/CanvasArenas/StickyCanvasArena.tsx b/app/client/src/pages/common/CanvasArenas/StickyCanvasArena.tsx index f6d08ded5620..4d762da08a09 100644 --- a/app/client/src/pages/common/CanvasArenas/StickyCanvasArena.tsx +++ b/app/client/src/pages/common/CanvasArenas/StickyCanvasArena.tsx @@ -127,6 +127,9 @@ export const StickyCanvasArena = forwardRef( data-testid={canvasId} id={canvasId} ref={stickyCanvasRef} + style={{ + position: "absolute", + }} /> Date: Wed, 28 Dec 2022 15:31:25 +0530 Subject: [PATCH 309/708] Code cleanup phase 1. --- app/client/src/actions/autoLayoutActions.ts | 6 +- .../src/ce/constants/ReduxActionConstants.tsx | 16 ++-- app/client/src/ce/sagas/index.tsx | 74 +++++++++---------- .../appsmith/autoLayout/FlexComponent.tsx | 11 ++- .../editorComponents/DraggableComponent.tsx | 25 +++---- .../pages/Editor/CanvasPropertyPane/index.tsx | 4 +- .../reducers/uiReducers/dragResizeReducer.ts | 4 + ...ateSagas.tsx => AutoLayoutUpdateSagas.tsx} | 0 .../src/utils/hooks/dragResizeHooks.tsx | 5 +- .../src/utils/hooks/useDynamicAppLayout.tsx | 4 +- app/client/src/widgets/BaseWidget.tsx | 8 +- app/client/src/widgets/WidgetUtils.ts | 43 +++++------ app/client/test/sagas.ts | 48 ++++++------ 13 files changed, 130 insertions(+), 118 deletions(-) rename app/client/src/sagas/{LayoutUpdateSagas.tsx => AutoLayoutUpdateSagas.tsx} (100%) diff --git a/app/client/src/actions/autoLayoutActions.ts b/app/client/src/actions/autoLayoutActions.ts index 49b2e3f8778c..ebb39faecf0a 100644 --- a/app/client/src/actions/autoLayoutActions.ts +++ b/app/client/src/actions/autoLayoutActions.ts @@ -1,18 +1,18 @@ import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants"; -export const removeWrappers = (parentId: string) => ({ +export const removeWrappersAction = (parentId: string) => ({ type: ReduxActionTypes.REMOVE_CHILD_WRAPPERS, payload: { parentId }, }); -export const addWrappers = (parentId: string) => ({ +export const addWrappersAction = (parentId: string) => ({ type: ReduxActionTypes.ADD_CHILD_WRAPPERS, payload: { parentId, }, }); -export const updateLayoutForMobileBreakpoint = ( +export const updateLayoutForMobileBreakpointAction = ( parentId: string, isMobile: boolean, canvasWidth: number, diff --git a/app/client/src/ce/constants/ReduxActionConstants.tsx b/app/client/src/ce/constants/ReduxActionConstants.tsx index acb0a7b37c88..e4f6e407a390 100644 --- a/app/client/src/ce/constants/ReduxActionConstants.tsx +++ b/app/client/src/ce/constants/ReduxActionConstants.tsx @@ -28,8 +28,6 @@ export const ReduxActionTypes = { INSTALL_LIBRARY_SUCCESS: "INSTALL_LIBRARY_SUCCESS", UNINSTALL_LIBRARY_INIT: "UNINSTALL_LIBRARY_INIT", UNINSTALL_LIBRARY_SUCCESS: "UNINSTALL_LIBRARY_SUCCESS", - SELECT_AUTOLAYOUT_HIGHLIGHT: "SELECT_AUTOLAYOUT_HIGHLIGHT", - CLEAR_HIGHLIGHT_SELECTION: "CLEAR_HIGHLIGHT_SELECTION", GIT_DISCARD_CHANGES_SUCCESS: "GIT_DISCARD_CHANGES_SUCCESS", GIT_DISCARD_CHANGES: "GIT_DISCARD_CHANGES", DELETE_BRANCH_INIT: "DELETE_BRANCH_INIT", @@ -748,18 +746,20 @@ export const ReduxActionTypes = { SET_LINT_ERRORS: "SET_LINT_ERRORS", SET_AUTO_HEIGHT_WITH_LIMITS_CHANGING: "SET_AUTO_HEIGHT_WITH_LIMITS_CHANGING", PROCESS_AUTO_HEIGHT_UPDATES: "PROCESS_AUTO_HEIGHT_UPDATES", - AUTOLAYOUT_REORDER_WIDGETS: "AUTOLAYOUT_REORDER_WIDGETS", - AUTOLAYOUT_ADD_NEW_WIDGETS: "AUTOLAYOUT_ADD_NEW_WIDGETS", - REMOVE_CHILD_WRAPPERS: "REMOVE_CHILD_WRAPPERS", - ADD_CHILD_WRAPPERS: "ADD_CHILD_WRAPPERS", - UPDATE_FILL_CHILD_LAYER: "UPDATE_FILL_CHILD_LAYER", - RECALCULATE_COLUMNS: "RECALCULATE_COLUMNS", LINT_TREE: "LINT_TREE", UPDATE_LINT_GLOBALS: "UPDATE_LINT_GLOBALS", REMOVE_TEMP_DATASOURCE_SUCCESS: "REMOVE_TEMP_DATASOURCE_SUCCESS", SET_DATASOURCE_SAVE_ACTION_FLAG: "SET_DATASOURCE_SAVE_ACTION_FLAG", SET_DATASOURCE_SAVE_ACTION_FROM_POPUP_FLAG: "SET_DATASOURCE_SAVE_ACTION_FROM_POPUP_FLAG", + SELECT_AUTOLAYOUT_HIGHLIGHT: "SELECT_AUTOLAYOUT_HIGHLIGHT", + CLEAR_HIGHLIGHT_SELECTION: "CLEAR_HIGHLIGHT_SELECTION", + AUTOLAYOUT_REORDER_WIDGETS: "AUTOLAYOUT_REORDER_WIDGETS", + AUTOLAYOUT_ADD_NEW_WIDGETS: "AUTOLAYOUT_ADD_NEW_WIDGETS", + REMOVE_CHILD_WRAPPERS: "REMOVE_CHILD_WRAPPERS", + ADD_CHILD_WRAPPERS: "ADD_CHILD_WRAPPERS", + UPDATE_FILL_CHILD_LAYER: "UPDATE_FILL_CHILD_LAYER", + RECALCULATE_COLUMNS: "RECALCULATE_COLUMNS", }; export type ReduxActionType = typeof ReduxActionTypes[keyof typeof ReduxActionTypes]; diff --git a/app/client/src/ce/sagas/index.tsx b/app/client/src/ce/sagas/index.tsx index 07107875abd7..dbfec18d7ee1 100644 --- a/app/client/src/ce/sagas/index.tsx +++ b/app/client/src/ce/sagas/index.tsx @@ -1,51 +1,51 @@ -import pageSagas from "sagas/PageSagas"; -import { watchActionSagas } from "sagas/ActionSagas"; -import { watchJSActionSagas } from "sagas/JSActionSagas"; import { watchActionExecutionSagas } from "@appsmith/sagas/ActionExecution/ActionExecutionSagas"; +import SuperUserSagas from "@appsmith/sagas/SuperUserSagas"; +import tenantSagas from "@appsmith/sagas/tenantSagas"; +import userSagas from "@appsmith/sagas/userSagas"; +import workspaceSagas from "@appsmith/sagas/WorkspaceSagas"; import { watchPluginActionExecutionSagas } from "sagas/ActionExecution/PluginActionSaga"; -import templateSagas from "sagas/TemplatesSagas"; -import widgetOperationSagas from "sagas/WidgetOperationSagas"; -import errorSagas from "sagas/ErrorSagas"; +import { watchActionSagas } from "sagas/ActionSagas"; +import apiPaneSagas from "sagas/ApiPaneSagas"; import applicationSagas from "sagas/ApplicationSagas"; +import appThemingSaga from "sagas/AppThemingSaga"; +import AutoHeightSagas from "sagas/autoHeightSagas"; +import autoLayoutUpdateSagas from "sagas/AutoLayoutUpdateSagas"; +import batchSagas from "sagas/BatchSagas"; +import autoLayoutDraggingSagas from "sagas/CanvasSagas/AutoLayoutDraggingSagas"; +import draggingCanvasSagas from "sagas/CanvasSagas/DraggingCanvasSagas"; +import selectionCanvasSagas from "sagas/CanvasSagas/SelectionCanvasSagas"; +import importedCollectionsSagas from "sagas/CollectionSagas"; +import curlImportSagas from "sagas/CurlImportSagas"; import { watchDatasourcesSagas } from "sagas/DatasourcesSagas"; +import debuggerSagas from "sagas/DebuggerSagas"; +import editorContextSagas from "sagas/editorContextSagas"; +import errorSagas from "sagas/ErrorSagas"; +import evaluationsSaga from "sagas/EvaluationsSaga"; +import formEvaluationChangeListener from "sagas/FormEvaluationSaga"; +import gitSyncSagas from "sagas/GitSyncSagas"; +import globalSearchSagas from "sagas/GlobalSearchSagas"; import initSagas from "sagas/InitSagas"; -import apiPaneSagas from "sagas/ApiPaneSagas"; +import { watchJSActionSagas } from "sagas/JSActionSagas"; +import JSLibrarySaga from "sagas/JSLibrarySaga"; import jsPaneSagas from "sagas/JSPaneSagas"; -import userSagas from "@appsmith/sagas/userSagas"; +import LintingSaga from "sagas/LintingSagas"; +import modalSagas from "sagas/ModalSagas"; +import NavigationSagas from "sagas/NavigationSagas"; +import onboardingSagas from "sagas/OnboardingSagas"; +import pageSagas from "sagas/PageSagas"; +import PageVisibilitySaga from "sagas/PageVisibilitySagas"; import pluginSagas from "sagas/PluginSagas"; -import workspaceSagas from "@appsmith/sagas/WorkspaceSagas"; -import importedCollectionsSagas from "sagas/CollectionSagas"; import providersSagas from "sagas/ProvidersSaga"; -import curlImportSagas from "sagas/CurlImportSagas"; -import snipingModeSagas from "sagas/SnipingModeSagas"; import queryPaneSagas from "sagas/QueryPaneSagas"; -import modalSagas from "sagas/ModalSagas"; -import batchSagas from "sagas/BatchSagas"; +import replaySaga from "sagas/ReplaySaga"; +import saaSPaneSagas from "sagas/SaaSPaneSagas"; +import snipingModeSagas from "sagas/SnipingModeSagas"; +import templateSagas from "sagas/TemplatesSagas"; import themeSagas from "sagas/ThemeSaga"; -import evaluationsSaga from "sagas/EvaluationsSaga"; -import onboardingSagas from "sagas/OnboardingSagas"; import utilSagas from "sagas/UtilSagas"; -import saaSPaneSagas from "sagas/SaaSPaneSagas"; -import actionExecutionChangeListeners from "sagas/WidgetLoadingSaga"; -import globalSearchSagas from "sagas/GlobalSearchSagas"; import websocketSagas from "sagas/WebsocketSagas/WebsocketSagas"; -import debuggerSagas from "sagas/DebuggerSagas"; -import replaySaga from "sagas/ReplaySaga"; -import selectionCanvasSagas from "sagas/CanvasSagas/SelectionCanvasSagas"; -import draggingCanvasSagas from "sagas/CanvasSagas/DraggingCanvasSagas"; -import gitSyncSagas from "sagas/GitSyncSagas"; -import appThemingSaga from "sagas/AppThemingSaga"; -import formEvaluationChangeListener from "sagas/FormEvaluationSaga"; -import SuperUserSagas from "@appsmith/sagas/SuperUserSagas"; -import NavigationSagas from "sagas/NavigationSagas"; -import editorContextSagas from "sagas/editorContextSagas"; -import PageVisibilitySaga from "sagas/PageVisibilitySagas"; -import JSLibrarySaga from "sagas/JSLibrarySaga"; -import AutoHeightSagas from "sagas/autoHeightSagas"; -import tenantSagas from "@appsmith/sagas/tenantSagas"; -import LintingSaga from "sagas/LintingSagas"; -import layoutUpdateSagas from "sagas/LayoutUpdateSagas"; -import autoLayoutDraggingSagas from "sagas/CanvasSagas/AutoLayoutDraggingSagas"; +import actionExecutionChangeListeners from "sagas/WidgetLoadingSaga"; +import widgetOperationSagas from "sagas/WidgetOperationSagas"; export const sagas = [ initSagas, @@ -94,6 +94,6 @@ export const sagas = [ tenantSagas, JSLibrarySaga, LintingSaga, - layoutUpdateSagas, + autoLayoutUpdateSagas, autoLayoutDraggingSagas, ]; diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx index 25e8224d5e1e..0db9514a0af6 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx @@ -81,10 +81,12 @@ export function FlexComponent(props: AutoLayoutProps) { clickToSelectWidget(props.widgetId); }, [props.widgetId, clickToSelectWidget]); - const { dragDetails } = useSelector( - (state: AppState) => state.ui.widgetDragResize, + const dragDetails = useSelector( + (state: AppState) => state.ui.widgetDragResize.dragDetails, + ); + const isDragging = useSelector( + (state: AppState) => state.ui.widgetDragResize.isDragging, ); - const isDragging: boolean = dragDetails?.draggedOn !== undefined; const siblingCount = useSelector( getSiblingCount(props.widgetId, props.parentId || MAIN_CONTAINER_WIDGET_ID), @@ -118,7 +120,8 @@ export function FlexComponent(props: AutoLayoutProps) { ? DEFAULT_MARGIN : Math.max(props.parentColumnSpace, DRAG_MARGIN); - const isAffectedByDrag: boolean = isDragging; + const isAffectedByDrag: boolean = + isDragging && dragDetails?.draggedOn !== undefined; // TODO: Simplify this logic. /** * resize logic: diff --git a/app/client/src/components/editorComponents/DraggableComponent.tsx b/app/client/src/components/editorComponents/DraggableComponent.tsx index f0b1866cda03..cf61aa721f61 100644 --- a/app/client/src/components/editorComponents/DraggableComponent.tsx +++ b/app/client/src/components/editorComponents/DraggableComponent.tsx @@ -1,23 +1,23 @@ -import React, { CSSProperties, useMemo, useRef } from "react"; -import styled from "styled-components"; -import { WidgetProps } from "widgets/BaseWidget"; -import { WIDGET_PADDING } from "constants/WidgetConstants"; -import { useSelector } from "react-redux"; import { AppState } from "@appsmith/reducers"; import { getColorWithOpacity } from "constants/DefaultTheme"; -import { - useShowTableFilterPane, - useWidgetDragResize, -} from "utils/hooks/dragResizeHooks"; +import { WIDGET_PADDING } from "constants/WidgetConstants"; +import React, { CSSProperties, useMemo, useRef } from "react"; +import { useSelector } from "react-redux"; import { previewModeSelector, snipingModeSelector, } from "selectors/editorSelectors"; -import { useWidgetSelection } from "utils/hooks/useWidgetSelection"; import { isCurrentWidgetFocused, isWidgetSelected, } from "selectors/widgetSelectors"; +import styled from "styled-components"; +import { + useShowTableFilterPane, + useWidgetDragResize, +} from "utils/hooks/dragResizeHooks"; +import { useWidgetSelection } from "utils/hooks/useWidgetSelection"; +import { WidgetProps } from "widgets/BaseWidget"; const DraggableWrapper = styled.div` display: block; @@ -78,7 +78,7 @@ function DraggableComponent(props: DraggableComponentProps) { const isPreviewMode = useSelector(previewModeSelector); // Dispatch hook handy to set any `DraggableComponent` as dragging/ not dragging // The value is boolean - const { setDraggingCanvas, setDraggingState } = useWidgetDragResize(); + const { setDraggingState } = useWidgetDragResize(); const showTableFilterPane = useShowTableFilterPane(); const isSelected = useSelector(isWidgetSelected(props.widgetId)); @@ -173,13 +173,12 @@ function DraggableComponent(props: DraggableComponentProps) { ), }; showTableFilterPane(); - setDraggingCanvas(props.parentId); - setDraggingState({ isDragging: true, dragGroupActualParent: props.parentId || "", draggingGroupCenter: { widgetId: props.widgetId }, startPoints, + draggedOn: props.parentId, }); } }; diff --git a/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx b/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx index 08e1c1200e52..cf4677f1ffc0 100644 --- a/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx +++ b/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx @@ -67,8 +67,8 @@ const Title = styled.p` // }, // ]), // ); -// if (isVerticalStack) dispatch(addWrappers(widgetId)); -// else removeWrappers(widgetId); +// if (isVerticalStack) dispatch(addWrappersAction(widgetId)); +// else removeWrappersAction(widgetId); // } // }} // > diff --git a/app/client/src/reducers/uiReducers/dragResizeReducer.ts b/app/client/src/reducers/uiReducers/dragResizeReducer.ts index 651749ee7252..e5e355f03a8d 100644 --- a/app/client/src/reducers/uiReducers/dragResizeReducer.ts +++ b/app/client/src/reducers/uiReducers/dragResizeReducer.ts @@ -41,6 +41,7 @@ export const widgetDraggingReducer = createImmerReducer(initialState, { dragGroupActualParent: string; draggingGroupCenter: DraggingGroupCenter; startPoints: any; + draggedOn?: string; }>, ) => { state.isDragging = action.payload.isDragging; @@ -49,6 +50,9 @@ export const widgetDraggingReducer = createImmerReducer(initialState, { draggingGroupCenter: action.payload.draggingGroupCenter, dragOffset: action.payload.startPoints, }; + if (action.payload.draggedOn) { + state.dragDetails.draggedOn = action.payload.draggedOn; + } }, [ReduxActionTypes.SET_NEW_WIDGET_DRAGGING]: ( state: WidgetDragResizeState, diff --git a/app/client/src/sagas/LayoutUpdateSagas.tsx b/app/client/src/sagas/AutoLayoutUpdateSagas.tsx similarity index 100% rename from app/client/src/sagas/LayoutUpdateSagas.tsx rename to app/client/src/sagas/AutoLayoutUpdateSagas.tsx diff --git a/app/client/src/utils/hooks/dragResizeHooks.tsx b/app/client/src/utils/hooks/dragResizeHooks.tsx index 94bc1045c194..6c61a5d92202 100644 --- a/app/client/src/utils/hooks/dragResizeHooks.tsx +++ b/app/client/src/utils/hooks/dragResizeHooks.tsx @@ -1,6 +1,6 @@ -import { useDispatch, useSelector } from "react-redux"; import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants"; import { useCallback, useEffect, useState } from "react"; +import { useDispatch, useSelector } from "react-redux"; import { snipingModeSelector } from "selectors/editorSelectors"; export const useShowPropertyPane = () => { @@ -113,11 +113,13 @@ export const useWidgetDragResize = () => { dragGroupActualParent = "", draggingGroupCenter = {}, startPoints, + draggedOn, }: { isDragging: boolean; dragGroupActualParent?: string; draggingGroupCenter?: Record; startPoints?: any; + draggedOn?: string; }) => { if (isDragging) { document.body.classList.add("dragging"); @@ -131,6 +133,7 @@ export const useWidgetDragResize = () => { dragGroupActualParent, draggingGroupCenter, startPoints, + draggedOn, }, }); }, diff --git a/app/client/src/utils/hooks/useDynamicAppLayout.tsx b/app/client/src/utils/hooks/useDynamicAppLayout.tsx index 2076599678b3..fc087565bdec 100644 --- a/app/client/src/utils/hooks/useDynamicAppLayout.tsx +++ b/app/client/src/utils/hooks/useDynamicAppLayout.tsx @@ -2,7 +2,7 @@ import { debounce, get } from "lodash"; import { useCallback, useEffect, useMemo } from "react"; import { useDispatch, useSelector } from "react-redux"; -import { updateLayoutForMobileBreakpoint } from "actions/autoLayoutActions"; +import { updateLayoutForMobileBreakpointAction } from "actions/autoLayoutActions"; import { updateCanvasLayoutAction } from "actions/editorActions"; import { APP_SETTINGS_PANE_WIDTH } from "constants/AppConstants"; import { @@ -225,7 +225,7 @@ export const useDynamicAppLayout = () => { useEffect(() => { function relayoutAtBreakpoint() { dispatch( - updateLayoutForMobileBreakpoint( + updateLayoutForMobileBreakpointAction( MAIN_CONTAINER_WIDGET_ID, mainCanvasProps?.isMobile, calculateCanvasWidth(), diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index adc9909ab9fa..d6be90f1a30a 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -432,7 +432,6 @@ abstract class BaseWidget< makeFlex(content: ReactNode) { const { componentHeight, componentWidth } = this.getComponentDimensions(); - return ( { + if (props.useAutoLayout) { + return false; + } if (shouldCheckIfEnabledWithLimits) { return props.dynamicHeight === DynamicHeight.AUTO_HEIGHT_WITH_LIMITS; } diff --git a/app/client/test/sagas.ts b/app/client/test/sagas.ts index e05eb41904b7..5d192f9fef89 100644 --- a/app/client/test/sagas.ts +++ b/app/client/test/sagas.ts @@ -1,36 +1,36 @@ -import initSagas from "../src/sagas/InitSagas"; -import apiPaneSagas from "../src/sagas/ApiPaneSagas"; -import jsPaneSagas from "../src/sagas/JSPaneSagas"; +import { watchActionExecutionSagas } from "@appsmith/sagas/ActionExecution/ActionExecutionSagas"; import userSagas from "@appsmith/sagas/userSagas"; -import pluginSagas from "../src/sagas/PluginSagas"; import workspaceSagas from "@appsmith/sagas/WorkspaceSagas"; +import { watchActionSagas } from "sagas/ActionSagas"; +import layoutUpdateSagas from "sagas/AutoLayoutUpdateSagas"; +import { watchDatasourcesSagas } from "sagas/DatasourcesSagas"; +import { watchJSActionSagas } from "sagas/JSActionSagas"; +import apiPaneSagas from "../src/sagas/ApiPaneSagas"; +import applicationSagas from "../src/sagas/ApplicationSagas"; +import batchSagas from "../src/sagas/BatchSagas"; +import draggingCanvasSagas from "../src/sagas/CanvasSagas/DraggingCanvasSagas"; +import selectionCanvasSagas from "../src/sagas/CanvasSagas/SelectionCanvasSagas"; import importedCollectionsSagas from "../src/sagas/CollectionSagas"; -import providersSagas from "../src/sagas/ProvidersSaga"; import curlImportSagas from "../src/sagas/CurlImportSagas"; -import snipingModeSagas from "../src/sagas/SnipingModeSagas"; -import queryPaneSagas from "../src/sagas/QueryPaneSagas"; +import debuggerSagas from "../src/sagas/DebuggerSagas"; +import formEvaluationChangeListener from "../src/sagas/FormEvaluationSaga"; +import globalSearchSagas from "../src/sagas/GlobalSearchSagas"; +import initSagas from "../src/sagas/InitSagas"; +import JSLibrarySaga from "../src/sagas/JSLibrarySaga"; +import jsPaneSagas from "../src/sagas/JSPaneSagas"; +import LintingSaga from "../src/sagas/LintingSagas"; import modalSagas from "../src/sagas/ModalSagas"; -import batchSagas from "../src/sagas/BatchSagas"; +import pluginSagas from "../src/sagas/PluginSagas"; +import providersSagas from "../src/sagas/ProvidersSaga"; +import queryPaneSagas from "../src/sagas/QueryPaneSagas"; +import recentEntitiesSagas from "../src/sagas/RecentEntitiesSagas"; +import saaSPaneSagas from "../src/sagas/SaaSPaneSagas"; +import snipingModeSagas from "../src/sagas/SnipingModeSagas"; import themeSagas from "../src/sagas/ThemeSaga"; import utilSagas from "../src/sagas/UtilSagas"; -import saaSPaneSagas from "../src/sagas/SaaSPaneSagas"; -import actionExecutionChangeListeners from "../src/sagas/WidgetLoadingSaga"; -import globalSearchSagas from "../src/sagas/GlobalSearchSagas"; -import recentEntitiesSagas from "../src/sagas/RecentEntitiesSagas"; import websocketSagas from "../src/sagas/WebsocketSagas/WebsocketSagas"; -import debuggerSagas from "../src/sagas/DebuggerSagas"; -import { watchActionSagas } from "sagas/ActionSagas"; -import { watchActionExecutionSagas } from "@appsmith/sagas/ActionExecution/ActionExecutionSagas"; +import actionExecutionChangeListeners from "../src/sagas/WidgetLoadingSaga"; import widgetOperationSagas from "../src/sagas/WidgetOperationSagas"; -import applicationSagas from "../src/sagas/ApplicationSagas"; -import { watchDatasourcesSagas } from "sagas/DatasourcesSagas"; -import { watchJSActionSagas } from "sagas/JSActionSagas"; -import selectionCanvasSagas from "../src/sagas/CanvasSagas/SelectionCanvasSagas"; -import draggingCanvasSagas from "../src/sagas/CanvasSagas/DraggingCanvasSagas"; -import formEvaluationChangeListener from "../src/sagas/FormEvaluationSaga"; -import LintingSaga from "../src/sagas/LintingSagas"; -import JSLibrarySaga from "../src/sagas/JSLibrarySaga"; -import layoutUpdateSagas from "sagas/LayoutUpdateSagas"; export const sagasToRunForTests = [ initSagas, From 6253c84a16daf957714def4ac794a3bca7aa67b6 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Wed, 28 Dec 2022 17:30:39 +0530 Subject: [PATCH 310/708] Disabling dynamic height temporarily for mobile responsiveness --- .../pages/Editor/CanvasPropertyPane/index.tsx | 75 ------------------- app/client/src/utils/WidgetFactoryHelpers.ts | 3 + .../src/utils/WidgetRegisterHelpers.tsx | 9 ++- 3 files changed, 8 insertions(+), 79 deletions(-) diff --git a/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx b/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx index cf4677f1ffc0..02f3f8b6778c 100644 --- a/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx +++ b/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx @@ -13,81 +13,6 @@ const Title = styled.p` color: ${Colors.GRAY_800}; `; -// export function CanvasPropertyPane() { -// const dispatch = useDispatch(); - -// const openAppSettingsPane = () => { -// dispatch(openAppSettingsPaneAction()); -// }; - -// const PositioningOptions = () => { -// const dispatch = useDispatch(); -// const widgets = useSelector(getWidgets); -// const options: DropdownOption[] = [ -// { label: "Fixed", value: Positioning.Fixed }, -// { label: "Vertical stack", value: Positioning.Vertical }, -// ]; -// const [selectedOption, setSelectedOption] = useState(() => { -// if (widgets && widgets["0"].positioning) { -// return options -// .map((each) => each.value) -// .indexOf(widgets["0"].positioning); -// } -// return 0; -// }); -// const renderOption: RenderOption = ({ -// isHighlighted, -// isSelectedNode, -// option, -// }) => ( -//
{ -// if (!isSelectedNode) { -// setSelectedOption(options.indexOf(option as DropdownOption)); -// const isVerticalStack = -// (option as DropdownOption).value === Positioning.Vertical; -// const widgetId = "0"; -// dispatch( -// batchUpdateMultipleWidgetProperties([ -// { -// widgetId, -// updates: { -// modify: { -// positioning: (option as DropdownOption).value, -// useAutoLayout: -// (option as DropdownOption).value !== Positioning.Fixed, -// direction: isVerticalStack -// ? LayoutDirection.Vertical -// : LayoutDirection.Horizontal, -// }, -// }, -// }, -// ]), -// ); -// if (isVerticalStack) dispatch(addWrappersAction(widgetId)); -// else removeWrappersAction(widgetId); -// } -// }} -// > -//
{(option as DropdownOption).label}
-//
-// ); -// return ( -//
-// -//
-// ); -// }; - export function CanvasPropertyPane() { const dispatch = useDispatch(); diff --git a/app/client/src/utils/WidgetFactoryHelpers.ts b/app/client/src/utils/WidgetFactoryHelpers.ts index dcc9d3f770b1..70a38b60026d 100644 --- a/app/client/src/utils/WidgetFactoryHelpers.ts +++ b/app/client/src/utils/WidgetFactoryHelpers.ts @@ -165,7 +165,10 @@ export function enhancePropertyPaneConfig( // Enhance property pane with widget features // TODO(abhinav): The following "configType" check should come // from the features themselves. + + // ToDO(Ashok): Need to bring back Dynamic Height features based on mode of the editor (Fixed vs Mobile responsiveness) if ( + false && features && (configType === undefined || configType === PropertyPaneConfigTypes.CONTENT) ) { diff --git a/app/client/src/utils/WidgetRegisterHelpers.tsx b/app/client/src/utils/WidgetRegisterHelpers.tsx index fc9337188585..ce00ae19e4d1 100644 --- a/app/client/src/utils/WidgetRegisterHelpers.tsx +++ b/app/client/src/utils/WidgetRegisterHelpers.tsx @@ -7,16 +7,16 @@ import BaseWidget from "widgets/BaseWidget"; import WidgetFactory, { NonSerialisableWidgetConfigs } from "./WidgetFactory"; import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants"; +import { memoize } from "lodash"; +import { WidgetConfiguration } from "widgets/constants"; import withMeta from "widgets/MetaHOC"; +import withWidgetProps from "widgets/withWidgetProps"; import { generateReactKey } from "./generators"; -import { memoize } from "lodash"; import { RegisteredWidgetFeatures, WidgetFeaturePropertyEnhancements, WidgetFeatureProps, } from "./WidgetFeatures"; -import { WidgetConfiguration } from "widgets/constants"; -import withWidgetProps from "widgets/withWidgetProps"; const generateWidget = memoize(function getWidgetComponent( Widget: typeof BaseWidget, @@ -56,7 +56,8 @@ export const registerWidget = (Widget: any, config: WidgetConfiguration) => { export const configureWidget = (config: WidgetConfiguration) => { let features: Record = {}; - if (config.features) { + // ToDO(Ashok): Need to bring back Dynamic Height features based on mode of the editor (Fixed vs Mobile responsiveness) + if (false && config.features) { Object.keys(config.features).forEach((registeredFeature: string) => { features = Object.assign( {}, From 69e6ed38d1bbc5573974c382eb53ea56fed1c78f Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Tue, 20 Dec 2022 15:39:27 +0530 Subject: [PATCH 311/708] disable resize for 1d widgets. --- .../editorComponents/ResizableComponent.tsx | 8 ++++--- app/client/src/constants/WidgetConstants.tsx | 23 +++++++++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/app/client/src/components/editorComponents/ResizableComponent.tsx b/app/client/src/components/editorComponents/ResizableComponent.tsx index 9fd98ee9e60e..96ca1b138a1f 100644 --- a/app/client/src/components/editorComponents/ResizableComponent.tsx +++ b/app/client/src/components/editorComponents/ResizableComponent.tsx @@ -3,7 +3,7 @@ import { batchUpdateMultipleWidgetProperties } from "actions/controlActions"; import { focusWidget } from "actions/widgetActions"; import { LayoutDirection, ResponsiveBehavior } from "components/constants"; import { EditorContext } from "components/editorComponents/EditorContextProvider"; -import { GridDefaults } from "constants/WidgetConstants"; +import { GridDefaults, NonResizableWidgets } from "constants/WidgetConstants"; import { get, omit } from "lodash"; import { XYCord } from "pages/common/CanvasArenas/hooks/useCanvasDragging"; import React, { memo, useContext, useMemo } from "react"; @@ -318,10 +318,12 @@ export const ResizableComponent = memo(function ResizableComponent( const isVerticalResizeEnabled = useMemo(() => { return !isAutoHeightEnabledForWidget(props) && isEnabled; }, [props, isAutoHeightEnabledForWidget, isEnabled]); - + const allowResize = !( + NonResizableWidgets.includes(props.type) || isMultiSelected + ); return ( Date: Wed, 28 Dec 2022 21:08:45 +0530 Subject: [PATCH 312/708] hide responsive behaviour options. --- .../editorComponents/ResizableComponent.tsx | 3 +- app/client/src/constants/WidgetConstants.tsx | 23 ------ app/client/src/utils/WidgetFactoryHelpers.ts | 69 ++++++++--------- .../src/utils/WidgetRegisterHelpers.tsx | 29 +++---- app/client/src/utils/layoutPropertiesUtils.ts | 77 ++++++++++++++++++- .../src/widgets/AudioRecorderWidget/index.ts | 8 +- .../AudioRecorderWidget/widget/index.tsx | 14 +--- app/client/src/widgets/AudioWidget/index.tsx | 8 +- .../src/widgets/AudioWidget/widget/index.tsx | 14 +--- .../src/widgets/BaseInputWidget/index.ts | 9 ++- .../widgets/BaseInputWidget/widget/index.tsx | 15 +--- .../src/widgets/ButtonGroupWidget/index.ts | 8 +- .../ButtonGroupWidget/widget/index.tsx | 19 +---- app/client/src/widgets/ButtonWidget/index.ts | 4 +- .../src/widgets/ButtonWidget/widget/index.tsx | 14 +--- .../src/widgets/CameraWidget/widget/index.tsx | 14 +--- app/client/src/widgets/CanvasWidget.tsx | 12 ++- .../widget/propertyConfig/contentConfig.ts | 15 +--- app/client/src/widgets/ChartWidget/index.ts | 4 +- .../ChartWidget/widget/propertyConfig.ts | 14 +--- .../src/widgets/CheckboxGroupWidget/index.ts | 5 +- .../CheckboxGroupWidget/widget/index.tsx | 14 +--- .../src/widgets/CheckboxWidget/index.ts | 11 +-- .../widgets/CheckboxWidget/widget/index.tsx | 15 +--- .../widget/propertyConfig/contentConfig.ts | 16 +--- .../src/widgets/ContainerWidget/index.ts | 9 +-- .../widgets/ContainerWidget/widget/index.tsx | 17 +--- .../src/widgets/CurrencyInputWidget/index.ts | 5 +- .../src/widgets/DatePickerWidget2/index.ts | 5 +- .../DatePickerWidget2/widget/index.tsx | 15 +--- app/client/src/widgets/DividerWidget/index.ts | 4 +- .../widgets/DividerWidget/widget/index.tsx | 14 +--- .../DocumentViewerWidget/widget/index.tsx | 14 +--- .../src/widgets/FilePickerWidgetV2/index.ts | 4 +- .../FilePickerWidgetV2/widget/index.tsx | 15 +--- app/client/src/widgets/FormWidget/index.ts | 9 +-- .../widgets/IconButtonWidget/widget/index.tsx | 19 +---- .../src/widgets/IframeWidget/widget/index.tsx | 14 +--- .../src/widgets/ImageWidget/widget/index.tsx | 14 +--- app/client/src/widgets/InputWidgetV2/index.ts | 5 +- .../src/widgets/JSONFormWidget/index.ts | 5 +- .../JSONFormWidget/widget/propertyConfig.ts | 20 +---- app/client/src/widgets/ListWidget/index.ts | 7 +- .../ListWidget/widget/propertyConfig.ts | 17 +--- .../widgets/MapChartWidget/widget/index.tsx | 14 +--- app/client/src/widgets/MapWidget/index.ts | 6 +- .../src/widgets/MapWidget/widget/index.tsx | 14 +--- .../widget/propertyConfig/contentConfig.ts | 15 +--- .../widgets/MultiSelectTreeWidget/index.ts | 6 +- .../MultiSelectTreeWidget/widget/index.tsx | 15 +--- .../src/widgets/MultiSelectWidget/index.ts | 6 +- .../MultiSelectWidget/widget/index.tsx | 26 +++---- .../src/widgets/MultiSelectWidgetV2/index.ts | 5 +- .../MultiSelectWidgetV2/widget/index.tsx | 15 +--- .../widget/propertyConfig/contentConfig.ts | 15 +--- .../src/widgets/PhoneInputWidget/index.ts | 5 +- .../widgets/ProgressWidget/widget/index.tsx | 14 +--- .../QRGeneratorWidget/widget/index.tsx | 14 +--- .../widgets/RadioGroupWidget/widget/index.tsx | 15 +--- .../widget/propertyConfig/contentConfig.ts | 15 +--- .../src/widgets/RateWidget/widget/index.tsx | 14 +--- .../src/widgets/RichTextEditorWidget/index.ts | 5 +- .../RichTextEditorWidget/widget/index.tsx | 16 +--- app/client/src/widgets/SelectWidget/index.ts | 5 +- .../src/widgets/SelectWidget/widget/index.tsx | 15 +--- .../widgets/SingleSelectTreeWidget/index.ts | 6 +- .../SingleSelectTreeWidget/widget/index.tsx | 15 +--- .../widgets/StatboxWidget/widget/index.tsx | 16 +--- .../SwitchGroupWidget/widget/index.tsx | 15 +--- .../src/widgets/SwitchWidget/widget/index.tsx | 15 +--- app/client/src/widgets/TableWidgetV2/index.ts | 6 +- .../PanelConfig/ResponsiveBehavior.ts | 18 ++--- .../propertyConfig/PanelConfig/index.ts | 3 +- app/client/src/widgets/TabsWidget/index.ts | 5 +- .../src/widgets/TabsWidget/widget/index.tsx | 17 +--- app/client/src/widgets/TextWidget/index.ts | 4 +- .../src/widgets/TextWidget/widget/index.tsx | 14 +--- .../src/widgets/VideoWidget/widget/index.tsx | 15 +--- 78 files changed, 337 insertions(+), 713 deletions(-) diff --git a/app/client/src/components/editorComponents/ResizableComponent.tsx b/app/client/src/components/editorComponents/ResizableComponent.tsx index 96ca1b138a1f..fe2807bee163 100644 --- a/app/client/src/components/editorComponents/ResizableComponent.tsx +++ b/app/client/src/components/editorComponents/ResizableComponent.tsx @@ -3,7 +3,7 @@ import { batchUpdateMultipleWidgetProperties } from "actions/controlActions"; import { focusWidget } from "actions/widgetActions"; import { LayoutDirection, ResponsiveBehavior } from "components/constants"; import { EditorContext } from "components/editorComponents/EditorContextProvider"; -import { GridDefaults, NonResizableWidgets } from "constants/WidgetConstants"; +import { GridDefaults } from "constants/WidgetConstants"; import { get, omit } from "lodash"; import { XYCord } from "pages/common/CanvasArenas/hooks/useCanvasDragging"; import React, { memo, useContext, useMemo } from "react"; @@ -28,6 +28,7 @@ import { useWidgetDragResize, } from "utils/hooks/dragResizeHooks"; import { useWidgetSelection } from "utils/hooks/useWidgetSelection"; +import { NonResizableWidgets } from "utils/layoutPropertiesUtils"; import { getSnapColumns } from "utils/WidgetPropsUtils"; import { WidgetOperations, diff --git a/app/client/src/constants/WidgetConstants.tsx b/app/client/src/constants/WidgetConstants.tsx index 91f2c3d855d4..477ae9d1b2c0 100644 --- a/app/client/src/constants/WidgetConstants.tsx +++ b/app/client/src/constants/WidgetConstants.tsx @@ -187,26 +187,3 @@ export const WIDGET_PROPS_TO_SKIP_FROM_EVAL = { version: true, displayName: true, }; - -export const NonResizableWidgets = [ - "AUDIO_WIDGET", - "BUTTON_WIDGET", - "BUTTON_GROUP_WIDGET", - "CHECKBOX_WIDGET", - "CURRENCY_INPUT_WIDGET", - "DATE_PICKER_WIDGET2", - "DIVIDER_WIDGET", - "FILE_PICKER_WIDGET_V2", - "ICON_WIDGET", - "INPUT_WIDGET_V2", - "MENU_BUTTON_WIDGET", - "MULTI_SELECT_TREE_WIDGET", - "MULTI_SELECT_WIDGET_V2", - "PHONE_INPUT_WIDGET", - "PROGRESS_WIDGET", - "RATE_WIDGET", - "SELECT_WIDGET", - "SWITCH_WIDGET", - "TEXT_WIDGET", - "SINGLE_SELECT_TREE_WIDGET", -]; diff --git a/app/client/src/utils/WidgetFactoryHelpers.ts b/app/client/src/utils/WidgetFactoryHelpers.ts index 70a38b60026d..ef2b91b1897a 100644 --- a/app/client/src/utils/WidgetFactoryHelpers.ts +++ b/app/client/src/utils/WidgetFactoryHelpers.ts @@ -4,15 +4,9 @@ import { PropertyPaneSectionConfig, } from "constants/PropertyControlConstants"; import { ValidationTypes } from "constants/WidgetValidation"; -import log from "loglevel"; import { generateReactKey } from "./generators"; import { WidgetType } from "./WidgetFactory"; -import { - PropertyPaneConfigTemplates, - RegisteredWidgetFeatures, - WidgetFeaturePropertyPaneEnhancements, - WidgetFeatures, -} from "./WidgetFeatures"; +import { WidgetFeatures } from "./WidgetFeatures"; export enum PropertyPaneConfigTypes { STYLE = "STYLE", @@ -167,37 +161,36 @@ export function enhancePropertyPaneConfig( // from the features themselves. // ToDO(Ashok): Need to bring back Dynamic Height features based on mode of the editor (Fixed vs Mobile responsiveness) - if ( - false && - features && - (configType === undefined || configType === PropertyPaneConfigTypes.CONTENT) - ) { - Object.keys(features).forEach((registeredFeature: string) => { - const { sectionIndex } = features[ - registeredFeature as RegisteredWidgetFeatures - ]; - const sectionName = (config[sectionIndex] as PropertyPaneSectionConfig) - ?.sectionName; - if (!sectionName || sectionName !== "General") { - log.error(`Invalid section index for feature: ${registeredFeature}`); - } - if ( - Array.isArray(config[sectionIndex].children) && - PropertyPaneConfigTemplates[ - registeredFeature as RegisteredWidgetFeatures - ] - ) { - config[sectionIndex].children?.push( - ...PropertyPaneConfigTemplates[ - registeredFeature as RegisteredWidgetFeatures - ], - ); - config = WidgetFeaturePropertyPaneEnhancements[ - registeredFeature as RegisteredWidgetFeatures - ](config, widgetType); - } - }); - } + // if ( + // features && + // (configType === undefined || configType === PropertyPaneConfigTypes.CONTENT) + // ) { + // Object.keys(features).forEach((registeredFeature: string) => { + // const { sectionIndex } = features[ + // registeredFeature as RegisteredWidgetFeatures + // ]; + // const sectionName = (config[sectionIndex] as PropertyPaneSectionConfig) + // ?.sectionName; + // if (!sectionName || sectionName !== "General") { + // log.error(`Invalid section index for feature: ${registeredFeature}`); + // } + // if ( + // Array.isArray(config[sectionIndex].children) && + // PropertyPaneConfigTemplates[ + // registeredFeature as RegisteredWidgetFeatures + // ] + // ) { + // config[sectionIndex].children?.push( + // ...PropertyPaneConfigTemplates[ + // registeredFeature as RegisteredWidgetFeatures + // ], + // ); + // config = WidgetFeaturePropertyPaneEnhancements[ + // registeredFeature as RegisteredWidgetFeatures + // ](config, widgetType); + // } + // }); + // } return config; } diff --git a/app/client/src/utils/WidgetRegisterHelpers.tsx b/app/client/src/utils/WidgetRegisterHelpers.tsx index ce00ae19e4d1..1146e5442fa9 100644 --- a/app/client/src/utils/WidgetRegisterHelpers.tsx +++ b/app/client/src/utils/WidgetRegisterHelpers.tsx @@ -12,11 +12,6 @@ import { WidgetConfiguration } from "widgets/constants"; import withMeta from "widgets/MetaHOC"; import withWidgetProps from "widgets/withWidgetProps"; import { generateReactKey } from "./generators"; -import { - RegisteredWidgetFeatures, - WidgetFeaturePropertyEnhancements, - WidgetFeatureProps, -} from "./WidgetFeatures"; const generateWidget = memoize(function getWidgetComponent( Widget: typeof BaseWidget, @@ -55,19 +50,19 @@ export const registerWidget = (Widget: any, config: WidgetConfiguration) => { }; export const configureWidget = (config: WidgetConfiguration) => { - let features: Record = {}; + const features: Record = {}; // ToDO(Ashok): Need to bring back Dynamic Height features based on mode of the editor (Fixed vs Mobile responsiveness) - if (false && config.features) { - Object.keys(config.features).forEach((registeredFeature: string) => { - features = Object.assign( - {}, - WidgetFeatureProps[registeredFeature as RegisteredWidgetFeatures], - WidgetFeaturePropertyEnhancements[ - registeredFeature as RegisteredWidgetFeatures - ](config), - ); - }); - } + // if (config.features) { + // Object.keys(config.features).forEach((registeredFeature: string) => { + // features = Object.assign( + // {}, + // WidgetFeatureProps[registeredFeature as RegisteredWidgetFeatures], + // WidgetFeaturePropertyEnhancements[ + // registeredFeature as RegisteredWidgetFeatures + // ](config), + // ); + // }); + // } const _config = { ...config.defaults, diff --git a/app/client/src/utils/layoutPropertiesUtils.ts b/app/client/src/utils/layoutPropertiesUtils.ts index 52d432e7a1ea..d1dfcdd999d3 100644 --- a/app/client/src/utils/layoutPropertiesUtils.ts +++ b/app/client/src/utils/layoutPropertiesUtils.ts @@ -3,12 +3,12 @@ import { AlignItems, Alignment, FlexDirection, + FlexVerticalAlignment, JustifyContent, LayoutDirection, Positioning, ResponsiveBehavior, Spacing, - FlexVerticalAlignment, } from "components/constants"; import { ValidationTypes } from "constants/WidgetValidation"; @@ -224,3 +224,78 @@ export const generateVerticalAlignmentConfig = ( export function getLayoutConfig(alignment: Alignment, spacing: Spacing): any[] { return [generateAlignmentConfig(alignment), generateSpacingConfig(spacing)]; } + +export const NonResizableWidgets = [ + "AUDIO_WIDGET", + "BUTTON_WIDGET", + "BUTTON_GROUP_WIDGET", + "CHECKBOX_WIDGET", + "CURRENCY_INPUT_WIDGET", + "DATE_PICKER_WIDGET2", + "DIVIDER_WIDGET", + "FILE_PICKER_WIDGET_V2", + "ICON_WIDGET", + "INPUT_WIDGET_V2", + "MENU_BUTTON_WIDGET", + "MULTI_SELECT_TREE_WIDGET", + "MULTI_SELECT_WIDGET_V2", + "PHONE_INPUT_WIDGET", + "PROGRESS_WIDGET", + "RATE_WIDGET", + "SELECT_WIDGET", + "SWITCH_WIDGET", + "TEXT_WIDGET", + "SINGLE_SELECT_TREE_WIDGET", +]; + +export const DefaultFillWidgets = [ + "CANVAS_WIDGET", + "AUDIO_WIDGET", + "AUDIO_RECORDER_WIDGET", + "BASE_INPUT_WIDGET", + "BUTTON_GROUP_WIDGET", + "CHART_WIDGET", + "CHECKBOX_WIDGET", + "CHECKBOX_GROUP_WIDGET", + "CURRENCY_INPUT_WIDGET", + "CONTAINER_WIDGET", + "DATE_PICKER_WIDGET2", + "DIVIDER_WIDGET", + "FORM_WIDGET", + "FILE_PICKER_WIDGET_V2", + "INPUT_WIDGET_V2", + "JSON_FORM_WIDGET", + "LIST_WIDGET", + "MAP_WIDGET", + "MULTI_SELECT_TREE_WIDGET", + "MULTI_SELECT_WIDGET", + "MULTI_SELECT_WIDGET_V2", + "PHONE_INPUT_WIDGET", + "SELECT_WIDGET", + "TEXT_WIDGET", + "SINGLE_SELECT_TREE_WIDGET", + "RICH_TEXT_EDITOR_WIDGET", + "TABS_WIDGET_", + "TABLE_WIDGET_V2", +]; + +export function getDefaultResponsiveBehavior(widgetType: string) { + return DefaultFillWidgets.includes(widgetType) + ? ResponsiveBehavior.Fill + : ResponsiveBehavior.Hug; +} + +export function getResponsiveLayoutConfig(widgetType: string) { + // ToDO(Ashok): disabling for now, will be revisited at a later point + + // const defaultBehavior = getDefaultResponsiveBehavior(widgetType); + return [ + // { + // sectionName: "Responsive Layout", + // children: [ + // generateResponsiveBehaviorConfig(defaultBehavior), + // generateVerticalAlignmentConfig(), + // ], + // }, + ]; +} diff --git a/app/client/src/widgets/AudioRecorderWidget/index.ts b/app/client/src/widgets/AudioRecorderWidget/index.ts index f2afc2c2a84d..f3d8186efaca 100644 --- a/app/client/src/widgets/AudioRecorderWidget/index.ts +++ b/app/client/src/widgets/AudioRecorderWidget/index.ts @@ -1,7 +1,7 @@ -import Widget from "./widget"; -import IconSVG from "./icon.svg"; -import { ResponsiveBehavior } from "components/constants"; import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; +import { getDefaultResponsiveBehavior } from "utils/layoutPropertiesUtils"; +import IconSVG from "./icon.svg"; +import Widget from "./widget"; export const CONFIG = { type: Widget.getWidgetType(), @@ -18,7 +18,7 @@ export const CONFIG = { widgetName: "AudioRecorder", version: 1, animateLoading: true, - responsiveBehavior: ResponsiveBehavior.Fill, + responsiveBehavior: getDefaultResponsiveBehavior(Widget.getWidgetType()), minWidth: FILL_WIDGET_MIN_WIDTH, }, properties: { diff --git a/app/client/src/widgets/AudioRecorderWidget/widget/index.tsx b/app/client/src/widgets/AudioRecorderWidget/widget/index.tsx index 0be9c59ba1a7..59e09bc8dfb0 100644 --- a/app/client/src/widgets/AudioRecorderWidget/widget/index.tsx +++ b/app/client/src/widgets/AudioRecorderWidget/widget/index.tsx @@ -1,15 +1,11 @@ import React from "react"; -import { ResponsiveBehavior } from "components/constants"; import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; import { WidgetType } from "constants/WidgetConstants"; import { ValidationTypes } from "constants/WidgetValidation"; import { Stylesheet } from "entities/AppTheming"; import { createBlobUrl } from "utils/AppsmithUtils"; -import { - generateResponsiveBehaviorConfig, - generateVerticalAlignmentConfig, -} from "utils/layoutPropertiesUtils"; +import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; import { FileDataTypes } from "widgets/constants"; @@ -74,13 +70,7 @@ class AudioRecorderWidget extends BaseWidget< }, ], }, - { - sectionName: "Responsive Layout", - children: [ - generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill), - generateVerticalAlignmentConfig(), - ], - }, + ...getResponsiveLayoutConfig(this.getWidgetType()), { sectionName: "Events", children: [ diff --git a/app/client/src/widgets/AudioWidget/index.tsx b/app/client/src/widgets/AudioWidget/index.tsx index d98b878483c8..2bfffca54df5 100644 --- a/app/client/src/widgets/AudioWidget/index.tsx +++ b/app/client/src/widgets/AudioWidget/index.tsx @@ -1,7 +1,7 @@ -import Widget from "./widget"; -import IconSVG from "./icon.svg"; -import { ResponsiveBehavior } from "components/constants"; import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; +import { getDefaultResponsiveBehavior } from "utils/layoutPropertiesUtils"; +import IconSVG from "./icon.svg"; +import Widget from "./widget"; export const CONFIG = { type: Widget.getWidgetType(), @@ -17,7 +17,7 @@ export const CONFIG = { autoPlay: false, version: 1, animateLoading: true, - responsiveBehavior: ResponsiveBehavior.Fill, + responsiveBehavior: getDefaultResponsiveBehavior(Widget.getWidgetType()), minWidth: FILL_WIDGET_MIN_WIDTH, }, properties: { diff --git a/app/client/src/widgets/AudioWidget/widget/index.tsx b/app/client/src/widgets/AudioWidget/widget/index.tsx index ae89341ec244..22a4a641b063 100644 --- a/app/client/src/widgets/AudioWidget/widget/index.tsx +++ b/app/client/src/widgets/AudioWidget/widget/index.tsx @@ -1,4 +1,3 @@ -import { ResponsiveBehavior } from "components/constants"; import Skeleton from "components/utils/Skeleton"; import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; import { WidgetType } from "constants/WidgetConstants"; @@ -7,10 +6,7 @@ import React, { lazy, Suspense } from "react"; import ReactPlayer from "react-player"; import { retryPromise } from "utils/AppsmithUtils"; import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService"; -import { - generateResponsiveBehaviorConfig, - generateVerticalAlignmentConfig, -} from "utils/layoutPropertiesUtils"; +import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import BaseWidget, { WidgetProps, WidgetState } from "../../BaseWidget"; const AudioComponent = lazy(() => retryPromise(() => import("../component"))); @@ -88,13 +84,7 @@ class AudioWidget extends BaseWidget { }, ], }, - { - sectionName: "Responsive Layout", - children: [ - generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill), - generateVerticalAlignmentConfig(), - ], - }, + ...getResponsiveLayoutConfig(this.getWidgetType()), { sectionName: "Events", children: [ diff --git a/app/client/src/widgets/BaseInputWidget/index.ts b/app/client/src/widgets/BaseInputWidget/index.ts index 4c9dd1ade907..dd7df0567131 100644 --- a/app/client/src/widgets/BaseInputWidget/index.ts +++ b/app/client/src/widgets/BaseInputWidget/index.ts @@ -1,8 +1,9 @@ -import Widget from "./widget"; -import IconSVG from "./icon.svg"; -import { LabelPosition, ResponsiveBehavior } from "components/constants"; import { Alignment } from "@blueprintjs/core"; +import { LabelPosition } from "components/constants"; import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; +import { getDefaultResponsiveBehavior } from "utils/layoutPropertiesUtils"; +import IconSVG from "./icon.svg"; +import Widget from "./widget"; export const CONFIG = { type: Widget.getWidgetType(), @@ -28,7 +29,7 @@ export const CONFIG = { isRequired: false, isDisabled: false, animateLoading: true, - responsiveBehavior: ResponsiveBehavior.Fill, + responsiveBehavior: getDefaultResponsiveBehavior(Widget.getWidgetType()), minWidth: FILL_WIDGET_MIN_WIDTH, }, properties: { diff --git a/app/client/src/widgets/BaseInputWidget/widget/index.tsx b/app/client/src/widgets/BaseInputWidget/widget/index.tsx index 2c896375cc90..89bc4736309e 100644 --- a/app/client/src/widgets/BaseInputWidget/widget/index.tsx +++ b/app/client/src/widgets/BaseInputWidget/widget/index.tsx @@ -1,6 +1,6 @@ import { Alignment } from "@blueprintjs/core"; import { IconName } from "@blueprintjs/icons"; -import { LabelPosition, ResponsiveBehavior } from "components/constants"; +import { LabelPosition } from "components/constants"; import { EventType, ExecutionResult, @@ -8,10 +8,7 @@ import { import { WidgetType } from "constants/WidgetConstants"; import { ValidationTypes } from "constants/WidgetValidation"; import React from "react"; -import { - generateResponsiveBehaviorConfig, - generateVerticalAlignmentConfig, -} from "utils/layoutPropertiesUtils"; +import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; import BaseInputComponent from "../component"; @@ -258,13 +255,7 @@ class BaseInputWidget< }, ], }, - { - sectionName: "Responsive Layout", - children: [ - generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill), - generateVerticalAlignmentConfig(), - ], - }, + ...getResponsiveLayoutConfig(this.getWidgetType()), { sectionName: "Events", children: [ diff --git a/app/client/src/widgets/ButtonGroupWidget/index.ts b/app/client/src/widgets/ButtonGroupWidget/index.ts index 78f98db74cc4..0ddbd0dddb6a 100644 --- a/app/client/src/widgets/ButtonGroupWidget/index.ts +++ b/app/client/src/widgets/ButtonGroupWidget/index.ts @@ -1,12 +1,12 @@ import { ButtonVariantTypes } from "components/constants"; +import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; +import { klona as clone } from "klona/full"; import { get } from "lodash"; +import { getDefaultResponsiveBehavior } from "utils/layoutPropertiesUtils"; import { WidgetProps } from "widgets/BaseWidget"; import { BlueprintOperationTypes } from "widgets/constants"; -import { klona as clone } from "klona/full"; import IconSVG from "./icon.svg"; import Widget from "./widget"; -import { ResponsiveBehavior } from "components/constants"; -import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; export const CONFIG = { type: Widget.getWidgetType(), @@ -24,7 +24,7 @@ export const CONFIG = { isVisible: true, version: 1, animateLoading: true, - responsiveBehavior: ResponsiveBehavior.Fill, + responsiveBehavior: getDefaultResponsiveBehavior(Widget.getWidgetType()), minWidth: FILL_WIDGET_MIN_WIDTH, groupButtons: { groupButton1: { diff --git a/app/client/src/widgets/ButtonGroupWidget/widget/index.tsx b/app/client/src/widgets/ButtonGroupWidget/widget/index.tsx index 555c08493bcb..46de3e05903d 100644 --- a/app/client/src/widgets/ButtonGroupWidget/widget/index.tsx +++ b/app/client/src/widgets/ButtonGroupWidget/widget/index.tsx @@ -5,17 +5,13 @@ import { ButtonPlacementTypes, ButtonVariant, ButtonVariantTypes, - ResponsiveBehavior, } from "components/constants"; import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; import { ValidationTypes } from "constants/WidgetValidation"; import { Stylesheet } from "entities/AppTheming"; import { get } from "lodash"; import React from "react"; -import { - generateResponsiveBehaviorConfig, - generateVerticalAlignmentConfig, -} from "utils/layoutPropertiesUtils"; +import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; import { MinimumPopupRows } from "widgets/constants"; import ButtonGroupComponent from "../component"; @@ -294,20 +290,9 @@ class ButtonGroupWidget extends BaseWidget< isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, - { - ...generateResponsiveBehaviorConfig( - ResponsiveBehavior.Fill, - ), - }, - ], - }, - { - sectionName: "Responsive Layout", - children: [ - generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill), - generateVerticalAlignmentConfig(), ], }, + ...getResponsiveLayoutConfig(this.getWidgetType()), { sectionName: "Events", hidden: ( diff --git a/app/client/src/widgets/ButtonWidget/index.ts b/app/client/src/widgets/ButtonWidget/index.ts index 69582f36974e..084bb982d583 100644 --- a/app/client/src/widgets/ButtonWidget/index.ts +++ b/app/client/src/widgets/ButtonWidget/index.ts @@ -2,9 +2,9 @@ import { ButtonPlacementTypes, ButtonVariantTypes, RecaptchaTypes, - ResponsiveBehavior, } from "components/constants"; import { BUTTON_MIN_WIDTH } from "constants/minWidthConstants"; +import { getDefaultResponsiveBehavior } from "utils/layoutPropertiesUtils"; import IconSVG from "./icon.svg"; import Widget from "./widget"; @@ -29,7 +29,7 @@ export const CONFIG = { resetFormOnClick: false, recaptchaType: RecaptchaTypes.V3, version: 1, - responsiveBehavior: ResponsiveBehavior.Hug, + responsiveBehavior: getDefaultResponsiveBehavior(Widget.getWidgetType()), minWidth: BUTTON_MIN_WIDTH, }, properties: { diff --git a/app/client/src/widgets/ButtonWidget/widget/index.tsx b/app/client/src/widgets/ButtonWidget/widget/index.tsx index 3c3ff8c1470a..66c5945b9501 100644 --- a/app/client/src/widgets/ButtonWidget/widget/index.tsx +++ b/app/client/src/widgets/ButtonWidget/widget/index.tsx @@ -7,7 +7,6 @@ import { ButtonVariantTypes, RecaptchaType, RecaptchaTypes, - ResponsiveBehavior, } from "components/constants"; import { EventType, @@ -17,10 +16,7 @@ import { WidgetType } from "constants/WidgetConstants"; import { ValidationTypes } from "constants/WidgetValidation"; import { Stylesheet } from "entities/AppTheming"; import React from "react"; -import { - generateResponsiveBehaviorConfig, - generateVerticalAlignmentConfig, -} from "utils/layoutPropertiesUtils"; +import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; import ButtonComponent, { ButtonType } from "../component"; @@ -109,13 +105,7 @@ class ButtonWidget extends BaseWidget { }, ], }, - { - sectionName: "Responsive Layout", - children: [ - generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), - generateVerticalAlignmentConfig(), - ], - }, + ...getResponsiveLayoutConfig(this.getWidgetType()), { sectionName: "Validation", children: [ diff --git a/app/client/src/widgets/CameraWidget/widget/index.tsx b/app/client/src/widgets/CameraWidget/widget/index.tsx index f8ecd5979d95..82db2da6535a 100644 --- a/app/client/src/widgets/CameraWidget/widget/index.tsx +++ b/app/client/src/widgets/CameraWidget/widget/index.tsx @@ -8,12 +8,8 @@ import { DerivedPropertiesMap } from "utils/WidgetFactory"; import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; import { FileDataTypes } from "widgets/constants"; -import { ResponsiveBehavior } from "components/constants"; import { Stylesheet } from "entities/AppTheming"; -import { - generateResponsiveBehaviorConfig, - generateVerticalAlignmentConfig, -} from "utils/layoutPropertiesUtils"; +import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import CameraComponent from "../component"; import { CameraMode, @@ -85,13 +81,7 @@ class CameraWidget extends BaseWidget { }, ], }, - { - sectionName: "Responsive Layout", - children: [ - generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), - generateVerticalAlignmentConfig(), - ], - }, + ...getResponsiveLayoutConfig(this.getWidgetType()), { sectionName: "Events", children: [ diff --git a/app/client/src/widgets/CanvasWidget.tsx b/app/client/src/widgets/CanvasWidget.tsx index 34f597588ca6..20dbc1ac416c 100644 --- a/app/client/src/widgets/CanvasWidget.tsx +++ b/app/client/src/widgets/CanvasWidget.tsx @@ -1,9 +1,4 @@ -import { - LayoutDirection, - Overflow, - Positioning, - ResponsiveBehavior, -} from "components/constants"; +import { LayoutDirection, Overflow, Positioning } from "components/constants"; import FlexBoxComponent from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; import DropTargetComponent from "components/editorComponents/DropTargetComponent"; import { CANVAS_DEFAULT_MIN_HEIGHT_PX } from "constants/AppConstants"; @@ -12,6 +7,7 @@ import { GridDefaults, RenderModes } from "constants/WidgetConstants"; import { CanvasDraggingArena } from "pages/common/CanvasArenas/CanvasDraggingArena"; import React, { CSSProperties } from "react"; import { getCanvasClassName } from "utils/generators"; +import { getDefaultResponsiveBehavior } from "utils/layoutPropertiesUtils"; import WidgetFactory, { DerivedPropertiesMap } from "utils/WidgetFactory"; import { getCanvasSnapRows } from "utils/WidgetPropsUtils"; import { WidgetProps } from "widgets/BaseWidget"; @@ -194,7 +190,9 @@ export const CONFIG = { version: 1, detachFromLayout: true, flexLayers: [], - responsiveBehavior: ResponsiveBehavior.Fill, + responsiveBehavior: getDefaultResponsiveBehavior( + CanvasWidget.getWidgetType(), + ), minWidth: FILL_WIDGET_MIN_WIDTH, }, properties: { diff --git a/app/client/src/widgets/CategorySliderWidget/widget/propertyConfig/contentConfig.ts b/app/client/src/widgets/CategorySliderWidget/widget/propertyConfig/contentConfig.ts index b6c1b5a2eeea..42a7ccb4ebb1 100644 --- a/app/client/src/widgets/CategorySliderWidget/widget/propertyConfig/contentConfig.ts +++ b/app/client/src/widgets/CategorySliderWidget/widget/propertyConfig/contentConfig.ts @@ -1,12 +1,9 @@ import { Alignment } from "@blueprintjs/core"; -import { LabelPosition, ResponsiveBehavior } from "components/constants"; +import { LabelPosition } from "components/constants"; import { ValidationTypes } from "constants/WidgetValidation"; import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService"; -import { - generateResponsiveBehaviorConfig, - generateVerticalAlignmentConfig, -} from "utils/layoutPropertiesUtils"; +import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import { CategorySliderWidgetProps } from ".."; import { defaultOptionValidation, @@ -192,13 +189,7 @@ export default [ }, ], }, - { - sectionName: "Responsive Layout", - children: [ - generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), - generateVerticalAlignmentConfig(), - ], - }, + ...getResponsiveLayoutConfig("CATEGORY_SLIDER_WIDGET"), { sectionName: "Events", children: [ diff --git a/app/client/src/widgets/ChartWidget/index.ts b/app/client/src/widgets/ChartWidget/index.ts index d2ed59c77dfb..af52c0e3063a 100644 --- a/app/client/src/widgets/ChartWidget/index.ts +++ b/app/client/src/widgets/ChartWidget/index.ts @@ -1,6 +1,6 @@ -import { ResponsiveBehavior } from "components/constants"; import { Colors } from "constants/Colors"; import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; +import { getDefaultResponsiveBehavior } from "utils/layoutPropertiesUtils"; import { generateReactKey } from "widgets/WidgetUtils"; import { LabelOrientation } from "./constants"; import IconSVG from "./icon.svg"; @@ -21,7 +21,7 @@ export const CONFIG = { allowScroll: false, version: 1, animateLoading: true, - responsiveBehavior: ResponsiveBehavior.Fill, + responsiveBehavior: getDefaultResponsiveBehavior(Widget.getWidgetType()), minWidth: FILL_WIDGET_MIN_WIDTH, chartData: { [generateReactKey()]: { diff --git a/app/client/src/widgets/ChartWidget/widget/propertyConfig.ts b/app/client/src/widgets/ChartWidget/widget/propertyConfig.ts index fa809c80953f..c73c57f07538 100644 --- a/app/client/src/widgets/ChartWidget/widget/propertyConfig.ts +++ b/app/client/src/widgets/ChartWidget/widget/propertyConfig.ts @@ -1,10 +1,6 @@ -import { ResponsiveBehavior } from "components/constants"; import { ValidationTypes } from "constants/WidgetValidation"; import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; -import { - generateResponsiveBehaviorConfig, - generateVerticalAlignmentConfig, -} from "utils/layoutPropertiesUtils"; +import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import { ChartWidgetProps } from "widgets/ChartWidget/widget"; import { isLabelOrientationApplicableFor } from "../component"; import { CUSTOM_CHART_TYPES, LabelOrientation } from "../constants"; @@ -315,13 +311,7 @@ export const contentConfig = [ }, ], }, - { - sectionName: "Responsive Layout", - children: [ - generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill), - generateVerticalAlignmentConfig(), - ], - }, + ...getResponsiveLayoutConfig("CHART_WIDGET"), { sectionName: "Events", children: [ diff --git a/app/client/src/widgets/CheckboxGroupWidget/index.ts b/app/client/src/widgets/CheckboxGroupWidget/index.ts index cd45fee8e6e6..9c6cad1bf981 100644 --- a/app/client/src/widgets/CheckboxGroupWidget/index.ts +++ b/app/client/src/widgets/CheckboxGroupWidget/index.ts @@ -1,6 +1,7 @@ import { Alignment } from "@blueprintjs/core"; -import { LabelPosition, ResponsiveBehavior } from "components/constants"; +import { LabelPosition } from "components/constants"; import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; +import { getDefaultResponsiveBehavior } from "utils/layoutPropertiesUtils"; import IconSVG from "./icon.svg"; import Widget from "./widget"; @@ -36,7 +37,7 @@ export const CONFIG = { labelWidth: 5, widgetName: "CheckboxGroup", version: 2, - responsiveBehavior: ResponsiveBehavior.Fill, + responsiveBehavior: getDefaultResponsiveBehavior(Widget.getWidgetType()), minWidth: FILL_WIDGET_MIN_WIDTH, }, properties: { diff --git a/app/client/src/widgets/CheckboxGroupWidget/widget/index.tsx b/app/client/src/widgets/CheckboxGroupWidget/widget/index.tsx index cb8426ff56d2..595646113b22 100644 --- a/app/client/src/widgets/CheckboxGroupWidget/widget/index.tsx +++ b/app/client/src/widgets/CheckboxGroupWidget/widget/index.tsx @@ -2,7 +2,6 @@ import { Alignment } from "@blueprintjs/core"; import { CheckboxGroupAlignmentTypes, LabelPosition, - ResponsiveBehavior, } from "components/constants"; import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; import { TextSize, WidgetType } from "constants/WidgetConstants"; @@ -22,10 +21,7 @@ import { isAutoHeightEnabledForWidget } from "widgets/WidgetUtils"; import CheckboxGroupComponent from "../component"; import { OptionProps, SelectAllState, SelectAllStates } from "../constants"; -import { - generateResponsiveBehaviorConfig, - generateVerticalAlignmentConfig, -} from "utils/layoutPropertiesUtils"; +import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; export function defaultSelectedValuesValidation( value: unknown, @@ -292,13 +288,7 @@ class CheckboxGroupWidget extends BaseWidget< }, ], }, - { - sectionName: "Responsive Layout", - children: [ - generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill), - generateVerticalAlignmentConfig(), - ], - }, + ...getResponsiveLayoutConfig(this.getWidgetType()), { sectionName: "Events", children: [ diff --git a/app/client/src/widgets/CheckboxWidget/index.ts b/app/client/src/widgets/CheckboxWidget/index.ts index 863c5f858482..901d2135438c 100644 --- a/app/client/src/widgets/CheckboxWidget/index.ts +++ b/app/client/src/widgets/CheckboxWidget/index.ts @@ -1,8 +1,9 @@ -import Widget from "./widget"; -import IconSVG from "./icon.svg"; -import { LabelPosition, ResponsiveBehavior } from "components/constants"; -import { AlignWidgetTypes } from "widgets/constants"; +import { LabelPosition } from "components/constants"; import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; +import { getDefaultResponsiveBehavior } from "utils/layoutPropertiesUtils"; +import { AlignWidgetTypes } from "widgets/constants"; +import IconSVG from "./icon.svg"; +import Widget from "./widget"; export const CONFIG = { features: { @@ -28,7 +29,7 @@ export const CONFIG = { isDisabled: false, isRequired: false, animateLoading: true, - responsiveBehavior: ResponsiveBehavior.Fill, + responsiveBehavior: getDefaultResponsiveBehavior(Widget.getWidgetType()), minWidth: FILL_WIDGET_MIN_WIDTH, }, properties: { diff --git a/app/client/src/widgets/CheckboxWidget/widget/index.tsx b/app/client/src/widgets/CheckboxWidget/widget/index.tsx index 1066b7082960..a7b9d1a6ae87 100644 --- a/app/client/src/widgets/CheckboxWidget/widget/index.tsx +++ b/app/client/src/widgets/CheckboxWidget/widget/index.tsx @@ -1,13 +1,10 @@ -import { LabelPosition, ResponsiveBehavior } from "components/constants"; +import { LabelPosition } from "components/constants"; import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; import { WidgetType } from "constants/WidgetConstants"; import { ValidationTypes } from "constants/WidgetValidation"; import { Stylesheet } from "entities/AppTheming"; import React from "react"; -import { - generateResponsiveBehaviorConfig, - generateVerticalAlignmentConfig, -} from "utils/layoutPropertiesUtils"; +import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; import { AlignWidgetTypes } from "widgets/constants"; import { isAutoHeightEnabledForWidget } from "widgets/WidgetUtils"; @@ -127,13 +124,7 @@ class CheckboxWidget extends BaseWidget { }, ], }, - { - sectionName: "Responsive Layout", - children: [ - generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill), - generateVerticalAlignmentConfig(), - ], - }, + ...getResponsiveLayoutConfig(this.getWidgetType()), { sectionName: "Events", children: [ diff --git a/app/client/src/widgets/CodeScannerWidget/widget/propertyConfig/contentConfig.ts b/app/client/src/widgets/CodeScannerWidget/widget/propertyConfig/contentConfig.ts index 4742d5dd2ba4..c62d1c9d4282 100644 --- a/app/client/src/widgets/CodeScannerWidget/widget/propertyConfig/contentConfig.ts +++ b/app/client/src/widgets/CodeScannerWidget/widget/propertyConfig/contentConfig.ts @@ -1,15 +1,10 @@ -import { ResponsiveBehavior } from "components/constants"; import { PropertyPaneConfig } from "constants/PropertyControlConstants"; import { ValidationTypes } from "constants/WidgetValidation"; -import { - generateResponsiveBehaviorConfig, - generateVerticalAlignmentConfig, -} from "utils/layoutPropertiesUtils"; +import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import { CodeScannerWidgetProps, ScannerLayout, } from "widgets/CodeScannerWidget/constants"; - export default [ { sectionName: "Basic", @@ -100,13 +95,8 @@ export default [ }, ], }, - { - sectionName: "Responsive Layout", - children: [ - generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), - generateVerticalAlignmentConfig(), - ], - }, + ...getResponsiveLayoutConfig("CODE_SCANNER_WIDGET"), + { sectionName: "Events", children: [ diff --git a/app/client/src/widgets/ContainerWidget/index.ts b/app/client/src/widgets/ContainerWidget/index.ts index 1790b4ee83c3..c3a52d9dd647 100644 --- a/app/client/src/widgets/ContainerWidget/index.ts +++ b/app/client/src/widgets/ContainerWidget/index.ts @@ -1,11 +1,8 @@ -import { - ButtonBoxShadowTypes, - Positioning, - ResponsiveBehavior, -} from "components/constants"; +import { ButtonBoxShadowTypes, Positioning } from "components/constants"; import { Colors } from "constants/Colors"; import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; import { WidgetHeightLimits } from "constants/WidgetConstants"; +import { getDefaultResponsiveBehavior } from "utils/layoutPropertiesUtils"; import IconSVG from "./icon.svg"; import Widget from "./widget"; @@ -48,7 +45,7 @@ export const CONFIG = { }, version: 1, positioning: Positioning.Vertical, - responsiveBehavior: ResponsiveBehavior.Fill, + responsiveBehavior: getDefaultResponsiveBehavior(Widget.getWidgetType()), minWidth: FILL_WIDGET_MIN_WIDTH, }, properties: { diff --git a/app/client/src/widgets/ContainerWidget/widget/index.tsx b/app/client/src/widgets/ContainerWidget/widget/index.tsx index 26ec655b650a..3b0267542e0d 100644 --- a/app/client/src/widgets/ContainerWidget/widget/index.tsx +++ b/app/client/src/widgets/ContainerWidget/widget/index.tsx @@ -16,13 +16,9 @@ import { ValidationTypes } from "constants/WidgetValidation"; import { compact, map, sortBy } from "lodash"; import WidgetsMultiSelectBox from "pages/Editor/WidgetsMultiSelectBox"; -import { Positioning, ResponsiveBehavior } from "components/constants"; +import { Positioning } from "components/constants"; import { Stylesheet } from "entities/AppTheming"; -import { - generatePositioningConfig, - generateResponsiveBehaviorConfig, - generateVerticalAlignmentConfig, -} from "utils/layoutPropertiesUtils"; +import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; export class ContainerWidget extends BaseWidget< ContainerWidgetProps, @@ -69,14 +65,7 @@ export class ContainerWidget extends BaseWidget< }, ], }, - { - sectionName: "Responsive Layout", - children: [ - generatePositioningConfig(Positioning.Vertical), - generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill), - generateVerticalAlignmentConfig(), - ], - }, + ...getResponsiveLayoutConfig(this.getWidgetType()), ]; } diff --git a/app/client/src/widgets/CurrencyInputWidget/index.ts b/app/client/src/widgets/CurrencyInputWidget/index.ts index 5ab6439a9a44..e54c87fe08b7 100644 --- a/app/client/src/widgets/CurrencyInputWidget/index.ts +++ b/app/client/src/widgets/CurrencyInputWidget/index.ts @@ -1,5 +1,6 @@ -import { LabelPosition, ResponsiveBehavior } from "components/constants"; +import { LabelPosition } from "components/constants"; import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; +import { getDefaultResponsiveBehavior } from "utils/layoutPropertiesUtils"; import { DynamicHeight } from "utils/WidgetFeatures"; import { CONFIG as BaseConfig } from "widgets/BaseInputWidget"; import { getDefaultCurrency } from "./component/CurrencyCodeDropdown"; @@ -29,7 +30,7 @@ export const CONFIG = { defaultCurrencyCode: getDefaultCurrency().currency, decimals: 0, showStepArrows: false, - responsiveBehavior: ResponsiveBehavior.Fill, + responsiveBehavior: getDefaultResponsiveBehavior(Widget.getWidgetType()), minWidth: FILL_WIDGET_MIN_WIDTH, }, properties: { diff --git a/app/client/src/widgets/DatePickerWidget2/index.ts b/app/client/src/widgets/DatePickerWidget2/index.ts index 44482e6b616f..b373d2cef336 100644 --- a/app/client/src/widgets/DatePickerWidget2/index.ts +++ b/app/client/src/widgets/DatePickerWidget2/index.ts @@ -1,7 +1,8 @@ import { Alignment } from "@blueprintjs/core"; -import { LabelPosition, ResponsiveBehavior } from "components/constants"; +import { LabelPosition } from "components/constants"; import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; import moment from "moment"; +import { getDefaultResponsiveBehavior } from "utils/layoutPropertiesUtils"; import { DynamicHeight } from "utils/WidgetFeatures"; import { TimePrecision } from "./constants"; import IconSVG from "./icon.svg"; @@ -42,7 +43,7 @@ export const CONFIG = { firstDayOfWeek: 0, timePrecision: TimePrecision.MINUTE, animateLoading: true, - responsiveBehavior: ResponsiveBehavior.Fill, + responsiveBehavior: getDefaultResponsiveBehavior(Widget.getWidgetType()), minWidth: FILL_WIDGET_MIN_WIDTH, }, properties: { diff --git a/app/client/src/widgets/DatePickerWidget2/widget/index.tsx b/app/client/src/widgets/DatePickerWidget2/widget/index.tsx index a5b2e63694e3..0c1df60ecfd5 100644 --- a/app/client/src/widgets/DatePickerWidget2/widget/index.tsx +++ b/app/client/src/widgets/DatePickerWidget2/widget/index.tsx @@ -9,12 +9,9 @@ import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; import { Alignment } from "@blueprintjs/core"; -import { LabelPosition, ResponsiveBehavior } from "components/constants"; +import { LabelPosition } from "components/constants"; import { Stylesheet } from "entities/AppTheming"; -import { - generateResponsiveBehaviorConfig, - generateVerticalAlignmentConfig, -} from "utils/layoutPropertiesUtils"; +import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import { GRID_DENSITY_MIGRATION_V1 } from "widgets/constants"; import { isAutoHeightEnabledForWidget } from "widgets/WidgetUtils"; import { DatePickerType, TimePrecision } from "../constants"; @@ -298,13 +295,7 @@ class DatePickerWidget extends BaseWidget { }, ], }, - { - sectionName: "Responsive Layout", - children: [ - generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill), - generateVerticalAlignmentConfig(), - ], - }, + ...getResponsiveLayoutConfig(this.getWidgetType()), { sectionName: "Events", children: [ diff --git a/app/client/src/widgets/DividerWidget/index.ts b/app/client/src/widgets/DividerWidget/index.ts index ed487c89a62e..58eb9941edee 100644 --- a/app/client/src/widgets/DividerWidget/index.ts +++ b/app/client/src/widgets/DividerWidget/index.ts @@ -1,6 +1,6 @@ -import { ResponsiveBehavior } from "components/constants"; import { Colors } from "constants/Colors"; import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; +import { getDefaultResponsiveBehavior } from "utils/layoutPropertiesUtils"; import IconSVG from "./icon.svg"; import Widget from "./widget"; @@ -22,7 +22,7 @@ export const CONFIG = { isVisible: true, version: 1, animateLoading: true, - responsiveBehavior: ResponsiveBehavior.Fill, + responsiveBehavior: getDefaultResponsiveBehavior(Widget.getWidgetType()), minWidth: FILL_WIDGET_MIN_WIDTH, }, properties: { diff --git a/app/client/src/widgets/DividerWidget/widget/index.tsx b/app/client/src/widgets/DividerWidget/widget/index.tsx index c9dd9ce77a85..f5ecce497403 100644 --- a/app/client/src/widgets/DividerWidget/widget/index.tsx +++ b/app/client/src/widgets/DividerWidget/widget/index.tsx @@ -3,12 +3,8 @@ import React from "react"; import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; import DividerComponent from "../component"; -import { ResponsiveBehavior } from "components/constants"; import { ValidationTypes } from "constants/WidgetValidation"; -import { - generateResponsiveBehaviorConfig, - generateVerticalAlignmentConfig, -} from "utils/layoutPropertiesUtils"; +import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; class DividerWidget extends BaseWidget { static getPropertyPaneContentConfig() { @@ -39,13 +35,7 @@ class DividerWidget extends BaseWidget { }, ], }, - { - sectionName: "Responsive Layout", - children: [ - generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill), - generateVerticalAlignmentConfig(), - ], - }, + ...getResponsiveLayoutConfig(this.getWidgetType()), ]; } diff --git a/app/client/src/widgets/DocumentViewerWidget/widget/index.tsx b/app/client/src/widgets/DocumentViewerWidget/widget/index.tsx index 47ab983a2b2f..11e9780ee305 100644 --- a/app/client/src/widgets/DocumentViewerWidget/widget/index.tsx +++ b/app/client/src/widgets/DocumentViewerWidget/widget/index.tsx @@ -1,14 +1,10 @@ -import { ResponsiveBehavior } from "components/constants"; import { ValidationResponse, ValidationTypes, } from "constants/WidgetValidation"; import React from "react"; import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService"; -import { - generateResponsiveBehaviorConfig, - generateVerticalAlignmentConfig, -} from "utils/layoutPropertiesUtils"; +import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; import DocumentViewerComponent from "../component"; @@ -123,13 +119,7 @@ class DocumentViewerWidget extends BaseWidget< }, ], }, - { - sectionName: "Responsive Layout", - children: [ - generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), - generateVerticalAlignmentConfig(), - ], - }, + ...getResponsiveLayoutConfig(this.getWidgetType()), ]; } diff --git a/app/client/src/widgets/FilePickerWidgetV2/index.ts b/app/client/src/widgets/FilePickerWidgetV2/index.ts index 91933825c2a0..a4cf9c0f90fe 100644 --- a/app/client/src/widgets/FilePickerWidgetV2/index.ts +++ b/app/client/src/widgets/FilePickerWidgetV2/index.ts @@ -1,5 +1,5 @@ -import { ResponsiveBehavior } from "components/constants"; import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; +import { getDefaultResponsiveBehavior } from "utils/layoutPropertiesUtils"; import FileDataTypes from "./constants"; import IconSVG from "./icon.svg"; import Widget from "./widget"; @@ -27,7 +27,7 @@ export const CONFIG = { isRequired: false, isDisabled: false, animateLoading: true, - responsiveBehavior: ResponsiveBehavior.Fill, + responsiveBehavior: getDefaultResponsiveBehavior(Widget.getWidgetType()), minWidth: FILL_WIDGET_MIN_WIDTH, }, properties: { diff --git a/app/client/src/widgets/FilePickerWidgetV2/widget/index.tsx b/app/client/src/widgets/FilePickerWidgetV2/widget/index.tsx index d167b250d1ae..9d1875f70d11 100644 --- a/app/client/src/widgets/FilePickerWidgetV2/widget/index.tsx +++ b/app/client/src/widgets/FilePickerWidgetV2/widget/index.tsx @@ -7,7 +7,6 @@ import { UppyFile } from "@uppy/utils"; import Webcam from "@uppy/webcam"; import CloseIcon from "assets/icons/ads/cross.svg"; import UpIcon from "assets/icons/ads/up-arrow.svg"; -import { ResponsiveBehavior } from "components/constants"; import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; import { Colors } from "constants/Colors"; import { WidgetType } from "constants/WidgetConstants"; @@ -22,10 +21,7 @@ import React from "react"; import shallowequal from "shallowequal"; import { createGlobalStyle } from "styled-components"; import { createBlobUrl, isBlobUrl } from "utils/AppsmithUtils"; -import { - generateResponsiveBehaviorConfig, - generateVerticalAlignmentConfig, -} from "utils/layoutPropertiesUtils"; +import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; import FilePickerComponent from "../component"; @@ -429,13 +425,8 @@ class FilePickerWidget extends BaseWidget< }, ], }, - { - sectionName: "Responsive Layout", - children: [ - generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill), - generateVerticalAlignmentConfig(), - ], - }, + ...getResponsiveLayoutConfig(this.getWidgetType()), + { sectionName: "Events", children: [ diff --git a/app/client/src/widgets/FormWidget/index.ts b/app/client/src/widgets/FormWidget/index.ts index 8562ad03d18e..7464099c3266 100644 --- a/app/client/src/widgets/FormWidget/index.ts +++ b/app/client/src/widgets/FormWidget/index.ts @@ -1,10 +1,7 @@ -import { - ButtonVariantTypes, - RecaptchaTypes, - ResponsiveBehavior, -} from "components/constants"; +import { ButtonVariantTypes, RecaptchaTypes } from "components/constants"; import { Colors } from "constants/Colors"; import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; +import { getDefaultResponsiveBehavior } from "utils/layoutPropertiesUtils"; import IconSVG from "./icon.svg"; import Widget from "./widget"; @@ -100,7 +97,7 @@ export const CONFIG = { }, ], }, - responsiveBehavior: ResponsiveBehavior.Fill, + responsiveBehavior: getDefaultResponsiveBehavior(Widget.getWidgetType()), minWidth: FILL_WIDGET_MIN_WIDTH, }, properties: { diff --git a/app/client/src/widgets/IconButtonWidget/widget/index.tsx b/app/client/src/widgets/IconButtonWidget/widget/index.tsx index 2acabbb04296..22423c610984 100644 --- a/app/client/src/widgets/IconButtonWidget/widget/index.tsx +++ b/app/client/src/widgets/IconButtonWidget/widget/index.tsx @@ -7,16 +7,9 @@ import { ValidationTypes } from "constants/WidgetValidation"; import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; import { IconNames } from "@blueprintjs/icons"; -import { - ButtonVariant, - ButtonVariantTypes, - ResponsiveBehavior, -} from "components/constants"; +import { ButtonVariant, ButtonVariantTypes } from "components/constants"; import { Stylesheet } from "entities/AppTheming"; -import { - generateResponsiveBehaviorConfig, - generateVerticalAlignmentConfig, -} from "utils/layoutPropertiesUtils"; +import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import IconButtonComponent from "../component"; const ICON_NAMES = Object.keys(IconNames).map( @@ -114,13 +107,7 @@ class IconButtonWidget extends BaseWidget { }, ], }, - { - sectionName: "Responsive Layout", - children: [ - generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), - generateVerticalAlignmentConfig(), - ], - }, + ...getResponsiveLayoutConfig(this.getWidgetType()), ]; } diff --git a/app/client/src/widgets/IframeWidget/widget/index.tsx b/app/client/src/widgets/IframeWidget/widget/index.tsx index b45d81a2e7c7..df244d5eefbb 100644 --- a/app/client/src/widgets/IframeWidget/widget/index.tsx +++ b/app/client/src/widgets/IframeWidget/widget/index.tsx @@ -1,12 +1,8 @@ -import { ResponsiveBehavior } from "components/constants"; import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; import { ValidationTypes } from "constants/WidgetValidation"; import { Stylesheet } from "entities/AppTheming"; import React from "react"; -import { - generateResponsiveBehaviorConfig, - generateVerticalAlignmentConfig, -} from "utils/layoutPropertiesUtils"; +import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import BaseWidget, { WidgetState } from "widgets/BaseWidget"; import IframeComponent from "../component"; import { IframeWidgetProps } from "../constants"; @@ -72,13 +68,7 @@ class IframeWidget extends BaseWidget { }, ], }, - { - sectionName: "Responsive Layout", - children: [ - generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), - generateVerticalAlignmentConfig(), - ], - }, + ...getResponsiveLayoutConfig(this.getWidgetType()), { sectionName: "Events", children: [ diff --git a/app/client/src/widgets/ImageWidget/widget/index.tsx b/app/client/src/widgets/ImageWidget/widget/index.tsx index 7d4e1660c23f..2527808b8028 100644 --- a/app/client/src/widgets/ImageWidget/widget/index.tsx +++ b/app/client/src/widgets/ImageWidget/widget/index.tsx @@ -3,14 +3,10 @@ import * as React from "react"; import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; import ImageComponent from "../component"; -import { ResponsiveBehavior } from "components/constants"; import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; import { ValidationTypes } from "constants/WidgetValidation"; import { Stylesheet } from "entities/AppTheming"; -import { - generateResponsiveBehaviorConfig, - generateVerticalAlignmentConfig, -} from "utils/layoutPropertiesUtils"; +import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; class ImageWidget extends BaseWidget { @@ -158,13 +154,7 @@ class ImageWidget extends BaseWidget { }, ], }, - { - sectionName: "Responsive Layout", - children: [ - generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), - generateVerticalAlignmentConfig(), - ], - }, + ...getResponsiveLayoutConfig(this.getWidgetType()), { sectionName: "Events", children: [ diff --git a/app/client/src/widgets/InputWidgetV2/index.ts b/app/client/src/widgets/InputWidgetV2/index.ts index 8bd3e39a0d4a..f6b7b6bc5956 100644 --- a/app/client/src/widgets/InputWidgetV2/index.ts +++ b/app/client/src/widgets/InputWidgetV2/index.ts @@ -1,5 +1,6 @@ -import { LabelPosition, ResponsiveBehavior } from "components/constants"; +import { LabelPosition } from "components/constants"; import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; +import { getDefaultResponsiveBehavior } from "utils/layoutPropertiesUtils"; import { DynamicHeight } from "utils/WidgetFeatures"; import { CONFIG as BaseConfig } from "widgets/BaseInputWidget"; import IconSVG from "./icon.svg"; @@ -26,7 +27,7 @@ export const CONFIG = { widgetName: "Input", version: 2, showStepArrows: false, - responsiveBehavior: ResponsiveBehavior.Fill, + responsiveBehavior: getDefaultResponsiveBehavior(Widget.getWidgetType()), minWidth: FILL_WIDGET_MIN_WIDTH, }, properties: { diff --git a/app/client/src/widgets/JSONFormWidget/index.ts b/app/client/src/widgets/JSONFormWidget/index.ts index 12e113c77fdb..4e3f0227ee2c 100644 --- a/app/client/src/widgets/JSONFormWidget/index.ts +++ b/app/client/src/widgets/JSONFormWidget/index.ts @@ -1,6 +1,7 @@ -import { ButtonVariantTypes, ResponsiveBehavior } from "components/constants"; +import { ButtonVariantTypes } from "components/constants"; import { Colors } from "constants/Colors"; import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; +import { getDefaultResponsiveBehavior } from "utils/layoutPropertiesUtils"; import { DynamicHeight } from "utils/WidgetFeatures"; import { BlueprintOperationTypes } from "widgets/constants"; import IconSVG from "./icon.svg"; @@ -27,7 +28,7 @@ export const CONFIG = { iconSVG: IconSVG, needsMeta: true, defaults: { - responsiveBehavior: ResponsiveBehavior.Fill, + responsiveBehavior: getDefaultResponsiveBehavior(Widget.getWidgetType()), minWidth: FILL_WIDGET_MIN_WIDTH, animateLoading: true, backgroundColor: "#fff", diff --git a/app/client/src/widgets/JSONFormWidget/widget/propertyConfig.ts b/app/client/src/widgets/JSONFormWidget/widget/propertyConfig.ts index 881a028c279c..6d613cb249aa 100644 --- a/app/client/src/widgets/JSONFormWidget/widget/propertyConfig.ts +++ b/app/client/src/widgets/JSONFormWidget/widget/propertyConfig.ts @@ -1,25 +1,17 @@ import { Alignment } from "@blueprintjs/core"; -import { - ButtonPlacementTypes, - ButtonVariantTypes, - ResponsiveBehavior, -} from "components/constants"; +import { ButtonPlacementTypes, ButtonVariantTypes } from "components/constants"; import { OnButtonClickProps } from "components/propertyControls/ButtonControl"; import { ValidationTypes } from "constants/WidgetValidation"; import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService"; import { EVALUATION_PATH } from "utils/DynamicBindingUtils"; -import { - generateResponsiveBehaviorConfig, - generateVerticalAlignmentConfig, -} from "utils/layoutPropertiesUtils"; +import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import { ButtonWidgetProps } from "widgets/ButtonWidget/widget"; import { JSONFormWidgetProps } from "."; import { ROOT_SCHEMA_KEY } from "../constants"; import { ComputedSchemaStatus, computeSchema } from "./helper"; import generatePanelPropertyConfig from "./propertyConfig/generatePanelPropertyConfig"; - const MAX_NESTING_LEVEL = 5; const panelConfig = generatePanelPropertyConfig(MAX_NESTING_LEVEL); @@ -273,13 +265,7 @@ export const contentConfig = [ }, ], }, - { - sectionName: "Responsive Layout", - children: [ - generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill), - generateVerticalAlignmentConfig(), - ], - }, + ...getResponsiveLayoutConfig("JSON_FORM_WIDGET"), { sectionName: "Events", children: [ diff --git a/app/client/src/widgets/ListWidget/index.ts b/app/client/src/widgets/ListWidget/index.ts index 1cf6c7817a5c..de8a5df29512 100644 --- a/app/client/src/widgets/ListWidget/index.ts +++ b/app/client/src/widgets/ListWidget/index.ts @@ -1,8 +1,11 @@ +import { Positioning } from "components/constants"; +import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; import { cloneDeep, get, indexOf, isString } from "lodash"; import { combineDynamicBindings, getDynamicBindings, } from "utils/DynamicBindingUtils"; +import { getDefaultResponsiveBehavior } from "utils/layoutPropertiesUtils"; import { WidgetProps } from "widgets/BaseWidget"; import { BlueprintOperationTypes, @@ -10,8 +13,6 @@ import { } from "widgets/constants"; import IconSVG from "./icon.svg"; import Widget from "./widget"; -import { Positioning, ResponsiveBehavior } from "components/constants"; -import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; export const CONFIG = { type: Widget.getWidgetType(), @@ -27,7 +28,7 @@ export const CONFIG = { animateLoading: true, gridType: "vertical", template: {}, - responsiveBehavior: ResponsiveBehavior.Fill, + responsiveBehavior: getDefaultResponsiveBehavior(Widget.getWidgetType()), minWidth: FILL_WIDGET_MIN_WIDTH, positioning: Positioning.Fixed, enhancements: { diff --git a/app/client/src/widgets/ListWidget/widget/propertyConfig.ts b/app/client/src/widgets/ListWidget/widget/propertyConfig.ts index d497442ce158..95c293d16b08 100644 --- a/app/client/src/widgets/ListWidget/widget/propertyConfig.ts +++ b/app/client/src/widgets/ListWidget/widget/propertyConfig.ts @@ -2,17 +2,11 @@ import { get } from "lodash"; import { WidgetProps } from "widgets/BaseWidget"; import { ListWidgetProps } from "../constants"; -import { Positioning, ResponsiveBehavior } from "components/constants"; import { ValidationTypes } from "constants/WidgetValidation"; import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService"; import { EVAL_VALUE_PATH } from "utils/DynamicBindingUtils"; -import { - generatePositioningConfig, - generateResponsiveBehaviorConfig, - generateVerticalAlignmentConfig, -} from "utils/layoutPropertiesUtils"; - +import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; export const PropertyPaneContentConfig = [ { sectionName: "Data", @@ -97,14 +91,7 @@ export const PropertyPaneContentConfig = [ }, ], }, - { - sectionName: "Responsive Layout", - children: [ - generatePositioningConfig(Positioning.Fixed), - generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill), - generateVerticalAlignmentConfig(), - ], - }, + ...getResponsiveLayoutConfig("LIST_WIDGET"), { sectionName: "Events", children: [ diff --git a/app/client/src/widgets/MapChartWidget/widget/index.tsx b/app/client/src/widgets/MapChartWidget/widget/index.tsx index ac02c4c114b3..b1371892b9a3 100644 --- a/app/client/src/widgets/MapChartWidget/widget/index.tsx +++ b/app/client/src/widgets/MapChartWidget/widget/index.tsx @@ -1,6 +1,5 @@ import React, { lazy, Suspense } from "react"; -import { ResponsiveBehavior } from "components/constants"; import Skeleton from "components/utils/Skeleton"; import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; import { WidgetType } from "constants/WidgetConstants"; @@ -9,10 +8,7 @@ import { Stylesheet } from "entities/AppTheming"; import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; import { retryPromise } from "utils/AppsmithUtils"; import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService"; -import { - generateResponsiveBehaviorConfig, - generateVerticalAlignmentConfig, -} from "utils/layoutPropertiesUtils"; +import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; import { MapType } from "../component"; import { @@ -210,13 +206,7 @@ class MapChartWidget extends BaseWidget { }, ], }, - { - sectionName: "Responsive Layout", - children: [ - generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), - generateVerticalAlignmentConfig(), - ], - }, + ...getResponsiveLayoutConfig(this.getWidgetType()), { sectionName: "Events", children: [ diff --git a/app/client/src/widgets/MapWidget/index.ts b/app/client/src/widgets/MapWidget/index.ts index 6b67343ee542..5469b6f6cf75 100644 --- a/app/client/src/widgets/MapWidget/index.ts +++ b/app/client/src/widgets/MapWidget/index.ts @@ -1,7 +1,7 @@ +import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; +import { getDefaultResponsiveBehavior } from "utils/layoutPropertiesUtils"; import IconSVG from "./icon.svg"; import Widget from "./widget"; -import { ResponsiveBehavior } from "components/constants"; -import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; export const CONFIG = { type: Widget.getWidgetType(), @@ -23,7 +23,7 @@ export const CONFIG = { isClickedMarkerCentered: true, version: 1, animateLoading: true, - responsiveBehavior: ResponsiveBehavior.Fill, + responsiveBehavior: getDefaultResponsiveBehavior(Widget.getWidgetType()), minWidth: FILL_WIDGET_MIN_WIDTH, }, properties: { diff --git a/app/client/src/widgets/MapWidget/widget/index.tsx b/app/client/src/widgets/MapWidget/widget/index.tsx index be12dae82b43..8c89e4531573 100644 --- a/app/client/src/widgets/MapWidget/widget/index.tsx +++ b/app/client/src/widgets/MapWidget/widget/index.tsx @@ -4,7 +4,6 @@ import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; import MapComponent from "../component"; import { getAppsmithConfigs } from "@appsmith/configs"; -import { ResponsiveBehavior } from "components/constants"; import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; import { getBorderCSSShorthand } from "constants/DefaultTheme"; import { DEFAULT_CENTER } from "constants/WidgetConstants"; @@ -12,10 +11,7 @@ import { ValidationTypes } from "constants/WidgetValidation"; import { Stylesheet } from "entities/AppTheming"; import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; import styled from "styled-components"; -import { - generateResponsiveBehaviorConfig, - generateVerticalAlignmentConfig, -} from "utils/layoutPropertiesUtils"; +import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; import { MarkerProps } from "../constants"; @@ -235,13 +231,7 @@ class MapWidget extends BaseWidget { }, ], }, - { - sectionName: "Responsive Layout", - children: [ - generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill), - generateVerticalAlignmentConfig(), - ], - }, + ...getResponsiveLayoutConfig(this.getWidgetType()), { sectionName: "Events", children: [ diff --git a/app/client/src/widgets/MenuButtonWidget/widget/propertyConfig/contentConfig.ts b/app/client/src/widgets/MenuButtonWidget/widget/propertyConfig/contentConfig.ts index edc252be0235..812a0759d5cb 100644 --- a/app/client/src/widgets/MenuButtonWidget/widget/propertyConfig/contentConfig.ts +++ b/app/client/src/widgets/MenuButtonWidget/widget/propertyConfig/contentConfig.ts @@ -1,18 +1,13 @@ -import { ResponsiveBehavior } from "components/constants"; import { PropertyPaneConfig } from "constants/PropertyControlConstants"; import { ValidationTypes } from "constants/WidgetValidation"; import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService"; -import { - generateResponsiveBehaviorConfig, - generateVerticalAlignmentConfig, -} from "utils/layoutPropertiesUtils"; +import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import { sourceDataArrayValidation } from "widgets/MenuButtonWidget/validations"; import { MenuButtonWidgetProps, MenuItemsSource } from "../../constants"; import configureMenuItemsConfig from "./childPanels/configureMenuItemsConfig"; import menuItemsConfig from "./childPanels/menuItemsConfig"; import { updateMenuItemsSource } from "./propertyUtils"; - export default [ { sectionName: "Basic", @@ -151,11 +146,5 @@ export default [ }, ], }, - { - sectionName: "Responsive Layout", - children: [ - generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), - generateVerticalAlignmentConfig(), - ], - }, + ...getResponsiveLayoutConfig("MENU_BUTTON_WIDGET"), ] as PropertyPaneConfig[]; diff --git a/app/client/src/widgets/MultiSelectTreeWidget/index.ts b/app/client/src/widgets/MultiSelectTreeWidget/index.ts index e0e71246f6f9..d274b904f4f8 100644 --- a/app/client/src/widgets/MultiSelectTreeWidget/index.ts +++ b/app/client/src/widgets/MultiSelectTreeWidget/index.ts @@ -1,10 +1,10 @@ import { Alignment } from "@blueprintjs/core"; import { LabelPosition } from "components/constants"; +import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; +import { getDefaultResponsiveBehavior } from "utils/layoutPropertiesUtils"; import { DynamicHeight } from "utils/WidgetFeatures"; import IconSVG from "./icon.svg"; import Widget from "./widget"; -import { ResponsiveBehavior } from "components/constants"; -import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; export const CONFIG = { features: { @@ -56,7 +56,7 @@ export const CONFIG = { labelAlignment: Alignment.LEFT, labelWidth: 5, labelTextSize: "0.875rem", - responsiveBehavior: ResponsiveBehavior.Fill, + responsiveBehavior: getDefaultResponsiveBehavior(Widget.getWidgetType()), minWidth: FILL_WIDGET_MIN_WIDTH, }, properties: { diff --git a/app/client/src/widgets/MultiSelectTreeWidget/widget/index.tsx b/app/client/src/widgets/MultiSelectTreeWidget/widget/index.tsx index 45efde7c049a..a6635a1b6993 100644 --- a/app/client/src/widgets/MultiSelectTreeWidget/widget/index.tsx +++ b/app/client/src/widgets/MultiSelectTreeWidget/widget/index.tsx @@ -1,5 +1,5 @@ import { Alignment } from "@blueprintjs/core"; -import { LabelPosition, ResponsiveBehavior } from "components/constants"; +import { LabelPosition } from "components/constants"; import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; import { Layers } from "constants/Layers"; import { TextSize, WidgetType } from "constants/WidgetConstants"; @@ -14,10 +14,7 @@ import { DefaultValueType } from "rc-tree-select/lib/interface"; import { CheckedStrategy } from "rc-tree-select/lib/utils/strategyUtil"; import React, { ReactNode } from "react"; import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService"; -import { - generateResponsiveBehaviorConfig, - generateVerticalAlignmentConfig, -} from "utils/layoutPropertiesUtils"; +import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; import { GRID_DENSITY_MIGRATION_V1, MinimumPopupRows } from "widgets/constants"; import { isAutoHeightEnabledForWidget } from "widgets/WidgetUtils"; @@ -336,13 +333,7 @@ class MultiSelectTreeWidget extends BaseWidget< }, ], }, - { - sectionName: "Responsive Layout", - children: [ - generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill), - generateVerticalAlignmentConfig(), - ], - }, + ...getResponsiveLayoutConfig(this.getWidgetType()), { sectionName: "Events", children: [ diff --git a/app/client/src/widgets/MultiSelectWidget/index.ts b/app/client/src/widgets/MultiSelectWidget/index.ts index 241ce4ad5f2c..3cb6b2885854 100644 --- a/app/client/src/widgets/MultiSelectWidget/index.ts +++ b/app/client/src/widgets/MultiSelectWidget/index.ts @@ -1,9 +1,9 @@ import { Alignment } from "@blueprintjs/core"; import { LabelPosition } from "components/constants"; +import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; +import { getDefaultResponsiveBehavior } from "utils/layoutPropertiesUtils"; import IconSVG from "./icon.svg"; import Widget from "./widget"; -import { ResponsiveBehavior } from "components/constants"; -import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; export const CONFIG = { type: Widget.getWidgetType(), @@ -33,7 +33,7 @@ export const CONFIG = { isRequired: false, isDisabled: false, placeholderText: "Select option(s)", - responsiveBehavior: ResponsiveBehavior.Fill, + responsiveBehavior: getDefaultResponsiveBehavior(Widget.getWidgetType()), minWidth: FILL_WIDGET_MIN_WIDTH, }, properties: { diff --git a/app/client/src/widgets/MultiSelectWidget/widget/index.tsx b/app/client/src/widgets/MultiSelectWidget/widget/index.tsx index 6844ac0946bd..8d275318cb5b 100644 --- a/app/client/src/widgets/MultiSelectWidget/widget/index.tsx +++ b/app/client/src/widgets/MultiSelectWidget/widget/index.tsx @@ -1,23 +1,23 @@ -import React from "react"; -import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; -import { WidgetType } from "constants/WidgetConstants"; import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; -import { isArray } from "lodash"; +import { WidgetType } from "constants/WidgetConstants"; import { ValidationResponse, ValidationTypes, } from "constants/WidgetValidation"; +import { isArray } from "lodash"; +import React from "react"; +import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; -import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; -import MultiSelectComponent from "../component"; -import { Layers } from "constants/Layers"; -import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService"; -import { MinimumPopupRows, GRID_DENSITY_MIGRATION_V1 } from "widgets/constants"; -import { LabelPosition, ResponsiveBehavior } from "components/constants"; import { Alignment } from "@blueprintjs/core"; -import { generateResponsiveBehaviorConfig } from "utils/layoutPropertiesUtils"; -import { DraftValueType } from "rc-select/lib/Select"; +import { LabelPosition } from "components/constants"; +import { Layers } from "constants/Layers"; import { Stylesheet } from "entities/AppTheming"; +import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; +import { DraftValueType } from "rc-select/lib/Select"; +import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService"; +import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; +import { GRID_DENSITY_MIGRATION_V1, MinimumPopupRows } from "widgets/constants"; +import MultiSelectComponent from "../component"; function defaultOptionValueValidation(value: unknown): ValidationResponse { let values: string[] = []; @@ -187,7 +187,7 @@ class MultiSelectWidget extends BaseWidget< isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, - { ...generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill) }, + ...getResponsiveLayoutConfig(this.getWidgetType()), ], }, { diff --git a/app/client/src/widgets/MultiSelectWidgetV2/index.ts b/app/client/src/widgets/MultiSelectWidgetV2/index.ts index fc95a66a9be1..dc65bd5d30cd 100644 --- a/app/client/src/widgets/MultiSelectWidgetV2/index.ts +++ b/app/client/src/widgets/MultiSelectWidgetV2/index.ts @@ -1,6 +1,7 @@ import { Alignment } from "@blueprintjs/core"; -import { LabelPosition, ResponsiveBehavior } from "components/constants"; +import { LabelPosition } from "components/constants"; import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; +import { getDefaultResponsiveBehavior } from "utils/layoutPropertiesUtils"; import { DynamicHeight } from "utils/WidgetFeatures"; import IconSVG from "./icon.svg"; import Widget from "./widget"; @@ -40,7 +41,7 @@ export const CONFIG = { isRequired: false, isDisabled: false, placeholderText: "Select option(s)", - responsiveBehavior: ResponsiveBehavior.Fill, + responsiveBehavior: getDefaultResponsiveBehavior(Widget.getWidgetType()), minWidth: FILL_WIDGET_MIN_WIDTH, }, properties: { diff --git a/app/client/src/widgets/MultiSelectWidgetV2/widget/index.tsx b/app/client/src/widgets/MultiSelectWidgetV2/widget/index.tsx index 4ffe5a8eef12..0603d9ad283a 100644 --- a/app/client/src/widgets/MultiSelectWidgetV2/widget/index.tsx +++ b/app/client/src/widgets/MultiSelectWidgetV2/widget/index.tsx @@ -1,5 +1,5 @@ import { Alignment } from "@blueprintjs/core"; -import { LabelPosition, ResponsiveBehavior } from "components/constants"; +import { LabelPosition } from "components/constants"; import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; import { Layers } from "constants/Layers"; import { WidgetType } from "constants/WidgetConstants"; @@ -14,10 +14,7 @@ import { isArray, isFinite, isString, LoDashStatic, xorWith } from "lodash"; import { DraftValueType, LabelInValueType } from "rc-select/lib/Select"; import React from "react"; import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService"; -import { - generateResponsiveBehaviorConfig, - generateVerticalAlignmentConfig, -} from "utils/layoutPropertiesUtils"; +import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; import { GRID_DENSITY_MIGRATION_V1, MinimumPopupRows } from "widgets/constants"; import { isAutoHeightEnabledForWidget } from "widgets/WidgetUtils"; @@ -450,13 +447,7 @@ class MultiSelectWidget extends BaseWidget< }, ], }, - { - sectionName: "Responsive Layout", - children: [ - generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill), - generateVerticalAlignmentConfig(), - ], - }, + ...getResponsiveLayoutConfig(this.getWidgetType()), { sectionName: "Events", children: [ diff --git a/app/client/src/widgets/NumberSliderWidget/widget/propertyConfig/contentConfig.ts b/app/client/src/widgets/NumberSliderWidget/widget/propertyConfig/contentConfig.ts index bd528c0123a6..f363b9cec673 100644 --- a/app/client/src/widgets/NumberSliderWidget/widget/propertyConfig/contentConfig.ts +++ b/app/client/src/widgets/NumberSliderWidget/widget/propertyConfig/contentConfig.ts @@ -1,11 +1,8 @@ import { Alignment } from "@blueprintjs/core"; -import { LabelPosition, ResponsiveBehavior } from "components/constants"; +import { LabelPosition } from "components/constants"; import { ValidationTypes } from "constants/WidgetValidation"; import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService"; -import { - generateResponsiveBehaviorConfig, - generateVerticalAlignmentConfig, -} from "utils/layoutPropertiesUtils"; +import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import { NumberSliderWidgetProps } from ".."; import { defaultValueValidation, @@ -278,13 +275,7 @@ export default [ }, ], }, - { - sectionName: "Responsive Layout", - children: [ - generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), - generateVerticalAlignmentConfig(), - ], - }, + ...getResponsiveLayoutConfig("NUMBER_SLIDER_WIDGET"), { sectionName: "Events", children: [ diff --git a/app/client/src/widgets/PhoneInputWidget/index.ts b/app/client/src/widgets/PhoneInputWidget/index.ts index 3b80150dc4ae..263deb603c60 100644 --- a/app/client/src/widgets/PhoneInputWidget/index.ts +++ b/app/client/src/widgets/PhoneInputWidget/index.ts @@ -1,5 +1,6 @@ -import { LabelPosition, ResponsiveBehavior } from "components/constants"; +import { LabelPosition } from "components/constants"; import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; +import { getDefaultResponsiveBehavior } from "utils/layoutPropertiesUtils"; import { DynamicHeight } from "utils/WidgetFeatures"; import { CONFIG as BaseConfig } from "widgets/BaseInputWidget"; import { getDefaultISDCode } from "./component/ISDCodeDropdown"; @@ -28,7 +29,7 @@ export const CONFIG = { defaultDialCode: getDefaultISDCode().dial_code, allowDialCodeChange: false, allowFormatting: true, - responsiveBehavior: ResponsiveBehavior.Fill, + responsiveBehavior: getDefaultResponsiveBehavior(Widget.getWidgetType()), minWidth: FILL_WIDGET_MIN_WIDTH, }, properties: { diff --git a/app/client/src/widgets/ProgressWidget/widget/index.tsx b/app/client/src/widgets/ProgressWidget/widget/index.tsx index 5485413bde86..636aa2f0d307 100644 --- a/app/client/src/widgets/ProgressWidget/widget/index.tsx +++ b/app/client/src/widgets/ProgressWidget/widget/index.tsx @@ -3,14 +3,10 @@ import React from "react"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; -import { ResponsiveBehavior } from "components/constants"; import { Colors } from "constants/Colors"; import { ValidationTypes } from "constants/WidgetValidation"; import { Stylesheet } from "entities/AppTheming"; -import { - generateResponsiveBehaviorConfig, - generateVerticalAlignmentConfig, -} from "utils/layoutPropertiesUtils"; +import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import ProgressComponent from "../component"; import { ProgressType, ProgressVariant } from "../constants"; @@ -130,13 +126,7 @@ class ProgressWidget extends BaseWidget { }, ], }, - { - sectionName: "Responsive Layout", - children: [ - generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), - generateVerticalAlignmentConfig(), - ], - }, + ...getResponsiveLayoutConfig(this.getWidgetType()), ]; } diff --git a/app/client/src/widgets/QRGeneratorWidget/widget/index.tsx b/app/client/src/widgets/QRGeneratorWidget/widget/index.tsx index bd0b9cf0efa2..cc2def2d3537 100644 --- a/app/client/src/widgets/QRGeneratorWidget/widget/index.tsx +++ b/app/client/src/widgets/QRGeneratorWidget/widget/index.tsx @@ -6,15 +6,11 @@ import { countOccurrences } from "workers/Evaluation/helpers"; import { ValidationTypes } from "constants/WidgetValidation"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; -import { ResponsiveBehavior } from "components/constants"; import WidgetStyleContainer from "components/designSystems/appsmith/WidgetStyleContainer"; import { Color } from "constants/Colors"; import { pick } from "lodash"; import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService"; -import { - generateResponsiveBehaviorConfig, - generateVerticalAlignmentConfig, -} from "utils/layoutPropertiesUtils"; +import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; import { ContainerStyle } from "widgets/ContainerWidget/component"; import TextComponent, { TextAlign } from "../component"; @@ -391,13 +387,7 @@ class TextWidget extends BaseWidget { }, ], }, - { - sectionName: "Responsive Layout", - children: [ - generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), - generateVerticalAlignmentConfig(), - ], - }, + ...getResponsiveLayoutConfig(this.getWidgetType()), ]; } diff --git a/app/client/src/widgets/RadioGroupWidget/widget/index.tsx b/app/client/src/widgets/RadioGroupWidget/widget/index.tsx index a266f56bc6f6..cd8611258dc7 100644 --- a/app/client/src/widgets/RadioGroupWidget/widget/index.tsx +++ b/app/client/src/widgets/RadioGroupWidget/widget/index.tsx @@ -2,7 +2,7 @@ import { Alignment } from "@blueprintjs/core"; import { compact, isArray, isNumber } from "lodash"; import React from "react"; -import { LabelPosition, ResponsiveBehavior } from "components/constants"; +import { LabelPosition } from "components/constants"; import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; import { TextSize, WidgetType } from "constants/WidgetConstants"; import { @@ -12,10 +12,7 @@ import { import { Stylesheet } from "entities/AppTheming"; import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService"; -import { - generateResponsiveBehaviorConfig, - generateVerticalAlignmentConfig, -} from "utils/layoutPropertiesUtils"; +import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import { GRID_DENSITY_MIGRATION_V1 } from "widgets/constants"; import { isAutoHeightEnabledForWidget } from "widgets/WidgetUtils"; import BaseWidget, { WidgetProps, WidgetState } from "../../BaseWidget"; @@ -352,13 +349,7 @@ class RadioGroupWidget extends BaseWidget { }, ], }, - { - sectionName: "Responsive Layout", - children: [ - generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), - generateVerticalAlignmentConfig(), - ], - }, + ...getResponsiveLayoutConfig(this.getWidgetType()), { sectionName: "Events", children: [ diff --git a/app/client/src/widgets/RangeSliderWidget/widget/propertyConfig/contentConfig.ts b/app/client/src/widgets/RangeSliderWidget/widget/propertyConfig/contentConfig.ts index daf7ce9da5a7..1f7d9ed7add5 100644 --- a/app/client/src/widgets/RangeSliderWidget/widget/propertyConfig/contentConfig.ts +++ b/app/client/src/widgets/RangeSliderWidget/widget/propertyConfig/contentConfig.ts @@ -1,11 +1,8 @@ import { Alignment } from "@blueprintjs/core"; -import { LabelPosition, ResponsiveBehavior } from "components/constants"; +import { LabelPosition } from "components/constants"; import { ValidationTypes } from "constants/WidgetValidation"; import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService"; -import { - generateResponsiveBehaviorConfig, - generateVerticalAlignmentConfig, -} from "utils/layoutPropertiesUtils"; +import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import { RangeSliderWidgetProps } from ".."; import { endValueValidation, @@ -320,13 +317,7 @@ export default [ }, ], }, - { - sectionName: "Responsive Layout", - children: [ - generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), - generateVerticalAlignmentConfig(), - ], - }, + ...getResponsiveLayoutConfig("RANGE_SLIDER_WIDGET"), { sectionName: "Events", children: [ diff --git a/app/client/src/widgets/RateWidget/widget/index.tsx b/app/client/src/widgets/RateWidget/widget/index.tsx index 313fbc4bed3c..8a8dabc2a7a9 100644 --- a/app/client/src/widgets/RateWidget/widget/index.tsx +++ b/app/client/src/widgets/RateWidget/widget/index.tsx @@ -4,15 +4,11 @@ import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; import RateComponent from "../component"; import { RateSize } from "../constants"; -import { ResponsiveBehavior } from "components/constants"; import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; import { ValidationTypes } from "constants/WidgetValidation"; import { Stylesheet } from "entities/AppTheming"; import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService"; -import { - generateResponsiveBehaviorConfig, - generateVerticalAlignmentConfig, -} from "utils/layoutPropertiesUtils"; +import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; function validateDefaultRate(value: unknown, props: any, _: any) { @@ -183,13 +179,7 @@ class RateWidget extends BaseWidget { }, ], }, - { - sectionName: "Responsive Layout", - children: [ - generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), - generateVerticalAlignmentConfig(), - ], - }, + ...getResponsiveLayoutConfig(this.getWidgetType()), { sectionName: "Events", children: [ diff --git a/app/client/src/widgets/RichTextEditorWidget/index.ts b/app/client/src/widgets/RichTextEditorWidget/index.ts index a4cebf691845..705ec97e304c 100644 --- a/app/client/src/widgets/RichTextEditorWidget/index.ts +++ b/app/client/src/widgets/RichTextEditorWidget/index.ts @@ -1,6 +1,7 @@ import { Alignment } from "@blueprintjs/core"; -import { LabelPosition, ResponsiveBehavior } from "components/constants"; +import { LabelPosition } from "components/constants"; import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; +import { getDefaultResponsiveBehavior } from "utils/layoutPropertiesUtils"; import { DynamicHeight } from "utils/WidgetFeatures"; import IconSVG from "./icon.svg"; import Widget from "./widget"; @@ -34,7 +35,7 @@ export const CONFIG = { labelAlignment: Alignment.LEFT, labelWidth: 5, version: 1, - responsiveBehavior: ResponsiveBehavior.Fill, + responsiveBehavior: getDefaultResponsiveBehavior(Widget.getWidgetType()), minWidth: FILL_WIDGET_MIN_WIDTH, }, properties: { diff --git a/app/client/src/widgets/RichTextEditorWidget/widget/index.tsx b/app/client/src/widgets/RichTextEditorWidget/widget/index.tsx index ba13ec279e27..995db256d56a 100644 --- a/app/client/src/widgets/RichTextEditorWidget/widget/index.tsx +++ b/app/client/src/widgets/RichTextEditorWidget/widget/index.tsx @@ -1,5 +1,5 @@ import { Alignment } from "@blueprintjs/core"; -import { LabelPosition, ResponsiveBehavior } from "components/constants"; +import { LabelPosition } from "components/constants"; import Skeleton from "components/utils/Skeleton"; import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; import { TextSize, WidgetType } from "constants/WidgetConstants"; @@ -7,10 +7,7 @@ import { ValidationTypes } from "constants/WidgetValidation"; import React, { lazy, Suspense } from "react"; import showdown from "showdown"; import { retryPromise } from "utils/AppsmithUtils"; -import { - generateResponsiveBehaviorConfig, - generateVerticalAlignmentConfig, -} from "utils/layoutPropertiesUtils"; +import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; import { GRID_DENSITY_MIGRATION_V1 } from "widgets/constants"; import { isAutoHeightEnabledForWidget } from "widgets/WidgetUtils"; @@ -215,13 +212,8 @@ class RichTextEditorWidget extends BaseWidget< }, ], }, - { - sectionName: "Responsive Layout", - children: [ - generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill), - generateVerticalAlignmentConfig(), - ], - }, + ...getResponsiveLayoutConfig(this.getWidgetType()), + { sectionName: "Events", children: [ diff --git a/app/client/src/widgets/SelectWidget/index.ts b/app/client/src/widgets/SelectWidget/index.ts index bcce268eef66..5bdb0d46cb3c 100644 --- a/app/client/src/widgets/SelectWidget/index.ts +++ b/app/client/src/widgets/SelectWidget/index.ts @@ -1,6 +1,7 @@ import { Alignment } from "@blueprintjs/core"; -import { LabelPosition, ResponsiveBehavior } from "components/constants"; +import { LabelPosition } from "components/constants"; import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; +import { getDefaultResponsiveBehavior } from "utils/layoutPropertiesUtils"; import { DynamicHeight } from "utils/WidgetFeatures"; import IconSVG from "./icon.svg"; import Widget from "./widget"; @@ -40,7 +41,7 @@ export const CONFIG = { isDisabled: false, animateLoading: true, labelTextSize: "0.875rem", - responsiveBehavior: ResponsiveBehavior.Fill, + responsiveBehavior: getDefaultResponsiveBehavior(Widget.getWidgetType()), minWidth: FILL_WIDGET_MIN_WIDTH, }, properties: { diff --git a/app/client/src/widgets/SelectWidget/widget/index.tsx b/app/client/src/widgets/SelectWidget/widget/index.tsx index a84c5d13651c..9369b866c5a4 100644 --- a/app/client/src/widgets/SelectWidget/widget/index.tsx +++ b/app/client/src/widgets/SelectWidget/widget/index.tsx @@ -1,5 +1,5 @@ import { Alignment } from "@blueprintjs/core"; -import { LabelPosition, ResponsiveBehavior } from "components/constants"; +import { LabelPosition } from "components/constants"; import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; import { WidgetType } from "constants/WidgetConstants"; import { @@ -19,10 +19,7 @@ import { } from "lodash"; import React from "react"; import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService"; -import { - generateResponsiveBehaviorConfig, - generateVerticalAlignmentConfig, -} from "utils/layoutPropertiesUtils"; +import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import { GRID_DENSITY_MIGRATION_V1, MinimumPopupRows } from "widgets/constants"; import { isAutoHeightEnabledForWidget } from "widgets/WidgetUtils"; import BaseWidget, { WidgetProps, WidgetState } from "../../BaseWidget"; @@ -375,13 +372,7 @@ class SelectWidget extends BaseWidget { }, ], }, - { - sectionName: "Responsive Layout", - children: [ - generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill), - generateVerticalAlignmentConfig(), - ], - }, + ...getResponsiveLayoutConfig(this.getWidgetType()), { sectionName: "Events", children: [ diff --git a/app/client/src/widgets/SingleSelectTreeWidget/index.ts b/app/client/src/widgets/SingleSelectTreeWidget/index.ts index 4437a59b1a77..8c8891423d6f 100644 --- a/app/client/src/widgets/SingleSelectTreeWidget/index.ts +++ b/app/client/src/widgets/SingleSelectTreeWidget/index.ts @@ -1,10 +1,10 @@ import { Alignment } from "@blueprintjs/core"; import { LabelPosition } from "components/constants"; +import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; +import { getDefaultResponsiveBehavior } from "utils/layoutPropertiesUtils"; import { DynamicHeight } from "utils/WidgetFeatures"; import IconSVG from "./icon.svg"; import Widget from "./widget"; -import { ResponsiveBehavior } from "components/constants"; -import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; export const CONFIG = { features: { @@ -55,7 +55,7 @@ export const CONFIG = { labelAlignment: Alignment.LEFT, labelWidth: 5, labelTextSize: "0.875rem", - responsiveBehavior: ResponsiveBehavior.Fill, + responsiveBehavior: getDefaultResponsiveBehavior(Widget.getWidgetType()), minWidth: FILL_WIDGET_MIN_WIDTH, }, properties: { diff --git a/app/client/src/widgets/SingleSelectTreeWidget/widget/index.tsx b/app/client/src/widgets/SingleSelectTreeWidget/widget/index.tsx index f25ef73afff1..38fbf0bc9ee0 100644 --- a/app/client/src/widgets/SingleSelectTreeWidget/widget/index.tsx +++ b/app/client/src/widgets/SingleSelectTreeWidget/widget/index.tsx @@ -1,5 +1,5 @@ import { Alignment } from "@blueprintjs/core"; -import { LabelPosition, ResponsiveBehavior } from "components/constants"; +import { LabelPosition } from "components/constants"; import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; import { Layers } from "constants/Layers"; import { TextSize, WidgetType } from "constants/WidgetConstants"; @@ -13,10 +13,7 @@ import { isArray } from "lodash"; import { DefaultValueType } from "rc-tree-select/lib/interface"; import React, { ReactNode } from "react"; import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService"; -import { - generateResponsiveBehaviorConfig, - generateVerticalAlignmentConfig, -} from "utils/layoutPropertiesUtils"; +import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; import { GRID_DENSITY_MIGRATION_V1, MinimumPopupRows } from "widgets/constants"; import { isAutoHeightEnabledForWidget } from "widgets/WidgetUtils"; @@ -299,13 +296,7 @@ class SingleSelectTreeWidget extends BaseWidget< }, ], }, - { - sectionName: "Responsive Layout", - children: [ - generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill), - generateVerticalAlignmentConfig(), - ], - }, + ...getResponsiveLayoutConfig(this.getWidgetType()), { sectionName: "Events", children: [ diff --git a/app/client/src/widgets/StatboxWidget/widget/index.tsx b/app/client/src/widgets/StatboxWidget/widget/index.tsx index 9a8958b0569d..1a34607c325e 100644 --- a/app/client/src/widgets/StatboxWidget/widget/index.tsx +++ b/app/client/src/widgets/StatboxWidget/widget/index.tsx @@ -1,14 +1,9 @@ import { WidgetType } from "constants/WidgetConstants"; import { ContainerWidget } from "widgets/ContainerWidget/widget"; -import { Positioning, ResponsiveBehavior } from "components/constants"; import { ValidationTypes } from "constants/WidgetValidation"; import { Stylesheet } from "entities/AppTheming"; -import { - generatePositioningConfig, - generateResponsiveBehaviorConfig, - generateVerticalAlignmentConfig, -} from "utils/layoutPropertiesUtils"; +import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; class StatboxWidget extends ContainerWidget { static getPropertyPaneContentConfig() { @@ -47,14 +42,7 @@ class StatboxWidget extends ContainerWidget { }, ], }, - { - sectionName: "Responsive Layout", - children: [ - generatePositioningConfig(Positioning.Fixed), - generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), - generateVerticalAlignmentConfig(), - ], - }, + ...getResponsiveLayoutConfig(this.getWidgetType()), ]; } diff --git a/app/client/src/widgets/SwitchGroupWidget/widget/index.tsx b/app/client/src/widgets/SwitchGroupWidget/widget/index.tsx index 825d0e2a25a8..995326d78982 100644 --- a/app/client/src/widgets/SwitchGroupWidget/widget/index.tsx +++ b/app/client/src/widgets/SwitchGroupWidget/widget/index.tsx @@ -7,13 +7,10 @@ import React from "react"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; -import { LabelPosition, ResponsiveBehavior } from "components/constants"; +import { LabelPosition } from "components/constants"; import { TextSize } from "constants/WidgetConstants"; import { Stylesheet } from "entities/AppTheming"; -import { - generateResponsiveBehaviorConfig, - generateVerticalAlignmentConfig, -} from "utils/layoutPropertiesUtils"; +import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import { GRID_DENSITY_MIGRATION_V1 } from "widgets/constants"; import { isAutoHeightEnabledForWidget } from "widgets/WidgetUtils"; import SwitchGroupComponent, { OptionProps } from "../component"; @@ -235,13 +232,7 @@ class SwitchGroupWidget extends BaseWidget< }, ], }, - { - sectionName: "Responsive Layout", - children: [ - generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), - generateVerticalAlignmentConfig(), - ], - }, + ...getResponsiveLayoutConfig(this.getWidgetType()), { sectionName: "Events", children: [ diff --git a/app/client/src/widgets/SwitchWidget/widget/index.tsx b/app/client/src/widgets/SwitchWidget/widget/index.tsx index bc6fbb83f028..61ff62fdac33 100644 --- a/app/client/src/widgets/SwitchWidget/widget/index.tsx +++ b/app/client/src/widgets/SwitchWidget/widget/index.tsx @@ -5,12 +5,9 @@ import SwitchComponent from "../component"; import { ValidationTypes } from "constants/WidgetValidation"; -import { LabelPosition, ResponsiveBehavior } from "components/constants"; +import { LabelPosition } from "components/constants"; import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; -import { - generateResponsiveBehaviorConfig, - generateVerticalAlignmentConfig, -} from "utils/layoutPropertiesUtils"; +import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; import { AlignWidgetTypes } from "widgets/constants"; @@ -116,13 +113,7 @@ class SwitchWidget extends BaseWidget { }, ], }, - { - sectionName: "Responsive Layout", - children: [ - generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), - generateVerticalAlignmentConfig(), - ], - }, + ...getResponsiveLayoutConfig(this.getWidgetType()), { sectionName: "Events", children: [ diff --git a/app/client/src/widgets/TableWidgetV2/index.ts b/app/client/src/widgets/TableWidgetV2/index.ts index b5be5f46799a..6e81943df329 100644 --- a/app/client/src/widgets/TableWidgetV2/index.ts +++ b/app/client/src/widgets/TableWidgetV2/index.ts @@ -1,17 +1,17 @@ import { Colors } from "constants/Colors"; +import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; import { cloneDeep, set } from "lodash"; import { combineDynamicBindings, getDynamicBindings, } from "utils/DynamicBindingUtils"; +import { getDefaultResponsiveBehavior } from "utils/layoutPropertiesUtils"; import { WidgetProps } from "widgets/BaseWidget"; import { BlueprintOperationTypes } from "widgets/constants"; import { InlineEditingSaveOptions } from "./constants"; import IconSVG from "./icon.svg"; import Widget from "./widget"; import { escapeString } from "./widget/utilities"; -import { ResponsiveBehavior } from "components/constants"; -import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; export const CONFIG = { type: Widget.getWidgetType(), @@ -19,7 +19,7 @@ export const CONFIG = { iconSVG: IconSVG, needsMeta: true, defaults: { - responsiveBehavior: ResponsiveBehavior.Fill, + responsiveBehavior: getDefaultResponsiveBehavior(Widget.getWidgetType()), minWidth: FILL_WIDGET_MIN_WIDTH, rows: 28, columns: 34, diff --git a/app/client/src/widgets/TableWidgetV2/widget/propertyConfig/PanelConfig/ResponsiveBehavior.ts b/app/client/src/widgets/TableWidgetV2/widget/propertyConfig/PanelConfig/ResponsiveBehavior.ts index 5c5b615bead5..a91fcb986cf6 100644 --- a/app/client/src/widgets/TableWidgetV2/widget/propertyConfig/PanelConfig/ResponsiveBehavior.ts +++ b/app/client/src/widgets/TableWidgetV2/widget/propertyConfig/PanelConfig/ResponsiveBehavior.ts @@ -1,13 +1,5 @@ -import { ResponsiveBehavior } from "components/constants"; -import { - generateResponsiveBehaviorConfig, - generateVerticalAlignmentConfig, -} from "utils/layoutPropertiesUtils"; - -export default { - sectionName: "Responsive Layout", - children: [ - generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill), - generateVerticalAlignmentConfig(), - ], -}; +import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; +import Widget from "../../index"; +export const ResponsiveBehaviorConfig = [ + ...getResponsiveLayoutConfig(Widget.getWidgetType()), +]; diff --git a/app/client/src/widgets/TableWidgetV2/widget/propertyConfig/PanelConfig/index.ts b/app/client/src/widgets/TableWidgetV2/widget/propertyConfig/PanelConfig/index.ts index be4ed06653ff..53c4de679bd8 100644 --- a/app/client/src/widgets/TableWidgetV2/widget/propertyConfig/PanelConfig/index.ts +++ b/app/client/src/widgets/TableWidgetV2/widget/propertyConfig/PanelConfig/index.ts @@ -10,7 +10,6 @@ import DiscardButtonproperties, { import Events from "./Events"; import General, { GeneralStyle } from "./General"; import Icon from "./Icon"; -import ResponsiveBehavior from "./ResponsiveBehavior"; import SaveButtonProperties, { saveButtonStyleConfig, } from "./SaveButtonProperties"; @@ -27,7 +26,7 @@ export default { Data, Basic, General, - ResponsiveBehavior, + // ...(ResponsiveBehaviorConfig as any), Validations, SaveButtonProperties, DiscardButtonproperties, diff --git a/app/client/src/widgets/TabsWidget/index.ts b/app/client/src/widgets/TabsWidget/index.ts index 3f844663dce5..3595f8538a32 100644 --- a/app/client/src/widgets/TabsWidget/index.ts +++ b/app/client/src/widgets/TabsWidget/index.ts @@ -1,7 +1,8 @@ -import { Positioning, ResponsiveBehavior } from "components/constants"; +import { Positioning } from "components/constants"; import { Colors } from "constants/Colors"; import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; import { WidgetHeightLimits } from "constants/WidgetConstants"; +import { getDefaultResponsiveBehavior } from "utils/layoutPropertiesUtils"; import { WidgetProps } from "widgets/BaseWidget"; import { BlueprintOperationTypes } from "widgets/constants"; import IconSVG from "./icon.svg"; @@ -27,7 +28,7 @@ export const CONFIG = { }, }, defaults: { - responsiveBehavior: ResponsiveBehavior.Fill, + responsiveBehavior: getDefaultResponsiveBehavior(Widget.getWidgetType()), minWidth: FILL_WIDGET_MIN_WIDTH, rows: WidgetHeightLimits.MIN_CANVAS_HEIGHT_IN_ROWS + 5, columns: 24, diff --git a/app/client/src/widgets/TabsWidget/widget/index.tsx b/app/client/src/widgets/TabsWidget/widget/index.tsx index d09fa0208fe4..82a31a51d18b 100644 --- a/app/client/src/widgets/TabsWidget/widget/index.tsx +++ b/app/client/src/widgets/TabsWidget/widget/index.tsx @@ -1,9 +1,5 @@ import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants"; -import { - LayoutDirection, - Positioning, - ResponsiveBehavior, -} from "components/constants"; +import { LayoutDirection, Positioning } from "components/constants"; import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; import { WIDGET_PADDING } from "constants/WidgetConstants"; import { @@ -17,8 +13,7 @@ import { WidgetProperties } from "selectors/propertyPaneSelectors"; import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService"; import { generatePositioningConfig, - generateResponsiveBehaviorConfig, - generateVerticalAlignmentConfig, + getResponsiveLayoutConfig, } from "utils/layoutPropertiesUtils"; import WidgetFactory from "utils/WidgetFactory"; import BaseWidget, { WidgetState } from "../../BaseWidget"; @@ -188,13 +183,7 @@ class TabsWidget extends BaseWidget< }, ], }, - { - sectionName: "Responsive Layout", - children: [ - generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill), - generateVerticalAlignmentConfig(), - ], - }, + ...getResponsiveLayoutConfig(this.getWidgetType()), { sectionName: "Events", children: [ diff --git a/app/client/src/widgets/TextWidget/index.ts b/app/client/src/widgets/TextWidget/index.ts index 23a216c73372..c8e308d90cda 100644 --- a/app/client/src/widgets/TextWidget/index.ts +++ b/app/client/src/widgets/TextWidget/index.ts @@ -1,6 +1,6 @@ -import { ResponsiveBehavior } from "components/constants"; import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; import { DEFAULT_FONT_SIZE } from "constants/WidgetConstants"; +import { getDefaultResponsiveBehavior } from "utils/layoutPropertiesUtils"; import { OverflowTypes } from "./constants"; import IconSVG from "./icon.svg"; import Widget from "./widget"; @@ -29,7 +29,7 @@ export const CONFIG = { overflow: OverflowTypes.NONE, version: 1, animateLoading: true, - responsiveBehavior: ResponsiveBehavior.Fill, + responsiveBehavior: getDefaultResponsiveBehavior(Widget.getWidgetType()), minWidth: FILL_WIDGET_MIN_WIDTH, }, properties: { diff --git a/app/client/src/widgets/TextWidget/widget/index.tsx b/app/client/src/widgets/TextWidget/widget/index.tsx index 5115ee390d07..40fc72f62160 100644 --- a/app/client/src/widgets/TextWidget/widget/index.tsx +++ b/app/client/src/widgets/TextWidget/widget/index.tsx @@ -6,16 +6,12 @@ import { countOccurrences } from "workers/Evaluation/helpers"; import { ValidationTypes } from "constants/WidgetValidation"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; -import { ResponsiveBehavior } from "components/constants"; import WidgetStyleContainer from "components/designSystems/appsmith/WidgetStyleContainer"; import { Color } from "constants/Colors"; import { Stylesheet } from "entities/AppTheming"; import { pick } from "lodash"; import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService"; -import { - generateResponsiveBehaviorConfig, - generateVerticalAlignmentConfig, -} from "utils/layoutPropertiesUtils"; +import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; import { ContainerStyle } from "widgets/ContainerWidget/component"; import TextComponent, { TextAlign } from "../component"; @@ -98,13 +94,7 @@ class TextWidget extends BaseWidget { }, ], }, - { - sectionName: "Responsive Layout", - children: [ - generateResponsiveBehaviorConfig(ResponsiveBehavior.Fill), - generateVerticalAlignmentConfig(), - ], - }, + ...getResponsiveLayoutConfig(this.getWidgetType()), ]; } diff --git a/app/client/src/widgets/VideoWidget/widget/index.tsx b/app/client/src/widgets/VideoWidget/widget/index.tsx index c852d712641e..bd787e3e1efa 100644 --- a/app/client/src/widgets/VideoWidget/widget/index.tsx +++ b/app/client/src/widgets/VideoWidget/widget/index.tsx @@ -1,4 +1,4 @@ -import { ButtonBorderRadius, ResponsiveBehavior } from "components/constants"; +import { ButtonBorderRadius } from "components/constants"; import Skeleton from "components/utils/Skeleton"; import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; import { WidgetType } from "constants/WidgetConstants"; @@ -8,10 +8,7 @@ import React, { lazy, Suspense } from "react"; import ReactPlayer from "react-player"; import { retryPromise } from "utils/AppsmithUtils"; import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService"; -import { - generateResponsiveBehaviorConfig, - generateVerticalAlignmentConfig, -} from "utils/layoutPropertiesUtils"; +import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import BaseWidget, { WidgetProps, WidgetState } from "../../BaseWidget"; const VideoComponent = lazy(() => retryPromise(() => import("../component"))); @@ -89,13 +86,7 @@ class VideoWidget extends BaseWidget { }, ], }, - { - sectionName: "Responsive Layout", - children: [ - generateResponsiveBehaviorConfig(ResponsiveBehavior.Hug), - generateVerticalAlignmentConfig(), - ], - }, + ...getResponsiveLayoutConfig(this.getWidgetType()), { sectionName: "Events", children: [ From 0794ba66b8b6cc3357672b17a2b44b46d10e2604 Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 28 Dec 2022 10:52:53 -0500 Subject: [PATCH 313/708] wip --- .../editorComponents/DraggableComponent.tsx | 4 +- .../CanvasArenas/hooks/useCanvasDragging.ts | 10 +- .../src/utils/autoLayout/positionUtils.ts | 223 +++++++++++++----- .../widgets/ContainerWidget/widget/index.tsx | 2 +- 4 files changed, 173 insertions(+), 66 deletions(-) diff --git a/app/client/src/components/editorComponents/DraggableComponent.tsx b/app/client/src/components/editorComponents/DraggableComponent.tsx index f0b1866cda03..b1f6c6981bf1 100644 --- a/app/client/src/components/editorComponents/DraggableComponent.tsx +++ b/app/client/src/components/editorComponents/DraggableComponent.tsx @@ -131,7 +131,9 @@ function DraggableComponent(props: DraggableComponentProps) { }; }, [isResizingOrDragging, isCurrentWidgetResizing]); - const widgetBoundaries = ; + const widgetBoundaries = props.isFlexChild ? null : ( + + ); const classNameForTesting = `t--draggable-${props.type .split("_") diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index f6bb1987d74c..757f84ea8a0a 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -3,6 +3,7 @@ import { OccupiedSpace } from "constants/CanvasEditorConstants"; import { CONTAINER_GRID_PADDING, GridDefaults, + MAIN_CONTAINER_WIDGET_ID, } from "constants/WidgetConstants"; import { debounce, isEmpty, throttle } from "lodash"; import { CanvasDraggingArenaProps } from "pages/common/CanvasArenas/CanvasDraggingArena"; @@ -237,7 +238,7 @@ export const useCanvasDragging = ( const scrollParent: Element | null = getNearestParentCanvas( slidingArenaRef.current, ); - // console.log(`#### init variable: ${widgetName}`); + let canvasIsDragging = false; let isUpdatingRows = false; let currentRectanglesToDraw: WidgetDraggingBlock[] = []; @@ -274,9 +275,11 @@ export const useCanvasDragging = ( stickyCanvasRef.current.height, ); slidingArenaRef.current.style.zIndex = ""; - // console.log(`#### reset canvas state: ${widgetName}`); canvasIsDragging = false; } + if (isDragging) { + setDraggingCanvas(MAIN_CONTAINER_WIDGET_ID); + } }; if (isDragging) { const startPoints = defaultHandlePositions; @@ -356,7 +359,6 @@ export const useCanvasDragging = ( }; const onFirstMoveOnCanvas = (e: any, over = false) => { - // console.log(`#### first move: ${widgetName}`); if ( !isResizing && isDragging && @@ -379,7 +381,6 @@ export const useCanvasDragging = ( logContainerJump(widgetId, speed, acceleration); containerJumpThresholdMetrics.clearMetrics(); // we can just use canvasIsDragging but this is needed to render the relative DragLayerComponent - // console.log(`#### set dragging canvas: ${widgetName}`); setDraggingCanvas(widgetId); } canvasIsDragging = true; @@ -566,7 +567,6 @@ export const useCanvasDragging = ( left: e.offsetX - startPoints.left - parentDiff.left, top: e.offsetY - startPoints.top - parentDiff.top, }; - // console.log("#### mouse move", delta); const drawingBlocks = blocksToDraw.map((each) => ({ ...each, left: each.left + delta.left, diff --git a/app/client/src/utils/autoLayout/positionUtils.ts b/app/client/src/utils/autoLayout/positionUtils.ts index cfe2d64b7cb8..acb6230b43ab 100644 --- a/app/client/src/utils/autoLayout/positionUtils.ts +++ b/app/client/src/utils/autoLayout/positionUtils.ts @@ -21,7 +21,7 @@ export function updateWidgetPositions( const payload: { height: number; widgets: CanvasWidgetsReduxState; - } = calculateWidgetPositions(widgets, layer, height); + } = calculateWidgetPositions(widgets, layer, height, isMobile); widgets = payload.widgets; height += payload.height; } @@ -36,70 +36,33 @@ function calculateWidgetPositions( allWidgets: CanvasWidgetsReduxState, layer: FlexLayer, topRow: number, + isMobile = false, ): { height: number; widgets: CanvasWidgetsReduxState } { let widgets = { ...allWidgets }; - const startChildren = [], - centerChildren = [], - endChildren = [], - fillChildren = []; - let startColumns = 0, - centerColumns = 0, - endColumns = 0; - let startSize = 0, - centerSize = 0, - endSize = 0; - - // Calculate the number of columns occupied by hug widgets in each alignment. - for (const child of layer.children) { - const widget = widgets[child.id]; - const isFillWidget = widget.responsiveBehavior === ResponsiveBehavior.Fill; - if (isFillWidget) fillChildren.push(child); - if (child.align === FlexLayerAlignment.Start) { - startChildren.push(widget); - if (!isFillWidget) startColumns += widget.rightColumn - widget.leftColumn; - } else if (child.align === FlexLayerAlignment.Center) { - centerChildren.push(widget); - if (!isFillWidget) - centerColumns += widget.rightColumn - widget.leftColumn; - } else if (child.align === FlexLayerAlignment.End) { - endChildren.push(widget); - if (!isFillWidget) endColumns += widget.rightColumn - widget.leftColumn; - } - } + const { + centerChildren, + centerColumns, + endChildren, + endColumns, + fillWidgetLength, + startChildren, + startColumns, + } = getIndividualAlignmentInfo(widgets, layer, isMobile); - const availableColumns = 64 - startColumns - centerColumns - endColumns; - const fillWidgetLength = availableColumns / fillChildren.length; - for (const child of fillChildren) { - if (child.align === FlexLayerAlignment.Start) { - startColumns += fillWidgetLength; - } else if (child.align === FlexLayerAlignment.Center) { - centerColumns += fillWidgetLength; - } else if (child.align === FlexLayerAlignment.End) { - endColumns += fillWidgetLength; - } - } + const isFlexWrapped: boolean = + isMobile && startColumns + centerColumns + endColumns > 64; const arr: { alignment: FlexLayerAlignment; columns: number }[] = [ { alignment: FlexLayerAlignment.Start, columns: startColumns }, { alignment: FlexLayerAlignment.Center, columns: centerColumns }, { alignment: FlexLayerAlignment.End, columns: endColumns }, - ].sort((a, b) => b.columns - a.columns); + ]; - const sizes: { - alignment: FlexLayerAlignment; - columns: number; - }[] = getAlignmentSizes(arr, 64, []); - - for (const each of sizes) { - if (each.alignment === FlexLayerAlignment.Start) { - startSize = each.columns; - } else if (each.alignment === FlexLayerAlignment.Center) { - centerSize = each.columns; - } else if (each.alignment === FlexLayerAlignment.End) { - endSize = each.columns; - } - } + const { centerSize, endSize, startSize } = getAlignmentSizeInfo( + arr.sort((a, b) => b.columns - a.columns), + isMobile, + ); let maxHeight = 0; [ @@ -119,14 +82,18 @@ function calculateWidgetPositions( const width = widget.responsiveBehavior === ResponsiveBehavior.Fill ? fillWidgetLength - : widget.rightColumn - widget.leftColumn; + : getRightColumn(widget, isMobile) - getLeftColumn(widget, isMobile); maxHeight = Math.max(maxHeight, height); + const widgetAfterLeftUpdate = setLeftColumn(widget, left, isMobile); + const widgetAfterRightUpdate = setRightColumn( + widgetAfterLeftUpdate, + left + width, + isMobile, + ); widgets = { ...widgets, [widget.widgetId]: { - ...widget, - leftColumn: left, - rightColumn: left + width, + ...widgetAfterRightUpdate, topRow, bottomRow: topRow + height, }, @@ -138,6 +105,7 @@ function calculateWidgetPositions( return { height: maxHeight, widgets }; } +// TODO: update this function to measure height as well. function getAlignmentSizes( arr: { alignment: FlexLayerAlignment; columns: number }[], space: number, @@ -159,3 +127,140 @@ function getAlignmentSizes( } return sizes; } + +function getIndividualAlignmentInfo( + widgets: CanvasWidgetsReduxState, + layer: FlexLayer, + isMobile: boolean, +) { + const startChildren = [], + centerChildren = [], + endChildren = [], + fillChildren = []; + let startColumns = 0, + centerColumns = 0, + endColumns = 0; + // Calculate the number of columns occupied by hug widgets in each alignment. + for (const child of layer.children) { + const widget = widgets[child.id]; + const isFillWidget = widget.responsiveBehavior === ResponsiveBehavior.Fill; + if (isFillWidget) fillChildren.push(child); + if (child.align === FlexLayerAlignment.Start) { + startChildren.push(widget); + if (!isFillWidget) + startColumns += + getRightColumn(widget, isMobile) - getLeftColumn(widget, isMobile); + } else if (child.align === FlexLayerAlignment.Center) { + centerChildren.push(widget); + if (!isFillWidget) + centerColumns += + getRightColumn(widget, isMobile) - getLeftColumn(widget, isMobile); + } else if (child.align === FlexLayerAlignment.End) { + endChildren.push(widget); + if (!isFillWidget) + endColumns += + getRightColumn(widget, isMobile) - getLeftColumn(widget, isMobile); + } + } + + const availableColumns: number = + 64 - startColumns - centerColumns - endColumns; + const fillWidgetLength: number = isMobile + ? 64 + : availableColumns / fillChildren.length; + for (const child of fillChildren) { + if (child.align === FlexLayerAlignment.Start) { + startColumns += fillWidgetLength; + } else if (child.align === FlexLayerAlignment.Center) { + centerColumns += fillWidgetLength; + } else if (child.align === FlexLayerAlignment.End) { + endColumns += fillWidgetLength; + } + } + + return { + startChildren, + centerChildren, + endChildren, + fillChildren, + fillWidgetLength, + startColumns, + centerColumns, + endColumns, + }; +} + +function getAlignmentSizeInfo( + arr: { alignment: FlexLayerAlignment; columns: number }[], + isMobile: boolean, +): { startSize: number; centerSize: number; endSize: number } { + let startSize = 0, + centerSize = 0, + endSize = 0; + const sizes: { + alignment: FlexLayerAlignment; + columns: number; + }[] = getAlignmentSizes(arr, 64, []); + + for (const each of sizes) { + if (each.alignment === FlexLayerAlignment.Start) { + startSize = each.columns; + } else if (each.alignment === FlexLayerAlignment.Center) { + centerSize = each.columns; + } else if (each.alignment === FlexLayerAlignment.End) { + endSize = each.columns; + } + } + return { startSize, centerSize, endSize }; +} + +function getWrappedAlignmentSize( + arr: { alignment: FlexLayerAlignment; columns: number }[], + res: { alignment: FlexLayerAlignment; columns: number }[][] = [[], [], []], + resIndex = 0, +): { alignment: FlexLayerAlignment; columns: number }[][] { + if (arr.length === 1) { + res[resIndex].push(arr[0]); + return res; + } + let index = 0; + let total = 0; + for (const each of arr) { + if (total + each.columns >= 64) { + let x = index; + if (!res[resIndex].length) { + res[resIndex].push(each); + x += 1; + } + return getWrappedAlignmentSize([...arr.slice(x)], res, resIndex + 1); + } + total += each.columns; + index += 1; + res[resIndex].push(each); + } + return res; +} + +function getRightColumn(widget: any, isMobile: boolean): number { + return isMobile && widget.mobileRightColumn !== undefined + ? widget.mobileRightColumn + : widget.rightColumn; +} + +function setRightColumn(widget: any, val: number, isMobile: boolean): any { + return isMobile && widget.mobileRightColumn !== undefined + ? { ...widget, mobileRightColumn: val } + : { ...widget, rightColumn: val }; +} + +function getLeftColumn(widget: any, isMobile: boolean): number { + return isMobile && widget.mobileLeftColumn !== undefined + ? widget.mobileLeftColumn + : widget.leftColumn; +} + +function setLeftColumn(widget: any, val: number, isMobile: boolean): any { + return isMobile && widget.mobileLeftColumn !== undefined + ? { ...widget, mobileLeftColumn: val } + : { ...widget, leftColumn: val }; +} diff --git a/app/client/src/widgets/ContainerWidget/widget/index.tsx b/app/client/src/widgets/ContainerWidget/widget/index.tsx index 26ec655b650a..dd70a2154f0e 100644 --- a/app/client/src/widgets/ContainerWidget/widget/index.tsx +++ b/app/client/src/widgets/ContainerWidget/widget/index.tsx @@ -230,7 +230,7 @@ export class ContainerWidget extends BaseWidget< renderAsContainerComponent(props: ContainerWidgetProps) { return ( - + Date: Wed, 28 Dec 2022 11:18:29 -0500 Subject: [PATCH 314/708] remove widget boundaries on drag --- .../src/components/editorComponents/DraggableComponent.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/client/src/components/editorComponents/DraggableComponent.tsx b/app/client/src/components/editorComponents/DraggableComponent.tsx index cf61aa721f61..90e46803e8dc 100644 --- a/app/client/src/components/editorComponents/DraggableComponent.tsx +++ b/app/client/src/components/editorComponents/DraggableComponent.tsx @@ -131,7 +131,9 @@ function DraggableComponent(props: DraggableComponentProps) { }; }, [isResizingOrDragging, isCurrentWidgetResizing]); - const widgetBoundaries = ; + const widgetBoundaries = props.isFlexChild ? null : ( + + ); const classNameForTesting = `t--draggable-${props.type .split("_") From d12de7ae8b243fa475141a16c9bdcd0b8499d181 Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 28 Dec 2022 22:58:57 -0500 Subject: [PATCH 315/708] basic logic for wrap positions and fix for canvas width --- .../appsmith/autoLayout/FlexBoxComponent.tsx | 6 +- .../editorComponents/ResizableComponent.tsx | 6 +- .../hooks/useAutoLayoutHighlights.ts | 5 + .../src/sagas/AutoLayoutUpdateSagas.tsx | 1 + app/client/src/sagas/AutoLayoutUtils.ts | 7 +- .../src/utils/autoLayout/highlightUtils.ts | 74 ++-- .../src/utils/autoLayout/positionUtils.ts | 335 ++++++++++++++++-- app/client/src/widgets/BaseWidget.tsx | 9 +- 8 files changed, 381 insertions(+), 62 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx index 66649ee9be26..a93b68e3eedc 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx @@ -128,8 +128,10 @@ function FlexBoxComponent(props: FlexBoxProps) { function getColumns(id: string, isMobile: boolean): number { const widget = allWidgets[id]; if (!widget) return 0; - return isMobile && widget.mobileRightColumn - ? widget.mobileRightColumn - widget.leftColumn + return isMobile && + widget.mobileRightColumn !== undefined && + widget.mobileLeftColumn !== undefined + ? widget.mobileRightColumn - widget.mobileLeftColumn : widget.rightColumn - widget.leftColumn; } diff --git a/app/client/src/components/editorComponents/ResizableComponent.tsx b/app/client/src/components/editorComponents/ResizableComponent.tsx index fe2807bee163..586ea4f3e5ff 100644 --- a/app/client/src/components/editorComponents/ResizableComponent.tsx +++ b/app/client/src/components/editorComponents/ResizableComponent.tsx @@ -101,10 +101,12 @@ export const ResizableComponent = memo(function ResizableComponent( // The ResizableContainer's size prop is controlled const dimensions: UIElementSize = { width: - ((props.isMobile && props.mobileRightColumn + ((props.isMobile && props.mobileRightColumn !== undefined ? props.mobileRightColumn : props.rightColumn) - - props.leftColumn) * + (props.isMobile && props.mobileLeftColumn !== undefined + ? props.mobileLeftColumn + : props.leftColumn)) * props.parentColumnSpace - 2 * props.paddingOffset, height: diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index 124c3eb20d7a..5d74e80b24bb 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -7,6 +7,7 @@ import { useSelector } from "react-redux"; import { ReflowDirection } from "reflow/reflowTypes"; import { getWidgets } from "sagas/selectors"; import { getCanvasWidth } from "selectors/editorSelectors"; +import { getIsMobile } from "selectors/mainCanvasSelectors"; import { deriveHighlightsFromLayers } from "utils/autoLayout/highlightUtils"; import WidgetFactory from "utils/WidgetFactory"; import { WidgetDraggingBlock } from "./useBlocksToBeDraggedOnCanvas"; @@ -56,6 +57,7 @@ export const useAutoLayoutHighlights = ({ }: AutoLayoutHighlightProps) => { const allWidgets = useSelector(getWidgets); const canvasWidth: number = useSelector(getCanvasWidth); + const isMobile = useSelector(getIsMobile); let highlights: HighlightInfo[] = []; let newLayers: { [key: string]: number } = {}; let lastActiveHighlight: HighlightInfo | undefined; @@ -140,6 +142,7 @@ export const useAutoLayoutHighlights = ({ canvasWidth, blocksToDraw.map((block) => block?.widgetId), isFillWidget, + isMobile, ); } // console.log("#### highlights", highlights); @@ -162,6 +165,7 @@ export const useAutoLayoutHighlights = ({ canvasWidth, blocksToDraw.map((block) => block?.widgetId), isFillWidget, + isMobile, ); // console.log("#### highlights", highlights); if (!highlights) return; @@ -191,6 +195,7 @@ export const useAutoLayoutHighlights = ({ canvasWidth, blocksToDraw.map((block) => block?.widgetId), isFillWidget, + isMobile, ); base = highlights; diff --git a/app/client/src/sagas/AutoLayoutUpdateSagas.tsx b/app/client/src/sagas/AutoLayoutUpdateSagas.tsx index 8bafdebf5658..d61eb421f04a 100644 --- a/app/client/src/sagas/AutoLayoutUpdateSagas.tsx +++ b/app/client/src/sagas/AutoLayoutUpdateSagas.tsx @@ -8,6 +8,7 @@ import { ResponsiveBehavior } from "components/constants"; import log from "loglevel"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; import { all, call, put, select, takeLatest } from "redux-saga/effects"; +import { updateWidgetPositions } from "utils/autoLayout/positionUtils"; import { alterLayoutForDesktop, alterLayoutForMobile, diff --git a/app/client/src/sagas/AutoLayoutUtils.ts b/app/client/src/sagas/AutoLayoutUtils.ts index cae584e5c53d..f8b427c0c97c 100644 --- a/app/client/src/sagas/AutoLayoutUtils.ts +++ b/app/client/src/sagas/AutoLayoutUtils.ts @@ -264,7 +264,7 @@ export function alterLayoutForMobile( if (widget.responsiveBehavior === ResponsiveBehavior.Fill) { widget.mobileRightColumn = 64; - widget.leftColumn = 0; + widget.mobileLeftColumn = 0; } else if ( widget.responsiveBehavior === ResponsiveBehavior.Hug && widget.minWidth @@ -272,7 +272,7 @@ export function alterLayoutForMobile( const { minWidth, rightColumn } = widget; const columnSpace = canvasWidth / 64; if (columnSpace * rightColumn < minWidth) { - widget.leftColumn = 0; + widget.mobileLeftColumn = 0; widget.mobileRightColumn = Math.min( Math.floor(minWidth / columnSpace), 64, @@ -286,6 +286,7 @@ export function alterLayoutForMobile( (canvasWidth * (widget.mobileRightColumn || 1)) / 64, ); widgets[child] = widget; + widgets = updateWidgetPositions(widgets, child, true); } return widgets; } @@ -306,7 +307,7 @@ export function alterLayoutForDesktop( for (const child of children) { widgets = alterLayoutForDesktop(widgets, child); } - + widgets = updateWidgetPositions(widgets, parentId, false); return widgets; } diff --git a/app/client/src/utils/autoLayout/highlightUtils.ts b/app/client/src/utils/autoLayout/highlightUtils.ts index 1ab41216db36..5622add8b247 100644 --- a/app/client/src/utils/autoLayout/highlightUtils.ts +++ b/app/client/src/utils/autoLayout/highlightUtils.ts @@ -12,6 +12,7 @@ import { } from "constants/WidgetConstants"; import { HighlightInfo } from "pages/common/CanvasArenas/hooks/useAutoLayoutHighlights"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; +import { getLeftColumn, getRightColumn } from "./positionUtils"; const HORIZONTAL_HIGHLIGHT_MARGIN = 4; @@ -29,6 +30,7 @@ export function deriveHighlightsFromLayers( mainCanvasWidth = 0, draggedWidgets: string[] = [], hasFillWidget = false, + isMobile = false, ): HighlightInfo[] { const widgets = { ...allWidgets }; try { @@ -39,6 +41,7 @@ export function deriveHighlightsFromLayers( canvas, widgets, mainCanvasWidth, + isMobile, ); const layers: FlexLayer[] = canvas.flexLayers || []; @@ -75,6 +78,7 @@ export function deriveHighlightsFromLayers( canvasId, columnSpace, draggedWidgets, + isMobile, }); if (!isEmpty) { @@ -132,6 +136,7 @@ function generateVerticalHighlights(data: { canvasId: string; columnSpace: number; draggedWidgets: string[]; + isMobile: boolean; }): VerticalHighlightsPayload { const { canvasId, @@ -140,6 +145,7 @@ function generateVerticalHighlights(data: { columnSpace, draggedWidgets, height, + isMobile, layer, layerIndex, offsetTop, @@ -161,12 +167,14 @@ function generateVerticalHighlights(data: { if (draggedWidgets.indexOf(child.id) > -1) continue; if (child.align === FlexLayerAlignment.End) { endChildren.push(widget); - endColumns += widget.rightColumn - widget.leftColumn; + endColumns += + getRightColumn(widget, isMobile) - getLeftColumn(widget, isMobile); } else if (child.align === FlexLayerAlignment.Center) { centerChildren.push(widget); } else { startChildren.push(widget); - startColumns += widget.rightColumn - widget.leftColumn; + startColumns += + getRightColumn(widget, isMobile) - getLeftColumn(widget, isMobile); } } @@ -183,6 +191,7 @@ function generateVerticalHighlights(data: { parentColumnSpace: columnSpace, parentRowSpace: widgets[canvasId].parentRowSpace, canvasWidth, + isMobile, }), ...generateHighlightsForSubWrapper({ arr: centerChildren, @@ -196,6 +205,7 @@ function generateVerticalHighlights(data: { parentRowSpace: widgets[canvasId].parentRowSpace, canvasWidth, avoidInitialHighlight: startColumns > 25 || endColumns > 25, + isMobile, }), ...generateHighlightsForSubWrapper({ arr: endChildren, @@ -208,6 +218,7 @@ function generateVerticalHighlights(data: { parentColumnSpace: columnSpace, parentRowSpace: widgets[canvasId].parentRowSpace, canvasWidth, + isMobile, }), ], childCount: count, @@ -226,6 +237,7 @@ function generateHighlightsForSubWrapper(data: { parentRowSpace: number; canvasWidth: number; avoidInitialHighlight?: boolean; + isMobile: boolean; }): HighlightInfo[] { const { alignment, @@ -235,6 +247,7 @@ function generateHighlightsForSubWrapper(data: { canvasWidth, childCount, height, + isMobile, layerIndex, offsetTop, parentColumnSpace, @@ -242,14 +255,14 @@ function generateHighlightsForSubWrapper(data: { const res: HighlightInfo[] = []; let count = 0; for (const child of arr) { - const { leftColumn } = child; + const left = getLeftColumn(child, isMobile); res.push({ isNewLayer: false, index: count + childCount, layerIndex, rowIndex: count, alignment, - posX: leftColumn * parentColumnSpace, + posX: left * parentColumnSpace, posY: offsetTop, width: DEFAULT_HIGHLIGHT_SIZE, height, @@ -273,8 +286,6 @@ function generateHighlightsForSubWrapper(data: { ? arr[arr.length - 1].rightColumn * parentColumnSpace : 0, canvasWidth, - parentColumnSpace, - canvasId, ), posY: offsetTop, width: DEFAULT_HIGHLIGHT_SIZE, @@ -290,13 +301,9 @@ function getPositionForInitialHighlight( alignment: FlexLayerAlignment, posX: number, containerWidth: number, - parentColumnSpace: number, - canvasId: string, ): number { if (alignment === FlexLayerAlignment.End) { - return ( - 64 * parentColumnSpace - (canvasId === MAIN_CONTAINER_WIDGET_ID ? 6 : 0) - ); + return containerWidth - 6; } else if (alignment === FlexLayerAlignment.Center) { if (!highlights.length) return containerWidth / 2; return posX; @@ -350,8 +357,14 @@ function getCanvasDimensions( canvas: any, widgets: CanvasWidgetsReduxState, mainCanvasWidth: number, + isMobile: boolean, ): { canvasWidth: number; columnSpace: number } { - const columns: number = canvas.rightColumn - canvas.leftColumn; + const canvasWidth: number = getCanvasWidth( + canvas, + widgets, + mainCanvasWidth, + isMobile, + ); let padding = (CONTAINER_GRID_PADDING + WIDGET_PADDING) * 2; if ( @@ -366,17 +379,28 @@ function getCanvasDimensions( padding = WIDGET_PADDING * 2; } const columnSpace: number = - canvas.parentColumnSpace === 1 - ? canvas.parentId - ? (widgets[canvas.parentId].parentColumnSpace * columns - - (padding || 0)) / - GridDefaults.DEFAULT_GRID_COLUMNS - : mainCanvasWidth / GridDefaults.DEFAULT_GRID_COLUMNS - : canvas.parentColumnSpace; - // const columnSpace = canvasWidth / GridDefaults.DEFAULT_GRID_COLUMNS; - const canvasWidth: number = - canvas.widgetId === MAIN_CONTAINER_WIDGET_ID - ? mainCanvasWidth - : columns * columnSpace - padding; - return { canvasWidth, columnSpace }; + (canvasWidth - padding) / GridDefaults.DEFAULT_GRID_COLUMNS; + + return { canvasWidth: canvasWidth - padding, columnSpace }; +} + +function getCanvasWidth( + canvas: any, + widgets: CanvasWidgetsReduxState, + mainCanvasWidth: number, + isMobile: boolean, +): number { + if (!mainCanvasWidth) return 0; + if (canvas.widgetId === MAIN_CONTAINER_WIDGET_ID) return mainCanvasWidth; + let widget = canvas; + let columns = + getRightColumn(widget, isMobile) - getLeftColumn(widget, isMobile); + let width = columns / GridDefaults.DEFAULT_GRID_COLUMNS; + while (widget.widgetId !== MAIN_CONTAINER_WIDGET_ID) { + columns = + getRightColumn(widget, isMobile) - getLeftColumn(widget, isMobile); + width *= columns / GridDefaults.DEFAULT_GRID_COLUMNS; + widget = widgets[widget.parentId]; + } + return width * mainCanvasWidth; } diff --git a/app/client/src/utils/autoLayout/positionUtils.ts b/app/client/src/utils/autoLayout/positionUtils.ts index acb6230b43ab..fb9f184c31f5 100644 --- a/app/client/src/utils/autoLayout/positionUtils.ts +++ b/app/client/src/utils/autoLayout/positionUtils.ts @@ -1,6 +1,19 @@ import { FlexLayerAlignment, ResponsiveBehavior } from "components/constants"; import { FlexLayer } from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; +import { GridDefaults } from "constants/WidgetConstants"; +import { isMobile } from "react-device-detect"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; +import { WidgetProps } from "widgets/BaseWidget"; + +type Widget = WidgetProps & { + children?: string[] | undefined; +}; + +interface AlignmentInfo { + alignment: FlexLayerAlignment; + columns: number; + children: Widget[]; +} /** * Calculate widget position on canvas. @@ -38,8 +51,6 @@ function calculateWidgetPositions( topRow: number, isMobile = false, ): { height: number; widgets: CanvasWidgetsReduxState } { - let widgets = { ...allWidgets }; - const { centerChildren, centerColumns, @@ -48,34 +59,91 @@ function calculateWidgetPositions( fillWidgetLength, startChildren, startColumns, - } = getIndividualAlignmentInfo(widgets, layer, isMobile); + } = getIndividualAlignmentInfo(allWidgets, layer, isMobile); const isFlexWrapped: boolean = - isMobile && startColumns + centerColumns + endColumns > 64; + isMobile && + startColumns + centerColumns + endColumns > + GridDefaults.DEFAULT_GRID_COLUMNS; - const arr: { alignment: FlexLayerAlignment; columns: number }[] = [ - { alignment: FlexLayerAlignment.Start, columns: startColumns }, - { alignment: FlexLayerAlignment.Center, columns: centerColumns }, - { alignment: FlexLayerAlignment.End, columns: endColumns }, + const arr: AlignmentInfo[] = [ + { + alignment: FlexLayerAlignment.Start, + columns: startColumns, + children: startChildren, + }, + { + alignment: FlexLayerAlignment.Center, + columns: centerColumns, + children: centerChildren, + }, + { + alignment: FlexLayerAlignment.End, + columns: endColumns, + children: endChildren, + }, ]; + if (isFlexWrapped) + return updatePositionsForFlexWrap({ + allWidgets, + topRow, + arr, + centerChildren, + centerColumns, + endChildren, + endColumns, + fillWidgetLength, + startChildren, + isMobile, + }); + + return placeWidgetsWithoutWrap( + allWidgets, + arr, + topRow, + startChildren, + centerChildren, + endChildren, + centerColumns, + endColumns, + fillWidgetLength, + isMobile, + ); +} + +function placeWidgetsWithoutWrap( + allWidgets: CanvasWidgetsReduxState, + arr: AlignmentInfo[], + topRow: number, + startChildren: Widget[], + centerChildren: Widget[], + endChildren: Widget[], + centerColumns: number, + endColumns: number, + fillWidgetLength: number, + isMobile = false, +): { height: number; widgets: CanvasWidgetsReduxState } { + let widgets = { ...allWidgets }; const { centerSize, endSize, startSize } = getAlignmentSizeInfo( arr.sort((a, b) => b.columns - a.columns), isMobile, ); let maxHeight = 0; - [ - { children: startChildren, leftColumn: 0 }, - { + const input = []; + if (startSize) input.push({ children: startChildren, leftColumn: 0 }); + if (centerSize) + input.push({ children: centerChildren, leftColumn: startSize + centerSize / 2 - centerColumns / 2, - }, - { + }); + if (endSize) + input.push({ children: endChildren, leftColumn: startSize + centerSize + endSize - endColumns, - }, - ].forEach((each) => { + }); + input.forEach((each) => { let left = each.leftColumn; for (const widget of each.children) { const height = widget.bottomRow - widget.topRow; @@ -107,9 +175,9 @@ function calculateWidgetPositions( // TODO: update this function to measure height as well. function getAlignmentSizes( - arr: { alignment: FlexLayerAlignment; columns: number }[], + arr: AlignmentInfo[], space: number, - sizes: { alignment: FlexLayerAlignment; columns: number }[] = [], + sizes: AlignmentInfo[] = [], ): { alignment: FlexLayerAlignment; columns: number }[] { if (arr.length === 0) return sizes; if (arr[0].columns > space / arr.length) { @@ -164,9 +232,12 @@ function getIndividualAlignmentInfo( } const availableColumns: number = - 64 - startColumns - centerColumns - endColumns; + GridDefaults.DEFAULT_GRID_COLUMNS - + startColumns - + centerColumns - + endColumns; const fillWidgetLength: number = isMobile - ? 64 + ? GridDefaults.DEFAULT_GRID_COLUMNS : availableColumns / fillChildren.length; for (const child of fillChildren) { if (child.align === FlexLayerAlignment.Start) { @@ -191,7 +262,7 @@ function getIndividualAlignmentInfo( } function getAlignmentSizeInfo( - arr: { alignment: FlexLayerAlignment; columns: number }[], + arr: AlignmentInfo[], isMobile: boolean, ): { startSize: number; centerSize: number; endSize: number } { let startSize = 0, @@ -200,7 +271,7 @@ function getAlignmentSizeInfo( const sizes: { alignment: FlexLayerAlignment; columns: number; - }[] = getAlignmentSizes(arr, 64, []); + }[] = getAlignmentSizes(arr, GridDefaults.DEFAULT_GRID_COLUMNS, []); for (const each of sizes) { if (each.alignment === FlexLayerAlignment.Start) { @@ -215,10 +286,10 @@ function getAlignmentSizeInfo( } function getWrappedAlignmentSize( - arr: { alignment: FlexLayerAlignment; columns: number }[], - res: { alignment: FlexLayerAlignment; columns: number }[][] = [[], [], []], + arr: AlignmentInfo[], + res: AlignmentInfo[][] = [[], [], []], resIndex = 0, -): { alignment: FlexLayerAlignment; columns: number }[][] { +): AlignmentInfo[][] { if (arr.length === 1) { res[resIndex].push(arr[0]); return res; @@ -226,7 +297,7 @@ function getWrappedAlignmentSize( let index = 0; let total = 0; for (const each of arr) { - if (total + each.columns >= 64) { + if (total + each.columns >= GridDefaults.DEFAULT_GRID_COLUMNS) { let x = index; if (!res[resIndex].length) { res[resIndex].push(each); @@ -241,26 +312,232 @@ function getWrappedAlignmentSize( return res; } -function getRightColumn(widget: any, isMobile: boolean): number { +export function getRightColumn(widget: any, isMobile: boolean): number { return isMobile && widget.mobileRightColumn !== undefined ? widget.mobileRightColumn : widget.rightColumn; } -function setRightColumn(widget: any, val: number, isMobile: boolean): any { +export function setRightColumn( + widget: any, + val: number, + isMobile: boolean, +): any { return isMobile && widget.mobileRightColumn !== undefined ? { ...widget, mobileRightColumn: val } : { ...widget, rightColumn: val }; } -function getLeftColumn(widget: any, isMobile: boolean): number { +export function getLeftColumn(widget: any, isMobile: boolean): number { return isMobile && widget.mobileLeftColumn !== undefined ? widget.mobileLeftColumn : widget.leftColumn; } -function setLeftColumn(widget: any, val: number, isMobile: boolean): any { +export function setLeftColumn( + widget: any, + val: number, + isMobile: boolean, +): any { return isMobile && widget.mobileLeftColumn !== undefined ? { ...widget, mobileLeftColumn: val } : { ...widget, leftColumn: val }; } + +export function setColumns( + widget: any, + left: number, + right: number, + isMobile: boolean, +) { + return setRightColumn(setLeftColumn(widget, left, isMobile), right, isMobile); +} + +function updatePositionsForFlexWrap(data: { + allWidgets: CanvasWidgetsReduxState; + topRow: number; + arr: AlignmentInfo[]; + centerChildren: Widget[]; + centerColumns: number; + endChildren: Widget[]; + endColumns: number; + fillWidgetLength: number; + startChildren: Widget[]; + isMobile: boolean; +}): { height: number; widgets: CanvasWidgetsReduxState } { + const { + allWidgets, + arr, + centerChildren, + centerColumns, + endChildren, + endColumns, + fillWidgetLength, + isMobile, + startChildren, + topRow, + } = data; + let widgets = { ...allWidgets }; + + /** + * Find the order in which the alignments are wrapped. + * if res.length === 1 => no wrapping. + * else for each row, fit in widgets in GridDefaults.DEFAULT_GRID_COLUMNS columns + */ + const wrappedAlignments: AlignmentInfo[][] = getWrappedAlignmentSize(arr); + + let top = topRow; + for (const each of wrappedAlignments) { + if (!each.length) break; + const payload = + each.length === 1 + ? placeWrappedWidgets(widgets, each, top, fillWidgetLength, isMobile) + : placeWidgetsWithoutWrap( + widgets, + each, + top, + startChildren, + centerChildren, + endChildren, + centerColumns, + endColumns, + fillWidgetLength, + isMobile, + ); + widgets = payload.widgets; + top += payload.height; + continue; + } + return { height: top - topRow, widgets }; +} + +function getStartingPosition( + alignment: FlexLayerAlignment, + startSize: number, + centerSize: number, + endSize: number, + centerColumns: number, + endColumns: number, +): number { + if (alignment === FlexLayerAlignment.Start) { + return 0; + } else if (alignment === FlexLayerAlignment.Center) { + return startSize + centerSize / 2 - centerColumns / 2; + } else if (alignment === FlexLayerAlignment.End) { + return startSize + centerSize + endSize - endColumns; + } + return 0; +} + +function placeWrappedWidgets( + allWidgets: CanvasWidgetsReduxState, + arr: AlignmentInfo[], + topRow: number, + fillWidgetLength: number, + isMobile = false, +): { height: number; widgets: CanvasWidgetsReduxState } { + let widgets = { ...allWidgets }; + /** + * arr could contain multiple alignments. + * More rows are needed only if it contains a single alignment, + * and it needs more than GridDefaults.DEFAULT_GRID_COLUMNS columns. + */ + let startRow = topRow; + if (arr.length === 1) { + // wrapped alignment + const rows: Row[] = getWrappedRows(arr[0], [], isMobile); + for (const row of rows) { + const { alignment, children, columns, height } = row; + const { centerSize, endSize, startSize } = getAlignmentSizeInfo( + [{ alignment, children, columns }], + isMobile, + ); + let left: number = getStartingPosition( + alignment, + startSize, + centerSize, + endSize, + alignment === FlexLayerAlignment.Center ? columns : 0, + alignment === FlexLayerAlignment.End ? columns : 0, + ); + for (const child of children) { + const height = child.bottomRow - child.topRow; + const width = + child.responsiveBehavior === ResponsiveBehavior.Fill + ? fillWidgetLength + : getRightColumn(child, isMobile) - getLeftColumn(child, isMobile); + const widgetAfterColumnUpdate = setColumns( + child, + left, + left + width, + isMobile, + ); + widgets = { + ...widgets, + [child.widgetId]: { + ...widgetAfterColumnUpdate, + topRow: startRow, + bottomRow: startRow + height, + }, + }; + left += width; + } + startRow += height; + } + } + return { height: startRow - topRow, widgets }; +} + +interface Row { + alignment: FlexLayerAlignment; + children: Widget[]; + columns: number; + height: number; +} + +function getWrappedRows( + arr: AlignmentInfo, + rows: Row[], + isMobile = false, +): Row[] { + const row: Row = { + alignment: arr.alignment, + children: [], + columns: 0, + height: 0, + }; + const space = GridDefaults.DEFAULT_GRID_COLUMNS; + const temp: Widget[] = []; + let columns = 0, + index = 0, + maxHeight = 0; + for (const child of arr.children) { + const width = + getRightColumn(child, isMobile) - getLeftColumn(child, isMobile); + if (columns + width > space) { + row.children.push(...temp); + row.height = maxHeight; + row.columns = columns; + rows.push(row); + return getWrappedRows( + { + ...arr, + children: [...arr.children.slice(index)], + }, + [...rows], + isMobile, + ); + } + temp.push(child); + maxHeight = Math.max(maxHeight, child.bottomRow - child.topRow); + columns += width; + index += 1; + } + if (temp.length) { + row.children.push(...temp); + row.height = maxHeight; + row.columns = columns; + rows.push(row); + } + return rows; +} diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index d6be90f1a30a..ec26369fbb10 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -242,6 +242,7 @@ abstract class BaseWidget< this.props.bottomRow, this.props.parentColumnSpace, this.props.parentRowSpace, + this.props.mobileLeftColumn, this.props.mobileRightColumn, this.props.isMobile, ); @@ -254,6 +255,7 @@ abstract class BaseWidget< bottomRow: number, parentColumnSpace: number, parentRowSpace: number, + mobileLeftColumn?: number, mobileRightColumn?: number, isMobile?: boolean, ): { @@ -264,8 +266,12 @@ abstract class BaseWidget< isMobile && mobileRightColumn && parentColumnSpace !== 1 ? mobileRightColumn : rightColumn; + const left = + isMobile && mobileLeftColumn && parentColumnSpace !== 1 + ? mobileLeftColumn + : leftColumn; return { - componentWidth: (right - leftColumn) * parentColumnSpace, + componentWidth: (right - left) * parentColumnSpace, componentHeight: (bottomRow - topRow) * parentRowSpace, }; } @@ -604,6 +610,7 @@ export type WidgetRowCols = { topRow: number; bottomRow: number; minHeight?: number; // Required to reduce the size of CanvasWidgets. + mobileLeftColumn?: number; mobileRightColumn?: number; height?: number; }; From f3cec96f31886b8c5525c6b46070633e5d6a159c Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 28 Dec 2022 23:40:58 -0500 Subject: [PATCH 316/708] remove updateSize function from sagas --- app/client/src/sagas/AutoLayoutUtils.ts | 5 ++--- .../CanvasSagas/AutoLayoutDraggingSagas.ts | 21 ++++--------------- 2 files changed, 6 insertions(+), 20 deletions(-) diff --git a/app/client/src/sagas/AutoLayoutUtils.ts b/app/client/src/sagas/AutoLayoutUtils.ts index f8b427c0c97c..45a7c86cd285 100644 --- a/app/client/src/sagas/AutoLayoutUtils.ts +++ b/app/client/src/sagas/AutoLayoutUtils.ts @@ -61,7 +61,7 @@ export function* wrapChildren( canvas = { ...canvas, flexLayers }; widgets[canvas.widgetId] = canvas; // update size - const updatedWidgets = updateSizeOfAllChildren(widgets, canvas.widgetId); + const updatedWidgets = updateWidgetPositions(widgets, canvas.widgetId); return updatedWidgets; } @@ -303,11 +303,10 @@ export function alterLayoutForDesktop( return widgets; if (!children || !children.length) return widgets; - widgets = updateSizeOfAllChildren(widgets, parentId); + widgets = updateWidgetPositions(widgets, parentId, false); for (const child of children) { widgets = alterLayoutForDesktop(widgets, child); } - widgets = updateWidgetPositions(widgets, parentId, false); return widgets; } diff --git a/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts b/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts index f2f12dda9a05..e931cedc4517 100644 --- a/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts +++ b/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts @@ -18,10 +18,7 @@ import log from "loglevel"; import { HighlightInfo } from "pages/common/CanvasArenas/hooks/useAutoLayoutHighlights"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; import { all, call, put, select, takeLatest } from "redux-saga/effects"; -import { - updateFlexChildColumns, - updateSizeOfAllChildren, -} from "sagas/AutoLayoutUtils"; +import { updateFlexChildColumns } from "sagas/AutoLayoutUtils"; import { getWidgets } from "sagas/selectors"; import { getUpdateDslAfterCreatingChild } from "sagas/WidgetAdditionSagas"; import { updateWidgetPositions } from "utils/autoLayout/positionUtils"; @@ -115,14 +112,8 @@ function* autoLayoutReorderSaga( rowIndex, }, ); - let updatedWidgetsAfterResizing = updatedWidgets; - if (direction === LayoutDirection.Vertical) - updatedWidgetsAfterResizing = updateSizeOfAllChildren( - updatedWidgets, - parentId, - ); - yield put(updateAndSaveLayout(updatedWidgetsAfterResizing)); + yield put(updateAndSaveLayout(updatedWidgets)); log.debug("reorder computations took", performance.now() - start, "ms"); } catch (e) { // console.error(e); @@ -286,12 +277,8 @@ function updateRelationships( } if (prevParents.length) { for (const id of prevParents) { - const updatedWidgets = updateSizeOfAllChildren(widgets, id); - const updatedWidgetsAfterPositionCalculation = updateWidgetPositions( - updatedWidgets, - id, - ); - return updatedWidgetsAfterPositionCalculation; + const updatedWidgets = updateWidgetPositions(widgets, id); + return updatedWidgets; } } return widgets; From b536927db71184a527cf6aecae50bae5aa9401a7 Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 28 Dec 2022 23:46:13 -0500 Subject: [PATCH 317/708] add Todos --- app/client/src/sagas/AutoLayoutUtils.ts | 2 +- app/client/src/utils/autoLayout/highlightUtils.ts | 2 +- app/client/src/utils/autoLayout/positionUtils.ts | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/client/src/sagas/AutoLayoutUtils.ts b/app/client/src/sagas/AutoLayoutUtils.ts index 45a7c86cd285..9c9c5c6bf6ff 100644 --- a/app/client/src/sagas/AutoLayoutUtils.ts +++ b/app/client/src/sagas/AutoLayoutUtils.ts @@ -279,7 +279,7 @@ export function alterLayoutForMobile( ); } } - + // TODO: Preet - update container row info if height changes on account of flex wrap. widgets = alterLayoutForMobile( widgets, child, diff --git a/app/client/src/utils/autoLayout/highlightUtils.ts b/app/client/src/utils/autoLayout/highlightUtils.ts index 5622add8b247..8b3d065b3015 100644 --- a/app/client/src/utils/autoLayout/highlightUtils.ts +++ b/app/client/src/utils/autoLayout/highlightUtils.ts @@ -15,7 +15,7 @@ import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsRe import { getLeftColumn, getRightColumn } from "./positionUtils"; const HORIZONTAL_HIGHLIGHT_MARGIN = 4; - +// TODO: Preet - update logic to account for flex wrap on mobile. /** * @param allWidgets : CanvasWidgetsReduxState * @param canvasId : string diff --git a/app/client/src/utils/autoLayout/positionUtils.ts b/app/client/src/utils/autoLayout/positionUtils.ts index fb9f184c31f5..e5a98914eef8 100644 --- a/app/client/src/utils/autoLayout/positionUtils.ts +++ b/app/client/src/utils/autoLayout/positionUtils.ts @@ -112,6 +112,7 @@ function calculateWidgetPositions( ); } +// TODO: Preet - abstract this method to use a single function for all usecases. function placeWidgetsWithoutWrap( allWidgets: CanvasWidgetsReduxState, arr: AlignmentInfo[], From a0d2e5ecefc2cae8988af0bf0061d8bcce9c70d8 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Thu, 29 Dec 2022 12:44:42 +0530 Subject: [PATCH 318/708] replacing padding with margin to allow access to underlying canvas --- .../appsmith/autoLayout/FlexComponent.tsx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx index 1368ca115d6f..95d3c0f3c821 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx @@ -1,6 +1,7 @@ import React, { CSSProperties, ReactNode, useCallback, useMemo } from "react"; import styled from "styled-components"; +import { AppState } from "ce/reducers"; import { FlexVerticalAlignment, LayoutDirection, @@ -14,11 +15,10 @@ import { import { useSelector } from "react-redux"; import { snipingModeSelector } from "selectors/editorSelectors"; import { getIsMobile } from "selectors/mainCanvasSelectors"; +import { isWidgetSelected } from "selectors/widgetSelectors"; import { useClickToSelectWidget } from "utils/hooks/useClickToSelectWidget"; import { usePositionedContainerZIndex } from "utils/hooks/usePositionedContainerZIndex"; import { checkIsDropTarget } from "../PositionedContainer"; -import { isWidgetSelected } from "selectors/widgetSelectors"; -import { AppState } from "ce/reducers"; export type AutoLayoutProps = { children: ReactNode; @@ -80,10 +80,12 @@ export function FlexComponent(props: AutoLayoutProps) { return { display: isSelected && isDragging ? "none" : "flex", zIndex, - width: `${Math.floor(props.componentWidth)}px`, - height: isMobile ? "auto" : Math.floor(props.componentHeight) + "px", + width: `${Math.floor(props.componentWidth) - WIDGET_PADDING * 2}px`, + height: isMobile + ? "auto" + : Math.floor(props.componentHeight) - WIDGET_PADDING * 2 + "px", minHeight: "30px", - padding: WIDGET_PADDING + "px", + margin: WIDGET_PADDING + "px", flexGrow: isFillWidget ? 1 : 0, alignSelf: props.flexVerticalAlignment, "&:hover": { From d472e71f95072816811eff9bd3acf97d69fd1bb2 Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 29 Dec 2022 12:13:00 -0500 Subject: [PATCH 319/708] fix highlight position on scroll --- .../pages/common/CanvasArenas/hooks/useCanvasDragging.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index 757f84ea8a0a..0f538e849000 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -705,7 +705,13 @@ export const useCanvasDragging = ( if (highlight) { canvasCtx.fillStyle = "rgba(196, 139, 181, 1)"; const { height, posX, posY, width } = highlight; - canvasCtx.fillRect(posX, posY, width, height); + const scrollTop = scrollParent?.scrollTop || 0; + canvasCtx.fillRect( + posX, + posY - scrollTop + startPoints?.top, + width, + height, + ); canvasCtx.save(); } canvasCtx.restore(); From df30d49224b924c7bbe0c3fff303a320e674fed4 Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 29 Dec 2022 12:48:59 -0500 Subject: [PATCH 320/708] fix highlight position --- .../common/CanvasArenas/hooks/useCanvasDragging.ts | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index 0f538e849000..0ce9eae73c93 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -705,13 +705,10 @@ export const useCanvasDragging = ( if (highlight) { canvasCtx.fillStyle = "rgba(196, 139, 181, 1)"; const { height, posX, posY, width } = highlight; - const scrollTop = scrollParent?.scrollTop || 0; - canvasCtx.fillRect( - posX, - posY - scrollTop + startPoints?.top, - width, - height, - ); + let val = 0; + if (scrollParent?.scrollTop) + val = scrollParent.scrollTop - startPoints?.top; + canvasCtx.fillRect(posX, posY - val, width, height); canvasCtx.save(); } canvasCtx.restore(); From 2aed287064b9ee2bae17966e4ddeaa2b110c639e Mon Sep 17 00:00:00 2001 From: Preet Date: Fri, 30 Dec 2022 08:37:08 -0500 Subject: [PATCH 321/708] clean up --- .../src/utils/autoLayout/flexWidgetUtils.ts | 100 ++++++++++++++++++ .../src/utils/autoLayout/positionUtils.ts | 90 +++++----------- 2 files changed, 124 insertions(+), 66 deletions(-) create mode 100644 app/client/src/utils/autoLayout/flexWidgetUtils.ts diff --git a/app/client/src/utils/autoLayout/flexWidgetUtils.ts b/app/client/src/utils/autoLayout/flexWidgetUtils.ts new file mode 100644 index 000000000000..56440090d5d3 --- /dev/null +++ b/app/client/src/utils/autoLayout/flexWidgetUtils.ts @@ -0,0 +1,100 @@ +export function getRightColumn(widget: any, isMobile: boolean): number { + return isMobile && widget.mobileRightColumn !== undefined + ? widget.mobileRightColumn + : widget.rightColumn; +} + +export function setRightColumn( + widget: any, + val: number, + isMobile: boolean, +): any { + if (isNaN(val)) return widget; + return isMobile && widget.mobileRightColumn !== undefined + ? { ...widget, mobileRightColumn: val } + : { ...widget, rightColumn: val }; +} + +export function getLeftColumn(widget: any, isMobile: boolean): number { + return isMobile && widget.mobileLeftColumn !== undefined + ? widget.mobileLeftColumn + : widget.leftColumn; +} + +export function setLeftColumn( + widget: any, + val: number, + isMobile: boolean, +): any { + if (isNaN(val)) return widget; + return isMobile && widget.mobileLeftColumn !== undefined + ? { ...widget, mobileLeftColumn: val } + : { ...widget, leftColumn: val }; +} + +export function getTopRow(widget: any, isMobile: boolean): number { + return isMobile && widget.mobileTopRow !== undefined + ? widget.mobileTopRow + : widget.topRow; +} + +export function setTopRow(widget: any, val: number, isMobile: boolean): any { + if (isNaN(val)) return widget; + return isMobile && widget.mobileTopRow !== undefined + ? { ...widget, mobileTopRow: val } + : { ...widget, topRow: val }; +} + +export function getBottomRow(widget: any, isMobile: boolean): number { + return isMobile && widget.mobileBottomRow !== undefined + ? widget.mobileBottomRow + : widget.bottomRow; +} + +export function setBottomRow(widget: any, val: number, isMobile: boolean): any { + if (isNaN(val)) return widget; + return isMobile && widget.mobileBottomRow !== undefined + ? { ...widget, mobileBottomRow: val } + : { ...widget, bottomRow: val }; +} + +export function setColumns( + widget: any, + left: number, + right: number, + isMobile: boolean, +) { + return setRightColumn(setLeftColumn(widget, left, isMobile), right, isMobile); +} + +export function setDimensions( + widget: any, + top: number, + bottom: number, + left: number, + right: number, + isMobile: boolean, +) { + try { + return setBottomRow( + setTopRow( + setLeftColumn(setRightColumn(widget, right, isMobile), left, isMobile), + top, + isMobile, + ), + bottom, + isMobile, + ); + } catch (e) { + console.log(e); + return widget; + } +} + +export function getWidgetWidth(widget: any, isMobile: boolean): number { + return getRightColumn(widget, isMobile) - getLeftColumn(widget, isMobile); +} + +export function getWidgetHeight(widget: any, isMobile: boolean): number { + return getBottomRow(widget, isMobile) - getTopRow(widget, isMobile); +} diff --git a/app/client/src/utils/autoLayout/positionUtils.ts b/app/client/src/utils/autoLayout/positionUtils.ts index e5a98914eef8..638b42c1f030 100644 --- a/app/client/src/utils/autoLayout/positionUtils.ts +++ b/app/client/src/utils/autoLayout/positionUtils.ts @@ -1,9 +1,15 @@ import { FlexLayerAlignment, ResponsiveBehavior } from "components/constants"; import { FlexLayer } from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; import { GridDefaults } from "constants/WidgetConstants"; -import { isMobile } from "react-device-detect"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; import { WidgetProps } from "widgets/BaseWidget"; +import { + getLeftColumn, + getRightColumn, + getWidgetHeight, + getWidgetWidth, + setDimensions, +} from "./flexWidgetUtils"; type Widget = WidgetProps & { children?: string[] | undefined; @@ -153,18 +159,18 @@ function placeWidgetsWithoutWrap( ? fillWidgetLength : getRightColumn(widget, isMobile) - getLeftColumn(widget, isMobile); maxHeight = Math.max(maxHeight, height); - const widgetAfterLeftUpdate = setLeftColumn(widget, left, isMobile); - const widgetAfterRightUpdate = setRightColumn( - widgetAfterLeftUpdate, + const updatedWidget = setDimensions( + widget, + topRow, + topRow + height, + left, left + width, isMobile, ); widgets = { ...widgets, [widget.widgetId]: { - ...widgetAfterRightUpdate, - topRow, - bottomRow: topRow + height, + ...updatedWidget, }, }; left += width; @@ -216,19 +222,13 @@ function getIndividualAlignmentInfo( if (isFillWidget) fillChildren.push(child); if (child.align === FlexLayerAlignment.Start) { startChildren.push(widget); - if (!isFillWidget) - startColumns += - getRightColumn(widget, isMobile) - getLeftColumn(widget, isMobile); + if (!isFillWidget) startColumns += getWidgetWidth(widget, isMobile); } else if (child.align === FlexLayerAlignment.Center) { centerChildren.push(widget); - if (!isFillWidget) - centerColumns += - getRightColumn(widget, isMobile) - getLeftColumn(widget, isMobile); + if (!isFillWidget) centerColumns += getWidgetWidth(widget, isMobile); } else if (child.align === FlexLayerAlignment.End) { endChildren.push(widget); - if (!isFillWidget) - endColumns += - getRightColumn(widget, isMobile) - getLeftColumn(widget, isMobile); + if (!isFillWidget) endColumns += getWidgetWidth(widget, isMobile); } } @@ -313,47 +313,6 @@ function getWrappedAlignmentSize( return res; } -export function getRightColumn(widget: any, isMobile: boolean): number { - return isMobile && widget.mobileRightColumn !== undefined - ? widget.mobileRightColumn - : widget.rightColumn; -} - -export function setRightColumn( - widget: any, - val: number, - isMobile: boolean, -): any { - return isMobile && widget.mobileRightColumn !== undefined - ? { ...widget, mobileRightColumn: val } - : { ...widget, rightColumn: val }; -} - -export function getLeftColumn(widget: any, isMobile: boolean): number { - return isMobile && widget.mobileLeftColumn !== undefined - ? widget.mobileLeftColumn - : widget.leftColumn; -} - -export function setLeftColumn( - widget: any, - val: number, - isMobile: boolean, -): any { - return isMobile && widget.mobileLeftColumn !== undefined - ? { ...widget, mobileLeftColumn: val } - : { ...widget, leftColumn: val }; -} - -export function setColumns( - widget: any, - left: number, - right: number, - isMobile: boolean, -) { - return setRightColumn(setLeftColumn(widget, left, isMobile), right, isMobile); -} - function updatePositionsForFlexWrap(data: { allWidgets: CanvasWidgetsReduxState; topRow: number; @@ -462,13 +421,15 @@ function placeWrappedWidgets( alignment === FlexLayerAlignment.End ? columns : 0, ); for (const child of children) { - const height = child.bottomRow - child.topRow; + const height = getWidgetHeight(child, isMobile); const width = child.responsiveBehavior === ResponsiveBehavior.Fill ? fillWidgetLength - : getRightColumn(child, isMobile) - getLeftColumn(child, isMobile); - const widgetAfterColumnUpdate = setColumns( + : getWidgetWidth(child, isMobile); + const updatedWidget = setDimensions( child, + startRow, + startRow + height, left, left + width, isMobile, @@ -476,9 +437,7 @@ function placeWrappedWidgets( widgets = { ...widgets, [child.widgetId]: { - ...widgetAfterColumnUpdate, - topRow: startRow, - bottomRow: startRow + height, + ...updatedWidget, }, }; left += width; @@ -513,8 +472,7 @@ function getWrappedRows( index = 0, maxHeight = 0; for (const child of arr.children) { - const width = - getRightColumn(child, isMobile) - getLeftColumn(child, isMobile); + const width = getWidgetWidth(child, isMobile); if (columns + width > space) { row.children.push(...temp); row.height = maxHeight; @@ -530,7 +488,7 @@ function getWrappedRows( ); } temp.push(child); - maxHeight = Math.max(maxHeight, child.bottomRow - child.topRow); + maxHeight = Math.max(maxHeight, getWidgetHeight(child, isMobile)); columns += width; index += 1; } From c0b3867223ebc92aa121ac8f9aa60a70b5604ff1 Mon Sep 17 00:00:00 2001 From: Preet Date: Fri, 30 Dec 2022 12:33:15 -0500 Subject: [PATCH 322/708] update parent height to accomodate children --- .../src/resizable/resizenreflow/index.tsx | 2 +- app/client/src/sagas/AutoLayoutUtils.ts | 2 + .../src/utils/autoLayout/flexWidgetUtils.ts | 32 ++++--- .../src/utils/autoLayout/highlightUtils.ts | 23 +++-- .../src/utils/autoLayout/positionUtils.ts | 86 ++++++++++++++++--- app/client/src/widgets/BaseWidget.tsx | 20 ++++- 6 files changed, 125 insertions(+), 40 deletions(-) diff --git a/app/client/src/resizable/resizenreflow/index.tsx b/app/client/src/resizable/resizenreflow/index.tsx index 7c173be00c12..e507d546c902 100644 --- a/app/client/src/resizable/resizenreflow/index.tsx +++ b/app/client/src/resizable/resizenreflow/index.tsx @@ -541,7 +541,7 @@ export function ReflowResizable(props: ResizableProps) { immediate={newDimensions.reset ? true : false} to={{ width: widgetWidth, - height: props.isMobile ? "auto" : widgetHeight, + height: widgetHeight, transform: `translate3d(${newDimensions.x}px,${newDimensions.y}px,0)`, }} > diff --git a/app/client/src/sagas/AutoLayoutUtils.ts b/app/client/src/sagas/AutoLayoutUtils.ts index 9c9c5c6bf6ff..f071ac11d24a 100644 --- a/app/client/src/sagas/AutoLayoutUtils.ts +++ b/app/client/src/sagas/AutoLayoutUtils.ts @@ -279,6 +279,8 @@ export function alterLayoutForMobile( ); } } + widget.mobileTopRow = widget.topRow; + widget.mobileBottomRow = widget.bottomRow; // TODO: Preet - update container row info if height changes on account of flex wrap. widgets = alterLayoutForMobile( widgets, diff --git a/app/client/src/utils/autoLayout/flexWidgetUtils.ts b/app/client/src/utils/autoLayout/flexWidgetUtils.ts index 56440090d5d3..f86609d9fa4e 100644 --- a/app/client/src/utils/autoLayout/flexWidgetUtils.ts +++ b/app/client/src/utils/autoLayout/flexWidgetUtils.ts @@ -6,10 +6,10 @@ export function getRightColumn(widget: any, isMobile: boolean): number { export function setRightColumn( widget: any, - val: number, + val: number | null, isMobile: boolean, ): any { - if (isNaN(val)) return widget; + if (val === null) return widget; return isMobile && widget.mobileRightColumn !== undefined ? { ...widget, mobileRightColumn: val } : { ...widget, rightColumn: val }; @@ -23,10 +23,10 @@ export function getLeftColumn(widget: any, isMobile: boolean): number { export function setLeftColumn( widget: any, - val: number, + val: number | null, isMobile: boolean, ): any { - if (isNaN(val)) return widget; + if (val === null) return widget; return isMobile && widget.mobileLeftColumn !== undefined ? { ...widget, mobileLeftColumn: val } : { ...widget, leftColumn: val }; @@ -38,8 +38,12 @@ export function getTopRow(widget: any, isMobile: boolean): number { : widget.topRow; } -export function setTopRow(widget: any, val: number, isMobile: boolean): any { - if (isNaN(val)) return widget; +export function setTopRow( + widget: any, + val: number | null, + isMobile: boolean, +): any { + if (val === null) return widget; return isMobile && widget.mobileTopRow !== undefined ? { ...widget, mobileTopRow: val } : { ...widget, topRow: val }; @@ -51,8 +55,12 @@ export function getBottomRow(widget: any, isMobile: boolean): number { : widget.bottomRow; } -export function setBottomRow(widget: any, val: number, isMobile: boolean): any { - if (isNaN(val)) return widget; +export function setBottomRow( + widget: any, + val: number | null, + isMobile: boolean, +): any { + if (val === null) return widget; return isMobile && widget.mobileBottomRow !== undefined ? { ...widget, mobileBottomRow: val } : { ...widget, bottomRow: val }; @@ -69,10 +77,10 @@ export function setColumns( export function setDimensions( widget: any, - top: number, - bottom: number, - left: number, - right: number, + top: number | null, + bottom: number | null, + left: number | null, + right: number | null, isMobile: boolean, ) { try { diff --git a/app/client/src/utils/autoLayout/highlightUtils.ts b/app/client/src/utils/autoLayout/highlightUtils.ts index 8b3d065b3015..59e03437a20e 100644 --- a/app/client/src/utils/autoLayout/highlightUtils.ts +++ b/app/client/src/utils/autoLayout/highlightUtils.ts @@ -12,7 +12,11 @@ import { } from "constants/WidgetConstants"; import { HighlightInfo } from "pages/common/CanvasArenas/hooks/useAutoLayoutHighlights"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; -import { getLeftColumn, getRightColumn } from "./positionUtils"; +import { + getLeftColumn, + getWidgetHeight, + getWidgetWidth, +} from "./flexWidgetUtils"; const HORIZONTAL_HIGHLIGHT_MARGIN = 4; // TODO: Preet - update logic to account for flex wrap on mobile. @@ -43,7 +47,6 @@ export function deriveHighlightsFromLayers( mainCanvasWidth, isMobile, ); - const layers: FlexLayer[] = canvas.flexLayers || []; const highlights: HighlightInfo[] = []; let childCount = 0; @@ -63,7 +66,7 @@ export function deriveHighlightsFromLayers( const widget = widgets[child.id]; return Math.max( acc, - (widget.bottomRow - widget.topRow) * widget.parentRowSpace, + getWidgetHeight(widget, isMobile) * widget.parentRowSpace, ); }, 0); @@ -167,14 +170,12 @@ function generateVerticalHighlights(data: { if (draggedWidgets.indexOf(child.id) > -1) continue; if (child.align === FlexLayerAlignment.End) { endChildren.push(widget); - endColumns += - getRightColumn(widget, isMobile) - getLeftColumn(widget, isMobile); + endColumns += getWidgetWidth(widget, isMobile); } else if (child.align === FlexLayerAlignment.Center) { centerChildren.push(widget); } else { startChildren.push(widget); - startColumns += - getRightColumn(widget, isMobile) - getLeftColumn(widget, isMobile); + startColumns += getWidgetWidth(widget, isMobile); } } @@ -303,7 +304,7 @@ function getPositionForInitialHighlight( containerWidth: number, ): number { if (alignment === FlexLayerAlignment.End) { - return containerWidth - 6; + return containerWidth; } else if (alignment === FlexLayerAlignment.Center) { if (!highlights.length) return containerWidth / 2; return posX; @@ -393,12 +394,10 @@ function getCanvasWidth( if (!mainCanvasWidth) return 0; if (canvas.widgetId === MAIN_CONTAINER_WIDGET_ID) return mainCanvasWidth; let widget = canvas; - let columns = - getRightColumn(widget, isMobile) - getLeftColumn(widget, isMobile); + let columns = getWidgetWidth(widget, isMobile); let width = columns / GridDefaults.DEFAULT_GRID_COLUMNS; while (widget.widgetId !== MAIN_CONTAINER_WIDGET_ID) { - columns = - getRightColumn(widget, isMobile) - getLeftColumn(widget, isMobile); + columns = getWidgetWidth(widget, isMobile); width *= columns / GridDefaults.DEFAULT_GRID_COLUMNS; widget = widgets[widget.parentId]; } diff --git a/app/client/src/utils/autoLayout/positionUtils.ts b/app/client/src/utils/autoLayout/positionUtils.ts index 638b42c1f030..dafdfc772321 100644 --- a/app/client/src/utils/autoLayout/positionUtils.ts +++ b/app/client/src/utils/autoLayout/positionUtils.ts @@ -1,17 +1,22 @@ import { FlexLayerAlignment, ResponsiveBehavior } from "components/constants"; import { FlexLayer } from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; -import { GridDefaults } from "constants/WidgetConstants"; +import { + GridDefaults, + MAIN_CONTAINER_WIDGET_ID, +} from "constants/WidgetConstants"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; import { WidgetProps } from "widgets/BaseWidget"; import { + getBottomRow, getLeftColumn, getRightColumn, + getTopRow, getWidgetHeight, getWidgetWidth, setDimensions, } from "./flexWidgetUtils"; -type Widget = WidgetProps & { +export type Widget = WidgetProps & { children?: string[] | undefined; }; @@ -23,27 +28,67 @@ interface AlignmentInfo { /** * Calculate widget position on canvas. + * Logic - + * 1. If widget contains flexLayers, then update positions for all widgets in layers. + * 2. Else if widget contains children ( implies fixed canvas), calculate the total height consumed by children. + * 3. If totalChildrenHeight in either case > widgetHeight and widget.parent.type === ContainerWidget || MainContainer, + * then update height of the widget and its parent. */ export function updateWidgetPositions( allWidgets: CanvasWidgetsReduxState, parentId: string, - isMobile?: boolean, + isMobile = false, ): CanvasWidgetsReduxState { let widgets = { ...allWidgets }; try { const parent = widgets[parentId]; - if (!parent || !parent.flexLayers || !parent.flexLayers?.length) - return widgets; + if (!parent) return widgets; let height = 0; - for (const layer of parent.flexLayers) { - const payload: { - height: number; - widgets: CanvasWidgetsReduxState; - } = calculateWidgetPositions(widgets, layer, height, isMobile); - widgets = payload.widgets; - height += payload.height; + if (parent.flexLayers && parent.flexLayers?.length) { + for (const layer of parent.flexLayers) { + const payload: { + height: number; + widgets: CanvasWidgetsReduxState; + } = calculateWidgetPositions(widgets, layer, height, isMobile); + widgets = payload.widgets; + height += payload.height; + } + } else if (parent.children?.length) { + let top = 10000, + bottom = 0; + for (const childId of parent.children) { + const child = widgets[childId]; + if (!child) continue; + const divisor = child.parentRowSpace === 1 ? 10 : 1; + top = Math.min(top, getTopRow(child, isMobile)); + bottom = Math.max(bottom, getBottomRow(child, isMobile) / divisor); + } + height = bottom - top; } + const divisor = parent.parentRowSpace === 1 ? 10 : 1; + const parentHeight = getWidgetHeight(parent, isMobile) / divisor; + if (parentHeight <= height) { + const parentTopRow = getTopRow(parent, isMobile); + const updatedParent = setDimensions( + parent, + parentTopRow, + (parentTopRow + height + 1) * divisor, + null, + null, + isMobile, + ); + widgets = { ...widgets, [parent.widgetId]: updatedParent }; + } + const shouldUpdateHeight = + parent.parentId && + ["CONTAINER_WIDGET", MAIN_CONTAINER_WIDGET_ID].includes( + allWidgets[parent.parentId].type, + ) && + parentHeight <= height; + + if (shouldUpdateHeight && parent.parentId) + return updateWidgetPositions(widgets, parent.parentId, isMobile); return widgets; } catch (e) { console.error(e); @@ -500,3 +545,20 @@ function getWrappedRows( } return rows; } + +// function updateHeight( +// allWidgets: CanvasWidgetsReduxState, +// widgetId: string, +// height: number, +// isMobile, +// ): void { +// const widgets = { ...allWidgets }; +// const widget = widgets[widgetId]; +// const children = widget.children; +// if (!children || !children?.length) return widgets; +// let maxHeight = 0; +// for (const child of children) { +// const childHeight = getWidgetHeight(child, isMobile); +// if (childHeight > maxHeight) maxHeight = childHeight; +// } +// } diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index ec26369fbb10..a5d33c781188 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -244,6 +244,8 @@ abstract class BaseWidget< this.props.parentRowSpace, this.props.mobileLeftColumn, this.props.mobileRightColumn, + this.props.mobileTopRow, + this.props.mobileBottomRow, this.props.isMobile, ); }; @@ -257,22 +259,32 @@ abstract class BaseWidget< parentRowSpace: number, mobileLeftColumn?: number, mobileRightColumn?: number, + mobileTopRow?: number, + mobileBottomRow?: number, isMobile?: boolean, ): { componentWidth: number; componentHeight: number; } { const right = - isMobile && mobileRightColumn && parentColumnSpace !== 1 + isMobile && mobileRightColumn !== undefined && parentColumnSpace !== 1 ? mobileRightColumn : rightColumn; const left = - isMobile && mobileLeftColumn && parentColumnSpace !== 1 + isMobile && mobileLeftColumn !== undefined && parentColumnSpace !== 1 ? mobileLeftColumn : leftColumn; + const top = + isMobile && mobileTopRow !== undefined && parentRowSpace !== 1 + ? mobileTopRow + : topRow; + const bottom = + isMobile && mobileBottomRow !== undefined && parentRowSpace !== 1 + ? mobileBottomRow + : bottomRow; return { componentWidth: (right - left) * parentColumnSpace, - componentHeight: (bottomRow - topRow) * parentRowSpace, + componentHeight: (bottom - top) * parentRowSpace, }; } @@ -612,6 +624,8 @@ export type WidgetRowCols = { minHeight?: number; // Required to reduce the size of CanvasWidgets. mobileLeftColumn?: number; mobileRightColumn?: number; + mobileTopRow?: number; + mobileBottomRow?: number; height?: number; }; From e32c8470de64a7767736dafefa2c17f417df453b Mon Sep 17 00:00:00 2001 From: Preet Date: Fri, 30 Dec 2022 15:56:36 -0500 Subject: [PATCH 323/708] refactor positionUtils --- .../src/utils/autoLayout/flexWidgetUtils.ts | 5 + .../src/utils/autoLayout/positionUtils.ts | 435 +++++++++--------- 2 files changed, 228 insertions(+), 212 deletions(-) diff --git a/app/client/src/utils/autoLayout/flexWidgetUtils.ts b/app/client/src/utils/autoLayout/flexWidgetUtils.ts index f86609d9fa4e..7f0bf3b7d103 100644 --- a/app/client/src/utils/autoLayout/flexWidgetUtils.ts +++ b/app/client/src/utils/autoLayout/flexWidgetUtils.ts @@ -106,3 +106,8 @@ export function getWidgetWidth(widget: any, isMobile: boolean): number { export function getWidgetHeight(widget: any, isMobile: boolean): number { return getBottomRow(widget, isMobile) - getTopRow(widget, isMobile); } + +export function getWidgetRows(widget: any, isMobile: boolean): number { + const divisor = widget.parentRowSpace === 1 ? 10 : 1; + return getBottomRow(widget, isMobile) / divisor - getTopRow(widget, isMobile); +} diff --git a/app/client/src/utils/autoLayout/positionUtils.ts b/app/client/src/utils/autoLayout/positionUtils.ts index dafdfc772321..252267cb4578 100644 --- a/app/client/src/utils/autoLayout/positionUtils.ts +++ b/app/client/src/utils/autoLayout/positionUtils.ts @@ -8,10 +8,9 @@ import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsRe import { WidgetProps } from "widgets/BaseWidget"; import { getBottomRow, - getLeftColumn, - getRightColumn, getTopRow, getWidgetHeight, + getWidgetRows, getWidgetWidth, setDimensions, } from "./flexWidgetUtils"; @@ -26,11 +25,15 @@ interface AlignmentInfo { children: Widget[]; } +interface Row extends AlignmentInfo { + height: number; +} + /** * Calculate widget position on canvas. * Logic - * 1. If widget contains flexLayers, then update positions for all widgets in layers. - * 2. Else if widget contains children ( implies fixed canvas), calculate the total height consumed by children. + * 2. Else if widget contains children (implies fixed canvas), calculate the total height assumed by children. * 3. If totalChildrenHeight in either case > widgetHeight and widget.parent.type === ContainerWidget || MainContainer, * then update height of the widget and its parent. */ @@ -46,6 +49,10 @@ export function updateWidgetPositions( let height = 0; if (parent.flexLayers && parent.flexLayers?.length) { + /** + * For each flex layer, calculate position of child widgets + * and calculate the total height of all layers. + */ for (const layer of parent.flexLayers) { const payload: { height: number; @@ -55,20 +62,18 @@ export function updateWidgetPositions( height += payload.height; } } else if (parent.children?.length) { - let top = 10000, - bottom = 0; - for (const childId of parent.children) { - const child = widgets[childId]; - if (!child) continue; - const divisor = child.parentRowSpace === 1 ? 10 : 1; - top = Math.min(top, getTopRow(child, isMobile)); - bottom = Math.max(bottom, getBottomRow(child, isMobile) / divisor); - } - height = bottom - top; + // calculate the total height required by all widgets. + height = getHeightOfFixedCanvas(widgets, parent, isMobile); } + const divisor = parent.parentRowSpace === 1 ? 10 : 1; - const parentHeight = getWidgetHeight(parent, isMobile) / divisor; + const parentHeight = getWidgetHeight(parent, isMobile); if (parentHeight <= height) { + /** + * if children height is greater than parent height, + * update the parent height to match the children height + * and add a buffer of 1 row to render the new layer highlights. + */ const parentTopRow = getTopRow(parent, isMobile); const updatedParent = setDimensions( parent, @@ -102,108 +107,86 @@ function calculateWidgetPositions( topRow: number, isMobile = false, ): { height: number; widgets: CanvasWidgetsReduxState } { - const { - centerChildren, - centerColumns, - endChildren, - endColumns, - fillWidgetLength, - startChildren, - startColumns, - } = getIndividualAlignmentInfo(allWidgets, layer, isMobile); - + /** + * Get information break down on each alignment within the layer. + * Information - children, columns, alignment. + * Also, retrieve the length of each fill widget within this layer. + */ + const { fillWidgetLength, info } = extractAlignmentInfo( + allWidgets, + layer, + isMobile, + ); + /** + * Check if this layer is wrapped by css flex. + * if isMobile && totalColumns > 64 => true + */ const isFlexWrapped: boolean = isMobile && - startColumns + centerColumns + endColumns > - GridDefaults.DEFAULT_GRID_COLUMNS; - - const arr: AlignmentInfo[] = [ - { - alignment: FlexLayerAlignment.Start, - columns: startColumns, - children: startChildren, - }, - { - alignment: FlexLayerAlignment.Center, - columns: centerColumns, - children: centerChildren, - }, - { - alignment: FlexLayerAlignment.End, - columns: endColumns, - children: endChildren, - }, - ]; + info.reduce((acc, curr) => { + return acc + curr.columns; + }, 0) > GridDefaults.DEFAULT_GRID_COLUMNS; if (isFlexWrapped) - return updatePositionsForFlexWrap({ + return updatePositionsForFlexWrap( allWidgets, + info, topRow, - arr, - centerChildren, - centerColumns, - endChildren, - endColumns, fillWidgetLength, - startChildren, isMobile, - }); + ); return placeWidgetsWithoutWrap( allWidgets, - arr, + info, topRow, - startChildren, - centerChildren, - endChildren, - centerColumns, - endColumns, fillWidgetLength, isMobile, ); } -// TODO: Preet - abstract this method to use a single function for all usecases. +/** + * Place all widgets in a particular row and update their positions. + * + * @param allWidgets | CanvasWidgetsReduxState : List of all widgets. + * @param arr | AlignmentInfo[] : Array of all alignments to be placed in this row. + * @param topRow | number : Starting row for placing the widgets. + * @param fillWidgetLength | number : Size of each fill widget in this row. + * @param isMobile : boolean : if the current viewport is mobile. default is false. + * @param totalHeight | number : total height assumed by the widgets in this row. + * @returns { height: number; widgets: CanvasWidgetsReduxState } + */ function placeWidgetsWithoutWrap( allWidgets: CanvasWidgetsReduxState, arr: AlignmentInfo[], topRow: number, - startChildren: Widget[], - centerChildren: Widget[], - endChildren: Widget[], - centerColumns: number, - endColumns: number, fillWidgetLength: number, isMobile = false, + totalHeight = 0, ): { height: number; widgets: CanvasWidgetsReduxState } { let widgets = { ...allWidgets }; - const { centerSize, endSize, startSize } = getAlignmentSizeInfo( - arr.sort((a, b) => b.columns - a.columns), - isMobile, - ); + /** + * Get the size (columns: number) of each alignment in this row. + */ + const { centerSize, endSize, startSize } = getAlignmentSizeInfo(arr); - let maxHeight = 0; - const input = []; - if (startSize) input.push({ children: startChildren, leftColumn: 0 }); - if (centerSize) - input.push({ - children: centerChildren, - leftColumn: startSize + centerSize / 2 - centerColumns / 2, - }); - if (endSize) - input.push({ - children: endChildren, - leftColumn: startSize + centerSize + endSize - endColumns, - }); - input.forEach((each) => { - let left = each.leftColumn; + let maxHeight = totalHeight ? totalHeight : 0; + arr.forEach((each: AlignmentInfo) => { + // Get the starting left column for each alignment in this row. + let left = getStartingPosition( + each.alignment, + startSize, + centerSize, + endSize, + each.columns, + ); for (const widget of each.children) { - const height = widget.bottomRow - widget.topRow; + const height = getWidgetHeight(widget, isMobile); const width = widget.responsiveBehavior === ResponsiveBehavior.Fill ? fillWidgetLength - : getRightColumn(widget, isMobile) - getLeftColumn(widget, isMobile); - maxHeight = Math.max(maxHeight, height); + : getWidgetWidth(widget, isMobile); + if (!totalHeight) maxHeight = Math.max(maxHeight, height); const updatedWidget = setDimensions( widget, topRow, @@ -225,13 +208,29 @@ function placeWidgetsWithoutWrap( return { height: maxHeight, widgets }; } -// TODO: update this function to measure height as well. +/** + * Extract the space assumed by each alignment in the row. + * - Alignments are designed to grow and shrink equally. each starting with equal share of parent's width. + * - If one alignment needs more than its share of width to layout its children, + * then the remaining alignments shrink to make room, if possible. + * Logic: + * - Sort the input alignments in descending order their column requirements. + * - if each.columns > space / input.length (i.e. the alignment needs more columns than it's starting share.) + * - add the alignment to the output array. + * - recursively repeat the exercise for the remaining alignments and remaining space. + * - attribute equal space to all alignments in input. + * @param input | AlignmentInfo[] : Array of all alignments to be placed in this row. + * @param space | number : Total space available for placing the widgets. + * @param sizes | AlignmentInfo[] : Array of all alignments to be placed in this row. + * @returns AlignmentInfo[] + */ function getAlignmentSizes( - arr: AlignmentInfo[], + input: AlignmentInfo[], space: number, sizes: AlignmentInfo[] = [], -): { alignment: FlexLayerAlignment; columns: number }[] { - if (arr.length === 0) return sizes; +): AlignmentInfo[] { + if (input.length === 0) return sizes; + const arr: AlignmentInfo[] = input.sort((a, b) => b.columns - a.columns); if (arr[0].columns > space / arr.length) { sizes.push(arr[0]); arr.shift(); @@ -248,11 +247,19 @@ function getAlignmentSizes( return sizes; } -function getIndividualAlignmentInfo( +/** + * Breakdown the current flex layer to extract information on each child alignment. + * Information for each alignment - children, columns, alignment. + * @param widgets | CanvasWidgetsReduxState: List of all widgets. + * @param layer | FlexLayer : current layer to be positioned on the canvas. + * @param isMobile | boolean + * @returns { info: AlignmentInfo[]; fillWidgetLength: number } + */ +function extractAlignmentInfo( widgets: CanvasWidgetsReduxState, layer: FlexLayer, isMobile: boolean, -) { +): { info: AlignmentInfo[]; fillWidgetLength: number } { const startChildren = [], centerChildren = [], endChildren = [], @@ -282,6 +289,7 @@ function getIndividualAlignmentInfo( startColumns - centerColumns - endColumns; + // Fill widgets are designed to take up parent's entire width on mobile viewport. const fillWidgetLength: number = isMobile ? GridDefaults.DEFAULT_GRID_COLUMNS : availableColumns / fillChildren.length; @@ -296,20 +304,29 @@ function getIndividualAlignmentInfo( } return { - startChildren, - centerChildren, - endChildren, - fillChildren, + info: [ + { + alignment: FlexLayerAlignment.Start, + columns: startColumns, + children: startChildren, + }, + { + alignment: FlexLayerAlignment.Center, + columns: centerColumns, + children: centerChildren, + }, + { + alignment: FlexLayerAlignment.End, + columns: endColumns, + children: endChildren, + }, + ], fillWidgetLength, - startColumns, - centerColumns, - endColumns, }; } function getAlignmentSizeInfo( arr: AlignmentInfo[], - isMobile: boolean, ): { startSize: number; centerSize: number; endSize: number } { let startSize = 0, centerSize = 0, @@ -331,7 +348,25 @@ function getAlignmentSizeInfo( return { startSize, centerSize, endSize }; } -function getWrappedAlignmentSize( +/** + * Find out which alignment is placed in which row. + * + * In case of flex wrap, + * - alignments within a FlexLayer are placed in multiple rows. + * Logic: + * - for each alignment in arr + * - if alignment.columns < 64 + * - add it to the current row (res[resIndex]) + * - and track the total occupied columns in this row (total) + * - else + * - add the current row to the output rows + * - and start a new row to repeat the process recursively. + * @param arr | AlignmentInfo[] : Array of alignments to be placed in this layer. + * @param res | AlignmentInfo[][] : Output array of alignments to be placed in this layer. + * @param resIndex | number : Last index of res. + * @returns AlignmentInfo[][] + */ +function getWrappedAlignmentInfo( arr: AlignmentInfo[], res: AlignmentInfo[][] = [[], [], []], resIndex = 0, @@ -349,7 +384,7 @@ function getWrappedAlignmentSize( res[resIndex].push(each); x += 1; } - return getWrappedAlignmentSize([...arr.slice(x)], res, resIndex + 1); + return getWrappedAlignmentInfo([...arr.slice(x)], res, resIndex + 1); } total += each.columns; index += 1; @@ -358,54 +393,36 @@ function getWrappedAlignmentSize( return res; } -function updatePositionsForFlexWrap(data: { - allWidgets: CanvasWidgetsReduxState; - topRow: number; - arr: AlignmentInfo[]; - centerChildren: Widget[]; - centerColumns: number; - endChildren: Widget[]; - endColumns: number; - fillWidgetLength: number; - startChildren: Widget[]; - isMobile: boolean; -}): { height: number; widgets: CanvasWidgetsReduxState } { - const { - allWidgets, - arr, - centerChildren, - centerColumns, - endChildren, - endColumns, - fillWidgetLength, - isMobile, - startChildren, - topRow, - } = data; +/** + * @param allWidgets | CanvasWidgetsReduxState: all widgets. + * @param arr | AlignmentInfo[] : Array of alignments to be placed in this layer. + * @param topRow | number : Starting row to place the widgets. + * @param fillWidgetLength | number : Length of fill widgets. + * @param isMobile | boolean : Is mobile viewport. + * @returns { height: number; widgets: CanvasWidgetsReduxState } + */ +function updatePositionsForFlexWrap( + allWidgets: CanvasWidgetsReduxState, + arr: AlignmentInfo[], + topRow: number, + fillWidgetLength: number, + isMobile: boolean, +): { height: number; widgets: CanvasWidgetsReduxState } { let widgets = { ...allWidgets }; - /** - * Find the order in which the alignments are wrapped. - * if res.length === 1 => no wrapping. - * else for each row, fit in widgets in GridDefaults.DEFAULT_GRID_COLUMNS columns - */ - const wrappedAlignments: AlignmentInfo[][] = getWrappedAlignmentSize(arr); + const wrappedAlignments: AlignmentInfo[][] = getWrappedAlignmentInfo(arr); let top = topRow; for (const each of wrappedAlignments) { if (!each.length) break; + // if there is only one alignment in this row, this implies that it may be wrapped. const payload = each.length === 1 - ? placeWrappedWidgets(widgets, each, top, fillWidgetLength, isMobile) + ? placeWrappedWidgets(widgets, each[0], top, fillWidgetLength, isMobile) : placeWidgetsWithoutWrap( widgets, each, top, - startChildren, - centerChildren, - endChildren, - centerColumns, - endColumns, fillWidgetLength, isMobile, ); @@ -421,85 +438,77 @@ function getStartingPosition( startSize: number, centerSize: number, endSize: number, - centerColumns: number, - endColumns: number, + columns: number, ): number { if (alignment === FlexLayerAlignment.Start) { return 0; } else if (alignment === FlexLayerAlignment.Center) { - return startSize + centerSize / 2 - centerColumns / 2; + return startSize + centerSize / 2 - columns / 2; } else if (alignment === FlexLayerAlignment.End) { - return startSize + centerSize + endSize - endColumns; + return startSize + centerSize + endSize - columns; } return 0; } +/** + * If the alignment requires more than 64 columns, it is wrapped. + * => a single alignment spans multiple layers. + * Logic: + * - Find out the number of rows required to position the widgets in the alignment. + * - Place each row normally. + * @param allWidgets | CanvasWidgetsReduxState: all widgets. + * @param alignment | AlignmentInfo: alignment to be positioned. + * @param topRow | number: top row to place the widgets. + * @param fillWidgetLength | number: length of fill widgets. + * @param isMobile | boolean: is mobile viewport. + * @returns { height: number; widgets: CanvasWidgetsReduxState } + */ function placeWrappedWidgets( allWidgets: CanvasWidgetsReduxState, - arr: AlignmentInfo[], + alignment: AlignmentInfo, topRow: number, fillWidgetLength: number, isMobile = false, ): { height: number; widgets: CanvasWidgetsReduxState } { let widgets = { ...allWidgets }; - /** - * arr could contain multiple alignments. - * More rows are needed only if it contains a single alignment, - * and it needs more than GridDefaults.DEFAULT_GRID_COLUMNS columns. - */ + let startRow = topRow; - if (arr.length === 1) { - // wrapped alignment - const rows: Row[] = getWrappedRows(arr[0], [], isMobile); - for (const row of rows) { - const { alignment, children, columns, height } = row; - const { centerSize, endSize, startSize } = getAlignmentSizeInfo( - [{ alignment, children, columns }], - isMobile, - ); - let left: number = getStartingPosition( - alignment, - startSize, - centerSize, - endSize, - alignment === FlexLayerAlignment.Center ? columns : 0, - alignment === FlexLayerAlignment.End ? columns : 0, - ); - for (const child of children) { - const height = getWidgetHeight(child, isMobile); - const width = - child.responsiveBehavior === ResponsiveBehavior.Fill - ? fillWidgetLength - : getWidgetWidth(child, isMobile); - const updatedWidget = setDimensions( - child, - startRow, - startRow + height, - left, - left + width, - isMobile, - ); - widgets = { - ...widgets, - [child.widgetId]: { - ...updatedWidget, - }, - }; - left += width; - } - startRow += height; - } + const rows: Row[] = getWrappedRows(alignment, [], isMobile); + for (const row of rows) { + const { alignment, children, columns, height } = row; + const result: { + height: number; + widgets: CanvasWidgetsReduxState; + } = placeWidgetsWithoutWrap( + widgets, + [{ alignment, children, columns }], + startRow, + fillWidgetLength, + isMobile, + height, + ); + widgets = result.widgets; + startRow += height; } - return { height: startRow - topRow, widgets }; -} -interface Row { - alignment: FlexLayerAlignment; - children: Widget[]; - columns: number; - height: number; + return { height: startRow - topRow, widgets }; } +/** + * Find out the number of rows required to position all the widgets in the given alignment. + * - for each child + * - if total columns < 64 + * - add it to the current row. + * - track the total consumed columns. + * - track the height of the current row. + * - else + * - add the current row to the output array. + * - recursively continue the process for remaining children. + * @param arr | AlignmentInfo: alignment to be positioned. + * @param rows | Row[]: output rows. + * @param isMobile | boolean: is mobile viewport. + * @returns Row[] + */ function getWrappedRows( arr: AlignmentInfo, rows: Row[], @@ -546,19 +555,21 @@ function getWrappedRows( return rows; } -// function updateHeight( -// allWidgets: CanvasWidgetsReduxState, -// widgetId: string, -// height: number, -// isMobile, -// ): void { -// const widgets = { ...allWidgets }; -// const widget = widgets[widgetId]; -// const children = widget.children; -// if (!children || !children?.length) return widgets; -// let maxHeight = 0; -// for (const child of children) { -// const childHeight = getWidgetHeight(child, isMobile); -// if (childHeight > maxHeight) maxHeight = childHeight; -// } -// } +function getHeightOfFixedCanvas( + widgets: CanvasWidgetsReduxState, + parent: Widget, + isMobile: boolean, +): number { + if (!parent.children || !parent.children.length) + return getWidgetRows(parent, isMobile); + let top = 10000, + bottom = 0; + for (const childId of parent.children) { + const child = widgets[childId]; + if (!child) continue; + const divisor = child.parentRowSpace === 1 ? 10 : 1; + top = Math.min(top, getTopRow(child, isMobile)); + bottom = Math.max(bottom, getBottomRow(child, isMobile) / divisor); + } + return bottom - top; +} From 32bd282bb25c6a1a7b2d30eee17ab7520f7f112a Mon Sep 17 00:00:00 2001 From: Preet Date: Sat, 31 Dec 2022 08:15:26 -0500 Subject: [PATCH 324/708] add comments to highlight utils --- .../src/utils/autoLayout/highlightUtils.ts | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/app/client/src/utils/autoLayout/highlightUtils.ts b/app/client/src/utils/autoLayout/highlightUtils.ts index 59e03437a20e..ffc519d32d0b 100644 --- a/app/client/src/utils/autoLayout/highlightUtils.ts +++ b/app/client/src/utils/autoLayout/highlightUtils.ts @@ -20,6 +20,7 @@ import { const HORIZONTAL_HIGHLIGHT_MARGIN = 4; // TODO: Preet - update logic to account for flex wrap on mobile. +// For mobile use widget positions to place highlights. /** * @param allWidgets : CanvasWidgetsReduxState * @param canvasId : string @@ -62,6 +63,7 @@ export function deriveHighlightsFromLayers( layer?.children?.filter( (child: LayerChild) => draggedWidgets.indexOf(child.id) === -1, ).length === 0; + // TODO: use getHeightOfFixedCanvas to measure the height of the layer. const tallestChild = layer.children?.reduce((acc, child) => { const widget = widgets[child.id]; return Math.max( @@ -297,6 +299,14 @@ function generateHighlightsForSubWrapper(data: { return res; } +/** + * Get the position of the initial / final highlight for an alignment. + * @param highlights | HighlightInfo[] : highlights for the current alignment + * @param alignment | FlexLayerAlignment : alignment of the current highlights + * @param posX | number : end position of the last widget in the current alignment. (rightColumn * columnSpace) + * @param containerWidth | number : width of the container + * @returns number + */ function getPositionForInitialHighlight( highlights: HighlightInfo[], alignment: FlexLayerAlignment, @@ -314,6 +324,19 @@ function getPositionForInitialHighlight( } } +/** + * Create a layer of horizontal alignments to denote new vertical drop zones. + * - if the layer has a fill widget, + * - Start alignment spans the entire container width. + * - else each layer takes up a third of the container width and are placed side to side. + * @param childIndex | number : child count of children placed in preceding layers. + * @param layerIndex | number + * @param offsetTop | number + * @param containerWidth | number + * @param canvasId | + * @param hasFillWidget | boolean : whether the layer has a fill widget or not. + * @returns HighlightInfo[] + */ function generateHorizontalHighlights( childIndex: number, layerIndex: number, From e70d0a0d675aef163a95d40b3426366067907e54 Mon Sep 17 00:00:00 2001 From: Preet Date: Sat, 31 Dec 2022 12:24:40 -0500 Subject: [PATCH 325/708] fix position util issues --- .../editorComponents/ResizableComponent.tsx | 8 ++++++- .../src/utils/autoLayout/positionUtils.ts | 21 ++++++++++++------- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/app/client/src/components/editorComponents/ResizableComponent.tsx b/app/client/src/components/editorComponents/ResizableComponent.tsx index 586ea4f3e5ff..e38187a508e0 100644 --- a/app/client/src/components/editorComponents/ResizableComponent.tsx +++ b/app/client/src/components/editorComponents/ResizableComponent.tsx @@ -110,7 +110,13 @@ export const ResizableComponent = memo(function ResizableComponent( props.parentColumnSpace - 2 * props.paddingOffset, height: - (props.bottomRow - props.topRow) * props.parentRowSpace - + ((props.isMobile && props.mobileBottomRow !== undefined + ? props.mobileBottomRow + : props.bottomRow) - + (props.isMobile && props.mobileTopRow !== undefined + ? props.mobileTopRow + : props.topRow)) * + props.parentRowSpace - 2 * props.paddingOffset, }; diff --git a/app/client/src/utils/autoLayout/positionUtils.ts b/app/client/src/utils/autoLayout/positionUtils.ts index 252267cb4578..c958e72993b2 100644 --- a/app/client/src/utils/autoLayout/positionUtils.ts +++ b/app/client/src/utils/autoLayout/positionUtils.ts @@ -67,7 +67,7 @@ export function updateWidgetPositions( } const divisor = parent.parentRowSpace === 1 ? 10 : 1; - const parentHeight = getWidgetHeight(parent, isMobile); + const parentHeight = getWidgetRows(parent, isMobile); if (parentHeight <= height) { /** * if children height is greater than parent height, @@ -87,7 +87,7 @@ export function updateWidgetPositions( } const shouldUpdateHeight = parent.parentId && - ["CONTAINER_WIDGET", MAIN_CONTAINER_WIDGET_ID].includes( + ["CONTAINER_WIDGET", "CANVAS_WIDGET"].includes( allWidgets[parent.parentId].type, ) && parentHeight <= height; @@ -169,9 +169,8 @@ function placeWidgetsWithoutWrap( * Get the size (columns: number) of each alignment in this row. */ const { centerSize, endSize, startSize } = getAlignmentSizeInfo(arr); - let maxHeight = totalHeight ? totalHeight : 0; - arr.forEach((each: AlignmentInfo) => { + for (const each of arr) { // Get the starting left column for each alignment in this row. let left = getStartingPosition( each.alignment, @@ -203,7 +202,7 @@ function placeWidgetsWithoutWrap( }; left += width; } - }); + } return { height: maxHeight, widgets }; } @@ -230,7 +229,7 @@ function getAlignmentSizes( sizes: AlignmentInfo[] = [], ): AlignmentInfo[] { if (input.length === 0) return sizes; - const arr: AlignmentInfo[] = input.sort((a, b) => b.columns - a.columns); + const arr: AlignmentInfo[] = [...input].sort((a, b) => b.columns - a.columns); if (arr[0].columns > space / arr.length) { sizes.push(arr[0]); arr.shift(); @@ -562,9 +561,17 @@ function getHeightOfFixedCanvas( ): number { if (!parent.children || !parent.children.length) return getWidgetRows(parent, isMobile); + return getTotalRowsOfAllChildren(widgets, parent.children, isMobile); +} + +export function getTotalRowsOfAllChildren( + widgets: CanvasWidgetsReduxState, + children: string[], + isMobile: boolean, +): number { let top = 10000, bottom = 0; - for (const childId of parent.children) { + for (const childId of children) { const child = widgets[childId]; if (!child) continue; const divisor = child.parentRowSpace === 1 ? 10 : 1; From ed7b49715aa04511acca39f936c8810ff06e331e Mon Sep 17 00:00:00 2001 From: Preet Date: Sat, 31 Dec 2022 13:47:24 -0500 Subject: [PATCH 326/708] fix highlight position on scroll --- .../src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index 0ce9eae73c93..78dcb38d97a4 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -706,8 +706,7 @@ export const useCanvasDragging = ( canvasCtx.fillStyle = "rgba(196, 139, 181, 1)"; const { height, posX, posY, width } = highlight; let val = 0; - if (scrollParent?.scrollTop) - val = scrollParent.scrollTop - startPoints?.top; + if (scrollParent?.scrollTop) val = scrollParent.scrollTop - 20; canvasCtx.fillRect(posX, posY - val, width, height); canvasCtx.save(); } From e65f13b3ef5dec4f7cac4b1892bfc27d330a7034 Mon Sep 17 00:00:00 2001 From: Preet Date: Sun, 1 Jan 2023 17:31:32 -0500 Subject: [PATCH 327/708] fix highlight position in container --- .../pages/common/CanvasArenas/hooks/useCanvasDragging.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index 78dcb38d97a4..7781a8e02600 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -706,7 +706,11 @@ export const useCanvasDragging = ( canvasCtx.fillStyle = "rgba(196, 139, 181, 1)"; const { height, posX, posY, width } = highlight; let val = 0; - if (scrollParent?.scrollTop) val = scrollParent.scrollTop - 20; + if ( + widgetId === MAIN_CONTAINER_WIDGET_ID && + scrollParent?.scrollTop + ) + val = scrollParent.scrollTop - 20; canvasCtx.fillRect(posX, posY - val, width, height); canvasCtx.save(); } From c7a090ea3f6b534e641ab6b5c0ae42f3174630dd Mon Sep 17 00:00:00 2001 From: Preet Date: Sun, 1 Jan 2023 17:54:41 -0500 Subject: [PATCH 328/708] remove auto height for containers on mobile --- .../components/editorComponents/DropTargetComponent.tsx | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/app/client/src/components/editorComponents/DropTargetComponent.tsx b/app/client/src/components/editorComponents/DropTargetComponent.tsx index c59ed3fb721e..63bbcc1c95a3 100644 --- a/app/client/src/components/editorComponents/DropTargetComponent.tsx +++ b/app/client/src/components/editorComponents/DropTargetComponent.tsx @@ -82,15 +82,12 @@ export const DropTargetContext: Context<{ */ function getDropTargetHeight( canDropTargetExtend: boolean, - isMobile: boolean, isPreviewMode: boolean, currentHeight: number, snapRowSpace: number, minHeight: number, ) { - let height = isMobile - ? "auto" - : canDropTargetExtend + let height = canDropTargetExtend ? `${Math.max(currentHeight * snapRowSpace, minHeight)}px` : "100%"; if (isPreviewMode && canDropTargetExtend) @@ -199,7 +196,6 @@ export function DropTargetComponent(props: DropTargetComponentProps) { if (dropTargetRef.current) { const height = getDropTargetHeight( canDropTargetExtend, - isMobile && props.widgetId !== MAIN_CONTAINER_WIDGET_ID, isPreviewMode, rowRef.current, props.snapRowSpace, @@ -262,7 +258,6 @@ export function DropTargetComponent(props: DropTargetComponentProps) { const height = getDropTargetHeight( canDropTargetExtend, - isMobile && props.widgetId !== MAIN_CONTAINER_WIDGET_ID, isPreviewMode, rowRef.current, props.snapRowSpace, From 469f247fda20579f464ed502424eac84e2b0e9c3 Mon Sep 17 00:00:00 2001 From: Preet Date: Sun, 1 Jan 2023 17:55:38 -0500 Subject: [PATCH 329/708] highlight utils update --- .../src/utils/autoLayout/highlightUtils.ts | 32 +++++++++++++++---- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/app/client/src/utils/autoLayout/highlightUtils.ts b/app/client/src/utils/autoLayout/highlightUtils.ts index ffc519d32d0b..f370274944a4 100644 --- a/app/client/src/utils/autoLayout/highlightUtils.ts +++ b/app/client/src/utils/autoLayout/highlightUtils.ts @@ -14,9 +14,12 @@ import { HighlightInfo } from "pages/common/CanvasArenas/hooks/useAutoLayoutHigh import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; import { getLeftColumn, + getRightColumn, + getTopRow, getWidgetHeight, getWidgetWidth, } from "./flexWidgetUtils"; +import { getTotalRowsOfAllChildren, Widget } from "./positionUtils"; const HORIZONTAL_HIGHLIGHT_MARGIN = 4; // TODO: Preet - update logic to account for flex wrap on mobile. @@ -71,6 +74,11 @@ export function deriveHighlightsFromLayers( getWidgetHeight(widget, isMobile) * widget.parentRowSpace, ); }, 0); + const childrenRows = getTotalRowsOfAllChildren( + widgets, + layer.children?.map((child) => child.id) || [], + isMobile, + ); const payload: VerticalHighlightsPayload = generateVerticalHighlights({ widgets, @@ -103,7 +111,7 @@ export function deriveHighlightsFromLayers( ); highlights.push(...payload.highlights); - offsetTop += tallestChild || 0; + offsetTop += childrenRows * 10 || 0; layerIndex += 1; } childCount += payload.childCount; @@ -266,7 +274,9 @@ function generateHighlightsForSubWrapper(data: { rowIndex: count, alignment, posX: left * parentColumnSpace, - posY: offsetTop, + posY: + getTopRow(child, isMobile) * child.parentRowSpace + + HORIZONTAL_HIGHLIGHT_MARGIN, width: DEFAULT_HIGHLIGHT_SIZE, height, isVertical: true, @@ -275,7 +285,8 @@ function generateHighlightsForSubWrapper(data: { count += 1; } - if (!avoidInitialHighlight) + if (!avoidInitialHighlight) { + const lastChild: Widget = arr && arr.length ? arr[arr.length - 1] : null; res.push({ isNewLayer: false, index: count + childCount, @@ -286,16 +297,22 @@ function generateHighlightsForSubWrapper(data: { res, alignment, arr && arr.length - ? arr[arr.length - 1].rightColumn * parentColumnSpace + ? getRightColumn(lastChild, isMobile) * parentColumnSpace : 0, canvasWidth, + canvasId, ), - posY: offsetTop, + posY: + lastChild === null + ? offsetTop + : getTopRow(lastChild, isMobile) * lastChild?.parentRowSpace + + HORIZONTAL_HIGHLIGHT_MARGIN, width: DEFAULT_HIGHLIGHT_SIZE, height, isVertical: true, canvasId, }); + } return res; } @@ -305,6 +322,7 @@ function generateHighlightsForSubWrapper(data: { * @param alignment | FlexLayerAlignment : alignment of the current highlights * @param posX | number : end position of the last widget in the current alignment. (rightColumn * columnSpace) * @param containerWidth | number : width of the container + * @param canvasId | string : id of the canvas * @returns number */ function getPositionForInitialHighlight( @@ -312,9 +330,10 @@ function getPositionForInitialHighlight( alignment: FlexLayerAlignment, posX: number, containerWidth: number, + canvasId: string, ): number { if (alignment === FlexLayerAlignment.End) { - return containerWidth; + return containerWidth - (canvasId !== MAIN_CONTAINER_WIDGET_ID ? 6 : 0); } else if (alignment === FlexLayerAlignment.Center) { if (!highlights.length) return containerWidth / 2; return posX; @@ -414,6 +433,7 @@ function getCanvasWidth( mainCanvasWidth: number, isMobile: boolean, ): number { + // TODO: @Preet - Update the logic to account for padding at each level. if (!mainCanvasWidth) return 0; if (canvas.widgetId === MAIN_CONTAINER_WIDGET_ID) return mainCanvasWidth; let widget = canvas; From bbb9df6ad9d321ceed3ff7a0b6c0f68a0c138614 Mon Sep 17 00:00:00 2001 From: Preet Date: Sun, 1 Jan 2023 17:59:38 -0500 Subject: [PATCH 330/708] clean up --- .../components/editorComponents/DropTargetComponent.tsx | 3 --- .../common/CanvasArenas/hooks/useAutoLayoutHighlights.ts | 2 -- app/client/src/utils/autoLayout/highlightUtils.ts | 2 +- app/client/src/utils/autoLayout/positionUtils.ts | 7 ++----- 4 files changed, 3 insertions(+), 11 deletions(-) diff --git a/app/client/src/components/editorComponents/DropTargetComponent.tsx b/app/client/src/components/editorComponents/DropTargetComponent.tsx index 63bbcc1c95a3..a12703259d4a 100644 --- a/app/client/src/components/editorComponents/DropTargetComponent.tsx +++ b/app/client/src/components/editorComponents/DropTargetComponent.tsx @@ -20,7 +20,6 @@ import { getOccupiedSpacesSelectorForContainer, previewModeSelector, } from "selectors/editorSelectors"; -import { getIsMobile } from "selectors/mainCanvasSelectors"; import styled from "styled-components"; import { useAutoHeightUIState } from "utils/hooks/autoHeightUIHooks"; import { @@ -118,8 +117,6 @@ export function DropTargetComponent(props: DropTargetComponentProps) { // Are we changing the auto height limits by dragging the signifiers? const { isAutoHeightWithLimitsChanging } = useAutoHeightUIState(); - const isMobile = useSelector(getIsMobile); - // dragDetails contains of info needed for a container jump: // which parent the dragging widget belongs, // which canvas is active(being dragged on), diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index 5d74e80b24bb..aaba6db0fb5b 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -59,7 +59,6 @@ export const useAutoLayoutHighlights = ({ const canvasWidth: number = useSelector(getCanvasWidth); const isMobile = useSelector(getIsMobile); let highlights: HighlightInfo[] = []; - let newLayers: { [key: string]: number } = {}; let lastActiveHighlight: HighlightInfo | undefined; let isFillWidget = false; @@ -99,7 +98,6 @@ export const useAutoLayoutHighlights = ({ // reset state lastActiveHighlight = undefined; highlights = []; - newLayers = {}; }; const checkForFillWidget = (): boolean => { diff --git a/app/client/src/utils/autoLayout/highlightUtils.ts b/app/client/src/utils/autoLayout/highlightUtils.ts index f370274944a4..1a02172d0532 100644 --- a/app/client/src/utils/autoLayout/highlightUtils.ts +++ b/app/client/src/utils/autoLayout/highlightUtils.ts @@ -129,7 +129,7 @@ export function deriveHighlightsFromLayers( ); return highlights; } catch (e) { - console.error(e); + // console.error(e); return []; } } diff --git a/app/client/src/utils/autoLayout/positionUtils.ts b/app/client/src/utils/autoLayout/positionUtils.ts index c958e72993b2..63188e2c4d98 100644 --- a/app/client/src/utils/autoLayout/positionUtils.ts +++ b/app/client/src/utils/autoLayout/positionUtils.ts @@ -1,9 +1,6 @@ import { FlexLayerAlignment, ResponsiveBehavior } from "components/constants"; import { FlexLayer } from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; -import { - GridDefaults, - MAIN_CONTAINER_WIDGET_ID, -} from "constants/WidgetConstants"; +import { GridDefaults } from "constants/WidgetConstants"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; import { WidgetProps } from "widgets/BaseWidget"; import { @@ -96,7 +93,7 @@ export function updateWidgetPositions( return updateWidgetPositions(widgets, parent.parentId, isMobile); return widgets; } catch (e) { - console.error(e); + // console.error(e); return widgets; } } From b89ce4f5c58d09a88a571b1e76c4ceaf02285cdb Mon Sep 17 00:00:00 2001 From: Preet Date: Sun, 1 Jan 2023 18:01:16 -0500 Subject: [PATCH 331/708] Revert "Merge branch 'mobile/v1/wrap_positions' into mobile/v1/main" This reverts commit e338be554e4d78e7d6b72aa0d15f5ed2a5a009f4. --- .../appsmith/autoLayout/AutoLayoutLayer.tsx | 3 + .../appsmith/autoLayout/FlexBoxComponent.tsx | 330 ++++++++-- .../appsmith/autoLayout/FlexComponent.tsx | 119 ++-- .../editorComponents/DraggableComponent.tsx | 4 +- .../editorComponents/DropTargetComponent.tsx | 10 +- .../editorComponents/ResizableComponent.tsx | 14 +- .../hooks/useAutoLayoutHighlights.ts | 161 +++-- .../CanvasArenas/hooks/useCanvasDragging.ts | 37 +- .../src/resizable/resizenreflow/index.tsx | 4 +- .../src/sagas/AutoLayoutUpdateSagas.tsx | 1 - app/client/src/sagas/AutoLayoutUtils.ts | 17 +- .../CanvasSagas/AutoLayoutDraggingSagas.ts | 35 +- app/client/src/sagas/WidgetOperationSagas.tsx | 5 +- .../src/utils/autoLayout/flexWidgetUtils.ts | 113 ---- .../src/utils/autoLayout/highlightUtils.ts | 448 -------------- .../src/utils/autoLayout/positionUtils.ts | 579 ------------------ app/client/src/widgets/BaseWidget.tsx | 27 +- .../widgets/ContainerWidget/widget/index.tsx | 2 +- 18 files changed, 546 insertions(+), 1363 deletions(-) delete mode 100644 app/client/src/utils/autoLayout/flexWidgetUtils.ts delete mode 100644 app/client/src/utils/autoLayout/highlightUtils.ts delete mode 100644 app/client/src/utils/autoLayout/positionUtils.ts diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx index 4ca922787adf..ad772870bcb0 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx @@ -2,6 +2,7 @@ import React, { ReactNode } from "react"; import styled from "styled-components"; import { FlexDirection, LayoutDirection } from "components/constants"; +import { DRAG_MARGIN } from "widgets/constants"; /** * 1. Given a direction if should employ flex in perpendicular direction. @@ -19,6 +20,7 @@ export interface AutoLayoutLayerProps { widgetId: string; isMobile?: boolean; isCurrentCanvasDragging: boolean; + currentChildCount: number; wrapStart: boolean; wrapCenter: boolean; wrapEnd: boolean; @@ -37,6 +39,7 @@ const LayoutLayerContainer = styled.div<{ flex-wrap: ${({ wrap }) => (wrap ? "wrap" : "nowrap")}; width: 100%; + margin-top: ${DRAG_MARGIN}px; `; const SubWrapper = styled.div<{ diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx index a93b68e3eedc..797f6f8d2496 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx @@ -11,6 +11,7 @@ import { import { APP_MODE } from "entities/App"; import { useSelector } from "react-redux"; import { getWidgets } from "sagas/selectors"; +import { isCurrentCanvasDragging } from "selectors/autoLayoutSelectors"; import { getAppMode } from "selectors/entitiesSelector"; import { getIsMobile } from "selectors/mainCanvasSelectors"; import AutoLayoutLayer from "./AutoLayoutLayer"; @@ -35,6 +36,27 @@ export interface FlexLayer { hasFillChild?: boolean; } +interface DropPositionProps { + isVertical: boolean; + alignment: FlexLayerAlignment; + layerIndex: number; + childIndex: number; + widgetId: string; + isNewLayer: boolean; + rowIndex: number; +} + +interface NewLayerProps { + alignment: FlexLayerAlignment; + childCount: number; + layerIndex: number; + isDragging: boolean; + isNewLayer: boolean; + isVertical: boolean; + map: { [key: string]: any }; + widgetId: string; +} + export const FlexContainer = styled.div<{ useAutoLayout?: boolean; direction?: LayoutDirection; @@ -60,12 +82,35 @@ export const FlexContainer = styled.div<{ overflow-y: ${({ isMainContainer, isMobile }) => isMainContainer || isMobile ? "auto" : "hidden"}; - padding: ${({ leaveSpaceForWidgetName }) => - leaveSpaceForWidgetName ? "4px 4px 22px 4px;" : "0px;"}; + padding: ${({ isDragging, leaveSpaceForWidgetName }) => + !isDragging && leaveSpaceForWidgetName ? "4px 4px 22px 4px;" : "4px;"}; `; export const DEFAULT_HIGHLIGHT_SIZE = 4; +export const DropPosition = styled.div<{ + isDragging: boolean; + isNewLayer: boolean; + isVertical: boolean; +}>` + width: ${({ isVertical }) => + isVertical ? `${DEFAULT_HIGHLIGHT_SIZE}px` : "calc(33% - 4px)"}; + height: ${({ isNewLayer, isVertical }) => + isVertical && !isNewLayer ? "auto" : `${DEFAULT_HIGHLIGHT_SIZE}px`}; + background-color: rgba(223, 158, 206, 0.6); + margin: ${({ isVertical }) => (isVertical ? "2px 4px" : "2px")}; + display: ${({ isDragging }) => (isDragging ? "block" : "none")}; + align-self: stretch; + opacity: 0; +`; + +export const NewLayerStyled = styled.div<{ + isDragging: boolean; +}>` + width: 100%; + height: ${({ isDragging }) => (isDragging ? "6px" : "0px")}; +`; + function FlexBoxComponent(props: FlexBoxProps) { // TODO: set isMobile as a prop at the top level const isMobile = useSelector(getIsMobile); @@ -78,6 +123,13 @@ function FlexBoxComponent(props: FlexBoxProps) { const { dragDetails } = useSelector( (state: AppState) => state.ui.widgetDragResize, ); + const draggedOn: string | undefined = dragDetails + ? dragDetails?.draggedOn + : undefined; + + const draggedWidget = dragDetails + ? dragDetails?.draggingGroupCenter?.widgetId + : ""; // const isDragging = useSelector(isCurrentCanvasDragging(props.widgetId)); const isDragging: boolean = dragDetails?.draggedOn !== undefined; @@ -86,7 +138,15 @@ function FlexBoxComponent(props: FlexBoxProps) { if (!props.children) return null; if (!props.useAutoLayout) return props.children; if (direction === LayoutDirection.Horizontal) { - return props.children; + return addDropPositions({ + arr: props.children as any, + childCount: 0, + layerIndex: 0, + alignment: FlexLayerAlignment.Start, + isVertical: true, + isNewLayer: true, + addInitialHighlight: true, + }); } /** @@ -115,34 +175,192 @@ function FlexBoxComponent(props: FlexBoxProps) { }); } - function processLayers(map: { [key: string]: any }) { - const layers = []; - let index = 0; - for (const layer of props.flexLayers) { - layers.push(processIndividualLayer(layer, map, index)); - index += 1; - } - return layers; + function DropPositionComponent(props: DropPositionProps) { + return ( + + ); + } + + function NewLayerComponent(props: NewLayerProps): JSX.Element { + const { childCount, isDragging, layerIndex, map, widgetId } = props; + + const { element: verticalHighlights } = processIndividualLayer( + { children: [], hasFillChild: false }, + childCount, + layerIndex, + map, + true, + ); + + return ( + + {verticalHighlights} + + ); } + const getDropPositionKey = ( + index: number, + alignment: FlexLayerAlignment, + layerIndex: number, + isVertical: boolean, + ): string => + `drop-layer-${props.widgetId}-${layerIndex}-${alignment}-${index}-${ + isVertical ? "vertical" : "horizontal" + }-${Math.random()}`; + + const addDropPositions = (data: { + arr: any[]; + childCount: number; + layerIndex: number; + alignment: FlexLayerAlignment; + isVertical: boolean; + isNewLayer: boolean; + addInitialHighlight: boolean; + }): any[] => { + const { + addInitialHighlight, + alignment, + arr, + childCount, + isNewLayer, + isVertical, + layerIndex, + } = data; + const res = addInitialHighlight + ? [ + , + ] + : []; + let count = 0; + if (arr) { + for (const item of arr) { + const widgetId = item + ? (item as JSX.Element)?.props.widgetId + : undefined; + if (draggedWidget && widgetId && draggedWidget === widgetId) continue; + count += 1; + res.push(item); + res.push( + , + ); + } + } + return res; + }; + function getColumns(id: string, isMobile: boolean): number { const widget = allWidgets[id]; if (!widget) return 0; - return isMobile && - widget.mobileRightColumn !== undefined && - widget.mobileLeftColumn !== undefined - ? widget.mobileRightColumn - widget.mobileLeftColumn - : widget.rightColumn - widget.leftColumn; + return isMobile && widget.mobileRightColumn + ? widget.mobileRightColumn + : widget.rightColumn; + } + + function processLayers(map: { [key: string]: any }) { + const layers = []; + let childCount = 0; + let layerIndex = 0; + for (const layer of props.flexLayers) { + const isEmpty = + layer?.children?.filter( + (child: LayerChild) => child.id !== draggedWidget, + ).length === 0; + + !isEmpty && + layers.push( + , + ); + + const { count, element } = processIndividualLayer( + layer, + childCount, + layerIndex, + map, + ); + childCount += count; + if (!isEmpty) { + layerIndex += 1; + layers.push(element); + } + } + layers.push( + , + ); + return layers; } function processIndividualLayer( layer: FlexLayer, - map: { [key: string]: any }, + childCount: number, index: number, + map: { [key: string]: any }, + isNewLayer = false, ) { const { children } = layer; - const start = [], + let count = 0; + let start = [], center = [], end = []; let startColumns = 0, @@ -150,6 +368,7 @@ function FlexBoxComponent(props: FlexBoxProps) { endColumns = 0; for (const child of children) { + count += 1; const widget = map[child.id]; if (child.align === "end") { @@ -163,25 +382,64 @@ function FlexBoxComponent(props: FlexBoxProps) { startColumns += getColumns(child.id, isMobile); } } + /** + * Add drop positions + */ + const startLength = start.length, + centerLength = center.length; - return ( - 64} - wrapEnd={endColumns > 64} - wrapLayer={startColumns + centerColumns + endColumns > 64} - wrapStart={startColumns > 64} - /> - ); + start = addDropPositions({ + arr: start, + childCount, + layerIndex: index, + alignment: FlexLayerAlignment.Start, + isVertical: !isNewLayer, + isNewLayer, + addInitialHighlight: !( + start.length === 0 && centerColumns + endColumns > 60 + ), + }); + center = addDropPositions({ + arr: center, + childCount: childCount + startLength, + layerIndex: index, + alignment: FlexLayerAlignment.Center, + isVertical: !isNewLayer, + isNewLayer, + addInitialHighlight: !(startColumns > 25 || endColumns > 25), + }); + end = addDropPositions({ + arr: end, + childCount: childCount + startLength + centerLength, + layerIndex: index, + alignment: FlexLayerAlignment.End, + isVertical: !isNewLayer, + isNewLayer, + addInitialHighlight: !(centerColumns + startColumns > 60), + }); + + return { + element: ( + 64} + wrapEnd={endColumns > 64} + wrapLayer={startColumns + centerColumns + endColumns > 64} + wrapStart={startColumns > 64} + /> + ), + count, + }; } return ( diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx index 95d3c0f3c821..0db9514a0af6 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx @@ -1,4 +1,4 @@ -import React, { CSSProperties, ReactNode, useCallback, useMemo } from "react"; +import React, { ReactNode, useCallback } from "react"; import styled from "styled-components"; import { AppState } from "ce/reducers"; @@ -8,16 +8,18 @@ import { ResponsiveBehavior, } from "components/constants"; import { + MAIN_CONTAINER_WIDGET_ID, WidgetType, widgetTypeClassname, WIDGET_PADDING, } from "constants/WidgetConstants"; import { useSelector } from "react-redux"; +import { getSiblingCount } from "selectors/autoLayoutSelectors"; import { snipingModeSelector } from "selectors/editorSelectors"; import { getIsMobile } from "selectors/mainCanvasSelectors"; -import { isWidgetSelected } from "selectors/widgetSelectors"; import { useClickToSelectWidget } from "utils/hooks/useClickToSelectWidget"; import { usePositionedContainerZIndex } from "utils/hooks/usePositionedContainerZIndex"; +import { DRAG_MARGIN } from "widgets/constants"; import { checkIsDropTarget } from "../PositionedContainer"; export type AutoLayoutProps = { @@ -35,23 +37,61 @@ export type AutoLayoutProps = { parentColumnSpace: number; flexVerticalAlignment: FlexVerticalAlignment; }; - -const FlexWidget = styled.div` +// TODO: create a memoized style object for the div instead. +const FlexWidget = styled.div<{ + componentHeight: number; + componentWidth: number; + isMobile: boolean; + isFillWidget: boolean; + padding: number; + zIndex: number; + zIndexOnHover: number; + dragMargin: number; + isAffectedByDrag: boolean; + parentId?: string; + flexVerticalAlignment: FlexVerticalAlignment; +}>` position: relative; + z-index: ${({ zIndex }) => zIndex}; + + width: ${({ componentWidth }) => `${Math.floor(componentWidth)}px`}; + height: ${({ componentHeight, isMobile }) => + isMobile ? "auto" : Math.floor(componentHeight) + "px"}; + + min-height: 30px; + padding: ${({ isAffectedByDrag, padding }) => + isAffectedByDrag ? 0 : padding + "px"}; + + flex-grow: ${({ isFillWidget }) => (isFillWidget ? "1" : "0")}; + align-self: ${({ flexVerticalAlignment }) => flexVerticalAlignment}; + + &:hover { + z-index: ${({ zIndexOnHover }) => zIndexOnHover} !important; + } + margin-top: ${({ isAffectedByDrag }) => (isAffectedByDrag ? "4px" : "0px")}; `; +const DEFAULT_MARGIN = 16; + export function FlexComponent(props: AutoLayoutProps) { const isMobile = useSelector(getIsMobile); const isSnipingMode = useSelector(snipingModeSelector); - const isSelected = useSelector(isWidgetSelected(props.widgetId)); - const isDragging = useSelector( - (state: AppState) => state.ui.widgetDragResize.isDragging, - ); const clickToSelectWidget = useClickToSelectWidget(props.widgetId); const onClickFn = useCallback(() => { clickToSelectWidget(props.widgetId); }, [props.widgetId, clickToSelectWidget]); + const dragDetails = useSelector( + (state: AppState) => state.ui.widgetDragResize.dragDetails, + ); + const isDragging = useSelector( + (state: AppState) => state.ui.widgetDragResize.isDragging, + ); + + const siblingCount = useSelector( + getSiblingCount(props.widgetId, props.parentId || MAIN_CONTAINER_WIDGET_ID), + ); + const isDropTarget = checkIsDropTarget(props.widgetType); const { onHoverZIndex, zIndex } = usePositionedContainerZIndex( isDropTarget, @@ -63,7 +103,6 @@ export function FlexComponent(props: AutoLayoutProps) { const stopEventPropagation = (e: any) => { !isSnipingMode && e.stopPropagation(); }; - /** * In a vertical stack, * Fill widgets grow / shrink to take up all the available space. @@ -76,40 +115,46 @@ export function FlexComponent(props: AutoLayoutProps) { props.widgetId } ${widgetTypeClassname(props.widgetType)}`; - const flexComponentStyle: CSSProperties = useMemo(() => { - return { - display: isSelected && isDragging ? "none" : "flex", - zIndex, - width: `${Math.floor(props.componentWidth) - WIDGET_PADDING * 2}px`, - height: isMobile - ? "auto" - : Math.floor(props.componentHeight) - WIDGET_PADDING * 2 + "px", - minHeight: "30px", - margin: WIDGET_PADDING + "px", - flexGrow: isFillWidget ? 1 : 0, - alignSelf: props.flexVerticalAlignment, - "&:hover": { - zIndex: onHoverZIndex + " !important", - }, - }; - }, [ - isDragging, - isFillWidget, - isMobile, - isSelected, - props.componentWidth, - props.componentHeight, - props.flexVerticalAlignment, - zIndex, - onHoverZIndex, - ]); + const dragMargin = + props.parentId === MAIN_CONTAINER_WIDGET_ID + ? DEFAULT_MARGIN + : Math.max(props.parentColumnSpace, DRAG_MARGIN); + + const isAffectedByDrag: boolean = + isDragging && dragDetails?.draggedOn !== undefined; + // TODO: Simplify this logic. + /** + * resize logic: + * if isAffectedByDrag + * newWidth = width - drag margin - + * (decrease in parent width) / # of siblings - + * width of the DropPosition introduced due to the widget. + */ + const resizedWidth: number = isAffectedByDrag + ? props.componentWidth - + dragMargin - + (siblingCount > 0 + ? (DEFAULT_MARGIN * siblingCount + 2) / siblingCount + : 0) + : props.componentWidth; return ( {props.children} diff --git a/app/client/src/components/editorComponents/DraggableComponent.tsx b/app/client/src/components/editorComponents/DraggableComponent.tsx index 90e46803e8dc..cf61aa721f61 100644 --- a/app/client/src/components/editorComponents/DraggableComponent.tsx +++ b/app/client/src/components/editorComponents/DraggableComponent.tsx @@ -131,9 +131,7 @@ function DraggableComponent(props: DraggableComponentProps) { }; }, [isResizingOrDragging, isCurrentWidgetResizing]); - const widgetBoundaries = props.isFlexChild ? null : ( - - ); + const widgetBoundaries = ; const classNameForTesting = `t--draggable-${props.type .split("_") diff --git a/app/client/src/components/editorComponents/DropTargetComponent.tsx b/app/client/src/components/editorComponents/DropTargetComponent.tsx index a12703259d4a..c59ed3fb721e 100644 --- a/app/client/src/components/editorComponents/DropTargetComponent.tsx +++ b/app/client/src/components/editorComponents/DropTargetComponent.tsx @@ -20,6 +20,7 @@ import { getOccupiedSpacesSelectorForContainer, previewModeSelector, } from "selectors/editorSelectors"; +import { getIsMobile } from "selectors/mainCanvasSelectors"; import styled from "styled-components"; import { useAutoHeightUIState } from "utils/hooks/autoHeightUIHooks"; import { @@ -81,12 +82,15 @@ export const DropTargetContext: Context<{ */ function getDropTargetHeight( canDropTargetExtend: boolean, + isMobile: boolean, isPreviewMode: boolean, currentHeight: number, snapRowSpace: number, minHeight: number, ) { - let height = canDropTargetExtend + let height = isMobile + ? "auto" + : canDropTargetExtend ? `${Math.max(currentHeight * snapRowSpace, minHeight)}px` : "100%"; if (isPreviewMode && canDropTargetExtend) @@ -117,6 +121,8 @@ export function DropTargetComponent(props: DropTargetComponentProps) { // Are we changing the auto height limits by dragging the signifiers? const { isAutoHeightWithLimitsChanging } = useAutoHeightUIState(); + const isMobile = useSelector(getIsMobile); + // dragDetails contains of info needed for a container jump: // which parent the dragging widget belongs, // which canvas is active(being dragged on), @@ -193,6 +199,7 @@ export function DropTargetComponent(props: DropTargetComponentProps) { if (dropTargetRef.current) { const height = getDropTargetHeight( canDropTargetExtend, + isMobile && props.widgetId !== MAIN_CONTAINER_WIDGET_ID, isPreviewMode, rowRef.current, props.snapRowSpace, @@ -255,6 +262,7 @@ export function DropTargetComponent(props: DropTargetComponentProps) { const height = getDropTargetHeight( canDropTargetExtend, + isMobile && props.widgetId !== MAIN_CONTAINER_WIDGET_ID, isPreviewMode, rowRef.current, props.snapRowSpace, diff --git a/app/client/src/components/editorComponents/ResizableComponent.tsx b/app/client/src/components/editorComponents/ResizableComponent.tsx index e38187a508e0..fe2807bee163 100644 --- a/app/client/src/components/editorComponents/ResizableComponent.tsx +++ b/app/client/src/components/editorComponents/ResizableComponent.tsx @@ -101,22 +101,14 @@ export const ResizableComponent = memo(function ResizableComponent( // The ResizableContainer's size prop is controlled const dimensions: UIElementSize = { width: - ((props.isMobile && props.mobileRightColumn !== undefined + ((props.isMobile && props.mobileRightColumn ? props.mobileRightColumn : props.rightColumn) - - (props.isMobile && props.mobileLeftColumn !== undefined - ? props.mobileLeftColumn - : props.leftColumn)) * + props.leftColumn) * props.parentColumnSpace - 2 * props.paddingOffset, height: - ((props.isMobile && props.mobileBottomRow !== undefined - ? props.mobileBottomRow - : props.bottomRow) - - (props.isMobile && props.mobileTopRow !== undefined - ? props.mobileTopRow - : props.topRow)) * - props.parentRowSpace - + (props.bottomRow - props.topRow) * props.parentRowSpace - 2 * props.paddingOffset, }; diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index aaba6db0fb5b..582cea0fee43 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -6,9 +6,6 @@ import { import { useSelector } from "react-redux"; import { ReflowDirection } from "reflow/reflowTypes"; import { getWidgets } from "sagas/selectors"; -import { getCanvasWidth } from "selectors/editorSelectors"; -import { getIsMobile } from "selectors/mainCanvasSelectors"; -import { deriveHighlightsFromLayers } from "utils/autoLayout/highlightUtils"; import WidgetFactory from "utils/WidgetFactory"; import { WidgetDraggingBlock } from "./useBlocksToBeDraggedOnCanvas"; @@ -28,8 +25,7 @@ export interface HighlightInfo { width: number; // width of the highlight. height: number; // height of the highlight. isVertical: boolean; // determines if the highlight is vertical or horizontal. - el?: Element; // dom node of the highlight. - canvasId: string; // widgetId of the canvas to which the highlight belongs. + el: Element; // dom node of the highlight. } export interface AutoLayoutHighlightProps { @@ -56,9 +52,8 @@ export const useAutoLayoutHighlights = ({ useAutoLayout, }: AutoLayoutHighlightProps) => { const allWidgets = useSelector(getWidgets); - const canvasWidth: number = useSelector(getCanvasWidth); - const isMobile = useSelector(getIsMobile); let highlights: HighlightInfo[] = []; + let newLayers: { [key: string]: number } = {}; let lastActiveHighlight: HighlightInfo | undefined; let isFillWidget = false; @@ -98,6 +93,51 @@ export const useAutoLayoutHighlights = ({ // reset state lastActiveHighlight = undefined; highlights = []; + newLayers = {}; + }; + + const getDropPositions = () => { + const els = document.querySelectorAll(`.t--drop-position-${canvasId}`); + const highlights: HighlightInfo[] = []; + + for (const el of els) { + const rect: DOMRect = el.getBoundingClientRect(); + const classList = Array.from(el.classList); + + const highlight: HighlightInfo = classList.reduce( + (acc: HighlightInfo, curr) => { + if (curr.indexOf("alignment") > -1) + acc.alignment = curr.split("-")[1] as FlexLayerAlignment; + else if (curr.indexOf("layer-index") > -1) + acc.layerIndex = parseInt(curr.split("layer-index-")[1]); + else if (curr.indexOf("child-index") > -1) + acc.index = parseInt(curr.split("child-index-")[1]); + else if (curr.indexOf("row-index") > -1) + acc.rowIndex = parseInt(curr.split("row-index-")[1]); + else if (curr.indexOf("isNewLayer") > -1) acc.isNewLayer = true; + else if (curr.indexOf("isVertical") > -1) acc.isVertical = true; + + return acc; + }, + { + isNewLayer: false, + index: 0, + layerIndex: 0, + rowIndex: 0, + alignment: FlexLayerAlignment.Start, + posX: rect.x - containerDimensions.left, + posY: rect.y - containerDimensions?.top, + width: rect.width, + height: rect.height, + isVertical: false, + el, + }, + ); + if (!highlight.isVertical) newLayers[highlights.length] = highlight.posY; + highlights.push(highlight); + } + + return highlights; }; const checkForFillWidget = (): boolean => { @@ -134,14 +174,7 @@ export const useAutoLayoutHighlights = ({ */ if (!updateContainerDimensions()) return []; isFillWidget = checkForFillWidget(); - highlights = deriveHighlightsFromLayers( - allWidgets, - canvasId, - canvasWidth, - blocksToDraw.map((block) => block?.widgetId), - isFillWidget, - isMobile, - ); + highlights = getDropPositions(); } // console.log("#### highlights", highlights); return highlights; @@ -151,32 +184,70 @@ export const useAutoLayoutHighlights = ({ * END AUTO LAYOUT OFFSET CALCULATION */ + const updateHighlight = (index: number): HighlightInfo => { + const highlight = highlights[index]; + if (!highlight || !highlight.el) return highlight; + const rect: DOMRect = highlight.el.getBoundingClientRect(); + + highlight.posX = rect.x - containerDimensions.left; + highlight.posY = rect.y - containerDimensions.top; + highlight.width = isFillWidget + ? highlight.alignment === FlexLayerAlignment.Start + ? containerDimensions.width + : 0 + : containerDimensions?.width / 3; + highlight.height = rect.height; + (highlight.el as HTMLElement).style.width = `${highlight.width}px`; + highlights[index] = highlight; + + return highlight; + }; + + const updateHighlights = (moveDirection?: ReflowDirection) => { + if (!highlights?.length || !moveDirection) return; + highlights.map((highlight: HighlightInfo, index: number) => { + let updatedHighlight: HighlightInfo = highlight; + if (highlight.isNewLayer || !highlight.height) + updatedHighlight = updateHighlight(index); + return updatedHighlight; + }); + }; + + const toggleHighlightVisibility = ( + arr: HighlightInfo[], + selected: HighlightInfo, + ): void => { + arr.forEach((each: HighlightInfo) => { + const el = each.el as HTMLElement; + // if (!isVerticalStack) el.style.opacity = "1"; + el.style.opacity = selected === each ? "1" : "0"; + }); + }; + + const updateSelection = (highlight: HighlightInfo): void => { + if (lastActiveHighlight) { + const lastEl = lastActiveHighlight?.el as HTMLElement; + if (lastEl) lastEl.style.backgroundColor = "rgba(223, 158, 206, 0.6)"; + } + const el = highlight.el as HTMLElement; + if (el) el.style.backgroundColor = "rgba(196, 139, 181, 1)"; + lastActiveHighlight = highlight; + }; + const highlightDropPosition = ( e: any, moveDirection: ReflowDirection, // acceleration: number, ): HighlightInfo | undefined => { - if (!highlights) - highlights = deriveHighlightsFromLayers( - allWidgets, - canvasId, - canvasWidth, - blocksToDraw.map((block) => block?.widgetId), - isFillWidget, - isMobile, - ); - // console.log("#### highlights", highlights); if (!highlights) return; - // updateHighlights(moveDirection); + updateHighlights(moveDirection); - const highlight: HighlightInfo | undefined = getHighlightPayload( - e, - moveDirection, - ); + const highlight: HighlightInfo = getHighlightPayload(e, moveDirection); + + if (!highlight) return; + + updateSelection(highlight); - // updateSelection(highlight); - // console.log("#### selection", highlight); - lastActiveHighlight = highlight; return highlight; }; @@ -184,17 +255,9 @@ export const useAutoLayoutHighlights = ({ e: any, moveDirection?: ReflowDirection, val?: XYCord, - ): HighlightInfo | undefined => { + ): HighlightInfo => { let base: HighlightInfo[] = []; - if (!highlights || !highlights.length) - highlights = deriveHighlightsFromLayers( - allWidgets, - canvasId, - canvasWidth, - blocksToDraw.map((block) => block?.widgetId), - isFillWidget, - isMobile, - ); + if (!highlights || !highlights.length) highlights = getDropPositions(); base = highlights; const pos: XYCord = { @@ -204,8 +267,8 @@ export const useAutoLayoutHighlights = ({ let filteredHighlights: HighlightInfo[] = []; filteredHighlights = getViableDropPositions(base, pos, moveDirection); - if (!filteredHighlights || !filteredHighlights?.length) return; - const arr = [...filteredHighlights]?.sort((a, b) => { + + const arr = filteredHighlights.sort((a, b) => { return ( calculateDistance( a, @@ -221,7 +284,7 @@ export const useAutoLayoutHighlights = ({ ) ); }); - + toggleHighlightVisibility(base, arr[0]); // console.log("#### arr", arr, base, moveDirection); return arr[0]; }; @@ -329,11 +392,7 @@ export const useAutoLayoutHighlights = ({ const getDropInfo = (val: XYCord): HighlightInfo | undefined => { if (lastActiveHighlight) return lastActiveHighlight; - const payload: HighlightInfo | undefined = getHighlightPayload( - null, - undefined, - val, - ); + const payload: HighlightInfo = getHighlightPayload(null, undefined, val); if (!payload) return; lastActiveHighlight = payload; return payload; diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index 7781a8e02600..aaf2ea93f030 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -3,7 +3,6 @@ import { OccupiedSpace } from "constants/CanvasEditorConstants"; import { CONTAINER_GRID_PADDING, GridDefaults, - MAIN_CONTAINER_WIDGET_ID, } from "constants/WidgetConstants"; import { debounce, isEmpty, throttle } from "lodash"; import { CanvasDraggingArenaProps } from "pages/common/CanvasArenas/CanvasDraggingArena"; @@ -238,7 +237,7 @@ export const useCanvasDragging = ( const scrollParent: Element | null = getNearestParentCanvas( slidingArenaRef.current, ); - + // console.log(`#### init variable: ${widgetName}`); let canvasIsDragging = false; let isUpdatingRows = false; let currentRectanglesToDraw: WidgetDraggingBlock[] = []; @@ -275,11 +274,9 @@ export const useCanvasDragging = ( stickyCanvasRef.current.height, ); slidingArenaRef.current.style.zIndex = ""; + // console.log(`#### reset canvas state: ${widgetName}`); canvasIsDragging = false; } - if (isDragging) { - setDraggingCanvas(MAIN_CONTAINER_WIDGET_ID); - } }; if (isDragging) { const startPoints = defaultHandlePositions; @@ -359,6 +356,7 @@ export const useCanvasDragging = ( }; const onFirstMoveOnCanvas = (e: any, over = false) => { + // console.log(`#### first move: ${widgetName}`); if ( !isResizing && isDragging && @@ -381,6 +379,7 @@ export const useCanvasDragging = ( logContainerJump(widgetId, speed, acceleration); containerJumpThresholdMetrics.clearMetrics(); // we can just use canvasIsDragging but this is needed to render the relative DragLayerComponent + // console.log(`#### set dragging canvas: ${widgetName}`); setDraggingCanvas(widgetId); } canvasIsDragging = true; @@ -567,6 +566,7 @@ export const useCanvasDragging = ( left: e.offsetX - startPoints.left - parentDiff.left, top: e.offsetY - startPoints.top - parentDiff.top, }; + // console.log("#### mouse move", delta); const drawingBlocks = blocksToDraw.map((each) => ({ ...each, left: each.left + delta.left, @@ -601,18 +601,15 @@ export const useCanvasDragging = ( } else if (!isUpdatingRows) { currentDirection.current = getMouseMoveDirection(e); triggerReflow(e, firstMove); - let highlight: HighlightInfo | undefined; if ( useAutoLayout && isCurrentDraggedCanvas && currentDirection.current !== ReflowDirection.UNSET - ) { - // debounce(() => { - // highlightDropPosition(e, currentDirection.current); - // }, 100)(); - highlight = highlightDropPosition(e, currentDirection.current); - } - renderBlocks(highlight); + ) + debounce(() => { + highlightDropPosition(e, currentDirection.current); + }, 100)(); + renderBlocks(); } scrollObj.lastMouseMoveEvent = { offsetX: e.offsetX, @@ -680,7 +677,7 @@ export const useCanvasDragging = ( }, ); - const renderBlocks = (highlight?: HighlightInfo | undefined) => { + const renderBlocks = () => { if ( slidingArenaRef.current && isCurrentDraggedCanvas && @@ -702,18 +699,6 @@ export const useCanvasDragging = ( drawBlockOnCanvas(each); }); } - if (highlight) { - canvasCtx.fillStyle = "rgba(196, 139, 181, 1)"; - const { height, posX, posY, width } = highlight; - let val = 0; - if ( - widgetId === MAIN_CONTAINER_WIDGET_ID && - scrollParent?.scrollTop - ) - val = scrollParent.scrollTop - 20; - canvasCtx.fillRect(posX, posY - val, width, height); - canvasCtx.save(); - } canvasCtx.restore(); } }; diff --git a/app/client/src/resizable/resizenreflow/index.tsx b/app/client/src/resizable/resizenreflow/index.tsx index e507d546c902..d8d33ec4f164 100644 --- a/app/client/src/resizable/resizenreflow/index.tsx +++ b/app/client/src/resizable/resizenreflow/index.tsx @@ -540,8 +540,8 @@ export function ReflowResizable(props: ResizableProps) { }} immediate={newDimensions.reset ? true : false} to={{ - width: widgetWidth, - height: widgetHeight, + width: props.isAffectedByDrag ? "auto" : widgetWidth, + height: props.isMobile ? "auto" : widgetHeight, transform: `translate3d(${newDimensions.x}px,${newDimensions.y}px,0)`, }} > diff --git a/app/client/src/sagas/AutoLayoutUpdateSagas.tsx b/app/client/src/sagas/AutoLayoutUpdateSagas.tsx index d61eb421f04a..8bafdebf5658 100644 --- a/app/client/src/sagas/AutoLayoutUpdateSagas.tsx +++ b/app/client/src/sagas/AutoLayoutUpdateSagas.tsx @@ -8,7 +8,6 @@ import { ResponsiveBehavior } from "components/constants"; import log from "loglevel"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; import { all, call, put, select, takeLatest } from "redux-saga/effects"; -import { updateWidgetPositions } from "utils/autoLayout/positionUtils"; import { alterLayoutForDesktop, alterLayoutForMobile, diff --git a/app/client/src/sagas/AutoLayoutUtils.ts b/app/client/src/sagas/AutoLayoutUtils.ts index f071ac11d24a..7dca477f6bb4 100644 --- a/app/client/src/sagas/AutoLayoutUtils.ts +++ b/app/client/src/sagas/AutoLayoutUtils.ts @@ -8,7 +8,6 @@ import { LayerChild, } from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; -import { updateWidgetPositions } from "utils/autoLayout/positionUtils"; function getCanvas(widgets: CanvasWidgetsReduxState, containerId: string) { const container = widgets[containerId]; @@ -61,7 +60,7 @@ export function* wrapChildren( canvas = { ...canvas, flexLayers }; widgets[canvas.widgetId] = canvas; // update size - const updatedWidgets = updateWidgetPositions(widgets, canvas.widgetId); + const updatedWidgets = updateSizeOfAllChildren(widgets, canvas.widgetId); return updatedWidgets; } @@ -115,7 +114,7 @@ export function* updateFlexLayersOnDelete( widgets[parentId] = parent; if (layerIndex === -1) return widgets; - return updateWidgetPositions(widgets, parentId); + return updateFlexChildColumns(widgets, layerIndex, parentId); } // TODO: refactor these implementations export function updateFillChildStatus( @@ -264,7 +263,7 @@ export function alterLayoutForMobile( if (widget.responsiveBehavior === ResponsiveBehavior.Fill) { widget.mobileRightColumn = 64; - widget.mobileLeftColumn = 0; + widget.leftColumn = 0; } else if ( widget.responsiveBehavior === ResponsiveBehavior.Hug && widget.minWidth @@ -272,23 +271,20 @@ export function alterLayoutForMobile( const { minWidth, rightColumn } = widget; const columnSpace = canvasWidth / 64; if (columnSpace * rightColumn < minWidth) { - widget.mobileLeftColumn = 0; + widget.leftColumn = 0; widget.mobileRightColumn = Math.min( Math.floor(minWidth / columnSpace), 64, ); } } - widget.mobileTopRow = widget.topRow; - widget.mobileBottomRow = widget.bottomRow; - // TODO: Preet - update container row info if height changes on account of flex wrap. + widgets = alterLayoutForMobile( widgets, child, (canvasWidth * (widget.mobileRightColumn || 1)) / 64, ); widgets[child] = widget; - widgets = updateWidgetPositions(widgets, child, true); } return widgets; } @@ -305,10 +301,11 @@ export function alterLayoutForDesktop( return widgets; if (!children || !children.length) return widgets; - widgets = updateWidgetPositions(widgets, parentId, false); + widgets = updateSizeOfAllChildren(widgets, parentId); for (const child of children) { widgets = alterLayoutForDesktop(widgets, child); } + return widgets; } diff --git a/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts b/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts index e931cedc4517..449da709c868 100644 --- a/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts +++ b/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts @@ -18,10 +18,12 @@ import log from "loglevel"; import { HighlightInfo } from "pages/common/CanvasArenas/hooks/useAutoLayoutHighlights"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; import { all, call, put, select, takeLatest } from "redux-saga/effects"; -import { updateFlexChildColumns } from "sagas/AutoLayoutUtils"; +import { + updateFlexChildColumns, + updateSizeOfAllChildren, +} from "sagas/AutoLayoutUtils"; import { getWidgets } from "sagas/selectors"; import { getUpdateDslAfterCreatingChild } from "sagas/WidgetAdditionSagas"; -import { updateWidgetPositions } from "utils/autoLayout/positionUtils"; function* addWidgetAndReorderSaga( actionPayload: ReduxAction<{ @@ -112,8 +114,14 @@ function* autoLayoutReorderSaga( rowIndex, }, ); + let updatedWidgetsAfterResizing = updatedWidgets; + if (direction === LayoutDirection.Vertical) + updatedWidgetsAfterResizing = updateSizeOfAllChildren( + updatedWidgets, + parentId, + ); - yield put(updateAndSaveLayout(updatedWidgets)); + yield put(updateAndSaveLayout(updatedWidgetsAfterResizing)); log.debug("reorder computations took", performance.now() - start, "ms"); } catch (e) { // console.error(e); @@ -218,12 +226,7 @@ function* reorderAutolayoutChildren(params: { }; } - const widgetsAfterPositionUpdate = updateWidgetPositions( - updatedWidgets, - parentId, - ); - - return widgetsAfterPositionUpdate; + return updatedWidgets; } /** @@ -245,14 +248,13 @@ function updateRelationships( const orphans = movedWidgets.filter( (item) => widgets[item].parentId !== parentId, ); - const prevParents: string[] = []; + let prevParentId: string | undefined; if (orphans && orphans.length) { //parent has changed orphans.forEach((item) => { // remove from previous parent - const prevParentId = widgets[item].parentId; + prevParentId = widgets[item].parentId; if (prevParentId !== undefined) { - prevParents.push(prevParentId); const prevParent = Object.assign({}, widgets[prevParentId]); if (prevParent.children && isArray(prevParent.children)) { const updatedPrevParent = { @@ -275,11 +277,8 @@ function updateRelationships( }; }); } - if (prevParents.length) { - for (const id of prevParents) { - const updatedWidgets = updateWidgetPositions(widgets, id); - return updatedWidgets; - } + if (prevParentId) { + return updateSizeOfAllChildren(widgets, prevParentId); } return widgets; } @@ -319,7 +318,7 @@ function updateExistingLayer( rowIndex: number, ): CanvasWidgetsReduxState { try { - const widgets: CanvasWidgetsReduxState = { ...allWidgets }; + const widgets: CanvasWidgetsReduxState = Object.assign({}, allWidgets); const canvas = widgets[parentId]; if (!canvas || !newLayer) return widgets; diff --git a/app/client/src/sagas/WidgetOperationSagas.tsx b/app/client/src/sagas/WidgetOperationSagas.tsx index 3e515b92a8ad..bd8e76dda814 100644 --- a/app/client/src/sagas/WidgetOperationSagas.tsx +++ b/app/client/src/sagas/WidgetOperationSagas.tsx @@ -113,6 +113,7 @@ import { collisionCheckPostReflow, getBottomRowAfterReflow, } from "utils/reflowHookUtils"; +import { updateChildrenSize } from "./AutoLayoutUtils"; import { getCanvasSizeAfterWidgetMove } from "./CanvasSagas/DraggingCanvasSagas"; import widgetAdditionSagas from "./WidgetAdditionSagas"; import { traverseTreeAndExecuteBlueprintChildOperations } from "./WidgetBlueprintSagas"; @@ -155,7 +156,6 @@ import { } from "./WidgetOperationUtils"; import { widgetSelectionSagas } from "./WidgetSelectionSagas"; import { getAllPaths } from "ce/workers/Evaluation/evaluationUtils"; -import { updateWidgetPositions } from "utils/autoLayout/positionUtils"; export function* updateAllChildCanvasHeights( currentContainerLikeWidgetId: string, @@ -255,9 +255,10 @@ export function* resizeSaga(resizeAction: ReduxAction) { bottomRow: updatedCanvasBottomRow, }; } - const updatedWidgetsAfterResizing = updateWidgetPositions( + const updatedWidgetsAfterResizing = updateChildrenSize( movedWidgets, parentId, + widgetId, ); log.debug("resize computations took", performance.now() - start, "ms"); yield put(stopReflowAction()); diff --git a/app/client/src/utils/autoLayout/flexWidgetUtils.ts b/app/client/src/utils/autoLayout/flexWidgetUtils.ts deleted file mode 100644 index 7f0bf3b7d103..000000000000 --- a/app/client/src/utils/autoLayout/flexWidgetUtils.ts +++ /dev/null @@ -1,113 +0,0 @@ -export function getRightColumn(widget: any, isMobile: boolean): number { - return isMobile && widget.mobileRightColumn !== undefined - ? widget.mobileRightColumn - : widget.rightColumn; -} - -export function setRightColumn( - widget: any, - val: number | null, - isMobile: boolean, -): any { - if (val === null) return widget; - return isMobile && widget.mobileRightColumn !== undefined - ? { ...widget, mobileRightColumn: val } - : { ...widget, rightColumn: val }; -} - -export function getLeftColumn(widget: any, isMobile: boolean): number { - return isMobile && widget.mobileLeftColumn !== undefined - ? widget.mobileLeftColumn - : widget.leftColumn; -} - -export function setLeftColumn( - widget: any, - val: number | null, - isMobile: boolean, -): any { - if (val === null) return widget; - return isMobile && widget.mobileLeftColumn !== undefined - ? { ...widget, mobileLeftColumn: val } - : { ...widget, leftColumn: val }; -} - -export function getTopRow(widget: any, isMobile: boolean): number { - return isMobile && widget.mobileTopRow !== undefined - ? widget.mobileTopRow - : widget.topRow; -} - -export function setTopRow( - widget: any, - val: number | null, - isMobile: boolean, -): any { - if (val === null) return widget; - return isMobile && widget.mobileTopRow !== undefined - ? { ...widget, mobileTopRow: val } - : { ...widget, topRow: val }; -} - -export function getBottomRow(widget: any, isMobile: boolean): number { - return isMobile && widget.mobileBottomRow !== undefined - ? widget.mobileBottomRow - : widget.bottomRow; -} - -export function setBottomRow( - widget: any, - val: number | null, - isMobile: boolean, -): any { - if (val === null) return widget; - return isMobile && widget.mobileBottomRow !== undefined - ? { ...widget, mobileBottomRow: val } - : { ...widget, bottomRow: val }; -} - -export function setColumns( - widget: any, - left: number, - right: number, - isMobile: boolean, -) { - return setRightColumn(setLeftColumn(widget, left, isMobile), right, isMobile); -} - -export function setDimensions( - widget: any, - top: number | null, - bottom: number | null, - left: number | null, - right: number | null, - isMobile: boolean, -) { - try { - return setBottomRow( - setTopRow( - setLeftColumn(setRightColumn(widget, right, isMobile), left, isMobile), - top, - isMobile, - ), - bottom, - isMobile, - ); - } catch (e) { - console.log(e); - return widget; - } -} - -export function getWidgetWidth(widget: any, isMobile: boolean): number { - return getRightColumn(widget, isMobile) - getLeftColumn(widget, isMobile); -} - -export function getWidgetHeight(widget: any, isMobile: boolean): number { - return getBottomRow(widget, isMobile) - getTopRow(widget, isMobile); -} - -export function getWidgetRows(widget: any, isMobile: boolean): number { - const divisor = widget.parentRowSpace === 1 ? 10 : 1; - return getBottomRow(widget, isMobile) / divisor - getTopRow(widget, isMobile); -} diff --git a/app/client/src/utils/autoLayout/highlightUtils.ts b/app/client/src/utils/autoLayout/highlightUtils.ts deleted file mode 100644 index 1a02172d0532..000000000000 --- a/app/client/src/utils/autoLayout/highlightUtils.ts +++ /dev/null @@ -1,448 +0,0 @@ -import { FlexLayerAlignment } from "components/constants"; -import { - DEFAULT_HIGHLIGHT_SIZE, - FlexLayer, - LayerChild, -} from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; -import { - CONTAINER_GRID_PADDING, - GridDefaults, - MAIN_CONTAINER_WIDGET_ID, - WIDGET_PADDING, -} from "constants/WidgetConstants"; -import { HighlightInfo } from "pages/common/CanvasArenas/hooks/useAutoLayoutHighlights"; -import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; -import { - getLeftColumn, - getRightColumn, - getTopRow, - getWidgetHeight, - getWidgetWidth, -} from "./flexWidgetUtils"; -import { getTotalRowsOfAllChildren, Widget } from "./positionUtils"; - -const HORIZONTAL_HIGHLIGHT_MARGIN = 4; -// TODO: Preet - update logic to account for flex wrap on mobile. -// For mobile use widget positions to place highlights. -/** - * @param allWidgets : CanvasWidgetsReduxState - * @param canvasId : string - * @param draggedWidgets : string[] - * @returns widgets: CanvasWidgetsReduxState - * - * This function is used to derive highlights from flex layers and stored in widget dsl. - */ -export function deriveHighlightsFromLayers( - allWidgets: CanvasWidgetsReduxState, - canvasId: string, - mainCanvasWidth = 0, - draggedWidgets: string[] = [], - hasFillWidget = false, - isMobile = false, -): HighlightInfo[] { - const widgets = { ...allWidgets }; - try { - const canvas = widgets[canvasId]; - if (!canvas) return []; - - const { canvasWidth, columnSpace } = getCanvasDimensions( - canvas, - widgets, - mainCanvasWidth, - isMobile, - ); - const layers: FlexLayer[] = canvas.flexLayers || []; - const highlights: HighlightInfo[] = []; - let childCount = 0; - let layerIndex = 0; - // TODO: remove offsetTop and use child positions after widget positioning on grid is solved. - let offsetTop = HORIZONTAL_HIGHLIGHT_MARGIN; // used to calculate distance of a highlight from parents's top. - for (const layer of layers) { - /** - * If the layer is empty, after discounting the dragged widgets, - * then don't process it for vertical highlights. - */ - const isEmpty: boolean = - layer?.children?.filter( - (child: LayerChild) => draggedWidgets.indexOf(child.id) === -1, - ).length === 0; - // TODO: use getHeightOfFixedCanvas to measure the height of the layer. - const tallestChild = layer.children?.reduce((acc, child) => { - const widget = widgets[child.id]; - return Math.max( - acc, - getWidgetHeight(widget, isMobile) * widget.parentRowSpace, - ); - }, 0); - const childrenRows = getTotalRowsOfAllChildren( - widgets, - layer.children?.map((child) => child.id) || [], - isMobile, - ); - - const payload: VerticalHighlightsPayload = generateVerticalHighlights({ - widgets, - layer, - childCount, - layerIndex, - height: tallestChild, - offsetTop, - canvasWidth, - canvasId, - columnSpace, - draggedWidgets, - isMobile, - }); - - if (!isEmpty) { - /** - * Add a layer of horizontal highlights before each flex layer - * to account for new vertical drop positions. - */ - highlights.push( - ...generateHorizontalHighlights( - childCount, - layerIndex, - offsetTop, - canvasWidth, - canvasId, - hasFillWidget, - ), - ); - - highlights.push(...payload.highlights); - offsetTop += childrenRows * 10 || 0; - layerIndex += 1; - } - childCount += payload.childCount; - } - // Add a layer of horizontal highlights for the empty space at the bottom of a stack. - highlights.push( - ...generateHorizontalHighlights( - childCount, - layerIndex, - offsetTop, - canvasWidth, - canvasId, - hasFillWidget, - ), - ); - return highlights; - } catch (e) { - // console.error(e); - return []; - } -} -interface VerticalHighlightsPayload { - childCount: number; - highlights: HighlightInfo[]; -} - -function generateVerticalHighlights(data: { - widgets: CanvasWidgetsReduxState; - layer: FlexLayer; - childCount: number; - layerIndex: number; - height: number; - offsetTop: number; - canvasWidth: number; - canvasId: string; - columnSpace: number; - draggedWidgets: string[]; - isMobile: boolean; -}): VerticalHighlightsPayload { - const { - canvasId, - canvasWidth, - childCount, - columnSpace, - draggedWidgets, - height, - isMobile, - layer, - layerIndex, - offsetTop, - widgets, - } = data; - const { children } = layer; - - let count = 0; - const startChildren = [], - centerChildren = [], - endChildren = []; - let startColumns = 0, - endColumns = 0; - - for (const child of children) { - const widget = widgets[child.id]; - if (!widget) continue; - count += 1; - if (draggedWidgets.indexOf(child.id) > -1) continue; - if (child.align === FlexLayerAlignment.End) { - endChildren.push(widget); - endColumns += getWidgetWidth(widget, isMobile); - } else if (child.align === FlexLayerAlignment.Center) { - centerChildren.push(widget); - } else { - startChildren.push(widget); - startColumns += getWidgetWidth(widget, isMobile); - } - } - - return { - highlights: [ - ...generateHighlightsForSubWrapper({ - arr: startChildren, - childCount, - layerIndex, - alignment: FlexLayerAlignment.Start, - height, - offsetTop, - canvasId, - parentColumnSpace: columnSpace, - parentRowSpace: widgets[canvasId].parentRowSpace, - canvasWidth, - isMobile, - }), - ...generateHighlightsForSubWrapper({ - arr: centerChildren, - childCount: childCount + startChildren.length, - layerIndex, - alignment: FlexLayerAlignment.Center, - height, - offsetTop, - canvasId, - parentColumnSpace: columnSpace, - parentRowSpace: widgets[canvasId].parentRowSpace, - canvasWidth, - avoidInitialHighlight: startColumns > 25 || endColumns > 25, - isMobile, - }), - ...generateHighlightsForSubWrapper({ - arr: endChildren, - childCount: childCount + startChildren.length + centerChildren.length, - layerIndex, - alignment: FlexLayerAlignment.End, - height, - offsetTop, - canvasId, - parentColumnSpace: columnSpace, - parentRowSpace: widgets[canvasId].parentRowSpace, - canvasWidth, - isMobile, - }), - ], - childCount: count, - }; -} - -function generateHighlightsForSubWrapper(data: { - arr: any[]; - childCount: number; - layerIndex: number; - alignment: FlexLayerAlignment; - height: number; - offsetTop: number; - canvasId: string; - parentColumnSpace: number; - parentRowSpace: number; - canvasWidth: number; - avoidInitialHighlight?: boolean; - isMobile: boolean; -}): HighlightInfo[] { - const { - alignment, - arr, - avoidInitialHighlight, - canvasId, - canvasWidth, - childCount, - height, - isMobile, - layerIndex, - offsetTop, - parentColumnSpace, - } = data; - const res: HighlightInfo[] = []; - let count = 0; - for (const child of arr) { - const left = getLeftColumn(child, isMobile); - res.push({ - isNewLayer: false, - index: count + childCount, - layerIndex, - rowIndex: count, - alignment, - posX: left * parentColumnSpace, - posY: - getTopRow(child, isMobile) * child.parentRowSpace + - HORIZONTAL_HIGHLIGHT_MARGIN, - width: DEFAULT_HIGHLIGHT_SIZE, - height, - isVertical: true, - canvasId, - }); - count += 1; - } - - if (!avoidInitialHighlight) { - const lastChild: Widget = arr && arr.length ? arr[arr.length - 1] : null; - res.push({ - isNewLayer: false, - index: count + childCount, - layerIndex, - rowIndex: count, - alignment, - posX: getPositionForInitialHighlight( - res, - alignment, - arr && arr.length - ? getRightColumn(lastChild, isMobile) * parentColumnSpace - : 0, - canvasWidth, - canvasId, - ), - posY: - lastChild === null - ? offsetTop - : getTopRow(lastChild, isMobile) * lastChild?.parentRowSpace + - HORIZONTAL_HIGHLIGHT_MARGIN, - width: DEFAULT_HIGHLIGHT_SIZE, - height, - isVertical: true, - canvasId, - }); - } - return res; -} - -/** - * Get the position of the initial / final highlight for an alignment. - * @param highlights | HighlightInfo[] : highlights for the current alignment - * @param alignment | FlexLayerAlignment : alignment of the current highlights - * @param posX | number : end position of the last widget in the current alignment. (rightColumn * columnSpace) - * @param containerWidth | number : width of the container - * @param canvasId | string : id of the canvas - * @returns number - */ -function getPositionForInitialHighlight( - highlights: HighlightInfo[], - alignment: FlexLayerAlignment, - posX: number, - containerWidth: number, - canvasId: string, -): number { - if (alignment === FlexLayerAlignment.End) { - return containerWidth - (canvasId !== MAIN_CONTAINER_WIDGET_ID ? 6 : 0); - } else if (alignment === FlexLayerAlignment.Center) { - if (!highlights.length) return containerWidth / 2; - return posX; - } else { - if (!highlights.length) return 2; - return posX; - } -} - -/** - * Create a layer of horizontal alignments to denote new vertical drop zones. - * - if the layer has a fill widget, - * - Start alignment spans the entire container width. - * - else each layer takes up a third of the container width and are placed side to side. - * @param childIndex | number : child count of children placed in preceding layers. - * @param layerIndex | number - * @param offsetTop | number - * @param containerWidth | number - * @param canvasId | - * @param hasFillWidget | boolean : whether the layer has a fill widget or not. - * @returns HighlightInfo[] - */ -function generateHorizontalHighlights( - childIndex: number, - layerIndex: number, - offsetTop: number, - containerWidth: number, - canvasId: string, - hasFillWidget: boolean, -): HighlightInfo[] { - const width = containerWidth / 3; - const arr: HighlightInfo[] = []; - [ - FlexLayerAlignment.Start, - FlexLayerAlignment.Center, - FlexLayerAlignment.End, - ].forEach((alignment, index) => { - arr.push({ - isNewLayer: true, - index: childIndex, - layerIndex, - rowIndex: 0, - alignment, - posX: hasFillWidget - ? alignment === FlexLayerAlignment.Start - ? 0 - : containerWidth - : width * index, - posY: offsetTop, - width: hasFillWidget - ? alignment === FlexLayerAlignment.Start - ? containerWidth - : 0 - : width, - height: DEFAULT_HIGHLIGHT_SIZE, - isVertical: false, - canvasId, - }); - }); - return arr; -} - -function getCanvasDimensions( - canvas: any, - widgets: CanvasWidgetsReduxState, - mainCanvasWidth: number, - isMobile: boolean, -): { canvasWidth: number; columnSpace: number } { - const canvasWidth: number = getCanvasWidth( - canvas, - widgets, - mainCanvasWidth, - isMobile, - ); - - let padding = (CONTAINER_GRID_PADDING + WIDGET_PADDING) * 2; - if ( - canvas.widgetId === MAIN_CONTAINER_WIDGET_ID || - canvas.type === "CONTAINER_WIDGET" - ) { - //For MainContainer and any Container Widget padding doesn't exist coz there is already container padding. - padding = CONTAINER_GRID_PADDING * 2; - } - if (canvas.noPad) { - // Widgets like ListWidget choose to have no container padding so will only have widget padding - padding = WIDGET_PADDING * 2; - } - const columnSpace: number = - (canvasWidth - padding) / GridDefaults.DEFAULT_GRID_COLUMNS; - - return { canvasWidth: canvasWidth - padding, columnSpace }; -} - -function getCanvasWidth( - canvas: any, - widgets: CanvasWidgetsReduxState, - mainCanvasWidth: number, - isMobile: boolean, -): number { - // TODO: @Preet - Update the logic to account for padding at each level. - if (!mainCanvasWidth) return 0; - if (canvas.widgetId === MAIN_CONTAINER_WIDGET_ID) return mainCanvasWidth; - let widget = canvas; - let columns = getWidgetWidth(widget, isMobile); - let width = columns / GridDefaults.DEFAULT_GRID_COLUMNS; - while (widget.widgetId !== MAIN_CONTAINER_WIDGET_ID) { - columns = getWidgetWidth(widget, isMobile); - width *= columns / GridDefaults.DEFAULT_GRID_COLUMNS; - widget = widgets[widget.parentId]; - } - return width * mainCanvasWidth; -} diff --git a/app/client/src/utils/autoLayout/positionUtils.ts b/app/client/src/utils/autoLayout/positionUtils.ts deleted file mode 100644 index 63188e2c4d98..000000000000 --- a/app/client/src/utils/autoLayout/positionUtils.ts +++ /dev/null @@ -1,579 +0,0 @@ -import { FlexLayerAlignment, ResponsiveBehavior } from "components/constants"; -import { FlexLayer } from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; -import { GridDefaults } from "constants/WidgetConstants"; -import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; -import { WidgetProps } from "widgets/BaseWidget"; -import { - getBottomRow, - getTopRow, - getWidgetHeight, - getWidgetRows, - getWidgetWidth, - setDimensions, -} from "./flexWidgetUtils"; - -export type Widget = WidgetProps & { - children?: string[] | undefined; -}; - -interface AlignmentInfo { - alignment: FlexLayerAlignment; - columns: number; - children: Widget[]; -} - -interface Row extends AlignmentInfo { - height: number; -} - -/** - * Calculate widget position on canvas. - * Logic - - * 1. If widget contains flexLayers, then update positions for all widgets in layers. - * 2. Else if widget contains children (implies fixed canvas), calculate the total height assumed by children. - * 3. If totalChildrenHeight in either case > widgetHeight and widget.parent.type === ContainerWidget || MainContainer, - * then update height of the widget and its parent. - */ -export function updateWidgetPositions( - allWidgets: CanvasWidgetsReduxState, - parentId: string, - isMobile = false, -): CanvasWidgetsReduxState { - let widgets = { ...allWidgets }; - try { - const parent = widgets[parentId]; - if (!parent) return widgets; - - let height = 0; - if (parent.flexLayers && parent.flexLayers?.length) { - /** - * For each flex layer, calculate position of child widgets - * and calculate the total height of all layers. - */ - for (const layer of parent.flexLayers) { - const payload: { - height: number; - widgets: CanvasWidgetsReduxState; - } = calculateWidgetPositions(widgets, layer, height, isMobile); - widgets = payload.widgets; - height += payload.height; - } - } else if (parent.children?.length) { - // calculate the total height required by all widgets. - height = getHeightOfFixedCanvas(widgets, parent, isMobile); - } - - const divisor = parent.parentRowSpace === 1 ? 10 : 1; - const parentHeight = getWidgetRows(parent, isMobile); - if (parentHeight <= height) { - /** - * if children height is greater than parent height, - * update the parent height to match the children height - * and add a buffer of 1 row to render the new layer highlights. - */ - const parentTopRow = getTopRow(parent, isMobile); - const updatedParent = setDimensions( - parent, - parentTopRow, - (parentTopRow + height + 1) * divisor, - null, - null, - isMobile, - ); - widgets = { ...widgets, [parent.widgetId]: updatedParent }; - } - const shouldUpdateHeight = - parent.parentId && - ["CONTAINER_WIDGET", "CANVAS_WIDGET"].includes( - allWidgets[parent.parentId].type, - ) && - parentHeight <= height; - - if (shouldUpdateHeight && parent.parentId) - return updateWidgetPositions(widgets, parent.parentId, isMobile); - return widgets; - } catch (e) { - // console.error(e); - return widgets; - } -} - -function calculateWidgetPositions( - allWidgets: CanvasWidgetsReduxState, - layer: FlexLayer, - topRow: number, - isMobile = false, -): { height: number; widgets: CanvasWidgetsReduxState } { - /** - * Get information break down on each alignment within the layer. - * Information - children, columns, alignment. - * Also, retrieve the length of each fill widget within this layer. - */ - const { fillWidgetLength, info } = extractAlignmentInfo( - allWidgets, - layer, - isMobile, - ); - /** - * Check if this layer is wrapped by css flex. - * if isMobile && totalColumns > 64 => true - */ - const isFlexWrapped: boolean = - isMobile && - info.reduce((acc, curr) => { - return acc + curr.columns; - }, 0) > GridDefaults.DEFAULT_GRID_COLUMNS; - - if (isFlexWrapped) - return updatePositionsForFlexWrap( - allWidgets, - info, - topRow, - fillWidgetLength, - isMobile, - ); - - return placeWidgetsWithoutWrap( - allWidgets, - info, - topRow, - fillWidgetLength, - isMobile, - ); -} - -/** - * Place all widgets in a particular row and update their positions. - * - * @param allWidgets | CanvasWidgetsReduxState : List of all widgets. - * @param arr | AlignmentInfo[] : Array of all alignments to be placed in this row. - * @param topRow | number : Starting row for placing the widgets. - * @param fillWidgetLength | number : Size of each fill widget in this row. - * @param isMobile : boolean : if the current viewport is mobile. default is false. - * @param totalHeight | number : total height assumed by the widgets in this row. - * @returns { height: number; widgets: CanvasWidgetsReduxState } - */ -function placeWidgetsWithoutWrap( - allWidgets: CanvasWidgetsReduxState, - arr: AlignmentInfo[], - topRow: number, - fillWidgetLength: number, - isMobile = false, - totalHeight = 0, -): { height: number; widgets: CanvasWidgetsReduxState } { - let widgets = { ...allWidgets }; - /** - * Get the size (columns: number) of each alignment in this row. - */ - const { centerSize, endSize, startSize } = getAlignmentSizeInfo(arr); - let maxHeight = totalHeight ? totalHeight : 0; - for (const each of arr) { - // Get the starting left column for each alignment in this row. - let left = getStartingPosition( - each.alignment, - startSize, - centerSize, - endSize, - each.columns, - ); - for (const widget of each.children) { - const height = getWidgetHeight(widget, isMobile); - const width = - widget.responsiveBehavior === ResponsiveBehavior.Fill - ? fillWidgetLength - : getWidgetWidth(widget, isMobile); - if (!totalHeight) maxHeight = Math.max(maxHeight, height); - const updatedWidget = setDimensions( - widget, - topRow, - topRow + height, - left, - left + width, - isMobile, - ); - widgets = { - ...widgets, - [widget.widgetId]: { - ...updatedWidget, - }, - }; - left += width; - } - } - - return { height: maxHeight, widgets }; -} - -/** - * Extract the space assumed by each alignment in the row. - * - Alignments are designed to grow and shrink equally. each starting with equal share of parent's width. - * - If one alignment needs more than its share of width to layout its children, - * then the remaining alignments shrink to make room, if possible. - * Logic: - * - Sort the input alignments in descending order their column requirements. - * - if each.columns > space / input.length (i.e. the alignment needs more columns than it's starting share.) - * - add the alignment to the output array. - * - recursively repeat the exercise for the remaining alignments and remaining space. - * - attribute equal space to all alignments in input. - * @param input | AlignmentInfo[] : Array of all alignments to be placed in this row. - * @param space | number : Total space available for placing the widgets. - * @param sizes | AlignmentInfo[] : Array of all alignments to be placed in this row. - * @returns AlignmentInfo[] - */ -function getAlignmentSizes( - input: AlignmentInfo[], - space: number, - sizes: AlignmentInfo[] = [], -): AlignmentInfo[] { - if (input.length === 0) return sizes; - const arr: AlignmentInfo[] = [...input].sort((a, b) => b.columns - a.columns); - if (arr[0].columns > space / arr.length) { - sizes.push(arr[0]); - arr.shift(); - return getAlignmentSizes( - arr, - space - sizes[sizes.length - 1].columns, - sizes, - ); - } else { - for (let i = 0; i < arr.length; i++) { - sizes.push({ ...arr[i], columns: space / arr.length }); - } - } - return sizes; -} - -/** - * Breakdown the current flex layer to extract information on each child alignment. - * Information for each alignment - children, columns, alignment. - * @param widgets | CanvasWidgetsReduxState: List of all widgets. - * @param layer | FlexLayer : current layer to be positioned on the canvas. - * @param isMobile | boolean - * @returns { info: AlignmentInfo[]; fillWidgetLength: number } - */ -function extractAlignmentInfo( - widgets: CanvasWidgetsReduxState, - layer: FlexLayer, - isMobile: boolean, -): { info: AlignmentInfo[]; fillWidgetLength: number } { - const startChildren = [], - centerChildren = [], - endChildren = [], - fillChildren = []; - let startColumns = 0, - centerColumns = 0, - endColumns = 0; - // Calculate the number of columns occupied by hug widgets in each alignment. - for (const child of layer.children) { - const widget = widgets[child.id]; - const isFillWidget = widget.responsiveBehavior === ResponsiveBehavior.Fill; - if (isFillWidget) fillChildren.push(child); - if (child.align === FlexLayerAlignment.Start) { - startChildren.push(widget); - if (!isFillWidget) startColumns += getWidgetWidth(widget, isMobile); - } else if (child.align === FlexLayerAlignment.Center) { - centerChildren.push(widget); - if (!isFillWidget) centerColumns += getWidgetWidth(widget, isMobile); - } else if (child.align === FlexLayerAlignment.End) { - endChildren.push(widget); - if (!isFillWidget) endColumns += getWidgetWidth(widget, isMobile); - } - } - - const availableColumns: number = - GridDefaults.DEFAULT_GRID_COLUMNS - - startColumns - - centerColumns - - endColumns; - // Fill widgets are designed to take up parent's entire width on mobile viewport. - const fillWidgetLength: number = isMobile - ? GridDefaults.DEFAULT_GRID_COLUMNS - : availableColumns / fillChildren.length; - for (const child of fillChildren) { - if (child.align === FlexLayerAlignment.Start) { - startColumns += fillWidgetLength; - } else if (child.align === FlexLayerAlignment.Center) { - centerColumns += fillWidgetLength; - } else if (child.align === FlexLayerAlignment.End) { - endColumns += fillWidgetLength; - } - } - - return { - info: [ - { - alignment: FlexLayerAlignment.Start, - columns: startColumns, - children: startChildren, - }, - { - alignment: FlexLayerAlignment.Center, - columns: centerColumns, - children: centerChildren, - }, - { - alignment: FlexLayerAlignment.End, - columns: endColumns, - children: endChildren, - }, - ], - fillWidgetLength, - }; -} - -function getAlignmentSizeInfo( - arr: AlignmentInfo[], -): { startSize: number; centerSize: number; endSize: number } { - let startSize = 0, - centerSize = 0, - endSize = 0; - const sizes: { - alignment: FlexLayerAlignment; - columns: number; - }[] = getAlignmentSizes(arr, GridDefaults.DEFAULT_GRID_COLUMNS, []); - - for (const each of sizes) { - if (each.alignment === FlexLayerAlignment.Start) { - startSize = each.columns; - } else if (each.alignment === FlexLayerAlignment.Center) { - centerSize = each.columns; - } else if (each.alignment === FlexLayerAlignment.End) { - endSize = each.columns; - } - } - return { startSize, centerSize, endSize }; -} - -/** - * Find out which alignment is placed in which row. - * - * In case of flex wrap, - * - alignments within a FlexLayer are placed in multiple rows. - * Logic: - * - for each alignment in arr - * - if alignment.columns < 64 - * - add it to the current row (res[resIndex]) - * - and track the total occupied columns in this row (total) - * - else - * - add the current row to the output rows - * - and start a new row to repeat the process recursively. - * @param arr | AlignmentInfo[] : Array of alignments to be placed in this layer. - * @param res | AlignmentInfo[][] : Output array of alignments to be placed in this layer. - * @param resIndex | number : Last index of res. - * @returns AlignmentInfo[][] - */ -function getWrappedAlignmentInfo( - arr: AlignmentInfo[], - res: AlignmentInfo[][] = [[], [], []], - resIndex = 0, -): AlignmentInfo[][] { - if (arr.length === 1) { - res[resIndex].push(arr[0]); - return res; - } - let index = 0; - let total = 0; - for (const each of arr) { - if (total + each.columns >= GridDefaults.DEFAULT_GRID_COLUMNS) { - let x = index; - if (!res[resIndex].length) { - res[resIndex].push(each); - x += 1; - } - return getWrappedAlignmentInfo([...arr.slice(x)], res, resIndex + 1); - } - total += each.columns; - index += 1; - res[resIndex].push(each); - } - return res; -} - -/** - * @param allWidgets | CanvasWidgetsReduxState: all widgets. - * @param arr | AlignmentInfo[] : Array of alignments to be placed in this layer. - * @param topRow | number : Starting row to place the widgets. - * @param fillWidgetLength | number : Length of fill widgets. - * @param isMobile | boolean : Is mobile viewport. - * @returns { height: number; widgets: CanvasWidgetsReduxState } - */ -function updatePositionsForFlexWrap( - allWidgets: CanvasWidgetsReduxState, - arr: AlignmentInfo[], - topRow: number, - fillWidgetLength: number, - isMobile: boolean, -): { height: number; widgets: CanvasWidgetsReduxState } { - let widgets = { ...allWidgets }; - - const wrappedAlignments: AlignmentInfo[][] = getWrappedAlignmentInfo(arr); - - let top = topRow; - for (const each of wrappedAlignments) { - if (!each.length) break; - // if there is only one alignment in this row, this implies that it may be wrapped. - const payload = - each.length === 1 - ? placeWrappedWidgets(widgets, each[0], top, fillWidgetLength, isMobile) - : placeWidgetsWithoutWrap( - widgets, - each, - top, - fillWidgetLength, - isMobile, - ); - widgets = payload.widgets; - top += payload.height; - continue; - } - return { height: top - topRow, widgets }; -} - -function getStartingPosition( - alignment: FlexLayerAlignment, - startSize: number, - centerSize: number, - endSize: number, - columns: number, -): number { - if (alignment === FlexLayerAlignment.Start) { - return 0; - } else if (alignment === FlexLayerAlignment.Center) { - return startSize + centerSize / 2 - columns / 2; - } else if (alignment === FlexLayerAlignment.End) { - return startSize + centerSize + endSize - columns; - } - return 0; -} - -/** - * If the alignment requires more than 64 columns, it is wrapped. - * => a single alignment spans multiple layers. - * Logic: - * - Find out the number of rows required to position the widgets in the alignment. - * - Place each row normally. - * @param allWidgets | CanvasWidgetsReduxState: all widgets. - * @param alignment | AlignmentInfo: alignment to be positioned. - * @param topRow | number: top row to place the widgets. - * @param fillWidgetLength | number: length of fill widgets. - * @param isMobile | boolean: is mobile viewport. - * @returns { height: number; widgets: CanvasWidgetsReduxState } - */ -function placeWrappedWidgets( - allWidgets: CanvasWidgetsReduxState, - alignment: AlignmentInfo, - topRow: number, - fillWidgetLength: number, - isMobile = false, -): { height: number; widgets: CanvasWidgetsReduxState } { - let widgets = { ...allWidgets }; - - let startRow = topRow; - const rows: Row[] = getWrappedRows(alignment, [], isMobile); - for (const row of rows) { - const { alignment, children, columns, height } = row; - const result: { - height: number; - widgets: CanvasWidgetsReduxState; - } = placeWidgetsWithoutWrap( - widgets, - [{ alignment, children, columns }], - startRow, - fillWidgetLength, - isMobile, - height, - ); - widgets = result.widgets; - startRow += height; - } - - return { height: startRow - topRow, widgets }; -} - -/** - * Find out the number of rows required to position all the widgets in the given alignment. - * - for each child - * - if total columns < 64 - * - add it to the current row. - * - track the total consumed columns. - * - track the height of the current row. - * - else - * - add the current row to the output array. - * - recursively continue the process for remaining children. - * @param arr | AlignmentInfo: alignment to be positioned. - * @param rows | Row[]: output rows. - * @param isMobile | boolean: is mobile viewport. - * @returns Row[] - */ -function getWrappedRows( - arr: AlignmentInfo, - rows: Row[], - isMobile = false, -): Row[] { - const row: Row = { - alignment: arr.alignment, - children: [], - columns: 0, - height: 0, - }; - const space = GridDefaults.DEFAULT_GRID_COLUMNS; - const temp: Widget[] = []; - let columns = 0, - index = 0, - maxHeight = 0; - for (const child of arr.children) { - const width = getWidgetWidth(child, isMobile); - if (columns + width > space) { - row.children.push(...temp); - row.height = maxHeight; - row.columns = columns; - rows.push(row); - return getWrappedRows( - { - ...arr, - children: [...arr.children.slice(index)], - }, - [...rows], - isMobile, - ); - } - temp.push(child); - maxHeight = Math.max(maxHeight, getWidgetHeight(child, isMobile)); - columns += width; - index += 1; - } - if (temp.length) { - row.children.push(...temp); - row.height = maxHeight; - row.columns = columns; - rows.push(row); - } - return rows; -} - -function getHeightOfFixedCanvas( - widgets: CanvasWidgetsReduxState, - parent: Widget, - isMobile: boolean, -): number { - if (!parent.children || !parent.children.length) - return getWidgetRows(parent, isMobile); - return getTotalRowsOfAllChildren(widgets, parent.children, isMobile); -} - -export function getTotalRowsOfAllChildren( - widgets: CanvasWidgetsReduxState, - children: string[], - isMobile: boolean, -): number { - let top = 10000, - bottom = 0; - for (const childId of children) { - const child = widgets[childId]; - if (!child) continue; - const divisor = child.parentRowSpace === 1 ? 10 : 1; - top = Math.min(top, getTopRow(child, isMobile)); - bottom = Math.max(bottom, getBottomRow(child, isMobile) / divisor); - } - return bottom - top; -} diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index a5d33c781188..d6be90f1a30a 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -242,10 +242,7 @@ abstract class BaseWidget< this.props.bottomRow, this.props.parentColumnSpace, this.props.parentRowSpace, - this.props.mobileLeftColumn, this.props.mobileRightColumn, - this.props.mobileTopRow, - this.props.mobileBottomRow, this.props.isMobile, ); }; @@ -257,34 +254,19 @@ abstract class BaseWidget< bottomRow: number, parentColumnSpace: number, parentRowSpace: number, - mobileLeftColumn?: number, mobileRightColumn?: number, - mobileTopRow?: number, - mobileBottomRow?: number, isMobile?: boolean, ): { componentWidth: number; componentHeight: number; } { const right = - isMobile && mobileRightColumn !== undefined && parentColumnSpace !== 1 + isMobile && mobileRightColumn && parentColumnSpace !== 1 ? mobileRightColumn : rightColumn; - const left = - isMobile && mobileLeftColumn !== undefined && parentColumnSpace !== 1 - ? mobileLeftColumn - : leftColumn; - const top = - isMobile && mobileTopRow !== undefined && parentRowSpace !== 1 - ? mobileTopRow - : topRow; - const bottom = - isMobile && mobileBottomRow !== undefined && parentRowSpace !== 1 - ? mobileBottomRow - : bottomRow; return { - componentWidth: (right - left) * parentColumnSpace, - componentHeight: (bottom - top) * parentRowSpace, + componentWidth: (right - leftColumn) * parentColumnSpace, + componentHeight: (bottomRow - topRow) * parentRowSpace, }; } @@ -622,10 +604,7 @@ export type WidgetRowCols = { topRow: number; bottomRow: number; minHeight?: number; // Required to reduce the size of CanvasWidgets. - mobileLeftColumn?: number; mobileRightColumn?: number; - mobileTopRow?: number; - mobileBottomRow?: number; height?: number; }; diff --git a/app/client/src/widgets/ContainerWidget/widget/index.tsx b/app/client/src/widgets/ContainerWidget/widget/index.tsx index a1722128a787..3b0267542e0d 100644 --- a/app/client/src/widgets/ContainerWidget/widget/index.tsx +++ b/app/client/src/widgets/ContainerWidget/widget/index.tsx @@ -219,7 +219,7 @@ export class ContainerWidget extends BaseWidget< renderAsContainerComponent(props: ContainerWidgetProps) { return ( - + Date: Sun, 1 Jan 2023 18:02:26 -0500 Subject: [PATCH 332/708] Revert "Merge branch 'mobile/v1/main' into mobile/v1/wrap_positions" This reverts commit 7af6d1784588a38419e1ce8a4739b618b10238f8. --- .../appsmith/autoLayout/AutoLayoutLayer.tsx | 3 - .../appsmith/autoLayout/FlexBoxComponent.tsx | 330 ++-------- .../appsmith/autoLayout/FlexComponent.tsx | 119 ++-- .../editorComponents/DraggableComponent.tsx | 4 +- .../editorComponents/DropTargetComponent.tsx | 10 +- .../editorComponents/ResizableComponent.tsx | 14 +- .../hooks/useAutoLayoutHighlights.ts | 161 ++--- .../CanvasArenas/hooks/useCanvasDragging.ts | 37 +- .../src/resizable/resizenreflow/index.tsx | 4 +- .../src/sagas/AutoLayoutUpdateSagas.tsx | 1 + app/client/src/sagas/AutoLayoutUtils.ts | 17 +- .../CanvasSagas/AutoLayoutDraggingSagas.ts | 35 +- app/client/src/sagas/WidgetOperationSagas.tsx | 5 +- .../src/utils/autoLayout/flexWidgetUtils.ts | 113 ++++ .../src/utils/autoLayout/highlightUtils.ts | 448 ++++++++++++++ .../src/utils/autoLayout/positionUtils.ts | 579 ++++++++++++++++++ app/client/src/widgets/BaseWidget.tsx | 27 +- .../widgets/ContainerWidget/widget/index.tsx | 2 +- 18 files changed, 1363 insertions(+), 546 deletions(-) create mode 100644 app/client/src/utils/autoLayout/flexWidgetUtils.ts create mode 100644 app/client/src/utils/autoLayout/highlightUtils.ts create mode 100644 app/client/src/utils/autoLayout/positionUtils.ts diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx index ad772870bcb0..4ca922787adf 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx @@ -2,7 +2,6 @@ import React, { ReactNode } from "react"; import styled from "styled-components"; import { FlexDirection, LayoutDirection } from "components/constants"; -import { DRAG_MARGIN } from "widgets/constants"; /** * 1. Given a direction if should employ flex in perpendicular direction. @@ -20,7 +19,6 @@ export interface AutoLayoutLayerProps { widgetId: string; isMobile?: boolean; isCurrentCanvasDragging: boolean; - currentChildCount: number; wrapStart: boolean; wrapCenter: boolean; wrapEnd: boolean; @@ -39,7 +37,6 @@ const LayoutLayerContainer = styled.div<{ flex-wrap: ${({ wrap }) => (wrap ? "wrap" : "nowrap")}; width: 100%; - margin-top: ${DRAG_MARGIN}px; `; const SubWrapper = styled.div<{ diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx index 797f6f8d2496..a93b68e3eedc 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx @@ -11,7 +11,6 @@ import { import { APP_MODE } from "entities/App"; import { useSelector } from "react-redux"; import { getWidgets } from "sagas/selectors"; -import { isCurrentCanvasDragging } from "selectors/autoLayoutSelectors"; import { getAppMode } from "selectors/entitiesSelector"; import { getIsMobile } from "selectors/mainCanvasSelectors"; import AutoLayoutLayer from "./AutoLayoutLayer"; @@ -36,27 +35,6 @@ export interface FlexLayer { hasFillChild?: boolean; } -interface DropPositionProps { - isVertical: boolean; - alignment: FlexLayerAlignment; - layerIndex: number; - childIndex: number; - widgetId: string; - isNewLayer: boolean; - rowIndex: number; -} - -interface NewLayerProps { - alignment: FlexLayerAlignment; - childCount: number; - layerIndex: number; - isDragging: boolean; - isNewLayer: boolean; - isVertical: boolean; - map: { [key: string]: any }; - widgetId: string; -} - export const FlexContainer = styled.div<{ useAutoLayout?: boolean; direction?: LayoutDirection; @@ -82,35 +60,12 @@ export const FlexContainer = styled.div<{ overflow-y: ${({ isMainContainer, isMobile }) => isMainContainer || isMobile ? "auto" : "hidden"}; - padding: ${({ isDragging, leaveSpaceForWidgetName }) => - !isDragging && leaveSpaceForWidgetName ? "4px 4px 22px 4px;" : "4px;"}; + padding: ${({ leaveSpaceForWidgetName }) => + leaveSpaceForWidgetName ? "4px 4px 22px 4px;" : "0px;"}; `; export const DEFAULT_HIGHLIGHT_SIZE = 4; -export const DropPosition = styled.div<{ - isDragging: boolean; - isNewLayer: boolean; - isVertical: boolean; -}>` - width: ${({ isVertical }) => - isVertical ? `${DEFAULT_HIGHLIGHT_SIZE}px` : "calc(33% - 4px)"}; - height: ${({ isNewLayer, isVertical }) => - isVertical && !isNewLayer ? "auto" : `${DEFAULT_HIGHLIGHT_SIZE}px`}; - background-color: rgba(223, 158, 206, 0.6); - margin: ${({ isVertical }) => (isVertical ? "2px 4px" : "2px")}; - display: ${({ isDragging }) => (isDragging ? "block" : "none")}; - align-self: stretch; - opacity: 0; -`; - -export const NewLayerStyled = styled.div<{ - isDragging: boolean; -}>` - width: 100%; - height: ${({ isDragging }) => (isDragging ? "6px" : "0px")}; -`; - function FlexBoxComponent(props: FlexBoxProps) { // TODO: set isMobile as a prop at the top level const isMobile = useSelector(getIsMobile); @@ -123,13 +78,6 @@ function FlexBoxComponent(props: FlexBoxProps) { const { dragDetails } = useSelector( (state: AppState) => state.ui.widgetDragResize, ); - const draggedOn: string | undefined = dragDetails - ? dragDetails?.draggedOn - : undefined; - - const draggedWidget = dragDetails - ? dragDetails?.draggingGroupCenter?.widgetId - : ""; // const isDragging = useSelector(isCurrentCanvasDragging(props.widgetId)); const isDragging: boolean = dragDetails?.draggedOn !== undefined; @@ -138,15 +86,7 @@ function FlexBoxComponent(props: FlexBoxProps) { if (!props.children) return null; if (!props.useAutoLayout) return props.children; if (direction === LayoutDirection.Horizontal) { - return addDropPositions({ - arr: props.children as any, - childCount: 0, - layerIndex: 0, - alignment: FlexLayerAlignment.Start, - isVertical: true, - isNewLayer: true, - addInitialHighlight: true, - }); + return props.children; } /** @@ -175,192 +115,34 @@ function FlexBoxComponent(props: FlexBoxProps) { }); } - function DropPositionComponent(props: DropPositionProps) { - return ( - - ); - } - - function NewLayerComponent(props: NewLayerProps): JSX.Element { - const { childCount, isDragging, layerIndex, map, widgetId } = props; - - const { element: verticalHighlights } = processIndividualLayer( - { children: [], hasFillChild: false }, - childCount, - layerIndex, - map, - true, - ); - - return ( - - {verticalHighlights} - - ); - } - - const getDropPositionKey = ( - index: number, - alignment: FlexLayerAlignment, - layerIndex: number, - isVertical: boolean, - ): string => - `drop-layer-${props.widgetId}-${layerIndex}-${alignment}-${index}-${ - isVertical ? "vertical" : "horizontal" - }-${Math.random()}`; - - const addDropPositions = (data: { - arr: any[]; - childCount: number; - layerIndex: number; - alignment: FlexLayerAlignment; - isVertical: boolean; - isNewLayer: boolean; - addInitialHighlight: boolean; - }): any[] => { - const { - addInitialHighlight, - alignment, - arr, - childCount, - isNewLayer, - isVertical, - layerIndex, - } = data; - const res = addInitialHighlight - ? [ - , - ] - : []; - let count = 0; - if (arr) { - for (const item of arr) { - const widgetId = item - ? (item as JSX.Element)?.props.widgetId - : undefined; - if (draggedWidget && widgetId && draggedWidget === widgetId) continue; - count += 1; - res.push(item); - res.push( - , - ); - } - } - return res; - }; - - function getColumns(id: string, isMobile: boolean): number { - const widget = allWidgets[id]; - if (!widget) return 0; - return isMobile && widget.mobileRightColumn - ? widget.mobileRightColumn - : widget.rightColumn; - } - function processLayers(map: { [key: string]: any }) { const layers = []; - let childCount = 0; - let layerIndex = 0; + let index = 0; for (const layer of props.flexLayers) { - const isEmpty = - layer?.children?.filter( - (child: LayerChild) => child.id !== draggedWidget, - ).length === 0; - - !isEmpty && - layers.push( - , - ); - - const { count, element } = processIndividualLayer( - layer, - childCount, - layerIndex, - map, - ); - childCount += count; - if (!isEmpty) { - layerIndex += 1; - layers.push(element); - } + layers.push(processIndividualLayer(layer, map, index)); + index += 1; } - layers.push( - , - ); return layers; } + function getColumns(id: string, isMobile: boolean): number { + const widget = allWidgets[id]; + if (!widget) return 0; + return isMobile && + widget.mobileRightColumn !== undefined && + widget.mobileLeftColumn !== undefined + ? widget.mobileRightColumn - widget.mobileLeftColumn + : widget.rightColumn - widget.leftColumn; + } + function processIndividualLayer( layer: FlexLayer, - childCount: number, - index: number, map: { [key: string]: any }, - isNewLayer = false, + index: number, ) { const { children } = layer; - let count = 0; - let start = [], + const start = [], center = [], end = []; let startColumns = 0, @@ -368,7 +150,6 @@ function FlexBoxComponent(props: FlexBoxProps) { endColumns = 0; for (const child of children) { - count += 1; const widget = map[child.id]; if (child.align === "end") { @@ -382,64 +163,25 @@ function FlexBoxComponent(props: FlexBoxProps) { startColumns += getColumns(child.id, isMobile); } } - /** - * Add drop positions - */ - const startLength = start.length, - centerLength = center.length; - start = addDropPositions({ - arr: start, - childCount, - layerIndex: index, - alignment: FlexLayerAlignment.Start, - isVertical: !isNewLayer, - isNewLayer, - addInitialHighlight: !( - start.length === 0 && centerColumns + endColumns > 60 - ), - }); - center = addDropPositions({ - arr: center, - childCount: childCount + startLength, - layerIndex: index, - alignment: FlexLayerAlignment.Center, - isVertical: !isNewLayer, - isNewLayer, - addInitialHighlight: !(startColumns > 25 || endColumns > 25), - }); - end = addDropPositions({ - arr: end, - childCount: childCount + startLength + centerLength, - layerIndex: index, - alignment: FlexLayerAlignment.End, - isVertical: !isNewLayer, - isNewLayer, - addInitialHighlight: !(centerColumns + startColumns > 60), - }); - - return { - element: ( - 64} - wrapEnd={endColumns > 64} - wrapLayer={startColumns + centerColumns + endColumns > 64} - wrapStart={startColumns > 64} - /> - ), - count, - }; + return ( + 64} + wrapEnd={endColumns > 64} + wrapLayer={startColumns + centerColumns + endColumns > 64} + wrapStart={startColumns > 64} + /> + ); } return ( diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx index 0db9514a0af6..95d3c0f3c821 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx @@ -1,4 +1,4 @@ -import React, { ReactNode, useCallback } from "react"; +import React, { CSSProperties, ReactNode, useCallback, useMemo } from "react"; import styled from "styled-components"; import { AppState } from "ce/reducers"; @@ -8,18 +8,16 @@ import { ResponsiveBehavior, } from "components/constants"; import { - MAIN_CONTAINER_WIDGET_ID, WidgetType, widgetTypeClassname, WIDGET_PADDING, } from "constants/WidgetConstants"; import { useSelector } from "react-redux"; -import { getSiblingCount } from "selectors/autoLayoutSelectors"; import { snipingModeSelector } from "selectors/editorSelectors"; import { getIsMobile } from "selectors/mainCanvasSelectors"; +import { isWidgetSelected } from "selectors/widgetSelectors"; import { useClickToSelectWidget } from "utils/hooks/useClickToSelectWidget"; import { usePositionedContainerZIndex } from "utils/hooks/usePositionedContainerZIndex"; -import { DRAG_MARGIN } from "widgets/constants"; import { checkIsDropTarget } from "../PositionedContainer"; export type AutoLayoutProps = { @@ -37,61 +35,23 @@ export type AutoLayoutProps = { parentColumnSpace: number; flexVerticalAlignment: FlexVerticalAlignment; }; -// TODO: create a memoized style object for the div instead. -const FlexWidget = styled.div<{ - componentHeight: number; - componentWidth: number; - isMobile: boolean; - isFillWidget: boolean; - padding: number; - zIndex: number; - zIndexOnHover: number; - dragMargin: number; - isAffectedByDrag: boolean; - parentId?: string; - flexVerticalAlignment: FlexVerticalAlignment; -}>` - position: relative; - z-index: ${({ zIndex }) => zIndex}; - - width: ${({ componentWidth }) => `${Math.floor(componentWidth)}px`}; - height: ${({ componentHeight, isMobile }) => - isMobile ? "auto" : Math.floor(componentHeight) + "px"}; - min-height: 30px; - padding: ${({ isAffectedByDrag, padding }) => - isAffectedByDrag ? 0 : padding + "px"}; - - flex-grow: ${({ isFillWidget }) => (isFillWidget ? "1" : "0")}; - align-self: ${({ flexVerticalAlignment }) => flexVerticalAlignment}; - - &:hover { - z-index: ${({ zIndexOnHover }) => zIndexOnHover} !important; - } - margin-top: ${({ isAffectedByDrag }) => (isAffectedByDrag ? "4px" : "0px")}; +const FlexWidget = styled.div` + position: relative; `; -const DEFAULT_MARGIN = 16; - export function FlexComponent(props: AutoLayoutProps) { const isMobile = useSelector(getIsMobile); const isSnipingMode = useSelector(snipingModeSelector); + const isSelected = useSelector(isWidgetSelected(props.widgetId)); + const isDragging = useSelector( + (state: AppState) => state.ui.widgetDragResize.isDragging, + ); const clickToSelectWidget = useClickToSelectWidget(props.widgetId); const onClickFn = useCallback(() => { clickToSelectWidget(props.widgetId); }, [props.widgetId, clickToSelectWidget]); - const dragDetails = useSelector( - (state: AppState) => state.ui.widgetDragResize.dragDetails, - ); - const isDragging = useSelector( - (state: AppState) => state.ui.widgetDragResize.isDragging, - ); - - const siblingCount = useSelector( - getSiblingCount(props.widgetId, props.parentId || MAIN_CONTAINER_WIDGET_ID), - ); - const isDropTarget = checkIsDropTarget(props.widgetType); const { onHoverZIndex, zIndex } = usePositionedContainerZIndex( isDropTarget, @@ -103,6 +63,7 @@ export function FlexComponent(props: AutoLayoutProps) { const stopEventPropagation = (e: any) => { !isSnipingMode && e.stopPropagation(); }; + /** * In a vertical stack, * Fill widgets grow / shrink to take up all the available space. @@ -115,46 +76,40 @@ export function FlexComponent(props: AutoLayoutProps) { props.widgetId } ${widgetTypeClassname(props.widgetType)}`; - const dragMargin = - props.parentId === MAIN_CONTAINER_WIDGET_ID - ? DEFAULT_MARGIN - : Math.max(props.parentColumnSpace, DRAG_MARGIN); - - const isAffectedByDrag: boolean = - isDragging && dragDetails?.draggedOn !== undefined; - // TODO: Simplify this logic. - /** - * resize logic: - * if isAffectedByDrag - * newWidth = width - drag margin - - * (decrease in parent width) / # of siblings - - * width of the DropPosition introduced due to the widget. - */ - const resizedWidth: number = isAffectedByDrag - ? props.componentWidth - - dragMargin - - (siblingCount > 0 - ? (DEFAULT_MARGIN * siblingCount + 2) / siblingCount - : 0) - : props.componentWidth; + const flexComponentStyle: CSSProperties = useMemo(() => { + return { + display: isSelected && isDragging ? "none" : "flex", + zIndex, + width: `${Math.floor(props.componentWidth) - WIDGET_PADDING * 2}px`, + height: isMobile + ? "auto" + : Math.floor(props.componentHeight) - WIDGET_PADDING * 2 + "px", + minHeight: "30px", + margin: WIDGET_PADDING + "px", + flexGrow: isFillWidget ? 1 : 0, + alignSelf: props.flexVerticalAlignment, + "&:hover": { + zIndex: onHoverZIndex + " !important", + }, + }; + }, [ + isDragging, + isFillWidget, + isMobile, + isSelected, + props.componentWidth, + props.componentHeight, + props.flexVerticalAlignment, + zIndex, + onHoverZIndex, + ]); return ( {props.children} diff --git a/app/client/src/components/editorComponents/DraggableComponent.tsx b/app/client/src/components/editorComponents/DraggableComponent.tsx index cf61aa721f61..90e46803e8dc 100644 --- a/app/client/src/components/editorComponents/DraggableComponent.tsx +++ b/app/client/src/components/editorComponents/DraggableComponent.tsx @@ -131,7 +131,9 @@ function DraggableComponent(props: DraggableComponentProps) { }; }, [isResizingOrDragging, isCurrentWidgetResizing]); - const widgetBoundaries = ; + const widgetBoundaries = props.isFlexChild ? null : ( + + ); const classNameForTesting = `t--draggable-${props.type .split("_") diff --git a/app/client/src/components/editorComponents/DropTargetComponent.tsx b/app/client/src/components/editorComponents/DropTargetComponent.tsx index c59ed3fb721e..a12703259d4a 100644 --- a/app/client/src/components/editorComponents/DropTargetComponent.tsx +++ b/app/client/src/components/editorComponents/DropTargetComponent.tsx @@ -20,7 +20,6 @@ import { getOccupiedSpacesSelectorForContainer, previewModeSelector, } from "selectors/editorSelectors"; -import { getIsMobile } from "selectors/mainCanvasSelectors"; import styled from "styled-components"; import { useAutoHeightUIState } from "utils/hooks/autoHeightUIHooks"; import { @@ -82,15 +81,12 @@ export const DropTargetContext: Context<{ */ function getDropTargetHeight( canDropTargetExtend: boolean, - isMobile: boolean, isPreviewMode: boolean, currentHeight: number, snapRowSpace: number, minHeight: number, ) { - let height = isMobile - ? "auto" - : canDropTargetExtend + let height = canDropTargetExtend ? `${Math.max(currentHeight * snapRowSpace, minHeight)}px` : "100%"; if (isPreviewMode && canDropTargetExtend) @@ -121,8 +117,6 @@ export function DropTargetComponent(props: DropTargetComponentProps) { // Are we changing the auto height limits by dragging the signifiers? const { isAutoHeightWithLimitsChanging } = useAutoHeightUIState(); - const isMobile = useSelector(getIsMobile); - // dragDetails contains of info needed for a container jump: // which parent the dragging widget belongs, // which canvas is active(being dragged on), @@ -199,7 +193,6 @@ export function DropTargetComponent(props: DropTargetComponentProps) { if (dropTargetRef.current) { const height = getDropTargetHeight( canDropTargetExtend, - isMobile && props.widgetId !== MAIN_CONTAINER_WIDGET_ID, isPreviewMode, rowRef.current, props.snapRowSpace, @@ -262,7 +255,6 @@ export function DropTargetComponent(props: DropTargetComponentProps) { const height = getDropTargetHeight( canDropTargetExtend, - isMobile && props.widgetId !== MAIN_CONTAINER_WIDGET_ID, isPreviewMode, rowRef.current, props.snapRowSpace, diff --git a/app/client/src/components/editorComponents/ResizableComponent.tsx b/app/client/src/components/editorComponents/ResizableComponent.tsx index fe2807bee163..e38187a508e0 100644 --- a/app/client/src/components/editorComponents/ResizableComponent.tsx +++ b/app/client/src/components/editorComponents/ResizableComponent.tsx @@ -101,14 +101,22 @@ export const ResizableComponent = memo(function ResizableComponent( // The ResizableContainer's size prop is controlled const dimensions: UIElementSize = { width: - ((props.isMobile && props.mobileRightColumn + ((props.isMobile && props.mobileRightColumn !== undefined ? props.mobileRightColumn : props.rightColumn) - - props.leftColumn) * + (props.isMobile && props.mobileLeftColumn !== undefined + ? props.mobileLeftColumn + : props.leftColumn)) * props.parentColumnSpace - 2 * props.paddingOffset, height: - (props.bottomRow - props.topRow) * props.parentRowSpace - + ((props.isMobile && props.mobileBottomRow !== undefined + ? props.mobileBottomRow + : props.bottomRow) - + (props.isMobile && props.mobileTopRow !== undefined + ? props.mobileTopRow + : props.topRow)) * + props.parentRowSpace - 2 * props.paddingOffset, }; diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index 582cea0fee43..aaba6db0fb5b 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -6,6 +6,9 @@ import { import { useSelector } from "react-redux"; import { ReflowDirection } from "reflow/reflowTypes"; import { getWidgets } from "sagas/selectors"; +import { getCanvasWidth } from "selectors/editorSelectors"; +import { getIsMobile } from "selectors/mainCanvasSelectors"; +import { deriveHighlightsFromLayers } from "utils/autoLayout/highlightUtils"; import WidgetFactory from "utils/WidgetFactory"; import { WidgetDraggingBlock } from "./useBlocksToBeDraggedOnCanvas"; @@ -25,7 +28,8 @@ export interface HighlightInfo { width: number; // width of the highlight. height: number; // height of the highlight. isVertical: boolean; // determines if the highlight is vertical or horizontal. - el: Element; // dom node of the highlight. + el?: Element; // dom node of the highlight. + canvasId: string; // widgetId of the canvas to which the highlight belongs. } export interface AutoLayoutHighlightProps { @@ -52,8 +56,9 @@ export const useAutoLayoutHighlights = ({ useAutoLayout, }: AutoLayoutHighlightProps) => { const allWidgets = useSelector(getWidgets); + const canvasWidth: number = useSelector(getCanvasWidth); + const isMobile = useSelector(getIsMobile); let highlights: HighlightInfo[] = []; - let newLayers: { [key: string]: number } = {}; let lastActiveHighlight: HighlightInfo | undefined; let isFillWidget = false; @@ -93,51 +98,6 @@ export const useAutoLayoutHighlights = ({ // reset state lastActiveHighlight = undefined; highlights = []; - newLayers = {}; - }; - - const getDropPositions = () => { - const els = document.querySelectorAll(`.t--drop-position-${canvasId}`); - const highlights: HighlightInfo[] = []; - - for (const el of els) { - const rect: DOMRect = el.getBoundingClientRect(); - const classList = Array.from(el.classList); - - const highlight: HighlightInfo = classList.reduce( - (acc: HighlightInfo, curr) => { - if (curr.indexOf("alignment") > -1) - acc.alignment = curr.split("-")[1] as FlexLayerAlignment; - else if (curr.indexOf("layer-index") > -1) - acc.layerIndex = parseInt(curr.split("layer-index-")[1]); - else if (curr.indexOf("child-index") > -1) - acc.index = parseInt(curr.split("child-index-")[1]); - else if (curr.indexOf("row-index") > -1) - acc.rowIndex = parseInt(curr.split("row-index-")[1]); - else if (curr.indexOf("isNewLayer") > -1) acc.isNewLayer = true; - else if (curr.indexOf("isVertical") > -1) acc.isVertical = true; - - return acc; - }, - { - isNewLayer: false, - index: 0, - layerIndex: 0, - rowIndex: 0, - alignment: FlexLayerAlignment.Start, - posX: rect.x - containerDimensions.left, - posY: rect.y - containerDimensions?.top, - width: rect.width, - height: rect.height, - isVertical: false, - el, - }, - ); - if (!highlight.isVertical) newLayers[highlights.length] = highlight.posY; - highlights.push(highlight); - } - - return highlights; }; const checkForFillWidget = (): boolean => { @@ -174,7 +134,14 @@ export const useAutoLayoutHighlights = ({ */ if (!updateContainerDimensions()) return []; isFillWidget = checkForFillWidget(); - highlights = getDropPositions(); + highlights = deriveHighlightsFromLayers( + allWidgets, + canvasId, + canvasWidth, + blocksToDraw.map((block) => block?.widgetId), + isFillWidget, + isMobile, + ); } // console.log("#### highlights", highlights); return highlights; @@ -184,70 +151,32 @@ export const useAutoLayoutHighlights = ({ * END AUTO LAYOUT OFFSET CALCULATION */ - const updateHighlight = (index: number): HighlightInfo => { - const highlight = highlights[index]; - if (!highlight || !highlight.el) return highlight; - const rect: DOMRect = highlight.el.getBoundingClientRect(); - - highlight.posX = rect.x - containerDimensions.left; - highlight.posY = rect.y - containerDimensions.top; - highlight.width = isFillWidget - ? highlight.alignment === FlexLayerAlignment.Start - ? containerDimensions.width - : 0 - : containerDimensions?.width / 3; - highlight.height = rect.height; - (highlight.el as HTMLElement).style.width = `${highlight.width}px`; - highlights[index] = highlight; - - return highlight; - }; - - const updateHighlights = (moveDirection?: ReflowDirection) => { - if (!highlights?.length || !moveDirection) return; - highlights.map((highlight: HighlightInfo, index: number) => { - let updatedHighlight: HighlightInfo = highlight; - if (highlight.isNewLayer || !highlight.height) - updatedHighlight = updateHighlight(index); - return updatedHighlight; - }); - }; - - const toggleHighlightVisibility = ( - arr: HighlightInfo[], - selected: HighlightInfo, - ): void => { - arr.forEach((each: HighlightInfo) => { - const el = each.el as HTMLElement; - // if (!isVerticalStack) el.style.opacity = "1"; - el.style.opacity = selected === each ? "1" : "0"; - }); - }; - - const updateSelection = (highlight: HighlightInfo): void => { - if (lastActiveHighlight) { - const lastEl = lastActiveHighlight?.el as HTMLElement; - if (lastEl) lastEl.style.backgroundColor = "rgba(223, 158, 206, 0.6)"; - } - const el = highlight.el as HTMLElement; - if (el) el.style.backgroundColor = "rgba(196, 139, 181, 1)"; - lastActiveHighlight = highlight; - }; - const highlightDropPosition = ( e: any, moveDirection: ReflowDirection, // acceleration: number, ): HighlightInfo | undefined => { + if (!highlights) + highlights = deriveHighlightsFromLayers( + allWidgets, + canvasId, + canvasWidth, + blocksToDraw.map((block) => block?.widgetId), + isFillWidget, + isMobile, + ); + // console.log("#### highlights", highlights); if (!highlights) return; - updateHighlights(moveDirection); + // updateHighlights(moveDirection); - const highlight: HighlightInfo = getHighlightPayload(e, moveDirection); - - if (!highlight) return; - - updateSelection(highlight); + const highlight: HighlightInfo | undefined = getHighlightPayload( + e, + moveDirection, + ); + // updateSelection(highlight); + // console.log("#### selection", highlight); + lastActiveHighlight = highlight; return highlight; }; @@ -255,9 +184,17 @@ export const useAutoLayoutHighlights = ({ e: any, moveDirection?: ReflowDirection, val?: XYCord, - ): HighlightInfo => { + ): HighlightInfo | undefined => { let base: HighlightInfo[] = []; - if (!highlights || !highlights.length) highlights = getDropPositions(); + if (!highlights || !highlights.length) + highlights = deriveHighlightsFromLayers( + allWidgets, + canvasId, + canvasWidth, + blocksToDraw.map((block) => block?.widgetId), + isFillWidget, + isMobile, + ); base = highlights; const pos: XYCord = { @@ -267,8 +204,8 @@ export const useAutoLayoutHighlights = ({ let filteredHighlights: HighlightInfo[] = []; filteredHighlights = getViableDropPositions(base, pos, moveDirection); - - const arr = filteredHighlights.sort((a, b) => { + if (!filteredHighlights || !filteredHighlights?.length) return; + const arr = [...filteredHighlights]?.sort((a, b) => { return ( calculateDistance( a, @@ -284,7 +221,7 @@ export const useAutoLayoutHighlights = ({ ) ); }); - toggleHighlightVisibility(base, arr[0]); + // console.log("#### arr", arr, base, moveDirection); return arr[0]; }; @@ -392,7 +329,11 @@ export const useAutoLayoutHighlights = ({ const getDropInfo = (val: XYCord): HighlightInfo | undefined => { if (lastActiveHighlight) return lastActiveHighlight; - const payload: HighlightInfo = getHighlightPayload(null, undefined, val); + const payload: HighlightInfo | undefined = getHighlightPayload( + null, + undefined, + val, + ); if (!payload) return; lastActiveHighlight = payload; return payload; diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index aaf2ea93f030..7781a8e02600 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -3,6 +3,7 @@ import { OccupiedSpace } from "constants/CanvasEditorConstants"; import { CONTAINER_GRID_PADDING, GridDefaults, + MAIN_CONTAINER_WIDGET_ID, } from "constants/WidgetConstants"; import { debounce, isEmpty, throttle } from "lodash"; import { CanvasDraggingArenaProps } from "pages/common/CanvasArenas/CanvasDraggingArena"; @@ -237,7 +238,7 @@ export const useCanvasDragging = ( const scrollParent: Element | null = getNearestParentCanvas( slidingArenaRef.current, ); - // console.log(`#### init variable: ${widgetName}`); + let canvasIsDragging = false; let isUpdatingRows = false; let currentRectanglesToDraw: WidgetDraggingBlock[] = []; @@ -274,9 +275,11 @@ export const useCanvasDragging = ( stickyCanvasRef.current.height, ); slidingArenaRef.current.style.zIndex = ""; - // console.log(`#### reset canvas state: ${widgetName}`); canvasIsDragging = false; } + if (isDragging) { + setDraggingCanvas(MAIN_CONTAINER_WIDGET_ID); + } }; if (isDragging) { const startPoints = defaultHandlePositions; @@ -356,7 +359,6 @@ export const useCanvasDragging = ( }; const onFirstMoveOnCanvas = (e: any, over = false) => { - // console.log(`#### first move: ${widgetName}`); if ( !isResizing && isDragging && @@ -379,7 +381,6 @@ export const useCanvasDragging = ( logContainerJump(widgetId, speed, acceleration); containerJumpThresholdMetrics.clearMetrics(); // we can just use canvasIsDragging but this is needed to render the relative DragLayerComponent - // console.log(`#### set dragging canvas: ${widgetName}`); setDraggingCanvas(widgetId); } canvasIsDragging = true; @@ -566,7 +567,6 @@ export const useCanvasDragging = ( left: e.offsetX - startPoints.left - parentDiff.left, top: e.offsetY - startPoints.top - parentDiff.top, }; - // console.log("#### mouse move", delta); const drawingBlocks = blocksToDraw.map((each) => ({ ...each, left: each.left + delta.left, @@ -601,15 +601,18 @@ export const useCanvasDragging = ( } else if (!isUpdatingRows) { currentDirection.current = getMouseMoveDirection(e); triggerReflow(e, firstMove); + let highlight: HighlightInfo | undefined; if ( useAutoLayout && isCurrentDraggedCanvas && currentDirection.current !== ReflowDirection.UNSET - ) - debounce(() => { - highlightDropPosition(e, currentDirection.current); - }, 100)(); - renderBlocks(); + ) { + // debounce(() => { + // highlightDropPosition(e, currentDirection.current); + // }, 100)(); + highlight = highlightDropPosition(e, currentDirection.current); + } + renderBlocks(highlight); } scrollObj.lastMouseMoveEvent = { offsetX: e.offsetX, @@ -677,7 +680,7 @@ export const useCanvasDragging = ( }, ); - const renderBlocks = () => { + const renderBlocks = (highlight?: HighlightInfo | undefined) => { if ( slidingArenaRef.current && isCurrentDraggedCanvas && @@ -699,6 +702,18 @@ export const useCanvasDragging = ( drawBlockOnCanvas(each); }); } + if (highlight) { + canvasCtx.fillStyle = "rgba(196, 139, 181, 1)"; + const { height, posX, posY, width } = highlight; + let val = 0; + if ( + widgetId === MAIN_CONTAINER_WIDGET_ID && + scrollParent?.scrollTop + ) + val = scrollParent.scrollTop - 20; + canvasCtx.fillRect(posX, posY - val, width, height); + canvasCtx.save(); + } canvasCtx.restore(); } }; diff --git a/app/client/src/resizable/resizenreflow/index.tsx b/app/client/src/resizable/resizenreflow/index.tsx index d8d33ec4f164..e507d546c902 100644 --- a/app/client/src/resizable/resizenreflow/index.tsx +++ b/app/client/src/resizable/resizenreflow/index.tsx @@ -540,8 +540,8 @@ export function ReflowResizable(props: ResizableProps) { }} immediate={newDimensions.reset ? true : false} to={{ - width: props.isAffectedByDrag ? "auto" : widgetWidth, - height: props.isMobile ? "auto" : widgetHeight, + width: widgetWidth, + height: widgetHeight, transform: `translate3d(${newDimensions.x}px,${newDimensions.y}px,0)`, }} > diff --git a/app/client/src/sagas/AutoLayoutUpdateSagas.tsx b/app/client/src/sagas/AutoLayoutUpdateSagas.tsx index 8bafdebf5658..d61eb421f04a 100644 --- a/app/client/src/sagas/AutoLayoutUpdateSagas.tsx +++ b/app/client/src/sagas/AutoLayoutUpdateSagas.tsx @@ -8,6 +8,7 @@ import { ResponsiveBehavior } from "components/constants"; import log from "loglevel"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; import { all, call, put, select, takeLatest } from "redux-saga/effects"; +import { updateWidgetPositions } from "utils/autoLayout/positionUtils"; import { alterLayoutForDesktop, alterLayoutForMobile, diff --git a/app/client/src/sagas/AutoLayoutUtils.ts b/app/client/src/sagas/AutoLayoutUtils.ts index 7dca477f6bb4..f071ac11d24a 100644 --- a/app/client/src/sagas/AutoLayoutUtils.ts +++ b/app/client/src/sagas/AutoLayoutUtils.ts @@ -8,6 +8,7 @@ import { LayerChild, } from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; +import { updateWidgetPositions } from "utils/autoLayout/positionUtils"; function getCanvas(widgets: CanvasWidgetsReduxState, containerId: string) { const container = widgets[containerId]; @@ -60,7 +61,7 @@ export function* wrapChildren( canvas = { ...canvas, flexLayers }; widgets[canvas.widgetId] = canvas; // update size - const updatedWidgets = updateSizeOfAllChildren(widgets, canvas.widgetId); + const updatedWidgets = updateWidgetPositions(widgets, canvas.widgetId); return updatedWidgets; } @@ -114,7 +115,7 @@ export function* updateFlexLayersOnDelete( widgets[parentId] = parent; if (layerIndex === -1) return widgets; - return updateFlexChildColumns(widgets, layerIndex, parentId); + return updateWidgetPositions(widgets, parentId); } // TODO: refactor these implementations export function updateFillChildStatus( @@ -263,7 +264,7 @@ export function alterLayoutForMobile( if (widget.responsiveBehavior === ResponsiveBehavior.Fill) { widget.mobileRightColumn = 64; - widget.leftColumn = 0; + widget.mobileLeftColumn = 0; } else if ( widget.responsiveBehavior === ResponsiveBehavior.Hug && widget.minWidth @@ -271,20 +272,23 @@ export function alterLayoutForMobile( const { minWidth, rightColumn } = widget; const columnSpace = canvasWidth / 64; if (columnSpace * rightColumn < minWidth) { - widget.leftColumn = 0; + widget.mobileLeftColumn = 0; widget.mobileRightColumn = Math.min( Math.floor(minWidth / columnSpace), 64, ); } } - + widget.mobileTopRow = widget.topRow; + widget.mobileBottomRow = widget.bottomRow; + // TODO: Preet - update container row info if height changes on account of flex wrap. widgets = alterLayoutForMobile( widgets, child, (canvasWidth * (widget.mobileRightColumn || 1)) / 64, ); widgets[child] = widget; + widgets = updateWidgetPositions(widgets, child, true); } return widgets; } @@ -301,11 +305,10 @@ export function alterLayoutForDesktop( return widgets; if (!children || !children.length) return widgets; - widgets = updateSizeOfAllChildren(widgets, parentId); + widgets = updateWidgetPositions(widgets, parentId, false); for (const child of children) { widgets = alterLayoutForDesktop(widgets, child); } - return widgets; } diff --git a/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts b/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts index 449da709c868..e931cedc4517 100644 --- a/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts +++ b/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts @@ -18,12 +18,10 @@ import log from "loglevel"; import { HighlightInfo } from "pages/common/CanvasArenas/hooks/useAutoLayoutHighlights"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; import { all, call, put, select, takeLatest } from "redux-saga/effects"; -import { - updateFlexChildColumns, - updateSizeOfAllChildren, -} from "sagas/AutoLayoutUtils"; +import { updateFlexChildColumns } from "sagas/AutoLayoutUtils"; import { getWidgets } from "sagas/selectors"; import { getUpdateDslAfterCreatingChild } from "sagas/WidgetAdditionSagas"; +import { updateWidgetPositions } from "utils/autoLayout/positionUtils"; function* addWidgetAndReorderSaga( actionPayload: ReduxAction<{ @@ -114,14 +112,8 @@ function* autoLayoutReorderSaga( rowIndex, }, ); - let updatedWidgetsAfterResizing = updatedWidgets; - if (direction === LayoutDirection.Vertical) - updatedWidgetsAfterResizing = updateSizeOfAllChildren( - updatedWidgets, - parentId, - ); - yield put(updateAndSaveLayout(updatedWidgetsAfterResizing)); + yield put(updateAndSaveLayout(updatedWidgets)); log.debug("reorder computations took", performance.now() - start, "ms"); } catch (e) { // console.error(e); @@ -226,7 +218,12 @@ function* reorderAutolayoutChildren(params: { }; } - return updatedWidgets; + const widgetsAfterPositionUpdate = updateWidgetPositions( + updatedWidgets, + parentId, + ); + + return widgetsAfterPositionUpdate; } /** @@ -248,13 +245,14 @@ function updateRelationships( const orphans = movedWidgets.filter( (item) => widgets[item].parentId !== parentId, ); - let prevParentId: string | undefined; + const prevParents: string[] = []; if (orphans && orphans.length) { //parent has changed orphans.forEach((item) => { // remove from previous parent - prevParentId = widgets[item].parentId; + const prevParentId = widgets[item].parentId; if (prevParentId !== undefined) { + prevParents.push(prevParentId); const prevParent = Object.assign({}, widgets[prevParentId]); if (prevParent.children && isArray(prevParent.children)) { const updatedPrevParent = { @@ -277,8 +275,11 @@ function updateRelationships( }; }); } - if (prevParentId) { - return updateSizeOfAllChildren(widgets, prevParentId); + if (prevParents.length) { + for (const id of prevParents) { + const updatedWidgets = updateWidgetPositions(widgets, id); + return updatedWidgets; + } } return widgets; } @@ -318,7 +319,7 @@ function updateExistingLayer( rowIndex: number, ): CanvasWidgetsReduxState { try { - const widgets: CanvasWidgetsReduxState = Object.assign({}, allWidgets); + const widgets: CanvasWidgetsReduxState = { ...allWidgets }; const canvas = widgets[parentId]; if (!canvas || !newLayer) return widgets; diff --git a/app/client/src/sagas/WidgetOperationSagas.tsx b/app/client/src/sagas/WidgetOperationSagas.tsx index bd8e76dda814..3e515b92a8ad 100644 --- a/app/client/src/sagas/WidgetOperationSagas.tsx +++ b/app/client/src/sagas/WidgetOperationSagas.tsx @@ -113,7 +113,6 @@ import { collisionCheckPostReflow, getBottomRowAfterReflow, } from "utils/reflowHookUtils"; -import { updateChildrenSize } from "./AutoLayoutUtils"; import { getCanvasSizeAfterWidgetMove } from "./CanvasSagas/DraggingCanvasSagas"; import widgetAdditionSagas from "./WidgetAdditionSagas"; import { traverseTreeAndExecuteBlueprintChildOperations } from "./WidgetBlueprintSagas"; @@ -156,6 +155,7 @@ import { } from "./WidgetOperationUtils"; import { widgetSelectionSagas } from "./WidgetSelectionSagas"; import { getAllPaths } from "ce/workers/Evaluation/evaluationUtils"; +import { updateWidgetPositions } from "utils/autoLayout/positionUtils"; export function* updateAllChildCanvasHeights( currentContainerLikeWidgetId: string, @@ -255,10 +255,9 @@ export function* resizeSaga(resizeAction: ReduxAction) { bottomRow: updatedCanvasBottomRow, }; } - const updatedWidgetsAfterResizing = updateChildrenSize( + const updatedWidgetsAfterResizing = updateWidgetPositions( movedWidgets, parentId, - widgetId, ); log.debug("resize computations took", performance.now() - start, "ms"); yield put(stopReflowAction()); diff --git a/app/client/src/utils/autoLayout/flexWidgetUtils.ts b/app/client/src/utils/autoLayout/flexWidgetUtils.ts new file mode 100644 index 000000000000..7f0bf3b7d103 --- /dev/null +++ b/app/client/src/utils/autoLayout/flexWidgetUtils.ts @@ -0,0 +1,113 @@ +export function getRightColumn(widget: any, isMobile: boolean): number { + return isMobile && widget.mobileRightColumn !== undefined + ? widget.mobileRightColumn + : widget.rightColumn; +} + +export function setRightColumn( + widget: any, + val: number | null, + isMobile: boolean, +): any { + if (val === null) return widget; + return isMobile && widget.mobileRightColumn !== undefined + ? { ...widget, mobileRightColumn: val } + : { ...widget, rightColumn: val }; +} + +export function getLeftColumn(widget: any, isMobile: boolean): number { + return isMobile && widget.mobileLeftColumn !== undefined + ? widget.mobileLeftColumn + : widget.leftColumn; +} + +export function setLeftColumn( + widget: any, + val: number | null, + isMobile: boolean, +): any { + if (val === null) return widget; + return isMobile && widget.mobileLeftColumn !== undefined + ? { ...widget, mobileLeftColumn: val } + : { ...widget, leftColumn: val }; +} + +export function getTopRow(widget: any, isMobile: boolean): number { + return isMobile && widget.mobileTopRow !== undefined + ? widget.mobileTopRow + : widget.topRow; +} + +export function setTopRow( + widget: any, + val: number | null, + isMobile: boolean, +): any { + if (val === null) return widget; + return isMobile && widget.mobileTopRow !== undefined + ? { ...widget, mobileTopRow: val } + : { ...widget, topRow: val }; +} + +export function getBottomRow(widget: any, isMobile: boolean): number { + return isMobile && widget.mobileBottomRow !== undefined + ? widget.mobileBottomRow + : widget.bottomRow; +} + +export function setBottomRow( + widget: any, + val: number | null, + isMobile: boolean, +): any { + if (val === null) return widget; + return isMobile && widget.mobileBottomRow !== undefined + ? { ...widget, mobileBottomRow: val } + : { ...widget, bottomRow: val }; +} + +export function setColumns( + widget: any, + left: number, + right: number, + isMobile: boolean, +) { + return setRightColumn(setLeftColumn(widget, left, isMobile), right, isMobile); +} + +export function setDimensions( + widget: any, + top: number | null, + bottom: number | null, + left: number | null, + right: number | null, + isMobile: boolean, +) { + try { + return setBottomRow( + setTopRow( + setLeftColumn(setRightColumn(widget, right, isMobile), left, isMobile), + top, + isMobile, + ), + bottom, + isMobile, + ); + } catch (e) { + console.log(e); + return widget; + } +} + +export function getWidgetWidth(widget: any, isMobile: boolean): number { + return getRightColumn(widget, isMobile) - getLeftColumn(widget, isMobile); +} + +export function getWidgetHeight(widget: any, isMobile: boolean): number { + return getBottomRow(widget, isMobile) - getTopRow(widget, isMobile); +} + +export function getWidgetRows(widget: any, isMobile: boolean): number { + const divisor = widget.parentRowSpace === 1 ? 10 : 1; + return getBottomRow(widget, isMobile) / divisor - getTopRow(widget, isMobile); +} diff --git a/app/client/src/utils/autoLayout/highlightUtils.ts b/app/client/src/utils/autoLayout/highlightUtils.ts new file mode 100644 index 000000000000..1a02172d0532 --- /dev/null +++ b/app/client/src/utils/autoLayout/highlightUtils.ts @@ -0,0 +1,448 @@ +import { FlexLayerAlignment } from "components/constants"; +import { + DEFAULT_HIGHLIGHT_SIZE, + FlexLayer, + LayerChild, +} from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; +import { + CONTAINER_GRID_PADDING, + GridDefaults, + MAIN_CONTAINER_WIDGET_ID, + WIDGET_PADDING, +} from "constants/WidgetConstants"; +import { HighlightInfo } from "pages/common/CanvasArenas/hooks/useAutoLayoutHighlights"; +import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; +import { + getLeftColumn, + getRightColumn, + getTopRow, + getWidgetHeight, + getWidgetWidth, +} from "./flexWidgetUtils"; +import { getTotalRowsOfAllChildren, Widget } from "./positionUtils"; + +const HORIZONTAL_HIGHLIGHT_MARGIN = 4; +// TODO: Preet - update logic to account for flex wrap on mobile. +// For mobile use widget positions to place highlights. +/** + * @param allWidgets : CanvasWidgetsReduxState + * @param canvasId : string + * @param draggedWidgets : string[] + * @returns widgets: CanvasWidgetsReduxState + * + * This function is used to derive highlights from flex layers and stored in widget dsl. + */ +export function deriveHighlightsFromLayers( + allWidgets: CanvasWidgetsReduxState, + canvasId: string, + mainCanvasWidth = 0, + draggedWidgets: string[] = [], + hasFillWidget = false, + isMobile = false, +): HighlightInfo[] { + const widgets = { ...allWidgets }; + try { + const canvas = widgets[canvasId]; + if (!canvas) return []; + + const { canvasWidth, columnSpace } = getCanvasDimensions( + canvas, + widgets, + mainCanvasWidth, + isMobile, + ); + const layers: FlexLayer[] = canvas.flexLayers || []; + const highlights: HighlightInfo[] = []; + let childCount = 0; + let layerIndex = 0; + // TODO: remove offsetTop and use child positions after widget positioning on grid is solved. + let offsetTop = HORIZONTAL_HIGHLIGHT_MARGIN; // used to calculate distance of a highlight from parents's top. + for (const layer of layers) { + /** + * If the layer is empty, after discounting the dragged widgets, + * then don't process it for vertical highlights. + */ + const isEmpty: boolean = + layer?.children?.filter( + (child: LayerChild) => draggedWidgets.indexOf(child.id) === -1, + ).length === 0; + // TODO: use getHeightOfFixedCanvas to measure the height of the layer. + const tallestChild = layer.children?.reduce((acc, child) => { + const widget = widgets[child.id]; + return Math.max( + acc, + getWidgetHeight(widget, isMobile) * widget.parentRowSpace, + ); + }, 0); + const childrenRows = getTotalRowsOfAllChildren( + widgets, + layer.children?.map((child) => child.id) || [], + isMobile, + ); + + const payload: VerticalHighlightsPayload = generateVerticalHighlights({ + widgets, + layer, + childCount, + layerIndex, + height: tallestChild, + offsetTop, + canvasWidth, + canvasId, + columnSpace, + draggedWidgets, + isMobile, + }); + + if (!isEmpty) { + /** + * Add a layer of horizontal highlights before each flex layer + * to account for new vertical drop positions. + */ + highlights.push( + ...generateHorizontalHighlights( + childCount, + layerIndex, + offsetTop, + canvasWidth, + canvasId, + hasFillWidget, + ), + ); + + highlights.push(...payload.highlights); + offsetTop += childrenRows * 10 || 0; + layerIndex += 1; + } + childCount += payload.childCount; + } + // Add a layer of horizontal highlights for the empty space at the bottom of a stack. + highlights.push( + ...generateHorizontalHighlights( + childCount, + layerIndex, + offsetTop, + canvasWidth, + canvasId, + hasFillWidget, + ), + ); + return highlights; + } catch (e) { + // console.error(e); + return []; + } +} +interface VerticalHighlightsPayload { + childCount: number; + highlights: HighlightInfo[]; +} + +function generateVerticalHighlights(data: { + widgets: CanvasWidgetsReduxState; + layer: FlexLayer; + childCount: number; + layerIndex: number; + height: number; + offsetTop: number; + canvasWidth: number; + canvasId: string; + columnSpace: number; + draggedWidgets: string[]; + isMobile: boolean; +}): VerticalHighlightsPayload { + const { + canvasId, + canvasWidth, + childCount, + columnSpace, + draggedWidgets, + height, + isMobile, + layer, + layerIndex, + offsetTop, + widgets, + } = data; + const { children } = layer; + + let count = 0; + const startChildren = [], + centerChildren = [], + endChildren = []; + let startColumns = 0, + endColumns = 0; + + for (const child of children) { + const widget = widgets[child.id]; + if (!widget) continue; + count += 1; + if (draggedWidgets.indexOf(child.id) > -1) continue; + if (child.align === FlexLayerAlignment.End) { + endChildren.push(widget); + endColumns += getWidgetWidth(widget, isMobile); + } else if (child.align === FlexLayerAlignment.Center) { + centerChildren.push(widget); + } else { + startChildren.push(widget); + startColumns += getWidgetWidth(widget, isMobile); + } + } + + return { + highlights: [ + ...generateHighlightsForSubWrapper({ + arr: startChildren, + childCount, + layerIndex, + alignment: FlexLayerAlignment.Start, + height, + offsetTop, + canvasId, + parentColumnSpace: columnSpace, + parentRowSpace: widgets[canvasId].parentRowSpace, + canvasWidth, + isMobile, + }), + ...generateHighlightsForSubWrapper({ + arr: centerChildren, + childCount: childCount + startChildren.length, + layerIndex, + alignment: FlexLayerAlignment.Center, + height, + offsetTop, + canvasId, + parentColumnSpace: columnSpace, + parentRowSpace: widgets[canvasId].parentRowSpace, + canvasWidth, + avoidInitialHighlight: startColumns > 25 || endColumns > 25, + isMobile, + }), + ...generateHighlightsForSubWrapper({ + arr: endChildren, + childCount: childCount + startChildren.length + centerChildren.length, + layerIndex, + alignment: FlexLayerAlignment.End, + height, + offsetTop, + canvasId, + parentColumnSpace: columnSpace, + parentRowSpace: widgets[canvasId].parentRowSpace, + canvasWidth, + isMobile, + }), + ], + childCount: count, + }; +} + +function generateHighlightsForSubWrapper(data: { + arr: any[]; + childCount: number; + layerIndex: number; + alignment: FlexLayerAlignment; + height: number; + offsetTop: number; + canvasId: string; + parentColumnSpace: number; + parentRowSpace: number; + canvasWidth: number; + avoidInitialHighlight?: boolean; + isMobile: boolean; +}): HighlightInfo[] { + const { + alignment, + arr, + avoidInitialHighlight, + canvasId, + canvasWidth, + childCount, + height, + isMobile, + layerIndex, + offsetTop, + parentColumnSpace, + } = data; + const res: HighlightInfo[] = []; + let count = 0; + for (const child of arr) { + const left = getLeftColumn(child, isMobile); + res.push({ + isNewLayer: false, + index: count + childCount, + layerIndex, + rowIndex: count, + alignment, + posX: left * parentColumnSpace, + posY: + getTopRow(child, isMobile) * child.parentRowSpace + + HORIZONTAL_HIGHLIGHT_MARGIN, + width: DEFAULT_HIGHLIGHT_SIZE, + height, + isVertical: true, + canvasId, + }); + count += 1; + } + + if (!avoidInitialHighlight) { + const lastChild: Widget = arr && arr.length ? arr[arr.length - 1] : null; + res.push({ + isNewLayer: false, + index: count + childCount, + layerIndex, + rowIndex: count, + alignment, + posX: getPositionForInitialHighlight( + res, + alignment, + arr && arr.length + ? getRightColumn(lastChild, isMobile) * parentColumnSpace + : 0, + canvasWidth, + canvasId, + ), + posY: + lastChild === null + ? offsetTop + : getTopRow(lastChild, isMobile) * lastChild?.parentRowSpace + + HORIZONTAL_HIGHLIGHT_MARGIN, + width: DEFAULT_HIGHLIGHT_SIZE, + height, + isVertical: true, + canvasId, + }); + } + return res; +} + +/** + * Get the position of the initial / final highlight for an alignment. + * @param highlights | HighlightInfo[] : highlights for the current alignment + * @param alignment | FlexLayerAlignment : alignment of the current highlights + * @param posX | number : end position of the last widget in the current alignment. (rightColumn * columnSpace) + * @param containerWidth | number : width of the container + * @param canvasId | string : id of the canvas + * @returns number + */ +function getPositionForInitialHighlight( + highlights: HighlightInfo[], + alignment: FlexLayerAlignment, + posX: number, + containerWidth: number, + canvasId: string, +): number { + if (alignment === FlexLayerAlignment.End) { + return containerWidth - (canvasId !== MAIN_CONTAINER_WIDGET_ID ? 6 : 0); + } else if (alignment === FlexLayerAlignment.Center) { + if (!highlights.length) return containerWidth / 2; + return posX; + } else { + if (!highlights.length) return 2; + return posX; + } +} + +/** + * Create a layer of horizontal alignments to denote new vertical drop zones. + * - if the layer has a fill widget, + * - Start alignment spans the entire container width. + * - else each layer takes up a third of the container width and are placed side to side. + * @param childIndex | number : child count of children placed in preceding layers. + * @param layerIndex | number + * @param offsetTop | number + * @param containerWidth | number + * @param canvasId | + * @param hasFillWidget | boolean : whether the layer has a fill widget or not. + * @returns HighlightInfo[] + */ +function generateHorizontalHighlights( + childIndex: number, + layerIndex: number, + offsetTop: number, + containerWidth: number, + canvasId: string, + hasFillWidget: boolean, +): HighlightInfo[] { + const width = containerWidth / 3; + const arr: HighlightInfo[] = []; + [ + FlexLayerAlignment.Start, + FlexLayerAlignment.Center, + FlexLayerAlignment.End, + ].forEach((alignment, index) => { + arr.push({ + isNewLayer: true, + index: childIndex, + layerIndex, + rowIndex: 0, + alignment, + posX: hasFillWidget + ? alignment === FlexLayerAlignment.Start + ? 0 + : containerWidth + : width * index, + posY: offsetTop, + width: hasFillWidget + ? alignment === FlexLayerAlignment.Start + ? containerWidth + : 0 + : width, + height: DEFAULT_HIGHLIGHT_SIZE, + isVertical: false, + canvasId, + }); + }); + return arr; +} + +function getCanvasDimensions( + canvas: any, + widgets: CanvasWidgetsReduxState, + mainCanvasWidth: number, + isMobile: boolean, +): { canvasWidth: number; columnSpace: number } { + const canvasWidth: number = getCanvasWidth( + canvas, + widgets, + mainCanvasWidth, + isMobile, + ); + + let padding = (CONTAINER_GRID_PADDING + WIDGET_PADDING) * 2; + if ( + canvas.widgetId === MAIN_CONTAINER_WIDGET_ID || + canvas.type === "CONTAINER_WIDGET" + ) { + //For MainContainer and any Container Widget padding doesn't exist coz there is already container padding. + padding = CONTAINER_GRID_PADDING * 2; + } + if (canvas.noPad) { + // Widgets like ListWidget choose to have no container padding so will only have widget padding + padding = WIDGET_PADDING * 2; + } + const columnSpace: number = + (canvasWidth - padding) / GridDefaults.DEFAULT_GRID_COLUMNS; + + return { canvasWidth: canvasWidth - padding, columnSpace }; +} + +function getCanvasWidth( + canvas: any, + widgets: CanvasWidgetsReduxState, + mainCanvasWidth: number, + isMobile: boolean, +): number { + // TODO: @Preet - Update the logic to account for padding at each level. + if (!mainCanvasWidth) return 0; + if (canvas.widgetId === MAIN_CONTAINER_WIDGET_ID) return mainCanvasWidth; + let widget = canvas; + let columns = getWidgetWidth(widget, isMobile); + let width = columns / GridDefaults.DEFAULT_GRID_COLUMNS; + while (widget.widgetId !== MAIN_CONTAINER_WIDGET_ID) { + columns = getWidgetWidth(widget, isMobile); + width *= columns / GridDefaults.DEFAULT_GRID_COLUMNS; + widget = widgets[widget.parentId]; + } + return width * mainCanvasWidth; +} diff --git a/app/client/src/utils/autoLayout/positionUtils.ts b/app/client/src/utils/autoLayout/positionUtils.ts new file mode 100644 index 000000000000..63188e2c4d98 --- /dev/null +++ b/app/client/src/utils/autoLayout/positionUtils.ts @@ -0,0 +1,579 @@ +import { FlexLayerAlignment, ResponsiveBehavior } from "components/constants"; +import { FlexLayer } from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; +import { GridDefaults } from "constants/WidgetConstants"; +import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; +import { WidgetProps } from "widgets/BaseWidget"; +import { + getBottomRow, + getTopRow, + getWidgetHeight, + getWidgetRows, + getWidgetWidth, + setDimensions, +} from "./flexWidgetUtils"; + +export type Widget = WidgetProps & { + children?: string[] | undefined; +}; + +interface AlignmentInfo { + alignment: FlexLayerAlignment; + columns: number; + children: Widget[]; +} + +interface Row extends AlignmentInfo { + height: number; +} + +/** + * Calculate widget position on canvas. + * Logic - + * 1. If widget contains flexLayers, then update positions for all widgets in layers. + * 2. Else if widget contains children (implies fixed canvas), calculate the total height assumed by children. + * 3. If totalChildrenHeight in either case > widgetHeight and widget.parent.type === ContainerWidget || MainContainer, + * then update height of the widget and its parent. + */ +export function updateWidgetPositions( + allWidgets: CanvasWidgetsReduxState, + parentId: string, + isMobile = false, +): CanvasWidgetsReduxState { + let widgets = { ...allWidgets }; + try { + const parent = widgets[parentId]; + if (!parent) return widgets; + + let height = 0; + if (parent.flexLayers && parent.flexLayers?.length) { + /** + * For each flex layer, calculate position of child widgets + * and calculate the total height of all layers. + */ + for (const layer of parent.flexLayers) { + const payload: { + height: number; + widgets: CanvasWidgetsReduxState; + } = calculateWidgetPositions(widgets, layer, height, isMobile); + widgets = payload.widgets; + height += payload.height; + } + } else if (parent.children?.length) { + // calculate the total height required by all widgets. + height = getHeightOfFixedCanvas(widgets, parent, isMobile); + } + + const divisor = parent.parentRowSpace === 1 ? 10 : 1; + const parentHeight = getWidgetRows(parent, isMobile); + if (parentHeight <= height) { + /** + * if children height is greater than parent height, + * update the parent height to match the children height + * and add a buffer of 1 row to render the new layer highlights. + */ + const parentTopRow = getTopRow(parent, isMobile); + const updatedParent = setDimensions( + parent, + parentTopRow, + (parentTopRow + height + 1) * divisor, + null, + null, + isMobile, + ); + widgets = { ...widgets, [parent.widgetId]: updatedParent }; + } + const shouldUpdateHeight = + parent.parentId && + ["CONTAINER_WIDGET", "CANVAS_WIDGET"].includes( + allWidgets[parent.parentId].type, + ) && + parentHeight <= height; + + if (shouldUpdateHeight && parent.parentId) + return updateWidgetPositions(widgets, parent.parentId, isMobile); + return widgets; + } catch (e) { + // console.error(e); + return widgets; + } +} + +function calculateWidgetPositions( + allWidgets: CanvasWidgetsReduxState, + layer: FlexLayer, + topRow: number, + isMobile = false, +): { height: number; widgets: CanvasWidgetsReduxState } { + /** + * Get information break down on each alignment within the layer. + * Information - children, columns, alignment. + * Also, retrieve the length of each fill widget within this layer. + */ + const { fillWidgetLength, info } = extractAlignmentInfo( + allWidgets, + layer, + isMobile, + ); + /** + * Check if this layer is wrapped by css flex. + * if isMobile && totalColumns > 64 => true + */ + const isFlexWrapped: boolean = + isMobile && + info.reduce((acc, curr) => { + return acc + curr.columns; + }, 0) > GridDefaults.DEFAULT_GRID_COLUMNS; + + if (isFlexWrapped) + return updatePositionsForFlexWrap( + allWidgets, + info, + topRow, + fillWidgetLength, + isMobile, + ); + + return placeWidgetsWithoutWrap( + allWidgets, + info, + topRow, + fillWidgetLength, + isMobile, + ); +} + +/** + * Place all widgets in a particular row and update their positions. + * + * @param allWidgets | CanvasWidgetsReduxState : List of all widgets. + * @param arr | AlignmentInfo[] : Array of all alignments to be placed in this row. + * @param topRow | number : Starting row for placing the widgets. + * @param fillWidgetLength | number : Size of each fill widget in this row. + * @param isMobile : boolean : if the current viewport is mobile. default is false. + * @param totalHeight | number : total height assumed by the widgets in this row. + * @returns { height: number; widgets: CanvasWidgetsReduxState } + */ +function placeWidgetsWithoutWrap( + allWidgets: CanvasWidgetsReduxState, + arr: AlignmentInfo[], + topRow: number, + fillWidgetLength: number, + isMobile = false, + totalHeight = 0, +): { height: number; widgets: CanvasWidgetsReduxState } { + let widgets = { ...allWidgets }; + /** + * Get the size (columns: number) of each alignment in this row. + */ + const { centerSize, endSize, startSize } = getAlignmentSizeInfo(arr); + let maxHeight = totalHeight ? totalHeight : 0; + for (const each of arr) { + // Get the starting left column for each alignment in this row. + let left = getStartingPosition( + each.alignment, + startSize, + centerSize, + endSize, + each.columns, + ); + for (const widget of each.children) { + const height = getWidgetHeight(widget, isMobile); + const width = + widget.responsiveBehavior === ResponsiveBehavior.Fill + ? fillWidgetLength + : getWidgetWidth(widget, isMobile); + if (!totalHeight) maxHeight = Math.max(maxHeight, height); + const updatedWidget = setDimensions( + widget, + topRow, + topRow + height, + left, + left + width, + isMobile, + ); + widgets = { + ...widgets, + [widget.widgetId]: { + ...updatedWidget, + }, + }; + left += width; + } + } + + return { height: maxHeight, widgets }; +} + +/** + * Extract the space assumed by each alignment in the row. + * - Alignments are designed to grow and shrink equally. each starting with equal share of parent's width. + * - If one alignment needs more than its share of width to layout its children, + * then the remaining alignments shrink to make room, if possible. + * Logic: + * - Sort the input alignments in descending order their column requirements. + * - if each.columns > space / input.length (i.e. the alignment needs more columns than it's starting share.) + * - add the alignment to the output array. + * - recursively repeat the exercise for the remaining alignments and remaining space. + * - attribute equal space to all alignments in input. + * @param input | AlignmentInfo[] : Array of all alignments to be placed in this row. + * @param space | number : Total space available for placing the widgets. + * @param sizes | AlignmentInfo[] : Array of all alignments to be placed in this row. + * @returns AlignmentInfo[] + */ +function getAlignmentSizes( + input: AlignmentInfo[], + space: number, + sizes: AlignmentInfo[] = [], +): AlignmentInfo[] { + if (input.length === 0) return sizes; + const arr: AlignmentInfo[] = [...input].sort((a, b) => b.columns - a.columns); + if (arr[0].columns > space / arr.length) { + sizes.push(arr[0]); + arr.shift(); + return getAlignmentSizes( + arr, + space - sizes[sizes.length - 1].columns, + sizes, + ); + } else { + for (let i = 0; i < arr.length; i++) { + sizes.push({ ...arr[i], columns: space / arr.length }); + } + } + return sizes; +} + +/** + * Breakdown the current flex layer to extract information on each child alignment. + * Information for each alignment - children, columns, alignment. + * @param widgets | CanvasWidgetsReduxState: List of all widgets. + * @param layer | FlexLayer : current layer to be positioned on the canvas. + * @param isMobile | boolean + * @returns { info: AlignmentInfo[]; fillWidgetLength: number } + */ +function extractAlignmentInfo( + widgets: CanvasWidgetsReduxState, + layer: FlexLayer, + isMobile: boolean, +): { info: AlignmentInfo[]; fillWidgetLength: number } { + const startChildren = [], + centerChildren = [], + endChildren = [], + fillChildren = []; + let startColumns = 0, + centerColumns = 0, + endColumns = 0; + // Calculate the number of columns occupied by hug widgets in each alignment. + for (const child of layer.children) { + const widget = widgets[child.id]; + const isFillWidget = widget.responsiveBehavior === ResponsiveBehavior.Fill; + if (isFillWidget) fillChildren.push(child); + if (child.align === FlexLayerAlignment.Start) { + startChildren.push(widget); + if (!isFillWidget) startColumns += getWidgetWidth(widget, isMobile); + } else if (child.align === FlexLayerAlignment.Center) { + centerChildren.push(widget); + if (!isFillWidget) centerColumns += getWidgetWidth(widget, isMobile); + } else if (child.align === FlexLayerAlignment.End) { + endChildren.push(widget); + if (!isFillWidget) endColumns += getWidgetWidth(widget, isMobile); + } + } + + const availableColumns: number = + GridDefaults.DEFAULT_GRID_COLUMNS - + startColumns - + centerColumns - + endColumns; + // Fill widgets are designed to take up parent's entire width on mobile viewport. + const fillWidgetLength: number = isMobile + ? GridDefaults.DEFAULT_GRID_COLUMNS + : availableColumns / fillChildren.length; + for (const child of fillChildren) { + if (child.align === FlexLayerAlignment.Start) { + startColumns += fillWidgetLength; + } else if (child.align === FlexLayerAlignment.Center) { + centerColumns += fillWidgetLength; + } else if (child.align === FlexLayerAlignment.End) { + endColumns += fillWidgetLength; + } + } + + return { + info: [ + { + alignment: FlexLayerAlignment.Start, + columns: startColumns, + children: startChildren, + }, + { + alignment: FlexLayerAlignment.Center, + columns: centerColumns, + children: centerChildren, + }, + { + alignment: FlexLayerAlignment.End, + columns: endColumns, + children: endChildren, + }, + ], + fillWidgetLength, + }; +} + +function getAlignmentSizeInfo( + arr: AlignmentInfo[], +): { startSize: number; centerSize: number; endSize: number } { + let startSize = 0, + centerSize = 0, + endSize = 0; + const sizes: { + alignment: FlexLayerAlignment; + columns: number; + }[] = getAlignmentSizes(arr, GridDefaults.DEFAULT_GRID_COLUMNS, []); + + for (const each of sizes) { + if (each.alignment === FlexLayerAlignment.Start) { + startSize = each.columns; + } else if (each.alignment === FlexLayerAlignment.Center) { + centerSize = each.columns; + } else if (each.alignment === FlexLayerAlignment.End) { + endSize = each.columns; + } + } + return { startSize, centerSize, endSize }; +} + +/** + * Find out which alignment is placed in which row. + * + * In case of flex wrap, + * - alignments within a FlexLayer are placed in multiple rows. + * Logic: + * - for each alignment in arr + * - if alignment.columns < 64 + * - add it to the current row (res[resIndex]) + * - and track the total occupied columns in this row (total) + * - else + * - add the current row to the output rows + * - and start a new row to repeat the process recursively. + * @param arr | AlignmentInfo[] : Array of alignments to be placed in this layer. + * @param res | AlignmentInfo[][] : Output array of alignments to be placed in this layer. + * @param resIndex | number : Last index of res. + * @returns AlignmentInfo[][] + */ +function getWrappedAlignmentInfo( + arr: AlignmentInfo[], + res: AlignmentInfo[][] = [[], [], []], + resIndex = 0, +): AlignmentInfo[][] { + if (arr.length === 1) { + res[resIndex].push(arr[0]); + return res; + } + let index = 0; + let total = 0; + for (const each of arr) { + if (total + each.columns >= GridDefaults.DEFAULT_GRID_COLUMNS) { + let x = index; + if (!res[resIndex].length) { + res[resIndex].push(each); + x += 1; + } + return getWrappedAlignmentInfo([...arr.slice(x)], res, resIndex + 1); + } + total += each.columns; + index += 1; + res[resIndex].push(each); + } + return res; +} + +/** + * @param allWidgets | CanvasWidgetsReduxState: all widgets. + * @param arr | AlignmentInfo[] : Array of alignments to be placed in this layer. + * @param topRow | number : Starting row to place the widgets. + * @param fillWidgetLength | number : Length of fill widgets. + * @param isMobile | boolean : Is mobile viewport. + * @returns { height: number; widgets: CanvasWidgetsReduxState } + */ +function updatePositionsForFlexWrap( + allWidgets: CanvasWidgetsReduxState, + arr: AlignmentInfo[], + topRow: number, + fillWidgetLength: number, + isMobile: boolean, +): { height: number; widgets: CanvasWidgetsReduxState } { + let widgets = { ...allWidgets }; + + const wrappedAlignments: AlignmentInfo[][] = getWrappedAlignmentInfo(arr); + + let top = topRow; + for (const each of wrappedAlignments) { + if (!each.length) break; + // if there is only one alignment in this row, this implies that it may be wrapped. + const payload = + each.length === 1 + ? placeWrappedWidgets(widgets, each[0], top, fillWidgetLength, isMobile) + : placeWidgetsWithoutWrap( + widgets, + each, + top, + fillWidgetLength, + isMobile, + ); + widgets = payload.widgets; + top += payload.height; + continue; + } + return { height: top - topRow, widgets }; +} + +function getStartingPosition( + alignment: FlexLayerAlignment, + startSize: number, + centerSize: number, + endSize: number, + columns: number, +): number { + if (alignment === FlexLayerAlignment.Start) { + return 0; + } else if (alignment === FlexLayerAlignment.Center) { + return startSize + centerSize / 2 - columns / 2; + } else if (alignment === FlexLayerAlignment.End) { + return startSize + centerSize + endSize - columns; + } + return 0; +} + +/** + * If the alignment requires more than 64 columns, it is wrapped. + * => a single alignment spans multiple layers. + * Logic: + * - Find out the number of rows required to position the widgets in the alignment. + * - Place each row normally. + * @param allWidgets | CanvasWidgetsReduxState: all widgets. + * @param alignment | AlignmentInfo: alignment to be positioned. + * @param topRow | number: top row to place the widgets. + * @param fillWidgetLength | number: length of fill widgets. + * @param isMobile | boolean: is mobile viewport. + * @returns { height: number; widgets: CanvasWidgetsReduxState } + */ +function placeWrappedWidgets( + allWidgets: CanvasWidgetsReduxState, + alignment: AlignmentInfo, + topRow: number, + fillWidgetLength: number, + isMobile = false, +): { height: number; widgets: CanvasWidgetsReduxState } { + let widgets = { ...allWidgets }; + + let startRow = topRow; + const rows: Row[] = getWrappedRows(alignment, [], isMobile); + for (const row of rows) { + const { alignment, children, columns, height } = row; + const result: { + height: number; + widgets: CanvasWidgetsReduxState; + } = placeWidgetsWithoutWrap( + widgets, + [{ alignment, children, columns }], + startRow, + fillWidgetLength, + isMobile, + height, + ); + widgets = result.widgets; + startRow += height; + } + + return { height: startRow - topRow, widgets }; +} + +/** + * Find out the number of rows required to position all the widgets in the given alignment. + * - for each child + * - if total columns < 64 + * - add it to the current row. + * - track the total consumed columns. + * - track the height of the current row. + * - else + * - add the current row to the output array. + * - recursively continue the process for remaining children. + * @param arr | AlignmentInfo: alignment to be positioned. + * @param rows | Row[]: output rows. + * @param isMobile | boolean: is mobile viewport. + * @returns Row[] + */ +function getWrappedRows( + arr: AlignmentInfo, + rows: Row[], + isMobile = false, +): Row[] { + const row: Row = { + alignment: arr.alignment, + children: [], + columns: 0, + height: 0, + }; + const space = GridDefaults.DEFAULT_GRID_COLUMNS; + const temp: Widget[] = []; + let columns = 0, + index = 0, + maxHeight = 0; + for (const child of arr.children) { + const width = getWidgetWidth(child, isMobile); + if (columns + width > space) { + row.children.push(...temp); + row.height = maxHeight; + row.columns = columns; + rows.push(row); + return getWrappedRows( + { + ...arr, + children: [...arr.children.slice(index)], + }, + [...rows], + isMobile, + ); + } + temp.push(child); + maxHeight = Math.max(maxHeight, getWidgetHeight(child, isMobile)); + columns += width; + index += 1; + } + if (temp.length) { + row.children.push(...temp); + row.height = maxHeight; + row.columns = columns; + rows.push(row); + } + return rows; +} + +function getHeightOfFixedCanvas( + widgets: CanvasWidgetsReduxState, + parent: Widget, + isMobile: boolean, +): number { + if (!parent.children || !parent.children.length) + return getWidgetRows(parent, isMobile); + return getTotalRowsOfAllChildren(widgets, parent.children, isMobile); +} + +export function getTotalRowsOfAllChildren( + widgets: CanvasWidgetsReduxState, + children: string[], + isMobile: boolean, +): number { + let top = 10000, + bottom = 0; + for (const childId of children) { + const child = widgets[childId]; + if (!child) continue; + const divisor = child.parentRowSpace === 1 ? 10 : 1; + top = Math.min(top, getTopRow(child, isMobile)); + bottom = Math.max(bottom, getBottomRow(child, isMobile) / divisor); + } + return bottom - top; +} diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index d6be90f1a30a..a5d33c781188 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -242,7 +242,10 @@ abstract class BaseWidget< this.props.bottomRow, this.props.parentColumnSpace, this.props.parentRowSpace, + this.props.mobileLeftColumn, this.props.mobileRightColumn, + this.props.mobileTopRow, + this.props.mobileBottomRow, this.props.isMobile, ); }; @@ -254,19 +257,34 @@ abstract class BaseWidget< bottomRow: number, parentColumnSpace: number, parentRowSpace: number, + mobileLeftColumn?: number, mobileRightColumn?: number, + mobileTopRow?: number, + mobileBottomRow?: number, isMobile?: boolean, ): { componentWidth: number; componentHeight: number; } { const right = - isMobile && mobileRightColumn && parentColumnSpace !== 1 + isMobile && mobileRightColumn !== undefined && parentColumnSpace !== 1 ? mobileRightColumn : rightColumn; + const left = + isMobile && mobileLeftColumn !== undefined && parentColumnSpace !== 1 + ? mobileLeftColumn + : leftColumn; + const top = + isMobile && mobileTopRow !== undefined && parentRowSpace !== 1 + ? mobileTopRow + : topRow; + const bottom = + isMobile && mobileBottomRow !== undefined && parentRowSpace !== 1 + ? mobileBottomRow + : bottomRow; return { - componentWidth: (right - leftColumn) * parentColumnSpace, - componentHeight: (bottomRow - topRow) * parentRowSpace, + componentWidth: (right - left) * parentColumnSpace, + componentHeight: (bottom - top) * parentRowSpace, }; } @@ -604,7 +622,10 @@ export type WidgetRowCols = { topRow: number; bottomRow: number; minHeight?: number; // Required to reduce the size of CanvasWidgets. + mobileLeftColumn?: number; mobileRightColumn?: number; + mobileTopRow?: number; + mobileBottomRow?: number; height?: number; }; diff --git a/app/client/src/widgets/ContainerWidget/widget/index.tsx b/app/client/src/widgets/ContainerWidget/widget/index.tsx index 3b0267542e0d..a1722128a787 100644 --- a/app/client/src/widgets/ContainerWidget/widget/index.tsx +++ b/app/client/src/widgets/ContainerWidget/widget/index.tsx @@ -219,7 +219,7 @@ export class ContainerWidget extends BaseWidget< renderAsContainerComponent(props: ContainerWidgetProps) { return ( - + Date: Sun, 1 Jan 2023 19:11:18 -0500 Subject: [PATCH 333/708] basic fix for mobile highlights --- .../src/utils/autoLayout/highlightUtils.ts | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/app/client/src/utils/autoLayout/highlightUtils.ts b/app/client/src/utils/autoLayout/highlightUtils.ts index 1a02172d0532..2acfe9dc7cd7 100644 --- a/app/client/src/utils/autoLayout/highlightUtils.ts +++ b/app/client/src/utils/autoLayout/highlightUtils.ts @@ -237,7 +237,7 @@ function generateVerticalHighlights(data: { } function generateHighlightsForSubWrapper(data: { - arr: any[]; + arr: Widget[]; childCount: number; layerIndex: number; alignment: FlexLayerAlignment; @@ -278,7 +278,9 @@ function generateHighlightsForSubWrapper(data: { getTopRow(child, isMobile) * child.parentRowSpace + HORIZONTAL_HIGHLIGHT_MARGIN, width: DEFAULT_HIGHLIGHT_SIZE, - height, + height: isMobile + ? getWidgetHeight(child, isMobile) * child.parentRowSpace + : height, isVertical: true, canvasId, }); @@ -286,7 +288,8 @@ function generateHighlightsForSubWrapper(data: { } if (!avoidInitialHighlight) { - const lastChild: Widget = arr && arr.length ? arr[arr.length - 1] : null; + const lastChild: Widget | null = + arr && arr.length ? arr[arr.length - 1] : null; res.push({ isNewLayer: false, index: count + childCount, @@ -308,7 +311,10 @@ function generateHighlightsForSubWrapper(data: { : getTopRow(lastChild, isMobile) * lastChild?.parentRowSpace + HORIZONTAL_HIGHLIGHT_MARGIN, width: DEFAULT_HIGHLIGHT_SIZE, - height, + height: + isMobile && lastChild !== null + ? getWidgetHeight(lastChild, isMobile) * lastChild.parentRowSpace + : height, isVertical: true, canvasId, }); @@ -332,14 +338,16 @@ function getPositionForInitialHighlight( containerWidth: number, canvasId: string, ): number { + const endPosition = + containerWidth - (canvasId !== MAIN_CONTAINER_WIDGET_ID ? 6 : 0); if (alignment === FlexLayerAlignment.End) { - return containerWidth - (canvasId !== MAIN_CONTAINER_WIDGET_ID ? 6 : 0); + return endPosition; } else if (alignment === FlexLayerAlignment.Center) { if (!highlights.length) return containerWidth / 2; - return posX; + return Math.min(posX, endPosition); } else { if (!highlights.length) return 2; - return posX; + return Math.min(posX, endPosition); } } From 18fc25eca50b65e3e0ff5a7c6de18ef04538fb4e Mon Sep 17 00:00:00 2001 From: Preet Date: Sun, 1 Jan 2023 19:47:21 -0500 Subject: [PATCH 334/708] pass isMobile prop from sagas --- .../src/sagas/AutoLayoutUpdateSagas.tsx | 4 ++- app/client/src/sagas/AutoLayoutUtils.ts | 10 +++++-- .../CanvasSagas/AutoLayoutDraggingSagas.ts | 13 +++++++-- app/client/src/sagas/WidgetDeletionSagas.ts | 7 +++++ app/client/src/sagas/WidgetOperationSagas.tsx | 3 ++ .../src/utils/autoLayout/highlightUtils.ts | 28 ++++++------------- 6 files changed, 41 insertions(+), 24 deletions(-) diff --git a/app/client/src/sagas/AutoLayoutUpdateSagas.tsx b/app/client/src/sagas/AutoLayoutUpdateSagas.tsx index d61eb421f04a..ac7ec0d0abcd 100644 --- a/app/client/src/sagas/AutoLayoutUpdateSagas.tsx +++ b/app/client/src/sagas/AutoLayoutUpdateSagas.tsx @@ -8,7 +8,7 @@ import { ResponsiveBehavior } from "components/constants"; import log from "loglevel"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; import { all, call, put, select, takeLatest } from "redux-saga/effects"; -import { updateWidgetPositions } from "utils/autoLayout/positionUtils"; +import { getIsMobile } from "selectors/mainCanvasSelectors"; import { alterLayoutForDesktop, alterLayoutForMobile, @@ -49,10 +49,12 @@ function* addChildWrappers(actionPayload: ReduxAction) { const start = performance.now(); const { parentId } = actionPayload.payload; const allWidgets: CanvasWidgetsReduxState = yield select(getWidgets); + const isMobile: boolean = yield select(getIsMobile); const updatedWidgets: CanvasWidgetsReduxState = yield call( wrapChildren, allWidgets, parentId, + isMobile, ); yield put(updateAndSaveLayout(updatedWidgets)); log.debug("empty wrapper removal took", performance.now() - start, "ms"); diff --git a/app/client/src/sagas/AutoLayoutUtils.ts b/app/client/src/sagas/AutoLayoutUtils.ts index f071ac11d24a..3c28632588f5 100644 --- a/app/client/src/sagas/AutoLayoutUtils.ts +++ b/app/client/src/sagas/AutoLayoutUtils.ts @@ -39,6 +39,7 @@ export function removeChildLayers( export function* wrapChildren( allWidgets: CanvasWidgetsReduxState, containerId: string, + isMobile?: boolean, ) { const widgets = { ...allWidgets }; let canvas = getCanvas(widgets, containerId); @@ -61,7 +62,11 @@ export function* wrapChildren( canvas = { ...canvas, flexLayers }; widgets[canvas.widgetId] = canvas; // update size - const updatedWidgets = updateWidgetPositions(widgets, canvas.widgetId); + const updatedWidgets = updateWidgetPositions( + widgets, + canvas.widgetId, + isMobile, + ); return updatedWidgets; } @@ -69,6 +74,7 @@ export function* updateFlexLayersOnDelete( allWidgets: CanvasWidgetsReduxState, widgetId: string, parentId: string, + isMobile?: boolean, ) { const widgets = { ...allWidgets }; let parent = widgets[parentId]; @@ -115,7 +121,7 @@ export function* updateFlexLayersOnDelete( widgets[parentId] = parent; if (layerIndex === -1) return widgets; - return updateWidgetPositions(widgets, parentId); + return updateWidgetPositions(widgets, parentId, isMobile); } // TODO: refactor these implementations export function updateFillChildStatus( diff --git a/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts b/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts index e931cedc4517..8f1924dac82b 100644 --- a/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts +++ b/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts @@ -21,6 +21,7 @@ import { all, call, put, select, takeLatest } from "redux-saga/effects"; import { updateFlexChildColumns } from "sagas/AutoLayoutUtils"; import { getWidgets } from "sagas/selectors"; import { getUpdateDslAfterCreatingChild } from "sagas/WidgetAdditionSagas"; +import { getIsMobile } from "selectors/mainCanvasSelectors"; import { updateWidgetPositions } from "utils/autoLayout/positionUtils"; function* addWidgetAndReorderSaga( @@ -34,6 +35,7 @@ function* addWidgetAndReorderSaga( const start = performance.now(); const { direction, dropPayload, newWidget, parentId } = actionPayload.payload; const { alignment, index, isNewLayer, layerIndex, rowIndex } = dropPayload; + const isMobile: boolean = yield select(getIsMobile); try { const updatedWidgetsOnAddition: CanvasWidgetsReduxState = yield call( getUpdateDslAfterCreatingChild, @@ -55,6 +57,7 @@ function* addWidgetAndReorderSaga( direction, layerIndex, rowIndex, + isMobile, }, ); let updatedWidgetsAfterResizing = updatedWidgetsOnMove; @@ -97,6 +100,7 @@ function* autoLayoutReorderSaga( try { const allWidgets: CanvasWidgetsReduxState = yield select(getWidgets); + const isMobile: boolean = yield select(getIsMobile); if (!parentId || !movedWidgets || !movedWidgets.length) return; const updatedWidgets: CanvasWidgetsReduxState = yield call( reorderAutolayoutChildren, @@ -110,6 +114,7 @@ function* autoLayoutReorderSaga( direction, layerIndex, rowIndex, + isMobile, }, ); @@ -130,12 +135,14 @@ function* reorderAutolayoutChildren(params: { direction: LayoutDirection; layerIndex?: number; rowIndex: number; + isMobile?: boolean; }) { const { alignment, allWidgets, direction, index, + isMobile, isNewLayer, layerIndex, movedWidgets, @@ -150,6 +157,7 @@ function* reorderAutolayoutChildren(params: { selectedWidgets, widgets, parentId, + isMobile, ); // Update flexLayers for a vertical stack. @@ -217,10 +225,10 @@ function* reorderAutolayoutChildren(params: { bottomRow: parentWidget.topRow + height, }; } - const widgetsAfterPositionUpdate = updateWidgetPositions( updatedWidgets, parentId, + isMobile, ); return widgetsAfterPositionUpdate; @@ -239,6 +247,7 @@ function updateRelationships( movedWidgets: string[], allWidgets: CanvasWidgetsReduxState, parentId: string, + isMobile = false, ): CanvasWidgetsReduxState { const widgets = { ...allWidgets }; // Check if parent has changed @@ -277,7 +286,7 @@ function updateRelationships( } if (prevParents.length) { for (const id of prevParents) { - const updatedWidgets = updateWidgetPositions(widgets, id); + const updatedWidgets = updateWidgetPositions(widgets, id, isMobile); return updatedWidgets; } } diff --git a/app/client/src/sagas/WidgetDeletionSagas.ts b/app/client/src/sagas/WidgetDeletionSagas.ts index 57f2396edc1e..8b30a1ae7b86 100644 --- a/app/client/src/sagas/WidgetDeletionSagas.ts +++ b/app/client/src/sagas/WidgetDeletionSagas.ts @@ -24,6 +24,7 @@ import { import { MainCanvasReduxState } from "reducers/uiReducers/mainCanvasReducer"; import { all, call, put, select, takeEvery } from "redux-saga/effects"; import { getMainCanvasProps } from "selectors/editorSelectors"; +import { getIsMobile } from "selectors/mainCanvasSelectors"; import { inGuidedTour, isExploringSelector, @@ -94,11 +95,13 @@ function* deleteTabChildSaga( }, }; // Update flex layers of a canvas upon deletion of a widget. + const isMobile: boolean = yield select(getIsMobile); const widgetsAfterUpdatingFlexLayers: CanvasWidgetsReduxState = yield call( updateFlexLayersOnDelete, parentUpdatedWidgets, widgetId, tabWidget.parentId, + isMobile, ); yield put(updateAndSaveLayout(widgetsAfterUpdatingFlexLayers)); yield call(postDelete, widgetId, label, otherWidgetsToDelete); @@ -240,12 +243,14 @@ function* deleteSaga(deleteAction: ReduxAction) { if (updatedObj) { const { finalWidgets, otherWidgetsToDelete, widgetName } = updatedObj; + const isMobile: boolean = yield select(getIsMobile); // Update flex layers of a canvas upon deletion of a widget. const widgetsAfterUpdatingFlexLayers: CanvasWidgetsReduxState = yield call( updateFlexLayersOnDelete, finalWidgets, widgetId, parentId, + isMobile, ); yield put(updateAndSaveLayout(widgetsAfterUpdatingFlexLayers)); yield put(generateAutoHeightLayoutTreeAction(true, true)); @@ -315,12 +320,14 @@ function* deleteAllSelectedWidgetsSaga( const parentId = widgets[selectedWidgets[0]].parentId; let widgetsAfterUpdatingFlexLayers: CanvasWidgetsReduxState = finalWidgets; if (parentId) { + const isMobile: boolean = yield select(getIsMobile); for (const widgetId of selectedWidgets) { widgetsAfterUpdatingFlexLayers = yield call( updateFlexLayersOnDelete, widgetsAfterUpdatingFlexLayers, widgetId, parentId, + isMobile, ); } } diff --git a/app/client/src/sagas/WidgetOperationSagas.tsx b/app/client/src/sagas/WidgetOperationSagas.tsx index d4778e881ebf..cd32991445af 100644 --- a/app/client/src/sagas/WidgetOperationSagas.tsx +++ b/app/client/src/sagas/WidgetOperationSagas.tsx @@ -156,6 +156,7 @@ import { widgetSelectionSagas } from "./WidgetSelectionSagas"; import { getAllPaths } from "ce/workers/Evaluation/evaluationUtils"; import { updateWidgetPositions } from "utils/autoLayout/positionUtils"; import { getSlidingArenaName } from "constants/componentClassNameConstants"; +import { getIsMobile } from "selectors/mainCanvasSelectors"; export function* updateAllChildCanvasHeights( currentContainerLikeWidgetId: string, @@ -255,9 +256,11 @@ export function* resizeSaga(resizeAction: ReduxAction) { bottomRow: updatedCanvasBottomRow, }; } + const isMobile: boolean = yield select(getIsMobile); const updatedWidgetsAfterResizing = updateWidgetPositions( movedWidgets, parentId, + isMobile, ); log.debug("resize computations took", performance.now() - start, "ms"); yield put(stopReflowAction()); diff --git a/app/client/src/utils/autoLayout/highlightUtils.ts b/app/client/src/utils/autoLayout/highlightUtils.ts index 2acfe9dc7cd7..3669f395e8b7 100644 --- a/app/client/src/utils/autoLayout/highlightUtils.ts +++ b/app/client/src/utils/autoLayout/highlightUtils.ts @@ -66,14 +66,6 @@ export function deriveHighlightsFromLayers( layer?.children?.filter( (child: LayerChild) => draggedWidgets.indexOf(child.id) === -1, ).length === 0; - // TODO: use getHeightOfFixedCanvas to measure the height of the layer. - const tallestChild = layer.children?.reduce((acc, child) => { - const widget = widgets[child.id]; - return Math.max( - acc, - getWidgetHeight(widget, isMobile) * widget.parentRowSpace, - ); - }, 0); const childrenRows = getTotalRowsOfAllChildren( widgets, layer.children?.map((child) => child.id) || [], @@ -85,7 +77,6 @@ export function deriveHighlightsFromLayers( layer, childCount, layerIndex, - height: tallestChild, offsetTop, canvasWidth, canvasId, @@ -143,7 +134,6 @@ function generateVerticalHighlights(data: { layer: FlexLayer; childCount: number; layerIndex: number; - height: number; offsetTop: number; canvasWidth: number; canvasId: string; @@ -157,7 +147,6 @@ function generateVerticalHighlights(data: { childCount, columnSpace, draggedWidgets, - height, isMobile, layer, layerIndex, @@ -172,12 +161,13 @@ function generateVerticalHighlights(data: { endChildren = []; let startColumns = 0, endColumns = 0; - + let maxHeight = 0; for (const child of children) { const widget = widgets[child.id]; if (!widget) continue; count += 1; if (draggedWidgets.indexOf(child.id) > -1) continue; + maxHeight = Math.max(maxHeight, getWidgetHeight(widget, isMobile)); if (child.align === FlexLayerAlignment.End) { endChildren.push(widget); endColumns += getWidgetWidth(widget, isMobile); @@ -196,7 +186,7 @@ function generateVerticalHighlights(data: { childCount, layerIndex, alignment: FlexLayerAlignment.Start, - height, + maxHeight, offsetTop, canvasId, parentColumnSpace: columnSpace, @@ -209,7 +199,7 @@ function generateVerticalHighlights(data: { childCount: childCount + startChildren.length, layerIndex, alignment: FlexLayerAlignment.Center, - height, + maxHeight, offsetTop, canvasId, parentColumnSpace: columnSpace, @@ -223,7 +213,7 @@ function generateVerticalHighlights(data: { childCount: childCount + startChildren.length + centerChildren.length, layerIndex, alignment: FlexLayerAlignment.End, - height, + maxHeight, offsetTop, canvasId, parentColumnSpace: columnSpace, @@ -241,7 +231,7 @@ function generateHighlightsForSubWrapper(data: { childCount: number; layerIndex: number; alignment: FlexLayerAlignment; - height: number; + maxHeight: number; offsetTop: number; canvasId: string; parentColumnSpace: number; @@ -257,9 +247,9 @@ function generateHighlightsForSubWrapper(data: { canvasId, canvasWidth, childCount, - height, isMobile, layerIndex, + maxHeight, offsetTop, parentColumnSpace, } = data; @@ -280,7 +270,7 @@ function generateHighlightsForSubWrapper(data: { width: DEFAULT_HIGHLIGHT_SIZE, height: isMobile ? getWidgetHeight(child, isMobile) * child.parentRowSpace - : height, + : maxHeight, isVertical: true, canvasId, }); @@ -314,7 +304,7 @@ function generateHighlightsForSubWrapper(data: { height: isMobile && lastChild !== null ? getWidgetHeight(lastChild, isMobile) * lastChild.parentRowSpace - : height, + : maxHeight, isVertical: true, canvasId, }); From b6e9875a9942a2012313a33f6797142724fcb0a2 Mon Sep 17 00:00:00 2001 From: Preet Date: Sun, 1 Jan 2023 19:52:35 -0500 Subject: [PATCH 335/708] no hiding dragged widgets --- .../appsmith/autoLayout/FlexComponent.tsx | 11 ++--------- app/client/src/utils/autoLayout/highlightUtils.ts | 5 ++++- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx index 95d3c0f3c821..a4a844aaeed2 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx @@ -1,7 +1,6 @@ import React, { CSSProperties, ReactNode, useCallback, useMemo } from "react"; import styled from "styled-components"; -import { AppState } from "ce/reducers"; import { FlexVerticalAlignment, LayoutDirection, @@ -15,7 +14,6 @@ import { import { useSelector } from "react-redux"; import { snipingModeSelector } from "selectors/editorSelectors"; import { getIsMobile } from "selectors/mainCanvasSelectors"; -import { isWidgetSelected } from "selectors/widgetSelectors"; import { useClickToSelectWidget } from "utils/hooks/useClickToSelectWidget"; import { usePositionedContainerZIndex } from "utils/hooks/usePositionedContainerZIndex"; import { checkIsDropTarget } from "../PositionedContainer"; @@ -43,10 +41,7 @@ const FlexWidget = styled.div` export function FlexComponent(props: AutoLayoutProps) { const isMobile = useSelector(getIsMobile); const isSnipingMode = useSelector(snipingModeSelector); - const isSelected = useSelector(isWidgetSelected(props.widgetId)); - const isDragging = useSelector( - (state: AppState) => state.ui.widgetDragResize.isDragging, - ); + const clickToSelectWidget = useClickToSelectWidget(props.widgetId); const onClickFn = useCallback(() => { clickToSelectWidget(props.widgetId); @@ -78,7 +73,7 @@ export function FlexComponent(props: AutoLayoutProps) { const flexComponentStyle: CSSProperties = useMemo(() => { return { - display: isSelected && isDragging ? "none" : "flex", + display: "flex", zIndex, width: `${Math.floor(props.componentWidth) - WIDGET_PADDING * 2}px`, height: isMobile @@ -93,10 +88,8 @@ export function FlexComponent(props: AutoLayoutProps) { }, }; }, [ - isDragging, isFillWidget, isMobile, - isSelected, props.componentWidth, props.componentHeight, props.flexVerticalAlignment, diff --git a/app/client/src/utils/autoLayout/highlightUtils.ts b/app/client/src/utils/autoLayout/highlightUtils.ts index 3669f395e8b7..130c7a838e7f 100644 --- a/app/client/src/utils/autoLayout/highlightUtils.ts +++ b/app/client/src/utils/autoLayout/highlightUtils.ts @@ -167,7 +167,10 @@ function generateVerticalHighlights(data: { if (!widget) continue; count += 1; if (draggedWidgets.indexOf(child.id) > -1) continue; - maxHeight = Math.max(maxHeight, getWidgetHeight(widget, isMobile)); + maxHeight = Math.max( + maxHeight, + getWidgetHeight(widget, isMobile) * widget.parentRowSpace, + ); if (child.align === FlexLayerAlignment.End) { endChildren.push(widget); endColumns += getWidgetWidth(widget, isMobile); From 88466e8dca03c7698adba3330d72ae6a86662e2a Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 2 Jan 2023 08:35:15 -0500 Subject: [PATCH 336/708] fix form widget --- app/client/src/widgets/FormWidget/index.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/client/src/widgets/FormWidget/index.ts b/app/client/src/widgets/FormWidget/index.ts index 7464099c3266..efb8b7a88c80 100644 --- a/app/client/src/widgets/FormWidget/index.ts +++ b/app/client/src/widgets/FormWidget/index.ts @@ -1,4 +1,8 @@ -import { ButtonVariantTypes, RecaptchaTypes } from "components/constants"; +import { + ButtonVariantTypes, + Positioning, + RecaptchaTypes, +} from "components/constants"; import { Colors } from "constants/Colors"; import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; import { getDefaultResponsiveBehavior } from "utils/layoutPropertiesUtils"; @@ -27,6 +31,7 @@ export const CONFIG = { widgetName: "Form", backgroundColor: Colors.WHITE, children: [], + positioning: Positioning.Fixed, blueprint: { view: [ { From a272b62286e1e416f2d1ec01e900a9a80b684639 Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 2 Jan 2023 08:54:31 -0500 Subject: [PATCH 337/708] remove horizontal resize for fill widgets --- .../editorComponents/ResizableComponent.tsx | 11 ++++++++++- app/client/src/resizable/resizenreflow/index.tsx | 10 +++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/app/client/src/components/editorComponents/ResizableComponent.tsx b/app/client/src/components/editorComponents/ResizableComponent.tsx index e38187a508e0..60c5b514c19c 100644 --- a/app/client/src/components/editorComponents/ResizableComponent.tsx +++ b/app/client/src/components/editorComponents/ResizableComponent.tsx @@ -327,6 +327,15 @@ export const ResizableComponent = memo(function ResizableComponent( const isVerticalResizeEnabled = useMemo(() => { return !isAutoHeightEnabledForWidget(props) && isEnabled; }, [props, isAutoHeightEnabledForWidget, isEnabled]); + const isHorizontalResizeEnabled = useMemo(() => { + return ( + isEnabled && + !( + props.isFlexChild && + props.responsiveBehavior === ResponsiveBehavior.Fill + ) + ); + }, [isEnabled, props.isFlexChild, props.responsiveBehavior]); const allowResize = !( NonResizableWidgets.includes(props.type) || isMultiSelected ); @@ -336,7 +345,7 @@ export const ResizableComponent = memo(function ResizableComponent( componentHeight={dimensions.height} componentWidth={dimensions.width} direction={props.direction} - enableHorizontalResize={isEnabled} + enableHorizontalResize={isHorizontalResizeEnabled} enableVerticalResize={isVerticalResizeEnabled} getResizedPositions={getResizedPositions} gridProps={gridProps} diff --git a/app/client/src/resizable/resizenreflow/index.tsx b/app/client/src/resizable/resizenreflow/index.tsx index e507d546c902..08ace75c3152 100644 --- a/app/client/src/resizable/resizenreflow/index.tsx +++ b/app/client/src/resizable/resizenreflow/index.tsx @@ -493,6 +493,14 @@ export function ReflowResizable(props: ResizableProps) { props.enableVerticalResize, handle.handleDirection, ); + console.log( + "#### enable", + props.widgetId, + props.enableHorizontalResize, + props.enableVerticalResize, + handle.handleDirection, + disableDot, + ); return ( {props.children} - {props.enableHorizontalResize && renderHandles} + {(props.enableHorizontalResize || props.isFlexChild) && renderHandles} )} From 30faaf934b80879864afef4a5dd3485c6fd3d8ba Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 2 Jan 2023 11:44:48 -0500 Subject: [PATCH 338/708] bug fixes --- .../appsmith/autoLayout/FlexBoxComponent.tsx | 9 +++++--- .../appsmith/autoLayout/FlexComponent.tsx | 6 ++---- .../editorComponents/ResizableComponent.tsx | 11 +--------- .../editorComponents/ResizableUtils.ts | 7 ++++++- .../WidgetNameComponent/index.tsx | 2 ++ app/client/src/constants/WidgetConstants.tsx | 2 ++ .../src/resizable/resizenreflow/index.tsx | 12 +++-------- .../src/utils/autoLayout/highlightUtils.ts | 21 ++++++++----------- .../src/utils/autoLayout/positionUtils.ts | 11 +++++++++- .../widgets/ContainerWidget/widget/index.tsx | 6 +++++- 10 files changed, 46 insertions(+), 41 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx index a93b68e3eedc..ef33376dd1f7 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx @@ -14,6 +14,7 @@ import { getWidgets } from "sagas/selectors"; import { getAppMode } from "selectors/entitiesSelector"; import { getIsMobile } from "selectors/mainCanvasSelectors"; import AutoLayoutLayer from "./AutoLayoutLayer"; +import { FLEXBOX_PADDING } from "constants/WidgetConstants"; export interface FlexBoxProps { direction?: LayoutDirection; @@ -35,6 +36,8 @@ export interface FlexLayer { hasFillChild?: boolean; } +export const DEFAULT_HIGHLIGHT_SIZE = 4; + export const FlexContainer = styled.div<{ useAutoLayout?: boolean; direction?: LayoutDirection; @@ -61,11 +64,11 @@ export const FlexContainer = styled.div<{ isMainContainer || isMobile ? "auto" : "hidden"}; padding: ${({ leaveSpaceForWidgetName }) => - leaveSpaceForWidgetName ? "4px 4px 22px 4px;" : "0px;"}; + leaveSpaceForWidgetName + ? `${FLEXBOX_PADDING}px ${FLEXBOX_PADDING}px 22px ${FLEXBOX_PADDING}px;` + : "0px;"}; `; -export const DEFAULT_HIGHLIGHT_SIZE = 4; - function FlexBoxComponent(props: FlexBoxProps) { // TODO: set isMobile as a prop at the top level const isMobile = useSelector(getIsMobile); diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx index a4a844aaeed2..add40c738204 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx @@ -75,10 +75,8 @@ export function FlexComponent(props: AutoLayoutProps) { return { display: "flex", zIndex, - width: `${Math.floor(props.componentWidth) - WIDGET_PADDING * 2}px`, - height: isMobile - ? "auto" - : Math.floor(props.componentHeight) - WIDGET_PADDING * 2 + "px", + width: `${props.componentWidth - WIDGET_PADDING * 2}px`, + height: props.componentHeight - WIDGET_PADDING * 2 + "px", minHeight: "30px", margin: WIDGET_PADDING + "px", flexGrow: isFillWidget ? 1 : 0, diff --git a/app/client/src/components/editorComponents/ResizableComponent.tsx b/app/client/src/components/editorComponents/ResizableComponent.tsx index 60c5b514c19c..e38187a508e0 100644 --- a/app/client/src/components/editorComponents/ResizableComponent.tsx +++ b/app/client/src/components/editorComponents/ResizableComponent.tsx @@ -327,15 +327,6 @@ export const ResizableComponent = memo(function ResizableComponent( const isVerticalResizeEnabled = useMemo(() => { return !isAutoHeightEnabledForWidget(props) && isEnabled; }, [props, isAutoHeightEnabledForWidget, isEnabled]); - const isHorizontalResizeEnabled = useMemo(() => { - return ( - isEnabled && - !( - props.isFlexChild && - props.responsiveBehavior === ResponsiveBehavior.Fill - ) - ); - }, [isEnabled, props.isFlexChild, props.responsiveBehavior]); const allowResize = !( NonResizableWidgets.includes(props.type) || isMultiSelected ); @@ -345,7 +336,7 @@ export const ResizableComponent = memo(function ResizableComponent( componentHeight={dimensions.height} componentWidth={dimensions.width} direction={props.direction} - enableHorizontalResize={isHorizontalResizeEnabled} + enableHorizontalResize={isEnabled} enableVerticalResize={isVerticalResizeEnabled} getResizedPositions={getResizedPositions} gridProps={gridProps} diff --git a/app/client/src/components/editorComponents/ResizableUtils.ts b/app/client/src/components/editorComponents/ResizableUtils.ts index d1395858d41d..1adb7d126432 100644 --- a/app/client/src/components/editorComponents/ResizableUtils.ts +++ b/app/client/src/components/editorComponents/ResizableUtils.ts @@ -2,6 +2,7 @@ import { WidgetProps, WidgetRowCols } from "widgets/BaseWidget"; import { GridDefaults } from "constants/WidgetConstants"; import { XYCord } from "pages/common/CanvasArenas/hooks/useCanvasDragging"; import { ReflowDirection } from "reflow/reflowTypes"; +import { ResponsiveBehavior } from "components/constants"; export type UIElementSize = { height: number; width: number }; @@ -89,6 +90,8 @@ export function isHandleResizeAllowed( horizontalEnabled: boolean, verticalEnabled: boolean, direction?: ReflowDirection, + isFlexChild?: boolean, + responsiveBehavior?: ResponsiveBehavior, ): boolean { if (direction === ReflowDirection.TOP || direction === ReflowDirection.BOTTOM) return verticalEnabled; @@ -96,7 +99,9 @@ export function isHandleResizeAllowed( direction === ReflowDirection.LEFT || direction === ReflowDirection.RIGHT ) { - return horizontalEnabled; + return isFlexChild && responsiveBehavior === ResponsiveBehavior.Fill + ? false + : horizontalEnabled; } return true; } diff --git a/app/client/src/components/editorComponents/WidgetNameComponent/index.tsx b/app/client/src/components/editorComponents/WidgetNameComponent/index.tsx index 6ee62f3a9901..e1d7c7062cee 100644 --- a/app/client/src/components/editorComponents/WidgetNameComponent/index.tsx +++ b/app/client/src/components/editorComponents/WidgetNameComponent/index.tsx @@ -1,5 +1,6 @@ import { AppState } from "@appsmith/reducers"; import { bindDataToWidget } from "actions/propertyPaneActions"; +import { Layers } from "constants/Layers"; import { WidgetType } from "constants/WidgetConstants"; import React from "react"; import { useDispatch, useSelector } from "react-redux"; @@ -31,6 +32,7 @@ const PositionStyle = styled.div<{ topRow: number; isSnipingMode: boolean }>` display: flex; padding: 0 4px; cursor: pointer; + z-index: ${Layers.widgetName}; `; const ControlGroup = styled.div` diff --git a/app/client/src/constants/WidgetConstants.tsx b/app/client/src/constants/WidgetConstants.tsx index 3f29e41f9663..82ca054d5e3e 100644 --- a/app/client/src/constants/WidgetConstants.tsx +++ b/app/client/src/constants/WidgetConstants.tsx @@ -187,3 +187,5 @@ export const WIDGET_PROPS_TO_SKIP_FROM_EVAL = { version: true, displayName: true, }; + +export const FLEXBOX_PADDING = 4; diff --git a/app/client/src/resizable/resizenreflow/index.tsx b/app/client/src/resizable/resizenreflow/index.tsx index 08ace75c3152..e7a658afde3c 100644 --- a/app/client/src/resizable/resizenreflow/index.tsx +++ b/app/client/src/resizable/resizenreflow/index.tsx @@ -492,14 +492,8 @@ export function ReflowResizable(props: ResizableProps) { props.enableHorizontalResize, props.enableVerticalResize, handle.handleDirection, - ); - console.log( - "#### enable", - props.widgetId, - props.enableHorizontalResize, - props.enableVerticalResize, - handle.handleDirection, - disableDot, + props.isFlexChild, + props.responsiveBehavior, ); return ( {props.children} - {(props.enableHorizontalResize || props.isFlexChild) && renderHandles} + {props.enableHorizontalResize && renderHandles} )} diff --git a/app/client/src/utils/autoLayout/highlightUtils.ts b/app/client/src/utils/autoLayout/highlightUtils.ts index 130c7a838e7f..f849ca22dde5 100644 --- a/app/client/src/utils/autoLayout/highlightUtils.ts +++ b/app/client/src/utils/autoLayout/highlightUtils.ts @@ -6,6 +6,7 @@ import { } from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; import { CONTAINER_GRID_PADDING, + FLEXBOX_PADDING, GridDefaults, MAIN_CONTAINER_WIDGET_ID, WIDGET_PADDING, @@ -21,9 +22,6 @@ import { } from "./flexWidgetUtils"; import { getTotalRowsOfAllChildren, Widget } from "./positionUtils"; -const HORIZONTAL_HIGHLIGHT_MARGIN = 4; -// TODO: Preet - update logic to account for flex wrap on mobile. -// For mobile use widget positions to place highlights. /** * @param allWidgets : CanvasWidgetsReduxState * @param canvasId : string @@ -56,7 +54,7 @@ export function deriveHighlightsFromLayers( let childCount = 0; let layerIndex = 0; // TODO: remove offsetTop and use child positions after widget positioning on grid is solved. - let offsetTop = HORIZONTAL_HIGHLIGHT_MARGIN; // used to calculate distance of a highlight from parents's top. + let offsetTop = FLEXBOX_PADDING; // used to calculate distance of a highlight from parents's top. for (const layer of layers) { /** * If the layer is empty, after discounting the dragged widgets, @@ -266,10 +264,8 @@ function generateHighlightsForSubWrapper(data: { layerIndex, rowIndex: count, alignment, - posX: left * parentColumnSpace, - posY: - getTopRow(child, isMobile) * child.parentRowSpace + - HORIZONTAL_HIGHLIGHT_MARGIN, + posX: left * parentColumnSpace + FLEXBOX_PADDING / 2, + posY: getTopRow(child, isMobile) * child.parentRowSpace + FLEXBOX_PADDING, width: DEFAULT_HIGHLIGHT_SIZE, height: isMobile ? getWidgetHeight(child, isMobile) * child.parentRowSpace @@ -293,7 +289,8 @@ function generateHighlightsForSubWrapper(data: { res, alignment, arr && arr.length - ? getRightColumn(lastChild, isMobile) * parentColumnSpace + ? getRightColumn(lastChild, isMobile) * parentColumnSpace + + FLEXBOX_PADDING / 2 : 0, canvasWidth, canvasId, @@ -302,7 +299,7 @@ function generateHighlightsForSubWrapper(data: { lastChild === null ? offsetTop : getTopRow(lastChild, isMobile) * lastChild?.parentRowSpace + - HORIZONTAL_HIGHLIGHT_MARGIN, + FLEXBOX_PADDING, width: DEFAULT_HIGHLIGHT_SIZE, height: isMobile && lastChild !== null @@ -332,7 +329,7 @@ function getPositionForInitialHighlight( canvasId: string, ): number { const endPosition = - containerWidth - (canvasId !== MAIN_CONTAINER_WIDGET_ID ? 6 : 0); + containerWidth - (canvasId !== MAIN_CONTAINER_WIDGET_ID ? 4 : 0); if (alignment === FlexLayerAlignment.End) { return endPosition; } else if (alignment === FlexLayerAlignment.Center) { @@ -416,7 +413,7 @@ function getCanvasDimensions( canvas.type === "CONTAINER_WIDGET" ) { //For MainContainer and any Container Widget padding doesn't exist coz there is already container padding. - padding = CONTAINER_GRID_PADDING * 2; + padding = FLEXBOX_PADDING * 2; } if (canvas.noPad) { // Widgets like ListWidget choose to have no container padding so will only have widget padding diff --git a/app/client/src/utils/autoLayout/positionUtils.ts b/app/client/src/utils/autoLayout/positionUtils.ts index 63188e2c4d98..5dec7089b31b 100644 --- a/app/client/src/utils/autoLayout/positionUtils.ts +++ b/app/client/src/utils/autoLayout/positionUtils.ts @@ -88,7 +88,16 @@ export function updateWidgetPositions( allWidgets[parent.parentId].type, ) && parentHeight <= height; - + // console.log( + // "#### update height", + // parent.widgetName, + // "parentHeight", + // parent.parent, + // "height", + // height, + // shouldUpdateHeight, + // parent.parentId ? widgets[parent.parentId].widgetName : "no parent", + // ); if (shouldUpdateHeight && parent.parentId) return updateWidgetPositions(widgets, parent.parentId, isMobile); return widgets; diff --git a/app/client/src/widgets/ContainerWidget/widget/index.tsx b/app/client/src/widgets/ContainerWidget/widget/index.tsx index a1722128a787..4169fcf1705b 100644 --- a/app/client/src/widgets/ContainerWidget/widget/index.tsx +++ b/app/client/src/widgets/ContainerWidget/widget/index.tsx @@ -2,6 +2,7 @@ import React from "react"; import { CONTAINER_GRID_PADDING, + FLEXBOX_PADDING, GridDefaults, MAIN_CONTAINER_WIDGET_ID, WIDGET_PADDING, @@ -167,7 +168,10 @@ export class ContainerWidget extends BaseWidget< this.props.type === "CONTAINER_WIDGET" ) { //For MainContainer and any Container Widget padding doesn't exist coz there is already container padding. - padding = CONTAINER_GRID_PADDING * 2; + padding = + this.props.positioning === Positioning.Vertical + ? FLEXBOX_PADDING * 2 + : CONTAINER_GRID_PADDING * 2; } if (this.props.noPad) { // Widgets like ListWidget choose to have no container padding so will only have widget padding From 765f6e57d19920b1965779b559c267d0953b9292 Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 2 Jan 2023 15:13:29 -0500 Subject: [PATCH 339/708] update breakpoint --- app/client/src/constants/minWidthConstants.ts | 5 +++-- app/client/src/reducers/uiReducers/mainCanvasReducer.ts | 9 ++++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/app/client/src/constants/minWidthConstants.ts b/app/client/src/constants/minWidthConstants.ts index 59fd4eb1ea8e..73bbfe0c9822 100644 --- a/app/client/src/constants/minWidthConstants.ts +++ b/app/client/src/constants/minWidthConstants.ts @@ -1,5 +1,6 @@ -import { MOBILE_MAX_WIDTH } from "./AppConstants"; +import { layoutConfigurations } from "./WidgetConstants"; -export const FILL_WIDGET_MIN_WIDTH: number = MOBILE_MAX_WIDTH; +export const FILL_WIDGET_MIN_WIDTH: number = + layoutConfigurations.MOBILE.maxWidth; export const ICON_BUTTON_MIN_WIDTH = 50; export const BUTTON_MIN_WIDTH = 120; diff --git a/app/client/src/reducers/uiReducers/mainCanvasReducer.ts b/app/client/src/reducers/uiReducers/mainCanvasReducer.ts index 2280a2b7aa48..b5b17186bc5d 100644 --- a/app/client/src/reducers/uiReducers/mainCanvasReducer.ts +++ b/app/client/src/reducers/uiReducers/mainCanvasReducer.ts @@ -4,9 +4,11 @@ import { ReduxActionTypes, UpdateCanvasPayload, } from "@appsmith/constants/ReduxActionConstants"; -import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; +import { + layoutConfigurations, + MAIN_CONTAINER_WIDGET_ID, +} from "constants/WidgetConstants"; import { UpdateCanvasLayoutPayload } from "actions/controlActions"; -import { MOBILE_MAX_WIDTH } from "constants/AppConstants"; const initialState: MainCanvasReduxState = { initialized: false, @@ -33,7 +35,8 @@ const mainCanvasReducer = createImmerReducer(initialState, { ) => { state.width = action.payload.width || state.width; state.initialized = true; - state.isMobile = action.payload.width <= MOBILE_MAX_WIDTH; + state.isMobile = + action.payload.width <= layoutConfigurations.MOBILE.maxWidth; }, }); From 59864d932f07a6b5747e8ff9ed9e5023bc3c45f3 Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 2 Jan 2023 16:42:16 -0500 Subject: [PATCH 340/708] account for padding in canvas width calculation --- app/client/src/sagas/AutoLayoutUtils.ts | 5 +- .../src/utils/autoLayout/highlightUtils.ts | 66 ++++++++++++------- 2 files changed, 43 insertions(+), 28 deletions(-) diff --git a/app/client/src/sagas/AutoLayoutUtils.ts b/app/client/src/sagas/AutoLayoutUtils.ts index 3c28632588f5..119880d3eea9 100644 --- a/app/client/src/sagas/AutoLayoutUtils.ts +++ b/app/client/src/sagas/AutoLayoutUtils.ts @@ -7,6 +7,7 @@ import { FlexLayer, LayerChild, } from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; +import { FLEXBOX_PADDING } from "constants/WidgetConstants"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; import { updateWidgetPositions } from "utils/autoLayout/positionUtils"; @@ -267,7 +268,6 @@ export function alterLayoutForMobile( for (const child of children) { const widget = { ...widgets[child] }; - if (widget.responsiveBehavior === ResponsiveBehavior.Fill) { widget.mobileRightColumn = 64; widget.mobileLeftColumn = 0; @@ -276,7 +276,7 @@ export function alterLayoutForMobile( widget.minWidth ) { const { minWidth, rightColumn } = widget; - const columnSpace = canvasWidth / 64; + const columnSpace = (canvasWidth - FLEXBOX_PADDING * 2) / 64; if (columnSpace * rightColumn < minWidth) { widget.mobileLeftColumn = 0; widget.mobileRightColumn = Math.min( @@ -287,7 +287,6 @@ export function alterLayoutForMobile( } widget.mobileTopRow = widget.topRow; widget.mobileBottomRow = widget.bottomRow; - // TODO: Preet - update container row info if height changes on account of flex wrap. widgets = alterLayoutForMobile( widgets, child, diff --git a/app/client/src/utils/autoLayout/highlightUtils.ts b/app/client/src/utils/autoLayout/highlightUtils.ts index f849ca22dde5..b7482df88543 100644 --- a/app/client/src/utils/autoLayout/highlightUtils.ts +++ b/app/client/src/utils/autoLayout/highlightUtils.ts @@ -5,7 +5,6 @@ import { LayerChild, } from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; import { - CONTAINER_GRID_PADDING, FLEXBOX_PADDING, GridDefaults, MAIN_CONTAINER_WIDGET_ID, @@ -193,6 +192,7 @@ function generateVerticalHighlights(data: { parentColumnSpace: columnSpace, parentRowSpace: widgets[canvasId].parentRowSpace, canvasWidth, + columnSpace, isMobile, }), ...generateHighlightsForSubWrapper({ @@ -206,6 +206,7 @@ function generateVerticalHighlights(data: { parentColumnSpace: columnSpace, parentRowSpace: widgets[canvasId].parentRowSpace, canvasWidth, + columnSpace, avoidInitialHighlight: startColumns > 25 || endColumns > 25, isMobile, }), @@ -220,6 +221,7 @@ function generateVerticalHighlights(data: { parentColumnSpace: columnSpace, parentRowSpace: widgets[canvasId].parentRowSpace, canvasWidth, + columnSpace, isMobile, }), ], @@ -238,6 +240,7 @@ function generateHighlightsForSubWrapper(data: { parentColumnSpace: number; parentRowSpace: number; canvasWidth: number; + columnSpace: number; avoidInitialHighlight?: boolean; isMobile: boolean; }): HighlightInfo[] { @@ -248,6 +251,7 @@ function generateHighlightsForSubWrapper(data: { canvasId, canvasWidth, childCount, + columnSpace, isMobile, layerIndex, maxHeight, @@ -294,6 +298,7 @@ function generateHighlightsForSubWrapper(data: { : 0, canvasWidth, canvasId, + columnSpace, ), posY: lastChild === null @@ -327,9 +332,10 @@ function getPositionForInitialHighlight( posX: number, containerWidth: number, canvasId: string, + columnSpace: number, ): number { const endPosition = - containerWidth - (canvasId !== MAIN_CONTAINER_WIDGET_ID ? 4 : 0); + 64 * columnSpace - (canvasId !== MAIN_CONTAINER_WIDGET_ID ? 4 : 0); if (alignment === FlexLayerAlignment.End) { return endPosition; } else if (alignment === FlexLayerAlignment.Center) { @@ -395,7 +401,7 @@ function generateHorizontalHighlights( } function getCanvasDimensions( - canvas: any, + canvas: Widget, widgets: CanvasWidgetsReduxState, mainCanvasWidth: number, isMobile: boolean, @@ -407,40 +413,50 @@ function getCanvasDimensions( isMobile, ); - let padding = (CONTAINER_GRID_PADDING + WIDGET_PADDING) * 2; - if ( - canvas.widgetId === MAIN_CONTAINER_WIDGET_ID || - canvas.type === "CONTAINER_WIDGET" - ) { - //For MainContainer and any Container Widget padding doesn't exist coz there is already container padding. - padding = FLEXBOX_PADDING * 2; - } - if (canvas.noPad) { - // Widgets like ListWidget choose to have no container padding so will only have widget padding - padding = WIDGET_PADDING * 2; - } - const columnSpace: number = - (canvasWidth - padding) / GridDefaults.DEFAULT_GRID_COLUMNS; + const columnSpace: number = canvasWidth / GridDefaults.DEFAULT_GRID_COLUMNS; - return { canvasWidth: canvasWidth - padding, columnSpace }; + return { canvasWidth: canvasWidth, columnSpace }; } function getCanvasWidth( - canvas: any, + canvas: Widget, widgets: CanvasWidgetsReduxState, mainCanvasWidth: number, isMobile: boolean, ): number { - // TODO: @Preet - Update the logic to account for padding at each level. if (!mainCanvasWidth) return 0; - if (canvas.widgetId === MAIN_CONTAINER_WIDGET_ID) return mainCanvasWidth; + if (canvas.widgetId === MAIN_CONTAINER_WIDGET_ID) + return mainCanvasWidth - getPadding(canvas); let widget = canvas; - let columns = getWidgetWidth(widget, isMobile); - let width = columns / GridDefaults.DEFAULT_GRID_COLUMNS; - while (widget.widgetId !== MAIN_CONTAINER_WIDGET_ID) { + let columns = 0; + let width = 1; + let padding = 0; + while (widget.parentId) { columns = getWidgetWidth(widget, isMobile); + padding += getPadding(widget); width *= columns / GridDefaults.DEFAULT_GRID_COLUMNS; widget = widgets[widget.parentId]; } - return width * mainCanvasWidth; + const totalWidth = width * mainCanvasWidth; + if (widget.widgetId === MAIN_CONTAINER_WIDGET_ID) + padding += getPadding(widget); + return totalWidth - padding; +} + +function getPadding(canvas: Widget): number { + let padding = 0; + if ( + canvas.widgetId === MAIN_CONTAINER_WIDGET_ID || + canvas.type === "CONTAINER_WIDGET" + ) { + //For MainContainer and any Container Widget padding doesn't exist coz there is already container padding. + padding = FLEXBOX_PADDING * 2; + } + if (canvas.noPad) { + // Widgets like ListWidget choose to have no container padding so will only have widget padding + padding = WIDGET_PADDING * 2; + } + // Account for container border. + padding += canvas.type === "CONTAINER_WIDGET" ? 2 : 0; + return padding; } From 22268d39645170409629ef6e43d4e17a0c3429a4 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Tue, 3 Jan 2023 12:16:16 +0530 Subject: [PATCH 341/708] adding resize handles for main canvas in preview mode as well. --- .../Editor/WidgetsEditor/CanvasContainer.tsx | 32 +++++++++++-------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx index 5c3f80fd6d64..3a30694fa3d2 100644 --- a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx +++ b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx @@ -106,7 +106,11 @@ function CanvasContainer() { } const appLayout = useSelector(getCurrentApplicationLayout); useEffect(() => { - if (!isPreviewMode && appLayout?.type === "FLUID") { + if (isPreviewMode) { + const ele: any = document.getElementById("canvas-viewport"); + ele.style.width = "inherit"; + } + if (appLayout?.type === "FLUID") { const smallestWidth = layoutConfigurations.MOBILE.minWidth; // Query the element const ele: any = document.getElementById("canvas-viewport"); @@ -164,17 +168,15 @@ function CanvasContainer() { }; const rightResizer: any = ele.querySelectorAll(".resizer-right")[0]; const leftResizer: any = ele.querySelectorAll(".resizer-left")[0]; + const rightMove = (e: any) => mouseDownHandler(e, true); + const leftMove = (e: any) => mouseDownHandler(e, false); - rightResizer.addEventListener("mousedown", (e: any) => - mouseDownHandler(e, true), - ); - - leftResizer.addEventListener("mousedown", (e: any) => - mouseDownHandler(e, false), - ); - } else { - const ele: any = document.getElementById("canvas-viewport"); - ele.style.width = "inherit"; + rightResizer.addEventListener("mousedown", rightMove); + leftResizer.addEventListener("mousedown", leftMove); + return () => { + rightResizer.removeEventListener("mousedown", rightMove); + leftResizer.removeEventListener("mousedown", leftMove); + }; } }, [appLayout, isPreviewMode, currentPageId]); @@ -201,7 +203,7 @@ function CanvasContainer() { fontFamily: fontFamily, }} > - {appLayout?.type === "FLUID" && !isPreviewMode && ( + {appLayout?.type === "FLUID" && ( <> @@ -233,8 +236,9 @@ function CanvasContainer() { cursor: "col-resize", width: "16px", height: "0px", - left: "calc(100% - 32px)", + left: isPreviewMode ? "100%" : "calc(100% - 32px)", top: "50%", + zIndex: isPreviewMode ? 2 : undefined, }} > From 44f01b0454a428c50230adb8530a5df7e87366f5 Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 3 Jan 2023 09:01:34 -0500 Subject: [PATCH 342/708] add responsiveBehavior to slider and camera widgets --- app/client/src/widgets/CameraWidget/index.ts | 2 ++ app/client/src/widgets/CategorySliderWidget/index.ts | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/app/client/src/widgets/CameraWidget/index.ts b/app/client/src/widgets/CameraWidget/index.ts index db2da2a0a4ca..9275404c63e5 100644 --- a/app/client/src/widgets/CameraWidget/index.ts +++ b/app/client/src/widgets/CameraWidget/index.ts @@ -1,3 +1,4 @@ +import { ResponsiveBehavior } from "components/constants"; import { CameraModeTypes } from "./constants"; import IconSVG from "./icon.svg"; import Widget from "./widget"; @@ -18,6 +19,7 @@ export const CONFIG = { isVisible: true, isMirrored: true, version: 1, + responsiveBehavior: ResponsiveBehavior.Hug, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/CategorySliderWidget/index.ts b/app/client/src/widgets/CategorySliderWidget/index.ts index 794fd10a5c5e..73a6b832c028 100644 --- a/app/client/src/widgets/CategorySliderWidget/index.ts +++ b/app/client/src/widgets/CategorySliderWidget/index.ts @@ -1,4 +1,4 @@ -import { LabelPosition } from "components/constants"; +import { LabelPosition, ResponsiveBehavior } from "components/constants"; import { Alignment } from "@blueprintjs/core"; import IconSVG from "./icon.svg"; @@ -35,6 +35,7 @@ export const CONFIG = { labelWidth: 5, labelTextSize: "0.875rem", sliderSize: "m", + responsiveBehavior: ResponsiveBehavior.Fill, }, properties: { derived: Widget.getDerivedPropertiesMap(), From 677f560e556ffc969229bf3b3c32bc3413ebd047 Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 3 Jan 2023 10:47:41 -0500 Subject: [PATCH 343/708] fix fill widget column allocation --- app/client/src/sagas/AutoLayoutUtils.ts | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/app/client/src/sagas/AutoLayoutUtils.ts b/app/client/src/sagas/AutoLayoutUtils.ts index 119880d3eea9..dc145d5ea9b2 100644 --- a/app/client/src/sagas/AutoLayoutUtils.ts +++ b/app/client/src/sagas/AutoLayoutUtils.ts @@ -199,9 +199,7 @@ export function updateFlexChildColumns( ); if (!fillChildren.length) return widgets; - const columnsPerFillChild = Math.floor( - (64 - hugChildrenColumns) / fillChildren.length, - ); + const columnsPerFillChild = (64 - hugChildrenColumns) / fillChildren.length; for (const child of fillChildren) { widgets[child.widgetId] = { @@ -279,10 +277,7 @@ export function alterLayoutForMobile( const columnSpace = (canvasWidth - FLEXBOX_PADDING * 2) / 64; if (columnSpace * rightColumn < minWidth) { widget.mobileLeftColumn = 0; - widget.mobileRightColumn = Math.min( - Math.floor(minWidth / columnSpace), - 64, - ); + widget.mobileRightColumn = Math.min(minWidth / columnSpace, 64); } } widget.mobileTopRow = widget.topRow; From 0c6c769045b6acbf7459179bd36349f9b2811f7f Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 3 Jan 2023 10:52:13 -0500 Subject: [PATCH 344/708] account for dragged widget height in offset calculation --- app/client/src/utils/autoLayout/highlightUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/client/src/utils/autoLayout/highlightUtils.ts b/app/client/src/utils/autoLayout/highlightUtils.ts index b7482df88543..1e6eb3018d49 100644 --- a/app/client/src/utils/autoLayout/highlightUtils.ts +++ b/app/client/src/utils/autoLayout/highlightUtils.ts @@ -99,9 +99,9 @@ export function deriveHighlightsFromLayers( ); highlights.push(...payload.highlights); - offsetTop += childrenRows * 10 || 0; layerIndex += 1; } + offsetTop += childrenRows * GridDefaults.DEFAULT_GRID_ROW_HEIGHT || 0; childCount += payload.childCount; } // Add a layer of horizontal highlights for the empty space at the bottom of a stack. From 6cd6f4ae406d8c9dc61bb2525a202f78e4ee6069 Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 3 Jan 2023 11:19:31 -0500 Subject: [PATCH 345/708] decouple fixed and flex canvases for widget sizes and resizing --- .../editorComponents/ResizableComponent.tsx | 6 +++--- app/client/src/sagas/WidgetAdditionSagas.ts | 12 +++++++++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/app/client/src/components/editorComponents/ResizableComponent.tsx b/app/client/src/components/editorComponents/ResizableComponent.tsx index e38187a508e0..ac388f825f36 100644 --- a/app/client/src/components/editorComponents/ResizableComponent.tsx +++ b/app/client/src/components/editorComponents/ResizableComponent.tsx @@ -327,9 +327,9 @@ export const ResizableComponent = memo(function ResizableComponent( const isVerticalResizeEnabled = useMemo(() => { return !isAutoHeightEnabledForWidget(props) && isEnabled; }, [props, isAutoHeightEnabledForWidget, isEnabled]); - const allowResize = !( - NonResizableWidgets.includes(props.type) || isMultiSelected - ); + const allowResize: boolean = + !(NonResizableWidgets.includes(props.type) || isMultiSelected) || + !props.isFlexChild; return ( Date: Tue, 3 Jan 2023 12:29:26 -0500 Subject: [PATCH 346/708] use interpolated data to reduce drag sensitivity --- .../hooks/useAutoLayoutHighlights.ts | 1 - .../CanvasArenas/hooks/useCanvasDragging.ts | 46 +++++++++++++++++-- 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index aaba6db0fb5b..c93e3a0f60bc 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -154,7 +154,6 @@ export const useAutoLayoutHighlights = ({ const highlightDropPosition = ( e: any, moveDirection: ReflowDirection, - // acceleration: number, ): HighlightInfo | undefined => { if (!highlights) highlights = deriveHighlightsFromLayers( diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index 7781a8e02600..a2674c8709aa 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -263,6 +263,13 @@ export const useCanvasDragging = ( leftColumn: 0, topRow: 0, }; + let lastMousePositionDeltas: { + deltaX: number; + deltaY: number; + }[] = new Array(5).fill({ + deltaX: 0, + deltaY: 0, + }); const resetCanvasState = () => { throttledStopReflowing(); @@ -413,14 +420,48 @@ export const useCanvasDragging = ( // } // }; + const addMousePositionDelta = (payload: { + deltaX: number; + deltaY: number; + }) => { + lastMousePositionDeltas = [ + payload, + ...lastMousePositionDeltas.slice(0, 4), + ]; + }; + const getInterpolatedDelta = () => { + return lastMousePositionDeltas.reduce( + (prev, cur) => { + return { + deltaX: prev.deltaX + cur.deltaX, + deltaY: prev.deltaY + cur.deltaY, + }; + }, + { + deltaX: 0, + deltaY: 0, + }, + ); + }; const getMouseMoveDirection = (event: any, minDelta = 0) => { if (lastMousePosition) { - const deltaX = lastMousePosition.x - event.clientX, + let deltaX = lastMousePosition.x - event.clientX, deltaY = lastMousePosition.y - event.clientY; lastMousePosition = { x: event.clientX, y: event.clientY, }; + if (useAutoLayout) { + /** + * Use interpolated data over the last five frames to calculate delta values. + * This is needed to reduce sensitivity to mouse move direction. + * Involuntary flickers while releasing the mouse should not impact the overall direction. + */ + addMousePositionDelta({ deltaX, deltaY }); + const delta = getInterpolatedDelta(); + deltaX = delta.deltaX; + deltaY = delta.deltaY; + } if ( deltaX === 0 && ["TOP", "BOTTOM"].includes(currentDirection.current) @@ -607,9 +648,6 @@ export const useCanvasDragging = ( isCurrentDraggedCanvas && currentDirection.current !== ReflowDirection.UNSET ) { - // debounce(() => { - // highlightDropPosition(e, currentDirection.current); - // }, 100)(); highlight = highlightDropPosition(e, currentDirection.current); } renderBlocks(highlight); From 740c53989c68971a3b4c246e7be5771872e2277d Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 3 Jan 2023 13:30:06 -0500 Subject: [PATCH 347/708] add comments and code cleanup --- .../hooks/useAutoLayoutHighlights.ts | 162 ++++++++---------- .../src/utils/autoLayout/highlightUtils.ts | 20 ++- 2 files changed, 84 insertions(+), 98 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index c93e3a0f60bc..fb6e1712a286 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -12,7 +12,7 @@ import { deriveHighlightsFromLayers } from "utils/autoLayout/highlightUtils"; import WidgetFactory from "utils/WidgetFactory"; import { WidgetDraggingBlock } from "./useBlocksToBeDraggedOnCanvas"; -interface XYCord { +interface Point { x: number; y: number; } @@ -63,37 +63,11 @@ export const useAutoLayoutHighlights = ({ let isFillWidget = false; const isVerticalStack = direction === LayoutDirection.Vertical; - let containerDimensions: { - top: number; - bottom: number; - left: number; - right: number; - width: number; - height: number; - }; /** * START AUTO LAYOUT OFFSET CALCULATION */ - // Fetch and update the dimensions of the containing canvas. - const updateContainerDimensions = (): boolean => { - const container = document.querySelector(`.appsmith_widget_${canvasId}`); - const containerRect: - | DOMRect - | undefined = container?.getBoundingClientRect(); - if (!container || !containerRect) return false; - containerDimensions = { - top: containerRect?.top || 0, - bottom: containerRect?.bottom - containerDimensions?.top || 0, - left: containerRect?.left || 0, - right: containerRect?.right - containerRect?.left || 0, - width: containerRect?.width, - height: containerRect?.height, - }; - return true; - }; - const cleanUpTempStyles = () => { // reset state lastActiveHighlight = undefined; @@ -127,12 +101,6 @@ export const useAutoLayoutHighlights = ({ cleanUpTempStyles(); if (useAutoLayout && isDragging && isCurrentDraggedCanvas) { if (!blocksToDraw || !blocksToDraw.length) return []; - /** - * update dimensions of the current canvas - * and break out of the function if returned value is false. - * That implies the container is null. - */ - if (!updateContainerDimensions()) return []; isFillWidget = checkForFillWidget(); highlights = deriveHighlightsFromLayers( allWidgets, @@ -151,29 +119,21 @@ export const useAutoLayoutHighlights = ({ * END AUTO LAYOUT OFFSET CALCULATION */ + /** + * Highlight a drop position based on mouse position and move direction. + * @param e | MouseMoveEvent + * @param moveDirection | ReflowDirection + * @returns HighlightInfo | undefined + */ const highlightDropPosition = ( e: any, moveDirection: ReflowDirection, ): HighlightInfo | undefined => { - if (!highlights) - highlights = deriveHighlightsFromLayers( - allWidgets, - canvasId, - canvasWidth, - blocksToDraw.map((block) => block?.widgetId), - isFillWidget, - isMobile, - ); - // console.log("#### highlights", highlights); - if (!highlights) return; - // updateHighlights(moveDirection); - const highlight: HighlightInfo | undefined = getHighlightPayload( e, moveDirection, ); - - // updateSelection(highlight); + if (!highlight) return; // console.log("#### selection", highlight); lastActiveHighlight = highlight; return highlight; @@ -182,9 +142,9 @@ export const useAutoLayoutHighlights = ({ const getHighlightPayload = ( e: any, moveDirection?: ReflowDirection, - val?: XYCord, + val?: Point, ): HighlightInfo | undefined => { - let base: HighlightInfo[] = []; + let base: HighlightInfo[] = []; // all highlight for the current canvas. if (!highlights || !highlights.length) highlights = deriveHighlightsFromLayers( allWidgets, @@ -195,8 +155,8 @@ export const useAutoLayoutHighlights = ({ isMobile, ); base = highlights; - - const pos: XYCord = { + // Current mouse coordinates. + const pos: Point = { x: e?.offsetX || val?.x, y: e?.offsetY || val?.y, }; @@ -204,30 +164,23 @@ export const useAutoLayoutHighlights = ({ let filteredHighlights: HighlightInfo[] = []; filteredHighlights = getViableDropPositions(base, pos, moveDirection); if (!filteredHighlights || !filteredHighlights?.length) return; + // Sort filtered highlights in ascending order of distance from mouse position. const arr = [...filteredHighlights]?.sort((a, b) => { return ( - calculateDistance( - a, - pos, - moveDirection, - a.isNewLayer && isVerticalStack, - ) - - calculateDistance( - b, - pos, - moveDirection, - a.isNewLayer && isVerticalStack, - ) + calculateDistance(a, pos, moveDirection) - + calculateDistance(b, pos, moveDirection) ); }); // console.log("#### arr", arr, base, moveDirection); + + // Return the closest highlight. return arr[0]; }; function getViableDropPositions( arr: HighlightInfo[], - pos: XYCord, + pos: Point, moveDirection?: ReflowDirection, ): HighlightInfo[] { if (!moveDirection || !arr) return arr || []; @@ -242,7 +195,7 @@ export const useAutoLayoutHighlights = ({ function getVerticalStackDropPositions( arr: HighlightInfo[], - pos: XYCord, + pos: Point, isVerticalDrag: boolean, ): HighlightInfo[] { // For vertical stacks, filter out the highlights based on drag direction and y position. @@ -266,9 +219,11 @@ export const useAutoLayoutHighlights = ({ ); }, ); - // console.log("#### pos", arr, filteredHighlights, isVerticalDrag); - // For horizontal drag, if no vertical highlight exists in the same x plane, - // return the horizontal highlights for the last layer. + /** + * For horizontal drag, if no vertical highlight exists in the same x plane, + * return the horizontal highlights for the last layer. + * In case of a dragged Fill widget, only return the Start alignment as it will span the entire width. + */ if (!isVerticalDrag && !filteredHighlights.length) filteredHighlights = arr .slice(arr.length - 3) @@ -283,7 +238,7 @@ export const useAutoLayoutHighlights = ({ function getHorizontalStackDropPositions( arr: HighlightInfo[], - pos: XYCord, + pos: Point, ): HighlightInfo[] { // For horizontal stack, return the highlights that lie in the same x plane. let filteredHighlights = arr.filter( @@ -295,37 +250,56 @@ export const useAutoLayoutHighlights = ({ return filteredHighlights; } - const calculateDistance = ( + /** + * Calculate distance between the mouse position and the closest point on the highlight. + * + * @param a | HighlightInfo : current highlight. + * @param b | Point : current mouse position. + * @param moveDirection | ReflowDirection : current drag direction. + * @returns number + */ + function calculateDistance( a: HighlightInfo, - b: XYCord, + b: Point, moveDirection?: ReflowDirection, - usePerpendicularDistance?: boolean, - ): number => { + ): number { + let distX = 0, + distY = 0; + if (a.isVertical) { + distX = b.x - a.posX; + if (b.y < a.posY) { + distY = b.y - a.posY; + } else if (b.y > a.posY + a.height) { + distY = b.y - (a.posY + a.height); + } else { + distY = 0; + } + } else { + distY = b.y - a.posY; + if (b.x < a.posX) { + distX = b.x - a.posX; + } else if (b.x > a.posX + a.width) { + distX = b.x - (a.posX + a.width); + } else { + distX = 0; + } + } + /** - * Calculate perpendicular distance of a point from a line. - * If moving vertically, x is fixed (x = 0) and vice versa. + * Emphasize move direction over actual distance. + * + * If the point is close to a highlight. However, it is moving in the opposite direction, + * then increase the appropriate distance to ensure that this highlight is discounted. */ - const isVerticalDrag = - moveDirection && - [ReflowDirection.TOP, ReflowDirection.BOTTOM].includes(moveDirection); - - let distX: number = - a.isVertical && isVerticalDrag - ? usePerpendicularDistance - ? (a.posX + a.width) / 2 - b.x - : 0 - : a.posX - b.x; - let distY: number = !a.isVertical && !isVerticalDrag ? 0 : a.posY - b.y; - - if (moveDirection === ReflowDirection.LEFT && distX > 20) distX += 2000; - if (moveDirection === ReflowDirection.RIGHT && distX < -20) distX -= 2000; - if (moveDirection === ReflowDirection.TOP && distY > 20) distY += 2000; - if (moveDirection === ReflowDirection.BOTTOM && distY < -20) distY -= 2000; + if (moveDirection === ReflowDirection.RIGHT && distX > 20) distX += 2000; + if (moveDirection === ReflowDirection.LEFT && distX < -20) distX -= 2000; + if (moveDirection === ReflowDirection.BOTTOM && distY > 20) distY += 2000; + if (moveDirection === ReflowDirection.TOP && distY < -20) distY -= 2000; return Math.abs(Math.sqrt(distX * distX + distY * distY)); - }; + } - const getDropInfo = (val: XYCord): HighlightInfo | undefined => { + const getDropInfo = (val: Point): HighlightInfo | undefined => { if (lastActiveHighlight) return lastActiveHighlight; const payload: HighlightInfo | undefined = getHighlightPayload( diff --git a/app/client/src/utils/autoLayout/highlightUtils.ts b/app/client/src/utils/autoLayout/highlightUtils.ts index 1e6eb3018d49..2fdd50d426b2 100644 --- a/app/client/src/utils/autoLayout/highlightUtils.ts +++ b/app/client/src/utils/autoLayout/highlightUtils.ts @@ -126,6 +126,11 @@ interface VerticalHighlightsPayload { highlights: HighlightInfo[]; } +/** + * Derive highlight information for all widgets within a layer. + * - Breakdown each layer into component alignments. + * - generate highlight information for each alignment. + */ function generateVerticalHighlights(data: { widgets: CanvasWidgetsReduxState; layer: FlexLayer; @@ -181,7 +186,7 @@ function generateVerticalHighlights(data: { return { highlights: [ - ...generateHighlightsForSubWrapper({ + ...generateHighlightsForAlignment({ arr: startChildren, childCount, layerIndex, @@ -195,7 +200,7 @@ function generateVerticalHighlights(data: { columnSpace, isMobile, }), - ...generateHighlightsForSubWrapper({ + ...generateHighlightsForAlignment({ arr: centerChildren, childCount: childCount + startChildren.length, layerIndex, @@ -210,7 +215,7 @@ function generateVerticalHighlights(data: { avoidInitialHighlight: startColumns > 25 || endColumns > 25, isMobile, }), - ...generateHighlightsForSubWrapper({ + ...generateHighlightsForAlignment({ arr: endChildren, childCount: childCount + startChildren.length + centerChildren.length, layerIndex, @@ -229,7 +234,14 @@ function generateVerticalHighlights(data: { }; } -function generateHighlightsForSubWrapper(data: { +/** + * Generate highlight information for a single alignment within a layer. + * - For each widget in the alignment + * - generate a highlight to mark the the start position of the widget. + * - Add another highlight at the end position of the last widget in the alignment. + * - If the alignment has no children, then add an initial highlight to mark the start of the alignment. + */ +function generateHighlightsForAlignment(data: { arr: Widget[]; childCount: number; layerIndex: number; From c0b5a66b48abd18d12fead8f66dfe9c91aa63c51 Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 3 Jan 2023 13:30:28 -0500 Subject: [PATCH 348/708] remove unused code --- .../pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index fb6e1712a286..53b3f10d1ad8 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -62,8 +62,6 @@ export const useAutoLayoutHighlights = ({ let lastActiveHighlight: HighlightInfo | undefined; let isFillWidget = false; - const isVerticalStack = direction === LayoutDirection.Vertical; - /** * START AUTO LAYOUT OFFSET CALCULATION */ From 695767cb76cd1d3677565f7f30b2bf36202e5eaf Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 3 Jan 2023 18:15:05 -0500 Subject: [PATCH 349/708] minor update --- app/client/src/utils/autoLayout/positionUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/client/src/utils/autoLayout/positionUtils.ts b/app/client/src/utils/autoLayout/positionUtils.ts index 5dec7089b31b..00bcea0b27fd 100644 --- a/app/client/src/utils/autoLayout/positionUtils.ts +++ b/app/client/src/utils/autoLayout/positionUtils.ts @@ -383,7 +383,7 @@ function getWrappedAlignmentInfo( let index = 0; let total = 0; for (const each of arr) { - if (total + each.columns >= GridDefaults.DEFAULT_GRID_COLUMNS) { + if (total + each.columns > GridDefaults.DEFAULT_GRID_COLUMNS) { let x = index; if (!res[resIndex].length) { res[resIndex].push(each); From 26d0cb0964a32e03a97e41e37acc453cfb104361 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Wed, 4 Jan 2023 11:33:12 +0530 Subject: [PATCH 350/708] adding toggle between fixed and auto on page level. --- .../pages/Editor/AppPositionTypeControl.tsx | 151 ++++++++++++++++++ .../pages/Editor/CanvasPropertyPane/index.tsx | 11 +- .../Editor/WidgetsEditor/CanvasContainer.tsx | 2 + .../entityReducers/pageListReducer.tsx | 15 +- .../src/resizable/resizenreflow/index.tsx | 2 +- app/client/src/selectors/editorSelectors.tsx | 31 +++- app/client/src/utils/layoutPropertiesUtils.ts | 1 + 7 files changed, 198 insertions(+), 15 deletions(-) create mode 100644 app/client/src/pages/Editor/AppPositionTypeControl.tsx diff --git a/app/client/src/pages/Editor/AppPositionTypeControl.tsx b/app/client/src/pages/Editor/AppPositionTypeControl.tsx new file mode 100644 index 000000000000..05b13a0e5db1 --- /dev/null +++ b/app/client/src/pages/Editor/AppPositionTypeControl.tsx @@ -0,0 +1,151 @@ +import classNames from "classnames"; +import React, { useMemo } from "react"; +import { useDispatch, useSelector } from "react-redux"; + +import { batchUpdateMultipleWidgetProperties } from "actions/controlActions"; +import { LayoutDirection, Positioning } from "components/constants"; +import { Colors } from "constants/Colors"; +import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; +import { Icon, IconName, IconSize, TooltipComponent } from "design-system"; +import { + AppPositioningTypeConfig, + AppPositioningTypes, +} from "reducers/entityReducers/pageListReducer"; +import { getCurrentAppPositioningType } from "selectors/editorSelectors"; +import { Title } from "./CanvasPropertyPane"; +import { MainContainerLayoutControl } from "./MainContainerLayoutControl"; + +interface ApplicationPositionTypeConfigOption { + name: string; + type: AppPositioningTypes; + icon?: IconName; +} + +export const AppsmithDefaultPositionType: AppPositioningTypeConfig = { + type: "FIXED", +}; + +const AppsmithLayoutTypes: ApplicationPositionTypeConfigOption[] = [ + { + name: "Fixed Layout", + type: "FIXED", + icon: "desktop", + }, + { + name: "Auto Layout", + type: "AUTO", + icon: "fluid", + }, +]; + +export function AppPositionTypeControl() { + const dispatch = useDispatch(); + const buttonRefs: Array = []; + const selectedOption = useSelector(getCurrentAppPositioningType); + /** + * return selected layout index. if there is no app + * layout, use the default one ( fluid ) + */ + const selectedIndex = useMemo(() => { + return AppsmithLayoutTypes.findIndex( + (each) => + each.type === (selectedOption || AppsmithDefaultPositionType.type), + ); + }, [selectedOption]); + + const [focusedIndex, setFocusedIndex] = React.useState(selectedIndex); + + const updateAppPositioningLayout = ( + layoutOption: ApplicationPositionTypeConfigOption, + ) => { + const selectedType = + layoutOption.type !== "AUTO" ? Positioning.Fixed : Positioning.Vertical; + dispatch( + batchUpdateMultipleWidgetProperties([ + { + widgetId: MAIN_CONTAINER_WIDGET_ID, + updates: { + modify: { + positioning: selectedType, + useAutoLayout: selectedType !== Positioning.Fixed, + direction: LayoutDirection.Vertical, + }, + }, + }, + ]), + ); + }; + + const handleKeyDown = (event: React.KeyboardEvent, index: number) => { + if (!buttonRefs.length) return; + + switch (event.key) { + case "ArrowRight": + case "Right": + const rightIndex = index === buttonRefs.length - 1 ? 0 : index + 1; + buttonRefs[rightIndex]?.focus(); + setFocusedIndex(rightIndex); + break; + case "ArrowLeft": + case "Left": + const leftIndex = index === 0 ? buttonRefs.length - 1 : index - 1; + buttonRefs[leftIndex]?.focus(); + setFocusedIndex(leftIndex); + break; + } + }; + + return ( + <> +
+
setFocusedIndex(selectedIndex)} + > + {AppsmithLayoutTypes.map((layoutOption: any, index: number) => { + return ( + + + + ); + })} +
+
+ {selectedOption === "FIXED" && ( + <> + Canvas Size + + + )} + + ); +} diff --git a/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx b/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx index 02f3f8b6778c..43f4895b25c4 100644 --- a/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx +++ b/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx @@ -3,16 +3,14 @@ import { Colors } from "constants/Colors"; import React from "react"; import { useDispatch } from "react-redux"; import styled from "styled-components"; -import { MainContainerLayoutControl } from "../MainContainerLayoutControl"; import { PopoverPosition } from "@blueprintjs/core"; import { openAppSettingsPaneAction } from "actions/appSettingsPaneActions"; import { Button, Category, Size, TooltipComponent } from "design-system"; - -const Title = styled.p` +import { AppPositionTypeControl } from "../AppPositionTypeControl"; +export const Title = styled.p` color: ${Colors.GRAY_800}; `; - export function CanvasPropertyPane() { const dispatch = useDispatch(); @@ -25,9 +23,8 @@ export function CanvasPropertyPane() {
- Canvas Size - - + App Positioning Type + diff --git a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx index 3a30694fa3d2..2c344ffd53a4 100644 --- a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx +++ b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx @@ -220,6 +220,7 @@ function CanvasContainer() { left: isPreviewMode ? "0px" : "16px", top: "50%", zIndex: isPreviewMode ? 2 : undefined, + float: "left", }} > @@ -239,6 +240,7 @@ function CanvasContainer() { left: isPreviewMode ? "100%" : "calc(100% - 32px)", top: "50%", zIndex: isPreviewMode ? 2 : undefined, + float: "right", }} > diff --git a/app/client/src/reducers/entityReducers/pageListReducer.tsx b/app/client/src/reducers/entityReducers/pageListReducer.tsx index ee43b92c889f..45d94bb18231 100644 --- a/app/client/src/reducers/entityReducers/pageListReducer.tsx +++ b/app/client/src/reducers/entityReducers/pageListReducer.tsx @@ -1,18 +1,18 @@ -import { sortBy } from "lodash"; import { - ReduxAction, - ReduxActionTypes, - Page, ClonePageSuccessPayload, + Page, + ReduxAction, ReduxActionErrorTypes, + ReduxActionTypes, } from "@appsmith/constants/ReduxActionConstants"; -import { createReducer } from "utils/ReducerUtils"; import { GenerateCRUDSuccess, UpdatePageErrorPayload, } from "actions/pageActions"; import { UpdatePageRequest, UpdatePageResponse } from "api/PageApi"; +import { sortBy } from "lodash"; import { DSL } from "reducers/uiReducers/pageCanvasStructureReducer"; +import { createReducer } from "utils/ReducerUtils"; const initialState: PageListReduxState = { pages: [], @@ -248,6 +248,11 @@ export interface AppLayoutConfig { type: SupportedLayouts; } +export type AppPositioningTypes = "AUTO" | "FIXED"; +export interface AppPositioningTypeConfig { + type: AppPositioningTypes; +} + export interface PageListReduxState { pages: Page[]; applicationId: string; diff --git a/app/client/src/resizable/resizenreflow/index.tsx b/app/client/src/resizable/resizenreflow/index.tsx index e7a658afde3c..02ced6727fd0 100644 --- a/app/client/src/resizable/resizenreflow/index.tsx +++ b/app/client/src/resizable/resizenreflow/index.tsx @@ -83,7 +83,7 @@ function ResizableHandle(props: ResizableHandleProps) { memo, movement: [mx, my], } = state; - if (!props.allowResize) { + if (!props.allowResize || props.disableDot) { return; } const scrollParent = getNearestParentCanvas(props.scrollParent); diff --git a/app/client/src/selectors/editorSelectors.tsx b/app/client/src/selectors/editorSelectors.tsx index 53f352c5bd33..5b0478b5e758 100644 --- a/app/client/src/selectors/editorSelectors.tsx +++ b/app/client/src/selectors/editorSelectors.tsx @@ -14,6 +14,7 @@ import { WidgetCardProps, WidgetProps } from "widgets/BaseWidget"; import { Page } from "@appsmith/constants/ReduxActionConstants"; import { ApplicationVersion } from "actions/applicationActions"; +import { Positioning } from "components/constants"; import { OccupiedSpace, WidgetSpace } from "constants/CanvasEditorConstants"; import { PLACEHOLDER_APP_SLUG, PLACEHOLDER_PAGE_SLUG } from "constants/routes"; import { @@ -25,6 +26,7 @@ import { APP_MODE } from "entities/App"; import { DataTree, DataTreeWidget } from "entities/DataTree/dataTreeFactory"; import { find, sortBy } from "lodash"; import CanvasWidgetsNormalizer from "normalizers/CanvasWidgetsNormalizer"; +import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; import { MainCanvasReduxState } from "reducers/uiReducers/mainCanvasReducer"; import { getDataTree, getLoadingEntities } from "selectors/dataTreeSelectors"; import { @@ -43,7 +45,6 @@ import { } from "utils/widgetRenderUtils"; import { ContainerWidgetProps } from "widgets/ContainerWidget/widget"; import { isAutoHeightEnabledForWidget } from "widgets/WidgetUtils"; - const getIsDraggingOrResizing = (state: AppState) => state.ui.widgetDragResize.isResizing || state.ui.widgetDragResize.isDragging; @@ -225,9 +226,35 @@ const defaultLayout: AppLayoutConfig = { type: "FLUID", }; -export const getCurrentApplicationLayout = (state: AppState) => +const getAppLayout = (state: AppState) => state.ui.applications.currentApplication?.appLayout || defaultLayout; +export const getMainCanvasPositioning = createSelector( + getWidgets, + (widgets) => { + return ( + widgets && + widgets[MAIN_CONTAINER_WIDGET_ID] && + widgets[MAIN_CONTAINER_WIDGET_ID].positioning + ); + }, +); + +export const getCurrentAppPositioningType = createSelector( + getMainCanvasPositioning, + (positioning: any): AppPositioningTypes => { + return positioning && positioning !== Positioning.Fixed ? "AUTO" : "FIXED"; + }, +); + +export const getCurrentApplicationLayout = createSelector( + getAppLayout, + getCurrentAppPositioningType, + (appLayout: AppLayoutConfig, appPositionType) => { + return appPositionType === "FIXED" ? appLayout : defaultLayout; + }, +); + export const getCanvasWidth = (state: AppState) => state.ui.mainCanvas.width; export const getMainCanvasProps = (state: AppState) => state.ui.mainCanvas; diff --git a/app/client/src/utils/layoutPropertiesUtils.ts b/app/client/src/utils/layoutPropertiesUtils.ts index d1dfcdd999d3..754574801002 100644 --- a/app/client/src/utils/layoutPropertiesUtils.ts +++ b/app/client/src/utils/layoutPropertiesUtils.ts @@ -235,6 +235,7 @@ export const NonResizableWidgets = [ "DIVIDER_WIDGET", "FILE_PICKER_WIDGET_V2", "ICON_WIDGET", + "ICON_BUTTON_WIDGET", "INPUT_WIDGET_V2", "MENU_BUTTON_WIDGET", "MULTI_SELECT_TREE_WIDGET", From d44c36232bf6e8fd2d017a5c20b6d4b87017750e Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Wed, 4 Jan 2023 12:15:45 +0530 Subject: [PATCH 351/708] feature flag changes for auto layout. --- .../appsmith/server/featureflags/FeatureFlagEnum.java | 1 + .../src/main/resources/features/init-flags.yml | 10 +++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/featureflags/FeatureFlagEnum.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/featureflags/FeatureFlagEnum.java index 6b8e6a47916b..b991dd762b3f 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/featureflags/FeatureFlagEnum.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/featureflags/FeatureFlagEnum.java @@ -32,6 +32,7 @@ public enum FeatureFlagEnum { CONTEXT_SWITCHING, DATASOURCE_ENVIRONMENTS, CUSTOM_JS_LIBRARY, + AUTO_LAYOUT, // Put EE flags below this line, to avoid conflicts. RBAC, diff --git a/app/server/appsmith-server/src/main/resources/features/init-flags.yml b/app/server/appsmith-server/src/main/resources/features/init-flags.yml index e88b9e68c3e9..f4c4cc99100d 100644 --- a/app/server/appsmith-server/src/main/resources/features/init-flags.yml +++ b/app/server/appsmith-server/src/main/resources/features/init-flags.yml @@ -88,7 +88,15 @@ ff4j: param: - name: emails value: multipanes@appsmith.com,ndx@appsmith.com - + - uid: AUTO_LAYOUT + enable: true + description: Enable auto layout editor by email domain of user. + flipstrategy: + class: com.appsmith.server.featureflags.strategies.EmailBasedRolloutStrategy + param: + - name: emailDomains + value: appsmith.com,moolya.com + # Put EE flags below this line, to avoid conflicts. - uid: RBAC enable: true From cd836aa8a26908c72fc562e5b2fcc5549a4c10f8 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Wed, 4 Jan 2023 12:47:24 +0530 Subject: [PATCH 352/708] Fixing multi selection --- .../appsmith/autoLayout/FlexComponent.tsx | 9 ++++++--- app/client/src/sagas/WidgetSelectionSagas.ts | 6 ++---- app/client/src/widgets/CanvasWidget.tsx | 10 +++++----- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx index add40c738204..ba3c65152f05 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx @@ -43,9 +43,12 @@ export function FlexComponent(props: AutoLayoutProps) { const isSnipingMode = useSelector(snipingModeSelector); const clickToSelectWidget = useClickToSelectWidget(props.widgetId); - const onClickFn = useCallback(() => { - clickToSelectWidget(props.widgetId); - }, [props.widgetId, clickToSelectWidget]); + const onClickFn = useCallback( + (e) => { + clickToSelectWidget(e); + }, + [props.widgetId, clickToSelectWidget], + ); const isDropTarget = checkIsDropTarget(props.widgetType); const { onHoverZIndex, zIndex } = usePositionedContainerZIndex( diff --git a/app/client/src/sagas/WidgetSelectionSagas.ts b/app/client/src/sagas/WidgetSelectionSagas.ts index 679a6cc6b4f5..2e1175e312c8 100644 --- a/app/client/src/sagas/WidgetSelectionSagas.ts +++ b/app/client/src/sagas/WidgetSelectionSagas.ts @@ -117,16 +117,14 @@ function* getLastSelectedCanvas() { const canvasWidgets: CanvasWidgetsReduxState = yield select(getWidgets); const widgetLastSelected = lastSelectedWidget && canvasWidgets[lastSelectedWidget]; - if (widgetLastSelected && !widgetLastSelected.flexLayers) { + if (widgetLastSelected) { const canvasToSelect: string = yield call( getDroppingCanvasOfWidget, widgetLastSelected, ); return canvasToSelect ? canvasToSelect : MAIN_CONTAINER_WIDGET_ID; } - if (!canvasWidgets[MAIN_CONTAINER_WIDGET_ID].flexLayers) { - return MAIN_CONTAINER_WIDGET_ID; - } + return MAIN_CONTAINER_WIDGET_ID; } // used for List widget cases diff --git a/app/client/src/widgets/CanvasWidget.tsx b/app/client/src/widgets/CanvasWidget.tsx index 20dbc1ac416c..491b024a18fe 100644 --- a/app/client/src/widgets/CanvasWidget.tsx +++ b/app/client/src/widgets/CanvasWidget.tsx @@ -5,6 +5,8 @@ import { CANVAS_DEFAULT_MIN_HEIGHT_PX } from "constants/AppConstants"; import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; import { GridDefaults, RenderModes } from "constants/WidgetConstants"; import { CanvasDraggingArena } from "pages/common/CanvasArenas/CanvasDraggingArena"; +import { CanvasSelectionArena } from "pages/common/CanvasArenas/CanvasSelectionArena"; +import WidgetsMultiSelectBox from "pages/Editor/WidgetsMultiSelectBox"; import React, { CSSProperties } from "react"; import { getCanvasClassName } from "utils/generators"; import { getDefaultResponsiveBehavior } from "utils/layoutPropertiesUtils"; @@ -97,8 +99,6 @@ class CanvasWidget extends ContainerWidget { widgetId={props.widgetId} widgetName={props.widgetName} /> - {/* - // Removing Canvas Selection and grouping in the POC */} + /> )} - {/* */} + /> {/* without the wrapping div onClick events are triggered twice */} Date: Wed, 4 Jan 2023 12:54:19 +0530 Subject: [PATCH 353/708] enable grouping for fixed canvas. --- app/client/src/widgets/CanvasWidget.tsx | 49 ++++++++++++++++--------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/app/client/src/widgets/CanvasWidget.tsx b/app/client/src/widgets/CanvasWidget.tsx index 491b024a18fe..9305859f31c8 100644 --- a/app/client/src/widgets/CanvasWidget.tsx +++ b/app/client/src/widgets/CanvasWidget.tsx @@ -81,7 +81,6 @@ class CanvasWidget extends ContainerWidget { ): JSX.Element { const direction = this.getDirection(); const snapRows = getCanvasSnapRows(props.bottomRow, props.canExtend); - const stretchFlexBox = !this.props.children || !this.props.children?.length; return ( {props.renderMode === RenderModes.CANVAS && ( @@ -109,28 +108,44 @@ class CanvasWidget extends ContainerWidget { /> )} + {this.props.useAutoLayout + ? this.renderFlexCanvas(direction) + : this.renderFixedCanvas(props)} + + ); + } + + renderFlexCanvas(direction: LayoutDirection) { + const stretchFlexBox = !this.props.children || !this.props.children?.length; + return ( + + {this.renderChildren()} + + ); + } + + renderFixedCanvas(props: ContainerWidgetProps) { + return ( + <> - {/* without the wrapping div onClick events are triggered twice */} - - {this.renderChildren()} - - + {this.renderChildren()} + ); } From 08afff5bafdfbc9fa7e3ad5251b0652c80c4a3c1 Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 4 Jan 2023 14:44:58 -0500 Subject: [PATCH 354/708] fix copy paste --- .../hooks/useAutoLayoutHighlights.ts | 1 - app/client/src/sagas/AutoLayoutUtils.ts | 116 ++++++++++++++++++ app/client/src/sagas/WidgetOperationSagas.tsx | 113 ++++++----------- .../src/utils/autoLayout/positionUtils.ts | 2 +- 4 files changed, 156 insertions(+), 76 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index 53b3f10d1ad8..f0f689762a0c 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -28,7 +28,6 @@ export interface HighlightInfo { width: number; // width of the highlight. height: number; // height of the highlight. isVertical: boolean; // determines if the highlight is vertical or horizontal. - el?: Element; // dom node of the highlight. canvasId: string; // widgetId of the canvas to which the highlight belongs. } diff --git a/app/client/src/sagas/AutoLayoutUtils.ts b/app/client/src/sagas/AutoLayoutUtils.ts index dc145d5ea9b2..505dfe70f1a5 100644 --- a/app/client/src/sagas/AutoLayoutUtils.ts +++ b/app/client/src/sagas/AutoLayoutUtils.ts @@ -318,3 +318,119 @@ function checkIsNotVerticalStack(widget: any): boolean { widget.positioning !== Positioning.Vertical ); } + +/** + * COPY PASTE UTILS + */ + +export function pasteWidgetInFlexLayers( + allWidgets: CanvasWidgetsReduxState, + parentId: string, + widget: any, + originalWidgetId: string, + isMobile: boolean, +): CanvasWidgetsReduxState { + let widgets = { ...allWidgets }; + const parent = widgets[parentId]; + let flexLayers: FlexLayer[] = parent.flexLayers || []; + /** + * If the new parent is not the same as the original parent, + * then add a new flex layer. + */ + if (widgets[originalWidgetId].parentId !== parentId) { + flexLayers = [ + ...flexLayers, + { + children: [ + { + id: widget.widgetId, + align: FlexLayerAlignment.Start, + }, + ], + hasFillChild: widget.responsiveBehavior === ResponsiveBehavior.Fill, + }, + ]; + } else { + /** + * If the new parent is the same as the original parent, + * then update the flex layer. + */ + let rowIndex = -1, + alignment = FlexLayerAlignment.Start; + const flexLayerIndex = flexLayers.findIndex((layer: FlexLayer) => { + const temp = layer.children.findIndex( + (child: LayerChild) => child.id === originalWidgetId, + ); + if (temp > -1) { + rowIndex = temp; + alignment = layer.children[temp].align; + } + return temp > -1; + }); + if (flexLayerIndex > -1 && rowIndex > -1) { + let selectedLayer = flexLayers[flexLayerIndex]; + selectedLayer = { + children: [ + ...selectedLayer.children.slice(0, rowIndex + 1), + { id: widget.widgetId, align: alignment }, + ...selectedLayer.children.slice(rowIndex + 1), + ], + hasFillChild: selectedLayer.hasFillChild, + }; + flexLayers = [ + ...flexLayers.slice(0, flexLayerIndex), + selectedLayer, + ...flexLayers.slice(flexLayerIndex + 1), + ]; + } + } + widgets = { + ...widgets, + [parentId]: { + ...parent, + flexLayers, + }, + }; + return updateWidgetPositions(widgets, parentId, isMobile); +} + +export function addChildToPastedFlexLayers( + allWidgets: CanvasWidgetsReduxState, + widget: any, + widgetIdMap: Record, + isMobile: boolean, +): CanvasWidgetsReduxState { + let widgets = { ...allWidgets }; + const parent = widgets[widget.parentId]; + const flexLayers = parent.flexLayers || []; + if (flexLayers.length > 0) { + let index = 0; + for (const layer of flexLayers) { + let children = layer.children; + let childIndex = 0; + for (const child of children) { + if (widgetIdMap[child.id] === widget.widgetId) { + children = [ + ...children.slice(0, childIndex), + { id: widget.widgetId, align: child.align }, + ...children.slice(childIndex + 1), + ]; + childIndex += 1; + } + } + flexLayers[index] = { + children, + hasFillChild: layer.hasFillChild, + }; + index += 1; + } + } + widgets = { + ...widgets, + [parent.widgetId]: { + ...parent, + flexLayers, + }, + }; + return updateWidgetPositions(widgets, parent.widgetId, isMobile); +} diff --git a/app/client/src/sagas/WidgetOperationSagas.tsx b/app/client/src/sagas/WidgetOperationSagas.tsx index cd32991445af..051d252d2a26 100644 --- a/app/client/src/sagas/WidgetOperationSagas.tsx +++ b/app/client/src/sagas/WidgetOperationSagas.tsx @@ -82,15 +82,7 @@ import { validateProperty } from "./EvaluationsSaga"; import { generateAutoHeightLayoutTreeAction } from "actions/autoHeightActions"; import { stopReflowAction } from "actions/reflowActions"; import { updateMultipleWidgetProperties } from "actions/widgetActions"; -import { - FlexLayerAlignment, - Positioning, - ResponsiveBehavior, -} from "components/constants"; -import { - FlexLayer, - LayerChild, -} from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; +import { Positioning } from "components/constants"; import { WidgetSpace } from "constants/CanvasEditorConstants"; import { DataTree } from "entities/DataTree/dataTreeFactory"; import { MetaState } from "reducers/entityReducers/metaReducer"; @@ -157,6 +149,10 @@ import { getAllPaths } from "ce/workers/Evaluation/evaluationUtils"; import { updateWidgetPositions } from "utils/autoLayout/positionUtils"; import { getSlidingArenaName } from "constants/componentClassNameConstants"; import { getIsMobile } from "selectors/mainCanvasSelectors"; +import { + addChildToPastedFlexLayers, + pasteWidgetInFlexLayers, +} from "./AutoLayoutUtils"; export function* updateAllChildCanvasHeights( currentContainerLikeWidgetId: string, @@ -1365,7 +1361,7 @@ function* pasteWidgetSaga( const canvasWidgets: CanvasWidgetsReduxState = yield select(getWidgets); let widgets: CanvasWidgetsReduxState = canvasWidgets; const selectedWidget: FlattenedWidgetProps = yield getSelectedWidgetWhenPasting(); - + const isMobile: boolean = yield select(getIsMobile); let reflowedMovementMap, bottomMostRow: number | undefined, gridProps: GridProps | undefined, @@ -1451,7 +1447,6 @@ function* pasteWidgetSaga( if (canvasId) pastingIntoWidgetId = canvasId; } - const pastingIntoWidget = widgets[pastingIntoWidgetId]; yield all( copiedWidgetGroups.map((copiedWidgets) => @@ -1501,6 +1496,7 @@ function* pasteWidgetSaga( // Get a flat list of all the widgets to be updated const widgetList = copiedWidgets.list; const widgetIdMap: Record = {}; + const reverseWidgetIdMap: Record = {}; const widgetNameMap: Record = {}; const newWidgetList: FlattenedWidgetProps[] = []; // Generate new widgetIds for the flat list of all the widgets to be updated @@ -1511,7 +1507,7 @@ function* pasteWidgetSaga( newWidget.widgetId = generateReactKey(); // Add the new widget id so that it maps the previous widget id widgetIdMap[widget.widgetId] = newWidget.widgetId; - + reverseWidgetIdMap[newWidget.widgetId] = widget.widgetId; // Add the new widget to the list newWidgetList.push(newWidget); }); @@ -1629,7 +1625,6 @@ function* pasteWidgetSaga( // to include this new copied widget's id in the parent's children let parentChildren = [widget.widgetId]; const widgetChildren = widgets[pastingParentId].children; - let flexLayers: FlexLayer[] = pastingIntoWidget.flexLayers || []; if (widgetChildren && Array.isArray(widgetChildren)) { // Add the new child to existing children after it's original siblings position. @@ -1643,66 +1638,6 @@ function* pasteWidgetSaga( ...parentChildren, ...widgetChildren.slice(originalWidgetIndex + 1), ]; - - /** - * If new parent is a vertical stack, then update flex layers. - */ - // TODO: Preet - add check for main container's positioning. - if ( - pastingIntoWidget.widgetId === MAIN_CONTAINER_WIDGET_ID || - pastingIntoWidget.positioning === Positioning.Vertical || - (pastingIntoWidget.type === "CANVAS_WIDGET" && - pastingIntoWidget.parentId && - widgets[pastingIntoWidget.parentId].positioning === - Positioning.Vertical) - ) { - if (widgets[originalWidgetId].parentId !== pastingParentId) { - flexLayers = [ - ...flexLayers, - { - children: [ - { - id: widget.widgetId, - align: FlexLayerAlignment.Start, - }, - ], - hasFillChild: - widget.responsiveBehavior === ResponsiveBehavior.Fill, - }, - ]; - } else { - let rowIndex = -1, - alignment = FlexLayerAlignment.Start; - const flexLayerIndex = flexLayers.findIndex( - (layer: FlexLayer) => { - const temp = layer.children.findIndex( - (child: LayerChild) => child.id === originalWidgetId, - ); - if (temp > -1) { - rowIndex = temp; - alignment = layer.children[temp].align; - } - return temp > -1; - }, - ); - if (flexLayerIndex > -1 && rowIndex > -1) { - let selectedLayer = flexLayers[flexLayerIndex]; - selectedLayer = { - children: [ - ...selectedLayer.children.slice(0, rowIndex + 1), - { id: widget.widgetId, align: alignment }, - ...selectedLayer.children.slice(rowIndex + 1), - ], - hasFillChild: selectedLayer.hasFillChild, - }; - flexLayers = [ - ...flexLayers.slice(0, flexLayerIndex), - selectedLayer, - ...flexLayers.slice(flexLayerIndex + 1), - ]; - } - } - } } const parentBottomRow = getParentBottomRowAfterAddingWidget( widgets[pastingParentId], @@ -1715,7 +1650,6 @@ function* pasteWidgetSaga( ...widgets[pastingParentId], bottomRow: Math.max(parentBottomRow, bottomMostRow || 0), children: parentChildren, - flexLayers: flexLayers, }, }; // If the copied widget's boundaries exceed the parent's @@ -1758,6 +1692,37 @@ function* pasteWidgetSaga( widgetNameMap[oldWidgetName] = widget.widgetName; // Add the new widget to the canvas widgets widgets[widget.widgetId] = widget; + + /** + * If new parent is a vertical stack, then update flex layers. + */ + if (widget.parentId) { + const pastingIntoWidget = widgets[widget.parentId]; + // TODO: Preet - add check for main container's positioning. + if ( + pastingIntoWidget.widgetId === MAIN_CONTAINER_WIDGET_ID || + pastingIntoWidget.positioning === Positioning.Vertical || + (pastingIntoWidget.parentId && + widgets[pastingIntoWidget.parentId].positioning === + Positioning.Vertical) + ) { + if (widget.widgetId === widgetIdMap[copiedWidget.widgetId]) + widgets = pasteWidgetInFlexLayers( + widgets, + widget.parentId, + widget, + reverseWidgetIdMap[widget.widgetId], + isMobile, + ); + else + widgets = addChildToPastedFlexLayers( + widgets, + widget, + widgetIdMap, + isMobile, + ); + } + } } newlyCreatedWidgetIds.push(widgetIdMap[copiedWidgetId]); // 1. updating template in the copied widget and deleting old template associations diff --git a/app/client/src/utils/autoLayout/positionUtils.ts b/app/client/src/utils/autoLayout/positionUtils.ts index 00bcea0b27fd..237bc4e14642 100644 --- a/app/client/src/utils/autoLayout/positionUtils.ts +++ b/app/client/src/utils/autoLayout/positionUtils.ts @@ -297,7 +297,7 @@ function extractAlignmentInfo( // Fill widgets are designed to take up parent's entire width on mobile viewport. const fillWidgetLength: number = isMobile ? GridDefaults.DEFAULT_GRID_COLUMNS - : availableColumns / fillChildren.length; + : Math.min(availableColumns / fillChildren.length, 64); for (const child of fillChildren) { if (child.align === FlexLayerAlignment.Start) { startColumns += fillWidgetLength; From 75b46105dc06d256d17d77c232ce98037bfaffd4 Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 5 Jan 2023 09:47:10 -0500 Subject: [PATCH 355/708] fix highlight issue on scroll --- .../src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index a2674c8709aa..e63040886bf4 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -748,7 +748,7 @@ export const useCanvasDragging = ( widgetId === MAIN_CONTAINER_WIDGET_ID && scrollParent?.scrollTop ) - val = scrollParent.scrollTop - 20; + val = scrollParent.scrollTop; canvasCtx.fillRect(posX, posY - val, width, height); canvasCtx.save(); } From 2954908e752ee718657015e8892851d21117c57a Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 5 Jan 2023 10:30:55 -0500 Subject: [PATCH 356/708] fix copy paste for tabs --- app/client/src/sagas/AutoLayoutUtils.ts | 20 ++++++++++++++++++- app/client/src/sagas/WidgetOperationSagas.tsx | 12 +++-------- app/client/src/utils/layoutPropertiesUtils.ts | 2 +- 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/app/client/src/sagas/AutoLayoutUtils.ts b/app/client/src/sagas/AutoLayoutUtils.ts index 505dfe70f1a5..d09ea98ebc2f 100644 --- a/app/client/src/sagas/AutoLayoutUtils.ts +++ b/app/client/src/sagas/AutoLayoutUtils.ts @@ -320,7 +320,7 @@ function checkIsNotVerticalStack(widget: any): boolean { } /** - * COPY PASTE UTILS + * START: COPY PASTE UTILS */ export function pasteWidgetInFlexLayers( @@ -434,3 +434,21 @@ export function addChildToPastedFlexLayers( }; return updateWidgetPositions(widgets, parent.widgetId, isMobile); } + +export function isStack( + allWidgets: CanvasWidgetsReduxState, + widget: any, +): boolean { + const parent = widget.parentId ? allWidgets[widget.parentId] : undefined; + return ( + widget.positioning === Positioning.Vertical || + (parent && parent.positioning === Positioning.Vertical) || + (parent !== undefined && + parent?.type === "TABS_WIDGET" && + parent?.tabsObj[widget.tabId].positioning === Positioning.Vertical) + ); +} + +/** + * END: copy paste utils + */ diff --git a/app/client/src/sagas/WidgetOperationSagas.tsx b/app/client/src/sagas/WidgetOperationSagas.tsx index 051d252d2a26..978b2b872c2a 100644 --- a/app/client/src/sagas/WidgetOperationSagas.tsx +++ b/app/client/src/sagas/WidgetOperationSagas.tsx @@ -151,6 +151,7 @@ import { getSlidingArenaName } from "constants/componentClassNameConstants"; import { getIsMobile } from "selectors/mainCanvasSelectors"; import { addChildToPastedFlexLayers, + isStack, pasteWidgetInFlexLayers, } from "./AutoLayoutUtils"; @@ -1698,14 +1699,7 @@ function* pasteWidgetSaga( */ if (widget.parentId) { const pastingIntoWidget = widgets[widget.parentId]; - // TODO: Preet - add check for main container's positioning. - if ( - pastingIntoWidget.widgetId === MAIN_CONTAINER_WIDGET_ID || - pastingIntoWidget.positioning === Positioning.Vertical || - (pastingIntoWidget.parentId && - widgets[pastingIntoWidget.parentId].positioning === - Positioning.Vertical) - ) { + if (isStack(widgets, pastingIntoWidget)) { if (widget.widgetId === widgetIdMap[copiedWidget.widgetId]) widgets = pasteWidgetInFlexLayers( widgets, @@ -1714,7 +1708,7 @@ function* pasteWidgetSaga( reverseWidgetIdMap[widget.widgetId], isMobile, ); - else + else if (widget.type !== "CANVAS_WIDGET") widgets = addChildToPastedFlexLayers( widgets, widget, diff --git a/app/client/src/utils/layoutPropertiesUtils.ts b/app/client/src/utils/layoutPropertiesUtils.ts index 754574801002..7435d73918eb 100644 --- a/app/client/src/utils/layoutPropertiesUtils.ts +++ b/app/client/src/utils/layoutPropertiesUtils.ts @@ -276,7 +276,7 @@ export const DefaultFillWidgets = [ "TEXT_WIDGET", "SINGLE_SELECT_TREE_WIDGET", "RICH_TEXT_EDITOR_WIDGET", - "TABS_WIDGET_", + "TABS_WIDGET", "TABLE_WIDGET_V2", ]; From ff4e55ddfd216393a8fb044b926ec495d6112873 Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 5 Jan 2023 14:04:00 -0500 Subject: [PATCH 357/708] fix mobile position calculation for main canvas --- app/client/src/sagas/AutoLayoutUtils.ts | 1 + .../src/utils/autoLayout/highlightUtils.ts | 123 +++++++++++------- .../src/utils/autoLayout/positionUtils.ts | 5 +- 3 files changed, 78 insertions(+), 51 deletions(-) diff --git a/app/client/src/sagas/AutoLayoutUtils.ts b/app/client/src/sagas/AutoLayoutUtils.ts index d09ea98ebc2f..d4caa0c20994 100644 --- a/app/client/src/sagas/AutoLayoutUtils.ts +++ b/app/client/src/sagas/AutoLayoutUtils.ts @@ -290,6 +290,7 @@ export function alterLayoutForMobile( widgets[child] = widget; widgets = updateWidgetPositions(widgets, child, true); } + widgets = updateWidgetPositions(widgets, parentId, true); return widgets; } diff --git a/app/client/src/utils/autoLayout/highlightUtils.ts b/app/client/src/utils/autoLayout/highlightUtils.ts index 2fdd50d426b2..aa9096a12822 100644 --- a/app/client/src/utils/autoLayout/highlightUtils.ts +++ b/app/client/src/utils/autoLayout/highlightUtils.ts @@ -19,7 +19,12 @@ import { getWidgetHeight, getWidgetWidth, } from "./flexWidgetUtils"; -import { getTotalRowsOfAllChildren, Widget } from "./positionUtils"; +import { + AlignmentInfo, + getTotalRowsOfAllChildren, + getWrappedAlignmentInfo, + Widget, +} from "./positionUtils"; /** * @param allWidgets : CanvasWidgetsReduxState @@ -162,6 +167,7 @@ function generateVerticalHighlights(data: { centerChildren = [], endChildren = []; let startColumns = 0, + centerColumns = 0, endColumns = 0; let maxHeight = 0; for (const child of children) { @@ -178,60 +184,79 @@ function generateVerticalHighlights(data: { endColumns += getWidgetWidth(widget, isMobile); } else if (child.align === FlexLayerAlignment.Center) { centerChildren.push(widget); + centerColumns += getWidgetWidth(widget, isMobile); } else { startChildren.push(widget); startColumns += getWidgetWidth(widget, isMobile); } } - return { - highlights: [ - ...generateHighlightsForAlignment({ - arr: startChildren, - childCount, - layerIndex, - alignment: FlexLayerAlignment.Start, - maxHeight, - offsetTop, - canvasId, - parentColumnSpace: columnSpace, - parentRowSpace: widgets[canvasId].parentRowSpace, - canvasWidth, - columnSpace, - isMobile, - }), - ...generateHighlightsForAlignment({ - arr: centerChildren, - childCount: childCount + startChildren.length, - layerIndex, - alignment: FlexLayerAlignment.Center, - maxHeight, - offsetTop, - canvasId, - parentColumnSpace: columnSpace, - parentRowSpace: widgets[canvasId].parentRowSpace, - canvasWidth, - columnSpace, - avoidInitialHighlight: startColumns > 25 || endColumns > 25, - isMobile, - }), - ...generateHighlightsForAlignment({ - arr: endChildren, - childCount: childCount + startChildren.length + centerChildren.length, - layerIndex, - alignment: FlexLayerAlignment.End, - maxHeight, - offsetTop, - canvasId, - parentColumnSpace: columnSpace, - parentRowSpace: widgets[canvasId].parentRowSpace, - canvasWidth, - columnSpace, - isMobile, - }), - ], - childCount: count, - }; + const alignmentInfo: AlignmentInfo[] = [ + { + alignment: FlexLayerAlignment.Start, + children: startChildren, + columns: startColumns, + }, + { + alignment: FlexLayerAlignment.Center, + children: centerChildren, + columns: centerColumns, + }, + { + alignment: FlexLayerAlignment.End, + children: endChildren, + columns: endColumns, + }, + ]; + + const wrappingInfo: AlignmentInfo[][] = isMobile + ? getWrappedAlignmentInfo(alignmentInfo) + : [alignmentInfo]; + + const highlights: HighlightInfo[] = []; + for (const each of wrappingInfo) { + if (!each.length) continue; + /** + * On mobile viewport, + * if the row is wrapped, i.e. it contains less than all three alignments + * and if total columns required by these alignments are zero; + * then don't add a highlight for them as they will be squashed. + */ + if ( + isMobile && + each.length < 3 && + each.reduce((a, b) => a + b.columns, 0) === 0 + ) + continue; + for (const item of each) { + highlights.push( + ...generateHighlightsForAlignment({ + arr: item.children, + childCount: + item.alignment === FlexLayerAlignment.Start + ? childCount + : item.alignment === FlexLayerAlignment.Center + ? childCount + startChildren.length + : childCount + startChildren.length + centerChildren.length, + layerIndex, + alignment: item.alignment, + maxHeight, + offsetTop, + canvasId, + parentColumnSpace: columnSpace, + parentRowSpace: widgets[canvasId].parentRowSpace, + canvasWidth, + columnSpace, + isMobile, + avoidInitialHighlight: + item.alignment === FlexLayerAlignment.Center && !isMobile + ? startColumns > 25 || endColumns > 25 + : false, + }), + ); + } + } + return { highlights, childCount: count }; } /** diff --git a/app/client/src/utils/autoLayout/positionUtils.ts b/app/client/src/utils/autoLayout/positionUtils.ts index 237bc4e14642..58b21d88b01b 100644 --- a/app/client/src/utils/autoLayout/positionUtils.ts +++ b/app/client/src/utils/autoLayout/positionUtils.ts @@ -16,7 +16,7 @@ export type Widget = WidgetProps & { children?: string[] | undefined; }; -interface AlignmentInfo { +export interface AlignmentInfo { alignment: FlexLayerAlignment; columns: number; children: Widget[]; @@ -82,6 +82,7 @@ export function updateWidgetPositions( ); widgets = { ...widgets, [parent.widgetId]: updatedParent }; } + const shouldUpdateHeight = parent.parentId && ["CONTAINER_WIDGET", "CANVAS_WIDGET"].includes( @@ -371,7 +372,7 @@ function getAlignmentSizeInfo( * @param resIndex | number : Last index of res. * @returns AlignmentInfo[][] */ -function getWrappedAlignmentInfo( +export function getWrappedAlignmentInfo( arr: AlignmentInfo[], res: AlignmentInfo[][] = [[], [], []], resIndex = 0, From c2bd34295c4e6efe06586d5e1ab0269521bb0c98 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Fri, 6 Jan 2023 01:04:32 +0530 Subject: [PATCH 358/708] Fixing issues --- .../editorComponents/DraggableComponent.tsx | 1 - .../pages/Editor/AppPositionTypeControl.tsx | 13 +- .../Editor/PropertyPane/PropertyControl.tsx | 2 +- .../src/pages/Editor/PropertyPane/helpers.ts | 5 + .../Editor/WidgetsEditor/CanvasContainer.tsx | 146 +++++++++--------- .../entityReducers/pageListReducer.tsx | 5 +- .../src/sagas/WidgetEnhancementHelpers.ts | 15 +- app/client/src/sagas/autoHeightSagas/index.ts | 4 + app/client/src/selectors/editorSelectors.tsx | 8 +- .../src/selectors/propertyPaneSelectors.tsx | 5 +- app/client/src/utils/WidgetFactory.tsx | 10 +- app/client/src/utils/WidgetFactoryHelpers.ts | 69 +++++---- .../src/utils/WidgetRegisterHelpers.tsx | 30 ++-- .../src/utils/hooks/useDynamicAppLayout.tsx | 9 +- app/client/src/utils/layoutPropertiesUtils.ts | 3 + .../src/widgets/ProgressBarWidget/index.ts | 6 +- 16 files changed, 187 insertions(+), 144 deletions(-) diff --git a/app/client/src/components/editorComponents/DraggableComponent.tsx b/app/client/src/components/editorComponents/DraggableComponent.tsx index 90e46803e8dc..4d8b71788926 100644 --- a/app/client/src/components/editorComponents/DraggableComponent.tsx +++ b/app/client/src/components/editorComponents/DraggableComponent.tsx @@ -38,7 +38,6 @@ const WidgetBoundaries = styled.div` border: 1px dashed ${(props) => getColorWithOpacity(props.theme.colors.textAnchor, 0.5)}; pointer-events: none; - z-index: 1000; `; type DraggableComponentProps = WidgetProps; diff --git a/app/client/src/pages/Editor/AppPositionTypeControl.tsx b/app/client/src/pages/Editor/AppPositionTypeControl.tsx index 05b13a0e5db1..fa9df13b02e5 100644 --- a/app/client/src/pages/Editor/AppPositionTypeControl.tsx +++ b/app/client/src/pages/Editor/AppPositionTypeControl.tsx @@ -22,18 +22,18 @@ interface ApplicationPositionTypeConfigOption { } export const AppsmithDefaultPositionType: AppPositioningTypeConfig = { - type: "FIXED", + type: AppPositioningTypes.FIXED, }; const AppsmithLayoutTypes: ApplicationPositionTypeConfigOption[] = [ { name: "Fixed Layout", - type: "FIXED", + type: AppPositioningTypes.FIXED, icon: "desktop", }, { name: "Auto Layout", - type: "AUTO", + type: AppPositioningTypes.AUTO, icon: "fluid", }, ]; @@ -59,7 +59,9 @@ export function AppPositionTypeControl() { layoutOption: ApplicationPositionTypeConfigOption, ) => { const selectedType = - layoutOption.type !== "AUTO" ? Positioning.Fixed : Positioning.Vertical; + layoutOption.type !== AppPositioningTypes.AUTO + ? Positioning.Fixed + : Positioning.Vertical; dispatch( batchUpdateMultipleWidgetProperties([ { @@ -121,7 +123,6 @@ export function AppPositionTypeControl() { "bg-gray-100 hover:bg-gray-200": selectedIndex !== index, })} onClick={() => { - // updateAppLayout(layoutOption); updateAppPositioningLayout(layoutOption); setFocusedIndex(index); }} @@ -140,7 +141,7 @@ export function AppPositionTypeControl() { })}
- {selectedOption === "FIXED" && ( + {selectedOption === AppPositioningTypes.FIXED && ( <> Canvas Size diff --git a/app/client/src/pages/Editor/PropertyPane/PropertyControl.tsx b/app/client/src/pages/Editor/PropertyPane/PropertyControl.tsx index 5ae68f6c5a63..cbc7e928c052 100644 --- a/app/client/src/pages/Editor/PropertyPane/PropertyControl.tsx +++ b/app/client/src/pages/Editor/PropertyPane/PropertyControl.tsx @@ -496,7 +496,7 @@ const PropertyControl = memo((props: Props) => { (props.hidden && props.hidden(widgetProperties, props.propertyName)) || props.invisible || (childWidgetShouldHidePropertyFn && - childWidgetShouldHidePropertyFn(props.propertyName)) + childWidgetShouldHidePropertyFn(widgetProperties, props.propertyName)) ) { return null; } diff --git a/app/client/src/pages/Editor/PropertyPane/helpers.ts b/app/client/src/pages/Editor/PropertyPane/helpers.ts index c1cddb14186f..d52db4fcba6b 100644 --- a/app/client/src/pages/Editor/PropertyPane/helpers.ts +++ b/app/client/src/pages/Editor/PropertyPane/helpers.ts @@ -5,6 +5,7 @@ import { } from "constants/PropertyControlConstants"; import { debounce } from "lodash"; import { useCallback, useState } from "react"; +import { appPositioningBasedPropertyFilter } from "sagas/WidgetEnhancementHelpers"; export function useSearchText(initialVal: string) { const [searchText, setSearchText] = useState(initialVal); @@ -57,6 +58,10 @@ export function evaluateHiddenProperty( } } else if (controlConfig.controlType) { const isControlHidden = + appPositioningBasedPropertyFilter( + widgetProps, + controlConfig.propertyName, + ) || (controlConfig.hidden && controlConfig.hidden(widgetProps, controlConfig.propertyName)) || (shouldHidePropertyFn && diff --git a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx index 2c344ffd53a4..19db894fd1b5 100644 --- a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx +++ b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx @@ -3,6 +3,7 @@ import { useSelector } from "react-redux"; import { getCanvasWidth, getCurrentApplicationLayout, + getCurrentAppPositioningType, getCurrentPageId, getIsFetchingPage, getViewModePageList, @@ -32,7 +33,7 @@ import { getCurrentThemeDetails } from "selectors/themeSelectors"; import { useDynamicAppLayout } from "utils/hooks/useDynamicAppLayout"; import useGoogleFont from "utils/hooks/useGoogleFont"; // import useHorizontalResize from "utils/hooks/useHorizontalResize"; -import { Positioning } from "components/constants"; +import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; import Canvas from "../Canvas"; const Container = styled.section<{ @@ -96,89 +97,88 @@ function CanvasContainer() { ); } + const appPositioningType = useSelector(getCurrentAppPositioningType); const appLayout = useSelector(getCurrentApplicationLayout); useEffect(() => { - if (isPreviewMode) { - const ele: any = document.getElementById("canvas-viewport"); - ele.style.width = "inherit"; - } - if (appLayout?.type === "FLUID") { - const smallestWidth = layoutConfigurations.MOBILE.minWidth; - // Query the element - const ele: any = document.getElementById("canvas-viewport"); - const initialWidth = ele.offsetWidth; - // The current position of mouse - let x = 0; - // let y = 0; + if (appPositioningType === AppPositioningTypes.AUTO) { + if (isPreviewMode) { + const ele: any = document.getElementById("canvas-viewport"); + ele.style.width = "inherit"; + } + if (appLayout?.type === "FLUID") { + const smallestWidth = layoutConfigurations.MOBILE.minWidth; + // Query the element + const ele: any = document.getElementById("canvas-viewport"); + const initialWidth = ele.offsetWidth; + // The current position of mouse + let x = 0; + // let y = 0; - // The dimension of the element - let w = 0; - // let h = 0; - let events: any = []; + // The dimension of the element + let w = 0; + // let h = 0; + let events: any = []; - // Handle the mousedown event - // that's triggered when user drags the resizer - const mouseDownHandler = function(e: any, rightHandle: boolean) { - // Get the current mouse position - x = e.clientX; - // y = e.clientY; + // Handle the mousedown event + // that's triggered when user drags the resizer + const mouseDownHandler = function(e: any, rightHandle: boolean) { + // Get the current mouse position + x = e.clientX; + // y = e.clientY; - // Calculate the dimension of element - const styles = window.getComputedStyle(ele); - w = parseInt(styles.width, 10); - // h = parseInt(styles.height, 10); - const mouseMove = (e: any) => mouseMoveHandler(e, rightHandle); - events.push(mouseMove); - // Attach the listeners to `document` - document.addEventListener("mousemove", mouseMove); - document.addEventListener("mouseup", mouseUpHandler); - // e.stopPropagation(); - }; + // Calculate the dimension of element + const styles = window.getComputedStyle(ele); + w = parseInt(styles.width, 10); + // h = parseInt(styles.height, 10); + const mouseMove = (e: any) => mouseMoveHandler(e, rightHandle); + events.push(mouseMove); + // Attach the listeners to `document` + document.addEventListener("mousemove", mouseMove); + document.addEventListener("mouseup", mouseUpHandler); + // e.stopPropagation(); + }; - const mouseMoveHandler = function(e: any, rightHandle: boolean) { - // How far the mouse has been moved - const multiplier = rightHandle ? 2 : -2; - const dx = (e.clientX - x) * multiplier; - if (initialWidth >= w + dx && smallestWidth <= w + dx) { - // Adjust the dimension of element - ele.style.width = `${w + dx}px`; - } - if (initialWidth < w + dx) { - ele.style.width = `${initialWidth}px`; - } - if (smallestWidth > w + dx) { - ele.style.width = `${smallestWidth}px`; - } - // e.stopPropagation(); - }; + const mouseMoveHandler = function(e: any, rightHandle: boolean) { + // How far the mouse has been moved + const multiplier = rightHandle ? 2 : -2; + const dx = (e.clientX - x) * multiplier; + if (initialWidth >= w + dx && smallestWidth <= w + dx) { + // Adjust the dimension of element + ele.style.width = `${w + dx}px`; + } + if (initialWidth < w + dx) { + ele.style.width = `${initialWidth}px`; + } + if (smallestWidth > w + dx) { + ele.style.width = `${smallestWidth}px`; + } + // e.stopPropagation(); + }; - const mouseUpHandler = function() { - // Remove the handlers of `mousemove` and `mouseup` - document.removeEventListener("mousemove", events[0] as any); - document.removeEventListener("mouseup", mouseUpHandler); - events = []; - }; - const rightResizer: any = ele.querySelectorAll(".resizer-right")[0]; - const leftResizer: any = ele.querySelectorAll(".resizer-left")[0]; - const rightMove = (e: any) => mouseDownHandler(e, true); - const leftMove = (e: any) => mouseDownHandler(e, false); + const mouseUpHandler = function() { + // Remove the handlers of `mousemove` and `mouseup` + document.removeEventListener("mousemove", events[0] as any); + document.removeEventListener("mouseup", mouseUpHandler); + events = []; + }; + const rightResizer: any = ele.querySelectorAll(".resizer-right")[0]; + const leftResizer: any = ele.querySelectorAll(".resizer-left")[0]; + const rightMove = (e: any) => mouseDownHandler(e, true); + const leftMove = (e: any) => mouseDownHandler(e, false); - rightResizer.addEventListener("mousedown", rightMove); - leftResizer.addEventListener("mousedown", leftMove); - return () => { - rightResizer.removeEventListener("mousedown", rightMove); - leftResizer.removeEventListener("mousedown", leftMove); - }; + rightResizer.addEventListener("mousedown", rightMove); + leftResizer.addEventListener("mousedown", leftMove); + return () => { + rightResizer.removeEventListener("mousedown", rightMove); + leftResizer.removeEventListener("mousedown", leftMove); + }; + } } - }, [appLayout, isPreviewMode, currentPageId]); + }, [appLayout, isPreviewMode, currentPageId, appPositioningType]); // calculating exact height to not allow scroll at this component, // calculating total height minus margin on top, top bar and bottom bar @@ -203,7 +203,7 @@ function CanvasContainer() { fontFamily: fontFamily, }} > - {appLayout?.type === "FLUID" && ( + {appPositioningType === AppPositioningTypes.AUTO && ( <> { - return positioning && positioning !== Positioning.Fixed ? "AUTO" : "FIXED"; + return positioning && positioning !== Positioning.Fixed + ? AppPositioningTypes.AUTO + : AppPositioningTypes.FIXED; }, ); @@ -251,7 +253,9 @@ export const getCurrentApplicationLayout = createSelector( getAppLayout, getCurrentAppPositioningType, (appLayout: AppLayoutConfig, appPositionType) => { - return appPositionType === "FIXED" ? appLayout : defaultLayout; + return appPositionType === AppPositioningTypes.FIXED + ? appLayout + : defaultLayout; }, ); diff --git a/app/client/src/selectors/propertyPaneSelectors.tsx b/app/client/src/selectors/propertyPaneSelectors.tsx index 4f2bd0db97d6..059640d216fc 100644 --- a/app/client/src/selectors/propertyPaneSelectors.tsx +++ b/app/client/src/selectors/propertyPaneSelectors.tsx @@ -21,6 +21,7 @@ import { } from "utils/DynamicBindingUtils"; import { generateClassName } from "utils/generators"; import { WidgetProps } from "widgets/BaseWidget"; +import { getCurrentAppPositioningType } from "./editorSelectors"; import { getCanvasWidgets } from "./entitiesSelector"; import { getLastSelectedWidget, getSelectedWidgets } from "./ui"; @@ -70,16 +71,18 @@ const getCurrentWidgetName = createSelector( export const getWidgetPropsForPropertyPane = createSelector( getCurrentWidgetProperties, + getCurrentAppPositioningType, getDataTree, ( widget: WidgetProps | undefined, + appPositioningType, evaluatedTree: DataTree, ): WidgetProps | undefined => { if (!widget) return undefined; const evaluatedWidget = find(evaluatedTree, { widgetId: widget.widgetId, }) as DataTreeWidget; - const widgetProperties = { ...widget }; + const widgetProperties = { ...widget, appPositioningType }; if (evaluatedWidget) { widgetProperties[EVALUATION_PATH] = evaluatedWidget[EVALUATION_PATH]; diff --git a/app/client/src/utils/WidgetFactory.tsx b/app/client/src/utils/WidgetFactory.tsx index 7a6054d9763a..64b41ac02d78 100644 --- a/app/client/src/utils/WidgetFactory.tsx +++ b/app/client/src/utils/WidgetFactory.tsx @@ -2,11 +2,7 @@ import { PropertyPaneConfig } from "constants/PropertyControlConstants"; import React from "react"; import { WidgetBuilder, WidgetProps, WidgetState } from "widgets/BaseWidget"; -import { Positioning } from "components/constants"; -import { - MAIN_CONTAINER_WIDGET_ID, - RenderMode, -} from "constants/WidgetConstants"; +import { RenderMode } from "constants/WidgetConstants"; import { Stylesheet } from "entities/AppTheming"; import * as log from "loglevel"; import { WidgetConfigProps } from "reducers/entityReducers/widgetConfigReducer"; @@ -209,10 +205,6 @@ class WidgetFactory { ...widgetData, renderMode, }; - if (widgetData.widgetId === MAIN_CONTAINER_WIDGET_ID) { - widgetProps.useAutoLayout = true; - widgetProps.positioning = Positioning.Vertical; - } const widgetBuilder = this.widgetMap.get(widgetData.type); if (widgetBuilder) { const widget = widgetBuilder.buildWidget(widgetProps); diff --git a/app/client/src/utils/WidgetFactoryHelpers.ts b/app/client/src/utils/WidgetFactoryHelpers.ts index ef2b91b1897a..79a188cee2f7 100644 --- a/app/client/src/utils/WidgetFactoryHelpers.ts +++ b/app/client/src/utils/WidgetFactoryHelpers.ts @@ -4,9 +4,15 @@ import { PropertyPaneSectionConfig, } from "constants/PropertyControlConstants"; import { ValidationTypes } from "constants/WidgetValidation"; +import log from "loglevel"; import { generateReactKey } from "./generators"; import { WidgetType } from "./WidgetFactory"; -import { WidgetFeatures } from "./WidgetFeatures"; +import { + PropertyPaneConfigTemplates, + RegisteredWidgetFeatures, + WidgetFeaturePropertyPaneEnhancements, + WidgetFeatures, +} from "./WidgetFeatures"; export enum PropertyPaneConfigTypes { STYLE = "STYLE", @@ -160,37 +166,36 @@ export function enhancePropertyPaneConfig( // TODO(abhinav): The following "configType" check should come // from the features themselves. - // ToDO(Ashok): Need to bring back Dynamic Height features based on mode of the editor (Fixed vs Mobile responsiveness) - // if ( - // features && - // (configType === undefined || configType === PropertyPaneConfigTypes.CONTENT) - // ) { - // Object.keys(features).forEach((registeredFeature: string) => { - // const { sectionIndex } = features[ - // registeredFeature as RegisteredWidgetFeatures - // ]; - // const sectionName = (config[sectionIndex] as PropertyPaneSectionConfig) - // ?.sectionName; - // if (!sectionName || sectionName !== "General") { - // log.error(`Invalid section index for feature: ${registeredFeature}`); - // } - // if ( - // Array.isArray(config[sectionIndex].children) && - // PropertyPaneConfigTemplates[ - // registeredFeature as RegisteredWidgetFeatures - // ] - // ) { - // config[sectionIndex].children?.push( - // ...PropertyPaneConfigTemplates[ - // registeredFeature as RegisteredWidgetFeatures - // ], - // ); - // config = WidgetFeaturePropertyPaneEnhancements[ - // registeredFeature as RegisteredWidgetFeatures - // ](config, widgetType); - // } - // }); - // } + if ( + features && + (configType === undefined || configType === PropertyPaneConfigTypes.CONTENT) + ) { + Object.keys(features).forEach((registeredFeature: string) => { + const { sectionIndex } = features[ + registeredFeature as RegisteredWidgetFeatures + ]; + const sectionName = (config[sectionIndex] as PropertyPaneSectionConfig) + ?.sectionName; + if (!sectionName || sectionName !== "General") { + log.error(`Invalid section index for feature: ${registeredFeature}`); + } + if ( + Array.isArray(config[sectionIndex].children) && + PropertyPaneConfigTemplates[ + registeredFeature as RegisteredWidgetFeatures + ] + ) { + config[sectionIndex].children?.push( + ...PropertyPaneConfigTemplates[ + registeredFeature as RegisteredWidgetFeatures + ], + ); + config = WidgetFeaturePropertyPaneEnhancements[ + registeredFeature as RegisteredWidgetFeatures + ](config, widgetType); + } + }); + } return config; } diff --git a/app/client/src/utils/WidgetRegisterHelpers.tsx b/app/client/src/utils/WidgetRegisterHelpers.tsx index 1146e5442fa9..c9cc30e3f7e9 100644 --- a/app/client/src/utils/WidgetRegisterHelpers.tsx +++ b/app/client/src/utils/WidgetRegisterHelpers.tsx @@ -12,6 +12,11 @@ import { WidgetConfiguration } from "widgets/constants"; import withMeta from "widgets/MetaHOC"; import withWidgetProps from "widgets/withWidgetProps"; import { generateReactKey } from "./generators"; +import { + RegisteredWidgetFeatures, + WidgetFeaturePropertyEnhancements, + WidgetFeatureProps, +} from "./WidgetFeatures"; const generateWidget = memoize(function getWidgetComponent( Widget: typeof BaseWidget, @@ -50,19 +55,18 @@ export const registerWidget = (Widget: any, config: WidgetConfiguration) => { }; export const configureWidget = (config: WidgetConfiguration) => { - const features: Record = {}; - // ToDO(Ashok): Need to bring back Dynamic Height features based on mode of the editor (Fixed vs Mobile responsiveness) - // if (config.features) { - // Object.keys(config.features).forEach((registeredFeature: string) => { - // features = Object.assign( - // {}, - // WidgetFeatureProps[registeredFeature as RegisteredWidgetFeatures], - // WidgetFeaturePropertyEnhancements[ - // registeredFeature as RegisteredWidgetFeatures - // ](config), - // ); - // }); - // } + let features: Record = {}; + if (config.features) { + Object.keys(config.features).forEach((registeredFeature: string) => { + features = Object.assign( + {}, + WidgetFeatureProps[registeredFeature as RegisteredWidgetFeatures], + WidgetFeaturePropertyEnhancements[ + registeredFeature as RegisteredWidgetFeatures + ](config), + ); + }); + } const _config = { ...config.defaults, diff --git a/app/client/src/utils/hooks/useDynamicAppLayout.tsx b/app/client/src/utils/hooks/useDynamicAppLayout.tsx index fc087565bdec..ede2d1486199 100644 --- a/app/client/src/utils/hooks/useDynamicAppLayout.tsx +++ b/app/client/src/utils/hooks/useDynamicAppLayout.tsx @@ -11,9 +11,11 @@ import { MAIN_CONTAINER_WIDGET_ID, } from "constants/WidgetConstants"; import { APP_MODE } from "entities/App"; +import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; import { getIsAppSettingsPaneOpen } from "selectors/appSettingsPaneSelectors"; import { getCurrentApplicationLayout, + getCurrentAppPositioningType, getCurrentPageId, getMainCanvasProps, previewModeSelector, @@ -221,19 +223,22 @@ export const useDynamicAppLayout = () => { propertyPaneWidth, isAppSettingsPaneOpen, ]); + const appPositioningType = useSelector(getCurrentAppPositioningType); useEffect(() => { function relayoutAtBreakpoint() { dispatch( updateLayoutForMobileBreakpointAction( MAIN_CONTAINER_WIDGET_ID, - mainCanvasProps?.isMobile, + appPositioningType === AppPositioningTypes.AUTO + ? mainCanvasProps?.isMobile + : false, calculateCanvasWidth(), ), ); } relayoutAtBreakpoint(); - }, [mainCanvasProps?.isMobile]); + }, [mainCanvasProps?.isMobile, appPositioningType]); return isCanvasInitialized; }; diff --git a/app/client/src/utils/layoutPropertiesUtils.ts b/app/client/src/utils/layoutPropertiesUtils.ts index 7435d73918eb..ac3904ed8664 100644 --- a/app/client/src/utils/layoutPropertiesUtils.ts +++ b/app/client/src/utils/layoutPropertiesUtils.ts @@ -271,6 +271,7 @@ export const DefaultFillWidgets = [ "MULTI_SELECT_TREE_WIDGET", "MULTI_SELECT_WIDGET", "MULTI_SELECT_WIDGET_V2", + "MENU_BUTTON_WIDGET", "PHONE_INPUT_WIDGET", "SELECT_WIDGET", "TEXT_WIDGET", @@ -278,6 +279,8 @@ export const DefaultFillWidgets = [ "RICH_TEXT_EDITOR_WIDGET", "TABS_WIDGET", "TABLE_WIDGET_V2", + "PROGRESS_WIDGET", + "SWITCH_WIDGET", ]; export function getDefaultResponsiveBehavior(widgetType: string) { diff --git a/app/client/src/widgets/ProgressBarWidget/index.ts b/app/client/src/widgets/ProgressBarWidget/index.ts index 8d72fa495ef3..48af511b9978 100644 --- a/app/client/src/widgets/ProgressBarWidget/index.ts +++ b/app/client/src/widgets/ProgressBarWidget/index.ts @@ -1,6 +1,7 @@ -import Widget from "./widget"; -import IconSVG from "./icon.svg"; +import { ResponsiveBehavior } from "components/constants"; import { BarType } from "./constants"; +import IconSVG from "./icon.svg"; +import Widget from "./widget"; export const CONFIG = { type: Widget.getWidgetType(), @@ -21,6 +22,7 @@ export const CONFIG = { progress: 50, steps: 1, version: 1, + responsiveBehavior: ResponsiveBehavior.Fill, }, properties: { derived: Widget.getDerivedPropertiesMap(), From 8f7ee0ec2c3976d4b7597e7c4c98d87d0e77b01f Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Fri, 6 Jan 2023 01:17:45 +0530 Subject: [PATCH 359/708] Enable reflow for fixed. --- .../pages/common/CanvasArenas/hooks/useCanvasDragging.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index e63040886bf4..ea3cfabfd954 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -9,12 +9,16 @@ import { debounce, isEmpty, throttle } from "lodash"; import { CanvasDraggingArenaProps } from "pages/common/CanvasArenas/CanvasDraggingArena"; import React, { useEffect, useRef } from "react"; import { useDispatch, useSelector } from "react-redux"; +import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; import { MovementLimitMap, ReflowDirection, ReflowedSpaceMap, } from "reflow/reflowTypes"; -import { getZoomLevel } from "selectors/editorSelectors"; +import { + getCurrentAppPositioningType, + getZoomLevel, +} from "selectors/editorSelectors"; import { getNearestParentCanvas } from "utils/generators"; import { getAbsolutePixels } from "utils/helpers"; import { useWidgetDragResize } from "utils/hooks/dragResizeHooks"; @@ -69,6 +73,7 @@ export const useCanvasDragging = ( const canvasZoomLevel = useSelector(getZoomLevel); const currentDirection = useRef(ReflowDirection.UNSET); const { devicePixelRatio: scale = 1 } = window; + const appPositioningType = useSelector(getCurrentAppPositioningType); const { blocksToDraw, defaultHandlePositions, @@ -499,7 +504,7 @@ export const useCanvasDragging = ( const canReflow = !currentRectanglesToDraw[0].detachFromLayout && !dropDisabled && - false; + appPositioningType === AppPositioningTypes.FIXED; const currentBlock = currentRectanglesToDraw[0]; const [leftColumn, topRow] = getDropZoneOffsets( snapColumnSpace, From b0d191067e3fabeee80133a58c049138eb117fb4 Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 5 Jan 2023 16:24:05 -0500 Subject: [PATCH 360/708] fix mobile highlight positioning and code clean up --- .../src/ce/constants/ReduxActionConstants.tsx | 2 -- .../CanvasArenas/hooks/useCanvasDragging.ts | 7 +---- .../reducers/uiReducers/dragResizeReducer.ts | 14 ---------- app/client/src/sagas/WidgetOperationSagas.tsx | 1 - .../src/utils/autoLayout/flexWidgetUtils.ts | 2 +- .../src/utils/autoLayout/highlightUtils.ts | 28 +++++++++++++++---- .../src/utils/autoLayout/positionUtils.ts | 4 +-- 7 files changed, 27 insertions(+), 31 deletions(-) diff --git a/app/client/src/ce/constants/ReduxActionConstants.tsx b/app/client/src/ce/constants/ReduxActionConstants.tsx index 0e588d9e9498..7e86fb638f75 100644 --- a/app/client/src/ce/constants/ReduxActionConstants.tsx +++ b/app/client/src/ce/constants/ReduxActionConstants.tsx @@ -757,8 +757,6 @@ export const ReduxActionTypes = { "SET_DATASOURCE_DEFAULT_KEY_VALUE_PAIR_SET", RESET_DATASOURCE_DEFAULT_KEY_VALUE_PAIR_SET: "RESET_DATASOURCE_DEFAULT_KEY_VALUE_PAIR_SET", - SELECT_AUTOLAYOUT_HIGHLIGHT: "SELECT_AUTOLAYOUT_HIGHLIGHT", - CLEAR_HIGHLIGHT_SELECTION: "CLEAR_HIGHLIGHT_SELECTION", AUTOLAYOUT_REORDER_WIDGETS: "AUTOLAYOUT_REORDER_WIDGETS", AUTOLAYOUT_ADD_NEW_WIDGETS: "AUTOLAYOUT_ADD_NEW_WIDGETS", REMOVE_CHILD_WRAPPERS: "REMOVE_CHILD_WRAPPERS", diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index e63040886bf4..f5c0a31a3a1b 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -1,4 +1,3 @@ -import { ReduxActionTypes } from "ce/constants/ReduxActionConstants"; import { OccupiedSpace } from "constants/CanvasEditorConstants"; import { CONTAINER_GRID_PADDING, @@ -8,7 +7,7 @@ import { import { debounce, isEmpty, throttle } from "lodash"; import { CanvasDraggingArenaProps } from "pages/common/CanvasArenas/CanvasDraggingArena"; import React, { useEffect, useRef } from "react"; -import { useDispatch, useSelector } from "react-redux"; +import { useSelector } from "react-redux"; import { MovementLimitMap, ReflowDirection, @@ -127,7 +126,6 @@ export const useCanvasDragging = ( isDragging, useAutoLayout, }); - const dispatch = useDispatch(); setTimeout(() => { calculateHighlights(); @@ -358,9 +356,6 @@ export const useCanvasDragging = ( }); } setDraggingCanvas(); - dispatch({ - type: ReduxActionTypes.CLEAR_HIGHLIGHT_SELECTION, - }); } }, 0); }; diff --git a/app/client/src/reducers/uiReducers/dragResizeReducer.ts b/app/client/src/reducers/uiReducers/dragResizeReducer.ts index e5e355f03a8d..3c79af526ae3 100644 --- a/app/client/src/reducers/uiReducers/dragResizeReducer.ts +++ b/app/client/src/reducers/uiReducers/dragResizeReducer.ts @@ -148,20 +148,6 @@ export const widgetDraggingReducer = createImmerReducer(initialState, { ) => { state.selectedWidgetAncestry = action.payload; }, - [ReduxActionTypes.SELECT_AUTOLAYOUT_HIGHLIGHT]: ( - state: WidgetDragResizeState, - action: ReduxAction<{ flexHighlight: HighlightInfo; blocksToDraw: any }>, - ) => { - state.flexHighlight = action.payload.flexHighlight; - state.autoLayoutDragDetails = action.payload.blocksToDraw; - }, - [ReduxActionTypes.CLEAR_HIGHLIGHT_SELECTION]: ( - state: WidgetDragResizeState, - // action: ReduxAction, - ) => { - state.flexHighlight = undefined; - state.autoLayoutDragDetails = undefined; - }, }); type DraggingGroupCenter = { diff --git a/app/client/src/sagas/WidgetOperationSagas.tsx b/app/client/src/sagas/WidgetOperationSagas.tsx index 978b2b872c2a..0c135f298ff7 100644 --- a/app/client/src/sagas/WidgetOperationSagas.tsx +++ b/app/client/src/sagas/WidgetOperationSagas.tsx @@ -82,7 +82,6 @@ import { validateProperty } from "./EvaluationsSaga"; import { generateAutoHeightLayoutTreeAction } from "actions/autoHeightActions"; import { stopReflowAction } from "actions/reflowActions"; import { updateMultipleWidgetProperties } from "actions/widgetActions"; -import { Positioning } from "components/constants"; import { WidgetSpace } from "constants/CanvasEditorConstants"; import { DataTree } from "entities/DataTree/dataTreeFactory"; import { MetaState } from "reducers/entityReducers/metaReducer"; diff --git a/app/client/src/utils/autoLayout/flexWidgetUtils.ts b/app/client/src/utils/autoLayout/flexWidgetUtils.ts index 7f0bf3b7d103..27e246d1b4bc 100644 --- a/app/client/src/utils/autoLayout/flexWidgetUtils.ts +++ b/app/client/src/utils/autoLayout/flexWidgetUtils.ts @@ -94,7 +94,7 @@ export function setDimensions( isMobile, ); } catch (e) { - console.log(e); + // console.log(e); return widget; } } diff --git a/app/client/src/utils/autoLayout/highlightUtils.ts b/app/client/src/utils/autoLayout/highlightUtils.ts index aa9096a12822..7830c9bc2aa3 100644 --- a/app/client/src/utils/autoLayout/highlightUtils.ts +++ b/app/client/src/utils/autoLayout/highlightUtils.ts @@ -21,6 +21,7 @@ import { } from "./flexWidgetUtils"; import { AlignmentInfo, + getAlignmentSizeInfo, getTotalRowsOfAllChildren, getWrappedAlignmentInfo, Widget, @@ -228,7 +229,20 @@ function generateVerticalHighlights(data: { each.reduce((a, b) => a + b.columns, 0) === 0 ) continue; + let index = 0; for (const item of each) { + let avoidInitialHighlight = false; + let startPosition: number | undefined; + if (item.alignment === FlexLayerAlignment.Center) { + const { centerSize } = getAlignmentSizeInfo(each); + avoidInitialHighlight = + startColumns > 25 || endColumns > 25 || centerSize === 0; + if (each.length === 2) + startPosition = + index === 0 + ? centerSize / 2 + : GridDefaults.DEFAULT_GRID_COLUMNS - centerSize / 2; + } highlights.push( ...generateHighlightsForAlignment({ arr: item.children, @@ -248,12 +262,11 @@ function generateVerticalHighlights(data: { canvasWidth, columnSpace, isMobile, - avoidInitialHighlight: - item.alignment === FlexLayerAlignment.Center && !isMobile - ? startColumns > 25 || endColumns > 25 - : false, + avoidInitialHighlight, + startPosition, }), ); + index += 1; } } return { highlights, childCount: count }; @@ -280,6 +293,7 @@ function generateHighlightsForAlignment(data: { columnSpace: number; avoidInitialHighlight?: boolean; isMobile: boolean; + startPosition: number | undefined; }): HighlightInfo[] { const { alignment, @@ -294,6 +308,7 @@ function generateHighlightsForAlignment(data: { maxHeight, offsetTop, parentColumnSpace, + startPosition, } = data; const res: HighlightInfo[] = []; let count = 0; @@ -336,6 +351,7 @@ function generateHighlightsForAlignment(data: { canvasWidth, canvasId, columnSpace, + startPosition, ), posY: lastChild === null @@ -370,13 +386,15 @@ function getPositionForInitialHighlight( containerWidth: number, canvasId: string, columnSpace: number, + startPosition: number | undefined, ): number { const endPosition = 64 * columnSpace - (canvasId !== MAIN_CONTAINER_WIDGET_ID ? 4 : 0); if (alignment === FlexLayerAlignment.End) { return endPosition; } else if (alignment === FlexLayerAlignment.Center) { - if (!highlights.length) return containerWidth / 2; + if (!highlights.length) + return startPosition !== undefined ? startPosition : containerWidth / 2; return Math.min(posX, endPosition); } else { if (!highlights.length) return 2; diff --git a/app/client/src/utils/autoLayout/positionUtils.ts b/app/client/src/utils/autoLayout/positionUtils.ts index 58b21d88b01b..1b42ac0f55b7 100644 --- a/app/client/src/utils/autoLayout/positionUtils.ts +++ b/app/client/src/utils/autoLayout/positionUtils.ts @@ -61,7 +61,7 @@ export function updateWidgetPositions( } else if (parent.children?.length) { // calculate the total height required by all widgets. height = getHeightOfFixedCanvas(widgets, parent, isMobile); - } + } else return widgets; const divisor = parent.parentRowSpace === 1 ? 10 : 1; const parentHeight = getWidgetRows(parent, isMobile); @@ -331,7 +331,7 @@ function extractAlignmentInfo( }; } -function getAlignmentSizeInfo( +export function getAlignmentSizeInfo( arr: AlignmentInfo[], ): { startSize: number; centerSize: number; endSize: number } { let startSize = 0, From 3e05834f8edaf016cd99ffaef4fba14ac2ad238c Mon Sep 17 00:00:00 2001 From: Arsalan Date: Fri, 6 Jan 2023 14:26:00 +0530 Subject: [PATCH 361/708] feat: added container query polyfill. --- app/client/package.json | 1 + app/client/src/Globals.d.ts | 1 + app/client/src/index.tsx | 7 +++++++ app/client/yarn.lock | 5 +++++ 4 files changed, 14 insertions(+) create mode 100644 app/client/src/Globals.d.ts diff --git a/app/client/package.json b/app/client/package.json index 8f4cae2d27c3..12c63aebf314 100644 --- a/app/client/package.json +++ b/app/client/package.json @@ -145,6 +145,7 @@ "remixicon-react": "^1.0.0", "reselect": "^4.0.0", "scroll-into-view-if-needed": "^2.2.26", + "shadow-container-query-polyfill": "^1.1.7", "shallowequal": "^1.1.0", "showdown": "^1.9.1", "smartlook-client": "^8.0.0", diff --git a/app/client/src/Globals.d.ts b/app/client/src/Globals.d.ts new file mode 100644 index 000000000000..1eabbb4297e5 --- /dev/null +++ b/app/client/src/Globals.d.ts @@ -0,0 +1 @@ +declare module "*.module.css"; diff --git a/app/client/src/index.tsx b/app/client/src/index.tsx index 0d88f36e57a2..4ad7f73298b0 100755 --- a/app/client/src/index.tsx +++ b/app/client/src/index.tsx @@ -23,6 +23,13 @@ import AppErrorBoundary from "AppErrorBoundry"; const shouldAutoFreeze = process.env.NODE_ENV === "development"; setAutoFreeze(shouldAutoFreeze); +const supportsContainerQueries = "container" in document.documentElement.style; + +if (!supportsContainerQueries) { + // @ts-expect-error: polyfill type declarations not found + import("shadow-container-query-polyfill"); +} + runSagaMiddleware(); appInitializer(); diff --git a/app/client/yarn.lock b/app/client/yarn.lock index 2a22a7463be2..6dd9f11fe7ce 100644 --- a/app/client/yarn.lock +++ b/app/client/yarn.lock @@ -13755,6 +13755,11 @@ setprototypeof@1.2.0: resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== +shadow-container-query-polyfill@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/shadow-container-query-polyfill/-/shadow-container-query-polyfill-1.1.7.tgz#3c75473e27f3a6acdc3b9200da54194a414a0d4d" + integrity sha512-XflRiqZvcTpXRTg60Tvq5Z4qCGgEmAHLNbSd/HJoS7HaGxpglJVntrwZ4L3IdS0ZsS2DzuMUP/6Q3E9ovDP+iA== + shallow-clone@^3.0.0: version "3.0.1" resolved "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz" From b8f477c82846f98f94f6e7d3eab8432222d5412c Mon Sep 17 00:00:00 2001 From: Preet Date: Fri, 6 Jan 2023 12:13:17 -0500 Subject: [PATCH 362/708] refactor flexbox and autoLayoutLayer --- .../appsmith/autoLayout/AutoLayoutLayer.tsx | 9 +---- .../appsmith/autoLayout/FlexBoxComponent.tsx | 40 +++---------------- .../editorComponents/DropTargetComponent.tsx | 16 +++++--- app/client/src/utils/WidgetPropsUtils.tsx | 8 ++-- app/client/src/widgets/CanvasWidget.tsx | 2 + 5 files changed, 24 insertions(+), 51 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx index 4ca922787adf..c3471eb2aeda 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx @@ -18,7 +18,6 @@ export interface AutoLayoutLayerProps { index: number; widgetId: string; isMobile?: boolean; - isCurrentCanvasDragging: boolean; wrapStart: boolean; wrapCenter: boolean; wrapEnd: boolean; @@ -27,7 +26,6 @@ export interface AutoLayoutLayerProps { const LayoutLayerContainer = styled.div<{ flexDirection: FlexDirection; - isCurrentCanvasDragging: boolean; wrap?: boolean; }>` display: flex; @@ -40,7 +38,6 @@ const LayoutLayerContainer = styled.div<{ `; const SubWrapper = styled.div<{ - isCurrentCanvasDragging: boolean; flexDirection: FlexDirection; wrap?: boolean; }>` @@ -83,26 +80,22 @@ function AutoLayoutLayer(props: AutoLayoutLayerProps) { {props.start} {props.center} {props.end} @@ -111,4 +104,4 @@ function AutoLayoutLayer(props: AutoLayoutLayerProps) { ); } -export default AutoLayoutLayer; +export default React.memo(AutoLayoutLayer); diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx index ef33376dd1f7..4b57a9140db6 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx @@ -2,7 +2,6 @@ import { isArray } from "lodash"; import React, { ReactNode } from "react"; import styled from "styled-components"; -import { AppState } from "ce/reducers"; import { FlexLayerAlignment, LayoutDirection, @@ -12,9 +11,9 @@ import { APP_MODE } from "entities/App"; import { useSelector } from "react-redux"; import { getWidgets } from "sagas/selectors"; import { getAppMode } from "selectors/entitiesSelector"; -import { getIsMobile } from "selectors/mainCanvasSelectors"; import AutoLayoutLayer from "./AutoLayoutLayer"; import { FLEXBOX_PADDING } from "constants/WidgetConstants"; +import { getWidgetWidth } from "utils/autoLayout/flexWidgetUtils"; export interface FlexBoxProps { direction?: LayoutDirection; @@ -24,6 +23,7 @@ export interface FlexBoxProps { widgetId: string; overflow: Overflow; flexLayers: FlexLayer[]; + isMobile?: boolean; } export interface LayerChild { @@ -45,8 +45,6 @@ export const FlexContainer = styled.div<{ overflow: Overflow; leaveSpaceForWidgetName: boolean; isMobile?: boolean; - isMainContainer: boolean; - isDragging: boolean; }>` display: ${({ useAutoLayout }) => (useAutoLayout ? "flex" : "block")}; flex-direction: ${({ direction }) => @@ -60,8 +58,6 @@ export const FlexContainer = styled.div<{ height: ${({ stretchHeight }) => (stretchHeight ? "100%" : "auto")}; overflow: hidden; - overflow-y: ${({ isMainContainer, isMobile }) => - isMainContainer || isMobile ? "auto" : "hidden"}; padding: ${({ leaveSpaceForWidgetName }) => leaveSpaceForWidgetName @@ -70,20 +66,12 @@ export const FlexContainer = styled.div<{ `; function FlexBoxComponent(props: FlexBoxProps) { - // TODO: set isMobile as a prop at the top level - const isMobile = useSelector(getIsMobile); const allWidgets = useSelector(getWidgets); const direction: LayoutDirection = props.direction || LayoutDirection.Horizontal; const appMode = useSelector(getAppMode); const leaveSpaceForWidgetName = appMode === APP_MODE.EDIT; - // TODO: Add support for multiple dragged widgets - const { dragDetails } = useSelector( - (state: AppState) => state.ui.widgetDragResize, - ); - - // const isDragging = useSelector(isCurrentCanvasDragging(props.widgetId)); - const isDragging: boolean = dragDetails?.draggedOn !== undefined; + const isMobile: boolean = props.isMobile || false; const renderChildren = () => { if (!props.children) return null; @@ -102,22 +90,11 @@ function FlexBoxComponent(props: FlexBoxProps) { } } - const layers: any[] = cleanLayers(processLayers(map)); + const layers: any[] = processLayers(map); return layers; }; - function cleanLayers(layers: any[]): any[] { - if (!layers) return []; - const set = new Set(); - return layers.filter((layer) => { - const key = (layer as JSX.Element).key as string; - const flag = set.has(key); - set.add(key); - return !flag; - }); - } - function processLayers(map: { [key: string]: any }) { const layers = []; let index = 0; @@ -131,11 +108,7 @@ function FlexBoxComponent(props: FlexBoxProps) { function getColumns(id: string, isMobile: boolean): number { const widget = allWidgets[id]; if (!widget) return 0; - return isMobile && - widget.mobileRightColumn !== undefined && - widget.mobileLeftColumn !== undefined - ? widget.mobileRightColumn - widget.mobileLeftColumn - : widget.rightColumn - widget.leftColumn; + return getWidgetWidth(widget, isMobile); } function processIndividualLayer( @@ -174,7 +147,6 @@ function FlexBoxComponent(props: FlexBoxProps) { end={end} hasFillChild={layer.hasFillChild} index={index} - isCurrentCanvasDragging={isDragging} isMobile={isMobile} key={index} start={start} @@ -191,8 +163,6 @@ function FlexBoxComponent(props: FlexBoxProps) { { @@ -248,9 +257,6 @@ export function DropTargetComponent(props: DropTargetComponentProps) { }; }, [updateDropTargetRows, occupiedSpacesByChildren]); - const wrapperClass = props.isWrapper - ? `auto-layout-parent-${props.parentId} auto-layout-child-${props.widgetId}` - : ""; /** EO PREPARE CONTEXT */ const height = getDropTargetHeight( @@ -289,7 +295,7 @@ export function DropTargetComponent(props: DropTargetComponentProps) { return ( { - const totalRows = Math.floor( - bottomRow / GridDefaults.DEFAULT_GRID_ROW_HEIGHT, - ); + const bottom = + isMobile && mobileBottomRow !== undefined ? mobileBottomRow : bottomRow; + const totalRows = Math.floor(bottom / GridDefaults.DEFAULT_GRID_ROW_HEIGHT); // Canvas Widgets do not need to accommodate for widget and container padding. // Only when they're extensible diff --git a/app/client/src/widgets/CanvasWidget.tsx b/app/client/src/widgets/CanvasWidget.tsx index 9305859f31c8..8aedc9d1e376 100644 --- a/app/client/src/widgets/CanvasWidget.tsx +++ b/app/client/src/widgets/CanvasWidget.tsx @@ -49,6 +49,7 @@ class CanvasWidget extends ContainerWidget { @@ -121,6 +122,7 @@ class CanvasWidget extends ContainerWidget { Date: Fri, 6 Jan 2023 13:39:12 -0500 Subject: [PATCH 363/708] pass ismobile as a prop --- .../appsmith/autoLayout/FlexComponent.tsx | 15 ++------------- .../editorComponents/ResizableComponent.tsx | 4 +--- app/client/src/widgets/BaseWidget.tsx | 2 ++ 3 files changed, 5 insertions(+), 16 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx index ba3c65152f05..8f0a48ce0188 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx @@ -13,7 +13,6 @@ import { } from "constants/WidgetConstants"; import { useSelector } from "react-redux"; import { snipingModeSelector } from "selectors/editorSelectors"; -import { getIsMobile } from "selectors/mainCanvasSelectors"; import { useClickToSelectWidget } from "utils/hooks/useClickToSelectWidget"; import { usePositionedContainerZIndex } from "utils/hooks/usePositionedContainerZIndex"; import { checkIsDropTarget } from "../PositionedContainer"; @@ -32,6 +31,7 @@ export type AutoLayoutProps = { widgetType: WidgetType; parentColumnSpace: number; flexVerticalAlignment: FlexVerticalAlignment; + isMobile?: boolean; }; const FlexWidget = styled.div` @@ -39,7 +39,6 @@ const FlexWidget = styled.div` `; export function FlexComponent(props: AutoLayoutProps) { - const isMobile = useSelector(getIsMobile); const isSnipingMode = useSelector(snipingModeSelector); const clickToSelectWidget = useClickToSelectWidget(props.widgetId); @@ -62,14 +61,6 @@ export function FlexComponent(props: AutoLayoutProps) { !isSnipingMode && e.stopPropagation(); }; - /** - * In a vertical stack, - * Fill widgets grow / shrink to take up all the available space. - * => width: auto && flex-grow: 1; - */ - const isFillWidget: boolean = - props.direction === LayoutDirection.Vertical && - props.responsiveBehavior === ResponsiveBehavior.Fill; const className = `auto-layout-parent-${props.parentId} auto-layout-child-${ props.widgetId } ${widgetTypeClassname(props.widgetType)}`; @@ -82,15 +73,13 @@ export function FlexComponent(props: AutoLayoutProps) { height: props.componentHeight - WIDGET_PADDING * 2 + "px", minHeight: "30px", margin: WIDGET_PADDING + "px", - flexGrow: isFillWidget ? 1 : 0, alignSelf: props.flexVerticalAlignment, "&:hover": { zIndex: onHoverZIndex + " !important", }, }; }, [ - isFillWidget, - isMobile, + props.isMobile, props.componentWidth, props.componentHeight, props.flexVerticalAlignment, diff --git a/app/client/src/components/editorComponents/ResizableComponent.tsx b/app/client/src/components/editorComponents/ResizableComponent.tsx index ac388f825f36..92dc857478be 100644 --- a/app/client/src/components/editorComponents/ResizableComponent.tsx +++ b/app/client/src/components/editorComponents/ResizableComponent.tsx @@ -13,7 +13,6 @@ import { previewModeSelector, snipingModeSelector, } from "selectors/editorSelectors"; -import { getIsMobile } from "selectors/mainCanvasSelectors"; import { getParentToOpenSelector, isCurrentWidgetFocused, @@ -96,7 +95,6 @@ export const ResizableComponent = memo(function ResizableComponent( ); const isWidgetFocused = isFocused || isLastSelected || isSelected; - const isMobile = useSelector(getIsMobile); // Calculate the dimensions of the widget, // The ResizableContainer's size prop is controlled const dimensions: UIElementSize = { @@ -343,7 +341,7 @@ export const ResizableComponent = memo(function ResizableComponent( handles={handles} isAffectedByDrag={isAffectedByDrag} isFlexChild={props.isFlexChild} - isMobile={isMobile} + isMobile={props.isMobile || false} onStart={handleResizeStart} onStop={updateSize} originalPositions={originalPositions} diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index 7a4fb3103e68..fca8b8e9e067 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -461,6 +461,7 @@ abstract class BaseWidget< this.props.flexVerticalAlignment || FlexVerticalAlignment.Top } focused={this.props.focused} + isMobile={this.props.isMobile} parentColumnSpace={this.props.parentColumnSpace} parentId={this.props.parentId} responsiveBehavior={this.props.responsiveBehavior} @@ -584,6 +585,7 @@ abstract class BaseWidget< resizeDisabled: false, disablePropertyPane: false, isFlexChild: false, + isMobile: false, }; } From 16e6d213e21327af71e8b02c1ef5bdbc90f91937 Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 9 Jan 2023 13:41:43 -0500 Subject: [PATCH 364/708] remove unused code from utils --- .../src/sagas/AutoLayoutUpdateSagas.tsx | 2 + app/client/src/sagas/AutoLayoutUtils.ts | 124 ++---------------- .../CanvasSagas/AutoLayoutDraggingSagas.ts | 14 +- 3 files changed, 16 insertions(+), 124 deletions(-) diff --git a/app/client/src/sagas/AutoLayoutUpdateSagas.tsx b/app/client/src/sagas/AutoLayoutUpdateSagas.tsx index ac7ec0d0abcd..13f24b0bd0f9 100644 --- a/app/client/src/sagas/AutoLayoutUpdateSagas.tsx +++ b/app/client/src/sagas/AutoLayoutUpdateSagas.tsx @@ -79,10 +79,12 @@ export function* updateFillChildInfo( const start = performance.now(); const { responsiveBehavior, widgetId } = actionPayload.payload; const allWidgets: CanvasWidgetsReduxState = yield select(getWidgets); + const isMobile: boolean = yield select(getIsMobile); const updatedWidgets: CanvasWidgetsReduxState = updateFillChildStatus( allWidgets, widgetId, responsiveBehavior === ResponsiveBehavior.Fill, + isMobile, ); yield put(updateAndSaveLayout(updatedWidgets)); log.debug("updating fill child info took", performance.now() - start, "ms"); diff --git a/app/client/src/sagas/AutoLayoutUtils.ts b/app/client/src/sagas/AutoLayoutUtils.ts index d4caa0c20994..0ec69daba507 100644 --- a/app/client/src/sagas/AutoLayoutUtils.ts +++ b/app/client/src/sagas/AutoLayoutUtils.ts @@ -124,130 +124,32 @@ export function* updateFlexLayersOnDelete( if (layerIndex === -1) return widgets; return updateWidgetPositions(widgets, parentId, isMobile); } -// TODO: refactor these implementations + export function updateFillChildStatus( allWidgets: CanvasWidgetsReduxState, widgetId: string, fill: boolean, + isMobile: boolean, ) { - const widgets = { ...allWidgets }; + let widgets = { ...allWidgets }; const widget = widgets[widgetId]; if (!widget || !widget.parentId) return widgets; - let canvas = getCanvas(widgets, widget.parentId); + const canvas = getCanvas(widgets, widget.parentId); if (!canvas) return widgets; const flexLayers: FlexLayer[] = canvas.flexLayers || []; - let layerIndex = -1; if (!flexLayers.length) return widgets; - - const updatedLayers = flexLayers?.map((layer, index: number) => { - const children = layer.children || []; - const selectedWidgetIndex: number = children.findIndex( - (each: LayerChild) => each.id === widgetId, - ); - if (selectedWidgetIndex === -1) return layer; - layerIndex = index; - return { - ...layer, - hasFillChild: children.reduce((acc, each, index) => { - const widget = widgets[each.id]; - if (index === selectedWidgetIndex) return acc || fill; - return acc || widget?.responsiveBehavior === ResponsiveBehavior.Fill; - }, false), - }; - }); - - canvas = { - ...canvas, - flexLayers: updatedLayers, - }; - widgets[canvas.widgetId] = canvas; - - if (layerIndex === -1) return widgets; - return updateFlexChildColumns(widgets, layerIndex, canvas.widgetId); -} - -export function updateFlexChildColumns( - allWidgets: CanvasWidgetsReduxState, - layerIndex: number, - parentId: string, -): CanvasWidgetsReduxState { - const widgets = Object.assign({}, allWidgets); - const canvas = widgets[parentId]; - const children = canvas.children; - if (!children || !children.length) return widgets; - - const layer = canvas.flexLayers[layerIndex]; - if (!layer || !layer?.children?.length || !layer.hasFillChild) return widgets; - - const fillChildren: any[] = []; - const hugChildrenColumns = layer?.children?.reduce( - (acc: number, child: LayerChild) => { - const widget = widgets[child.id]; - if (widget.responsiveBehavior === ResponsiveBehavior.Fill) { - fillChildren.push(widget); - return acc; - } - return ( - acc + - (widget.columns - ? widget.columns - : widget.rightColumn - widget.leftColumn) - ); - }, - 0, - ); - if (!fillChildren.length) return widgets; - - const columnsPerFillChild = (64 - hugChildrenColumns) / fillChildren.length; - - for (const child of fillChildren) { - widgets[child.widgetId] = { - ...child, - rightColumn: child.leftColumn + columnsPerFillChild, - }; - } - return widgets; -} - -export function updateChildrenSize( - allWidgets: CanvasWidgetsReduxState, - parentId: string, - widgetId: string, -): CanvasWidgetsReduxState { - const widgets = Object.assign({}, allWidgets); - const parent = widgets[parentId]; - if (!parent || !parent?.flexLayers || !parent?.flexLayers?.length) - return widgets; - - const layerIndex = parent.flexLayers.reduce( - (acc: number, layer: FlexLayer, index: number) => { - if (layer.children.some((child: LayerChild) => child.id === widgetId)) { - return index; - } - return acc; + widgets = { + ...widgets, + [widgetId]: { + ...widget, + ResponsiveBehavior: fill + ? ResponsiveBehavior.Fill + : ResponsiveBehavior.Hug, }, - -1, - ); - - return updateFlexChildColumns(widgets, layerIndex, parentId); -} - -export function updateSizeOfAllChildren( - allWidgets: CanvasWidgetsReduxState, - parentId: string, -): CanvasWidgetsReduxState { - let widgets = Object.assign({}, allWidgets); - const parent = widgets[parentId]; - - if (!parent || !parent?.flexLayers || !parent?.flexLayers?.length) - return widgets; - - for (let i = 0; i < parent.flexLayers.length; i++) { - widgets = updateFlexChildColumns(widgets, i, parentId); - } + }; - return widgets; + return updateWidgetPositions(widgets, canvas.widgetId, isMobile); } export function alterLayoutForMobile( diff --git a/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts b/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts index 8f1924dac82b..3bd52fde18bf 100644 --- a/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts +++ b/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts @@ -18,7 +18,6 @@ import log from "loglevel"; import { HighlightInfo } from "pages/common/CanvasArenas/hooks/useAutoLayoutHighlights"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; import { all, call, put, select, takeLatest } from "redux-saga/effects"; -import { updateFlexChildColumns } from "sagas/AutoLayoutUtils"; import { getWidgets } from "sagas/selectors"; import { getUpdateDslAfterCreatingChild } from "sagas/WidgetAdditionSagas"; import { getIsMobile } from "selectors/mainCanvasSelectors"; @@ -60,19 +59,8 @@ function* addWidgetAndReorderSaga( isMobile, }, ); - let updatedWidgetsAfterResizing = updatedWidgetsOnMove; - if ( - !isNewLayer && - direction === LayoutDirection.Vertical && - layerIndex !== undefined - ) - updatedWidgetsAfterResizing = updateFlexChildColumns( - updatedWidgetsOnMove, - layerIndex, - parentId, - ); - yield put(updateAndSaveLayout(updatedWidgetsAfterResizing)); + yield put(updateAndSaveLayout(updatedWidgetsOnMove)); log.debug("reorder computations took", performance.now() - start, "ms"); } catch (e) { // console.error(e); From a954fd69697418c54ed611cc537d3f5dd6b82929 Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 9 Jan 2023 20:51:17 -0500 Subject: [PATCH 365/708] merge fixes --- .../hooks/canvasDraggingUtils.test.ts | 9 ++++++++ .../CanvasArenas/hooks/canvasDraggingUtils.ts | 19 ++++++++++++----- .../CanvasArenas/hooks/useCanvasDragging.ts | 21 +++++++++++-------- app/client/src/selectors/editorSelectors.tsx | 1 + .../src/selectors/propertyPaneSelectors.tsx | 20 ++++++++++++++---- 5 files changed, 52 insertions(+), 18 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/canvasDraggingUtils.test.ts b/app/client/src/pages/common/CanvasArenas/hooks/canvasDraggingUtils.test.ts index 589ed20aae40..725c1430a50e 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/canvasDraggingUtils.test.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/canvasDraggingUtils.test.ts @@ -78,6 +78,7 @@ describe("test canvasDraggingUtils Methods", () => { rowHeight: 90, widgetId: "id", isNotColliding: true, + type: "CANVAS_WIDGET", }; const spaceMap = { id: { @@ -174,6 +175,7 @@ describe("test canvasDraggingUtils Methods", () => { rowHeight: 90, widgetId: "id", isNotColliding: true, + type: "CANVAS_WIDGET", }; const modifiedBlock = { left: 0, @@ -200,6 +202,7 @@ describe("test canvasDraggingUtils Methods", () => { rowHeight: 72, widgetId: "id", isNotColliding: true, + type: "CANVAS_WIDGET", }; const modifiedBlock = { left: -10, @@ -226,6 +229,7 @@ describe("test canvasDraggingUtils Methods", () => { rowHeight: 90, widgetId: "id", isNotColliding: true, + type: "CANVAS_WIDGET", }; const modifiedBlock = { left: 400, @@ -253,6 +257,7 @@ describe("test canvasDraggingUtils Methods", () => { widgetId: "id", isNotColliding: true, fixedHeight: 90, + type: "CANVAS_WIDGET", }; const modifiedBlock = { left: 630, @@ -283,6 +288,7 @@ describe("test canvasDraggingUtils Methods", () => { rowHeight: 90, widgetId: "1", isNotColliding: true, + type: "CANVAS_WIDGET", }, { left: 100, @@ -293,6 +299,7 @@ describe("test canvasDraggingUtils Methods", () => { rowHeight: 95, widgetId: "2", isNotColliding: true, + type: "CANVAS_WIDGET", }, { left: 300, @@ -303,6 +310,7 @@ describe("test canvasDraggingUtils Methods", () => { rowHeight: 34, widgetId: "3", isNotColliding: true, + type: "CANVAS_WIDGET", }, { left: 400, @@ -313,6 +321,7 @@ describe("test canvasDraggingUtils Methods", () => { rowHeight: 12, widgetId: "4", isNotColliding: true, + type: "CANVAS_WIDGET", }, ]; diff --git a/app/client/src/pages/common/CanvasArenas/hooks/canvasDraggingUtils.ts b/app/client/src/pages/common/CanvasArenas/hooks/canvasDraggingUtils.ts index 7ec0ad81c632..0f26c1148f2a 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/canvasDraggingUtils.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/canvasDraggingUtils.ts @@ -317,19 +317,28 @@ export const updateRectanglesPostReflow = ( }; export function getInterpolatedMoveDirection( - deltas: { deltaX: number; deltaY: number }[], + spaces: OccupiedSpace[], currentPosition: OccupiedSpace, direction: ReflowDirection, ): ReflowDirection { - const lastPosition = deltas.reduce( + const accumulatedPositions = spaces.reduce( (acc, curr) => { return { ...acc, - top: acc.top + curr.deltaY, - right: acc.right + curr.deltaX, + top: acc.top + curr.top, + right: acc.right + curr.right, + bottom: acc.bottom + curr.bottom, + left: acc.left + curr.left, }; }, - { ...currentPosition, top: 0, right: 0 }, + { top: 0, right: 0, bottom: 0, left: 0, id: currentPosition.id }, ); + const lastPosition = { + ...accumulatedPositions, + top: accumulatedPositions.top / spaces.length, + right: accumulatedPositions.right / spaces.length, + bottom: accumulatedPositions.bottom / spaces.length, + left: accumulatedPositions.left / spaces.length, + }; return getMoveDirection(lastPosition, currentPosition, direction); } diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index 4720718c579c..1329273c4d1d 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -13,7 +13,10 @@ import { ReflowedSpaceMap, SpaceMap, } from "reflow/reflowTypes"; -import { getCanvasScale } from "selectors/editorSelectors"; +import { + getCanvasScale, + getCurrentAppPositioningType, +} from "selectors/editorSelectors"; import { getNearestParentCanvas } from "utils/generators"; import { useWidgetDragResize } from "utils/hooks/dragResizeHooks"; import { ReflowInterface, useReflow } from "utils/hooks/useReflow"; @@ -42,6 +45,7 @@ import { HighlightInfo, useAutoLayoutHighlights, } from "./useAutoLayoutHighlights"; +import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; export const useCanvasDragging = ( slidingArenaRef: React.RefObject, @@ -63,11 +67,11 @@ export const useCanvasDragging = ( const currentDirection = useRef(ReflowDirection.UNSET); let { devicePixelRatio: scale = 1 } = window; scale *= canvasScale; + const appPositioningType = useSelector(getCurrentAppPositioningType); const { blocksToDraw, defaultHandlePositions, draggingSpaces, - getSnappedXY, isChildOfCanvas, isCurrentDraggedCanvas, @@ -193,7 +197,7 @@ export const useCanvasDragging = ( bottom: 0, id: "", }; - let lastSnappedPositionDeltas: { deltaX: number; deltaY: number }[] = []; + let lastSnappedPositionDeltas: OccupiedSpace[] = []; const resetCanvasState = () => { throttledStopReflowing(); @@ -299,7 +303,9 @@ export const useCanvasDragging = ( const triggerReflow = (e: any, firstMove: boolean) => { const canReflow = - !currentRectanglesToDraw[0].detachFromLayout && !dropDisabled; + !currentRectanglesToDraw[0].detachFromLayout && + !dropDisabled && + appPositioningType === AppPositioningTypes.FIXED; const isReflowing = !isEmpty(currentReflowParams.movementMap) || (!isEmpty(currentReflowParams.movementLimitMap) && @@ -440,13 +446,10 @@ export const useCanvasDragging = ( currentDirection.current, ); lastSnappedPositionDeltas = [ - { - deltaX: - currentSnappedPosition.right - lastSnappedPosition.right, - deltaY: currentSnappedPosition.top - lastSnappedPosition.top, - }, + currentSnappedPosition, ...lastSnappedPositionDeltas.slice(0, 4), ]; + triggerReflow(e, firstMove); if ( useAutoLayout && diff --git a/app/client/src/selectors/editorSelectors.tsx b/app/client/src/selectors/editorSelectors.tsx index 125029bc4e74..2cfb065ea88d 100644 --- a/app/client/src/selectors/editorSelectors.tsx +++ b/app/client/src/selectors/editorSelectors.tsx @@ -45,6 +45,7 @@ import { } from "utils/widgetRenderUtils"; import { ContainerWidgetProps } from "widgets/ContainerWidget/widget"; import { isAutoHeightEnabledForWidget } from "widgets/WidgetUtils"; +import { checkIsDropTarget } from "components/designSystems/appsmith/PositionedContainer"; const getIsDraggingOrResizing = (state: AppState) => state.ui.widgetDragResize.isResizing || state.ui.widgetDragResize.isDragging; diff --git a/app/client/src/selectors/propertyPaneSelectors.tsx b/app/client/src/selectors/propertyPaneSelectors.tsx index 059640d216fc..95b9df123058 100644 --- a/app/client/src/selectors/propertyPaneSelectors.tsx +++ b/app/client/src/selectors/propertyPaneSelectors.tsx @@ -21,9 +21,12 @@ import { } from "utils/DynamicBindingUtils"; import { generateClassName } from "utils/generators"; import { WidgetProps } from "widgets/BaseWidget"; -import { getCurrentAppPositioningType } from "./editorSelectors"; import { getCanvasWidgets } from "./entitiesSelector"; import { getLastSelectedWidget, getSelectedWidgets } from "./ui"; +import { getCurrentAppPositioningType } from "./editorSelectors"; +import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; +import { Positioning } from "components/constants"; +import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; export type WidgetProperties = WidgetProps & { [EVALUATION_PATH]?: DataTreeEntity; @@ -71,18 +74,27 @@ const getCurrentWidgetName = createSelector( export const getWidgetPropsForPropertyPane = createSelector( getCurrentWidgetProperties, - getCurrentAppPositioningType, + getWidgets, getDataTree, ( widget: WidgetProps | undefined, - appPositioningType, + widgets, evaluatedTree: DataTree, ): WidgetProps | undefined => { if (!widget) return undefined; + const appPositioningType = + widgets && widgets[MAIN_CONTAINER_WIDGET_ID] + ? widgets[MAIN_CONTAINER_WIDGET_ID].positioning === Positioning.Vertical + ? AppPositioningTypes.AUTO + : AppPositioningTypes.FIXED + : AppPositioningTypes.FIXED; const evaluatedWidget = find(evaluatedTree, { widgetId: widget.widgetId, }) as DataTreeWidget; - const widgetProperties = { ...widget, appPositioningType }; + const widgetProperties = { + ...widget, + appPositioningType, + }; if (evaluatedWidget) { widgetProperties[EVALUATION_PATH] = evaluatedWidget[EVALUATION_PATH]; From d2db85d9abfdb74481f607d9f525922b4cf093ae Mon Sep 17 00:00:00 2001 From: Aswath K Date: Tue, 10 Jan 2023 12:11:37 +0530 Subject: [PATCH 366/708] feat: Input widgets POC --- .../appsmith/PositionedContainer.tsx | 2 ++ .../appsmith/autoLayout/FlexComponent.tsx | 2 ++ .../BaseInputWidget/component/index.tsx | 11 ++++++--- .../component/styles.module.css | 23 +++++++++++++++++++ app/client/src/widgets/InputWidgetV2/index.ts | 2 +- .../widgets/components/LabelWithTooltip.tsx | 3 ++- 6 files changed, 38 insertions(+), 5 deletions(-) create mode 100644 app/client/src/widgets/BaseInputWidget/component/styles.module.css diff --git a/app/client/src/components/designSystems/appsmith/PositionedContainer.tsx b/app/client/src/components/designSystems/appsmith/PositionedContainer.tsx index 5afd77afaf60..762d52cc24cf 100644 --- a/app/client/src/components/designSystems/appsmith/PositionedContainer.tsx +++ b/app/client/src/components/designSystems/appsmith/PositionedContainer.tsx @@ -27,6 +27,8 @@ const PositionedWidget = styled.div<{ zIndexOnHover: number; disabled?: boolean; }>` + container-name: widget-container; + container-type: inline-size; &:hover { z-index: ${(props) => props.zIndexOnHover} !important; } diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx index 8f0a48ce0188..04353d894902 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx @@ -36,6 +36,8 @@ export type AutoLayoutProps = { const FlexWidget = styled.div` position: relative; + container-name: widget-container; + container-type: inline-size; `; export function FlexComponent(props: AutoLayoutProps) { diff --git a/app/client/src/widgets/BaseInputWidget/component/index.tsx b/app/client/src/widgets/BaseInputWidget/component/index.tsx index 74c373717946..f011cb3ff94d 100644 --- a/app/client/src/widgets/BaseInputWidget/component/index.tsx +++ b/app/client/src/widgets/BaseInputWidget/component/index.tsx @@ -21,6 +21,7 @@ import { INPUT_WIDGET_DEFAULT_VALIDATION_ERROR, } from "@appsmith/constants/messages"; import { InputTypes, NumberInputStepButtonPosition } from "../constants"; +import styles from "./styles.module.css"; // TODO(abhinav): All of the following imports should not be in widgets. import ErrorTooltip from "components/editorComponents/ErrorTooltip"; @@ -364,7 +365,10 @@ const TextInputWrapper = styled.div<{ }}; border-radius: ${({ borderRadius }) => borderRadius} !important; box-shadow: ${({ boxShadow }) => `${boxShadow}`} !important; - min-height: 32px; + min-height: 40px; + &&& { + flex: 0 40px; + } &:hover { border-color: ${({ disabled, hasError }) => { @@ -664,6 +668,7 @@ class BaseInputComponent extends React.Component< return ( Date: Tue, 10 Jan 2023 08:00:37 -0500 Subject: [PATCH 367/708] fix copy paste between canvases --- .../CanvasArenas/hooks/canvasDraggingUtils.ts | 32 ++++++++++++------- app/client/src/sagas/AutoLayoutUtils.ts | 5 ++- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/canvasDraggingUtils.ts b/app/client/src/pages/common/CanvasArenas/hooks/canvasDraggingUtils.ts index 0f26c1148f2a..20ed967125f9 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/canvasDraggingUtils.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/canvasDraggingUtils.ts @@ -148,19 +148,27 @@ export function getMoveDirection( currentDirection: ReflowDirection, ) { if (!prevPosition || !currentPosition) return currentDirection; + const deltaX = Math.max( + Math.abs(currentPosition.left - prevPosition.left), + Math.abs(currentPosition.right - prevPosition.right), + ); + const deltaY = Math.max( + Math.abs(currentPosition.top - prevPosition.top), + Math.abs(currentPosition.bottom - prevPosition.bottom), + ); + if (deltaX >= deltaY) { + if ( + currentPosition.right - prevPosition.right > 0 || + currentPosition.left - prevPosition.left > 0 + ) + return ReflowDirection.RIGHT; - if ( - currentPosition.right - prevPosition.right > 0 || - currentPosition.left - prevPosition.left > 0 - ) - return ReflowDirection.RIGHT; - - if ( - currentPosition.right - prevPosition.right < 0 || - currentPosition.left - prevPosition.left < 0 - ) - return ReflowDirection.LEFT; - + if ( + currentPosition.right - prevPosition.right < 0 || + currentPosition.left - prevPosition.left < 0 + ) + return ReflowDirection.LEFT; + } if ( currentPosition.bottom - prevPosition.bottom > 0 || currentPosition.top - prevPosition.top > 0 diff --git a/app/client/src/sagas/AutoLayoutUtils.ts b/app/client/src/sagas/AutoLayoutUtils.ts index 0ec69daba507..5f4149788fd6 100644 --- a/app/client/src/sagas/AutoLayoutUtils.ts +++ b/app/client/src/sagas/AutoLayoutUtils.ts @@ -240,7 +240,10 @@ export function pasteWidgetInFlexLayers( * If the new parent is not the same as the original parent, * then add a new flex layer. */ - if (widgets[originalWidgetId].parentId !== parentId) { + if ( + !widgets[originalWidgetId] || + widgets[originalWidgetId].parentId !== parentId + ) { flexLayers = [ ...flexLayers, { From 791ca5282c5850d8a8041e40a2b049f4bda51438 Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 10 Jan 2023 11:21:44 -0500 Subject: [PATCH 368/708] wip --- .../CanvasArenas/hooks/canvasDraggingUtils.ts | 1 - .../utils/autoLayout/positionUtils.test.ts | 149 ++++++++++++++++++ .../src/utils/autoLayout/positionUtils.ts | 2 +- 3 files changed, 150 insertions(+), 2 deletions(-) create mode 100644 app/client/src/utils/autoLayout/positionUtils.test.ts diff --git a/app/client/src/pages/common/CanvasArenas/hooks/canvasDraggingUtils.ts b/app/client/src/pages/common/CanvasArenas/hooks/canvasDraggingUtils.ts index 20ed967125f9..e0e85d9df9ed 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/canvasDraggingUtils.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/canvasDraggingUtils.ts @@ -1,6 +1,5 @@ import { OccupiedSpace } from "constants/CanvasEditorConstants"; import { GridDefaults } from "constants/WidgetConstants"; -import { current } from "immer"; import { HORIZONTAL_RESIZE_MIN_LIMIT, MovementLimitMap, diff --git a/app/client/src/utils/autoLayout/positionUtils.test.ts b/app/client/src/utils/autoLayout/positionUtils.test.ts new file mode 100644 index 000000000000..b9ff665abcd8 --- /dev/null +++ b/app/client/src/utils/autoLayout/positionUtils.test.ts @@ -0,0 +1,149 @@ +import { FlexLayerAlignment, ResponsiveBehavior } from "components/constants"; +import { FlexLayer } from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; +import { RenderModes } from "constants/WidgetConstants"; +import { extractAlignmentInfo } from "./positionUtils"; + +describe("test PositionUtils methods", () => { + describe("test extractAlignmentInfo method", () => { + it("should extract children and required columns for each alignment", () => { + const widgets = { + "1": { + widgetId: "1", + leftColumn: 0, + rightColumn: 16, + alignment: FlexLayerAlignment.Start, + topRow: 0, + bottomRow: 4, + type: "", + widgetName: "", + renderMode: RenderModes.CANVAS, + version: 1, + parentColumnSpace: 10, + parentRowSpace: 10, + isLoading: false, + }, + "2": { + widgetId: "2", + leftColumn: 16, + rightColumn: 40, + alignment: FlexLayerAlignment.Start, + topRow: 0, + bottomRow: 7, + type: "", + widgetName: "", + renderMode: RenderModes.CANVAS, + version: 1, + parentColumnSpace: 10, + parentRowSpace: 10, + isLoading: false, + }, + "3": { + widgetId: "3", + leftColumn: 48, + rightColumn: 64, + alignment: FlexLayerAlignment.End, + topRow: 0, + bottomRow: 7, + type: "", + widgetName: "", + renderMode: RenderModes.CANVAS, + version: 1, + parentColumnSpace: 10, + parentRowSpace: 10, + isLoading: false, + }, + }; + const layer: FlexLayer = { + children: [ + { id: "1", align: FlexLayerAlignment.Start }, + { id: "2", align: FlexLayerAlignment.Start }, + { id: "3", align: FlexLayerAlignment.End }, + ], + hasFillChild: false, + }; + expect(extractAlignmentInfo(widgets, layer, false)).toEqual({ + info: [ + { + alignment: FlexLayerAlignment.Start, + columns: 40, + children: [widgets["1"], widgets["2"]], + }, + { + alignment: FlexLayerAlignment.Center, + columns: 0, + children: [], + }, + { + alignment: FlexLayerAlignment.End, + columns: 16, + children: [widgets["3"]], + }, + ], + fillWidgetLength: 64, + }); + }); + it("should calculate columns for fill widgets", () => { + const widgets = { + "1": { + widgetId: "1", + leftColumn: 0, + rightColumn: 16, + alignment: FlexLayerAlignment.Start, + topRow: 0, + bottomRow: 4, + type: "", + widgetName: "", + renderMode: RenderModes.CANVAS, + version: 1, + parentColumnSpace: 10, + parentRowSpace: 10, + isLoading: false, + responsiveBehavior: ResponsiveBehavior.Hug, + }, + "2": { + widgetId: "2", + leftColumn: 16, + rightColumn: 64, + alignment: FlexLayerAlignment.Start, + topRow: 0, + bottomRow: 7, + type: "", + widgetName: "", + renderMode: RenderModes.CANVAS, + version: 1, + parentColumnSpace: 10, + parentRowSpace: 10, + isLoading: false, + responsiveBehavior: ResponsiveBehavior.Fill, + }, + "3": { + widgetId: "3", + leftColumn: 48, + rightColumn: 64, + alignment: FlexLayerAlignment.End, + topRow: 0, + bottomRow: 7, + type: "", + widgetName: "", + renderMode: RenderModes.CANVAS, + version: 1, + parentColumnSpace: 10, + parentRowSpace: 10, + isLoading: false, + responsiveBehavior: ResponsiveBehavior.Fill, + }, + }; + const layer: FlexLayer = { + children: [ + { id: "1", align: FlexLayerAlignment.Start }, + { id: "2", align: FlexLayerAlignment.Start }, + { id: "3", align: FlexLayerAlignment.End }, + ], + hasFillChild: true, + }; + expect( + extractAlignmentInfo(widgets, layer, false).fillWidgetLength, + ).toEqual(24); + }); + }); +}); diff --git a/app/client/src/utils/autoLayout/positionUtils.ts b/app/client/src/utils/autoLayout/positionUtils.ts index 1b42ac0f55b7..5a5cf737da78 100644 --- a/app/client/src/utils/autoLayout/positionUtils.ts +++ b/app/client/src/utils/autoLayout/positionUtils.ts @@ -261,7 +261,7 @@ function getAlignmentSizes( * @param isMobile | boolean * @returns { info: AlignmentInfo[]; fillWidgetLength: number } */ -function extractAlignmentInfo( +export function extractAlignmentInfo( widgets: CanvasWidgetsReduxState, layer: FlexLayer, isMobile: boolean, From d0770bc60fc3085199d2342000d267ee2f520e24 Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 10 Jan 2023 11:28:19 -0500 Subject: [PATCH 369/708] remove hasFillChild from FlexLayer --- .../appsmith/autoLayout/AutoLayoutLayer.tsx | 1 - .../appsmith/autoLayout/FlexBoxComponent.tsx | 2 -- app/client/src/sagas/AutoLayoutUtils.ts | 10 ---------- .../CanvasSagas/AutoLayoutDraggingSagas.ts | 19 +++---------------- 4 files changed, 3 insertions(+), 29 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx index c3471eb2aeda..fe8a045d8b00 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx @@ -14,7 +14,6 @@ export interface AutoLayoutLayerProps { center?: ReactNode; end?: ReactNode; direction: LayoutDirection; - hasFillChild?: boolean; index: number; widgetId: string; isMobile?: boolean; diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx index 4b57a9140db6..4599bc72a444 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx @@ -33,7 +33,6 @@ export interface LayerChild { export interface FlexLayer { children: LayerChild[]; - hasFillChild?: boolean; } export const DEFAULT_HIGHLIGHT_SIZE = 4; @@ -145,7 +144,6 @@ function FlexBoxComponent(props: FlexBoxProps) { center={center} direction={direction} end={end} - hasFillChild={layer.hasFillChild} index={index} isMobile={isMobile} key={index} diff --git a/app/client/src/sagas/AutoLayoutUtils.ts b/app/client/src/sagas/AutoLayoutUtils.ts index 5f4149788fd6..ec6eaa0a739f 100644 --- a/app/client/src/sagas/AutoLayoutUtils.ts +++ b/app/client/src/sagas/AutoLayoutUtils.ts @@ -56,8 +56,6 @@ export function* wrapChildren( if (!child) continue; flexLayers.push({ children: [{ id: child.widgetId, align: FlexLayerAlignment.Start }], - hasFillChild: - child.responsiveBehavior === ResponsiveBehavior.Fill || false, }); } canvas = { ...canvas, flexLayers }; @@ -104,11 +102,6 @@ export function* updateFlexLayersOnDelete( ...flexLayers.slice(0, layerIndex), { children: updatedChildren, - hasFillChild: updatedChildren.some( - (each: LayerChild) => - widgets[each.id] && - widgets[each.id]?.responsiveBehavior === ResponsiveBehavior.Fill, - ), }, ...flexLayers.slice(layerIndex + 1), ]; @@ -253,7 +246,6 @@ export function pasteWidgetInFlexLayers( align: FlexLayerAlignment.Start, }, ], - hasFillChild: widget.responsiveBehavior === ResponsiveBehavior.Fill, }, ]; } else { @@ -281,7 +273,6 @@ export function pasteWidgetInFlexLayers( { id: widget.widgetId, align: alignment }, ...selectedLayer.children.slice(rowIndex + 1), ], - hasFillChild: selectedLayer.hasFillChild, }; flexLayers = [ ...flexLayers.slice(0, flexLayerIndex), @@ -326,7 +317,6 @@ export function addChildToPastedFlexLayers( } flexLayers[index] = { children, - hasFillChild: layer.hasFillChild, }; index += 1; } diff --git a/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts b/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts index 3bd52fde18bf..5c46be545c7b 100644 --- a/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts +++ b/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts @@ -3,11 +3,7 @@ import { ReduxAction, ReduxActionTypes, } from "ce/constants/ReduxActionConstants"; -import { - FlexLayerAlignment, - LayoutDirection, - ResponsiveBehavior, -} from "components/constants"; +import { FlexLayerAlignment, LayoutDirection } from "components/constants"; import { FlexLayer, LayerChild, @@ -344,7 +340,6 @@ function updateExistingLayer( ...map[FlexLayerAlignment.Center], ...map[FlexLayerAlignment.End], ], - hasFillChild: newLayer.hasFillChild || layers[layerIndex]?.hasFillChild, }; const updatedCanvas = { @@ -370,30 +365,26 @@ function updateExistingLayer( * @param movedWidgets * @param allWidgets * @param alignment - * @returns hasFillChild: boolean, layerChildren: string[] + * @returns FlexLayer */ function createFlexLayer( movedWidgets: string[], allWidgets: CanvasWidgetsReduxState, alignment: FlexLayerAlignment, ): FlexLayer { - let hasFillChild = false; const children = []; if (movedWidgets && movedWidgets.length) { for (const id of movedWidgets) { const widget = allWidgets[id]; if (!widget) continue; - if (widget.responsiveBehavior === ResponsiveBehavior.Fill) - hasFillChild = true; children.push({ id, align: alignment }); } } - return { children, hasFillChild }; + return { children }; } /** * Remove moved widgets from current layers. - * and update hasFillChild property. * Return non-empty layers. * @param allWidgets * @param movedWidgets @@ -414,10 +405,6 @@ export function removeWidgetsFromCurrentLayers( acc.push({ ...layer, children, - hasFillChild: children.some( - (each: LayerChild) => - allWidgets[each.id].responsiveBehavior === ResponsiveBehavior.Fill, - ), }); } return acc; From 243d055df25c9a88fad08e5210e6559c0338ee39 Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 10 Jan 2023 13:30:45 -0500 Subject: [PATCH 370/708] add positionUtils unit tests --- .../utils/autoLayout/positionUtils.test.ts | 719 +++++++++++++++++- .../src/utils/autoLayout/positionUtils.ts | 46 +- 2 files changed, 739 insertions(+), 26 deletions(-) diff --git a/app/client/src/utils/autoLayout/positionUtils.test.ts b/app/client/src/utils/autoLayout/positionUtils.test.ts index b9ff665abcd8..38e0dbacd3e2 100644 --- a/app/client/src/utils/autoLayout/positionUtils.test.ts +++ b/app/client/src/utils/autoLayout/positionUtils.test.ts @@ -1,7 +1,19 @@ import { FlexLayerAlignment, ResponsiveBehavior } from "components/constants"; import { FlexLayer } from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; import { RenderModes } from "constants/WidgetConstants"; -import { extractAlignmentInfo } from "./positionUtils"; +import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; +import { + AlignmentInfo, + extractAlignmentInfo, + getAlignmentSizeInfo, + getStartingPosition, + getWrappedAlignmentInfo, + getWrappedRows, + placeWidgetsWithoutWrap, + placeWrappedWidgets, + Row, + updateWidgetPositions, +} from "./positionUtils"; describe("test PositionUtils methods", () => { describe("test extractAlignmentInfo method", () => { @@ -59,7 +71,6 @@ describe("test PositionUtils methods", () => { { id: "2", align: FlexLayerAlignment.Start }, { id: "3", align: FlexLayerAlignment.End }, ], - hasFillChild: false, }; expect(extractAlignmentInfo(widgets, layer, false)).toEqual({ info: [ @@ -139,11 +150,713 @@ describe("test PositionUtils methods", () => { { id: "2", align: FlexLayerAlignment.Start }, { id: "3", align: FlexLayerAlignment.End }, ], - hasFillChild: true, }; expect( extractAlignmentInfo(widgets, layer, false).fillWidgetLength, ).toEqual(24); }); }); + + describe("test getAlignmentSizeinfo method", () => { + it("should distribute the space evenly across alignments if they don't need more than their equal share", () => { + const arr: AlignmentInfo[] = [ + { alignment: FlexLayerAlignment.Start, columns: 0, children: [] }, + { alignment: FlexLayerAlignment.Center, columns: 0, children: [] }, + { alignment: FlexLayerAlignment.End, columns: 0, children: [] }, + ]; + const { centerSize, endSize, startSize } = getAlignmentSizeInfo(arr); + expect(startSize).toEqual(endSize); + expect(startSize).toEqual(centerSize); + }); + it("should distribute the space evenly across alignments if 1) they don't need more than their equal share, and 2) there are fewer than three alignments in the layer", () => { + const arr: AlignmentInfo[] = [ + { alignment: FlexLayerAlignment.Center, columns: 0, children: [] }, + { alignment: FlexLayerAlignment.End, columns: 0, children: [] }, + ]; + const { centerSize, endSize, startSize } = getAlignmentSizeInfo(arr); + expect(startSize).toEqual(0); + expect(endSize).toEqual(32); + expect(endSize).toEqual(centerSize); + }); + it("should assign appropriate columns to an alignment that needs more space, and distribute the remaining space evenly amongst the other alignments", () => { + const arr: AlignmentInfo[] = [ + { alignment: FlexLayerAlignment.Start, columns: 40, children: [] }, + { alignment: FlexLayerAlignment.Center, columns: 0, children: [] }, + { alignment: FlexLayerAlignment.End, columns: 0, children: [] }, + ]; + const { centerSize, endSize, startSize } = getAlignmentSizeInfo(arr); + expect(startSize).toEqual(40); + expect(endSize).toEqual(12); + expect(endSize).toEqual(centerSize); + }); + }); + describe("test getWrappedAlignmentInfo method", () => { + it("should place all alignments in a single row if combined column requirement <= 64", () => { + const arr: AlignmentInfo[] = [ + { alignment: FlexLayerAlignment.Start, columns: 16, children: [] }, + { alignment: FlexLayerAlignment.Center, columns: 20, children: [] }, + { alignment: FlexLayerAlignment.End, columns: 26, children: [] }, + ]; + const rows: AlignmentInfo[][] = getWrappedAlignmentInfo(arr); + expect(rows.length).toEqual(3); + expect(rows[0].length).toEqual(3); + expect(rows.filter((row) => !row.length).length).toEqual(2); + expect(rows[0]).toEqual(arr); + }); + it("should wrap an alignment requiring > 64 columns in a separate row", () => { + const arr: AlignmentInfo[] = [ + { alignment: FlexLayerAlignment.Start, columns: 80, children: [] }, + { alignment: FlexLayerAlignment.Center, columns: 20, children: [] }, + { alignment: FlexLayerAlignment.End, columns: 26, children: [] }, + ]; + const rows: AlignmentInfo[][] = getWrappedAlignmentInfo(arr); + expect(rows.length).toEqual(3); + expect(rows[0].length).toEqual(1); + expect(rows[1].length).toEqual(2); + expect(rows.filter((row) => !row.length).length).toEqual(1); + expect(rows[0]).toEqual([arr[0]]); + expect(rows[1]).toEqual([arr[1], arr[2]]); + }); + it("should wrap alignments into multiple rows if combined columns requirement > 64", () => { + const arr: AlignmentInfo[] = [ + { alignment: FlexLayerAlignment.Start, columns: 30, children: [] }, + { alignment: FlexLayerAlignment.Center, columns: 40, children: [] }, + { alignment: FlexLayerAlignment.End, columns: 10, children: [] }, + ]; + const rows: AlignmentInfo[][] = getWrappedAlignmentInfo(arr); + expect(rows.length).toEqual(3); + expect(rows[0].length).toEqual(1); + expect(rows[1].length).toEqual(2); + expect(rows.filter((row) => !row.length).length).toEqual(1); + expect(rows[0]).toEqual([arr[0]]); + expect(rows[1]).toEqual([arr[1], arr[2]]); + }); + }); + + describe("test getStartingPosition method", () => { + it("should return 0 as the starting position for FlexLayerAlignment.Start", () => { + expect( + getStartingPosition(FlexLayerAlignment.Start, 21.3, 21.3, 21.3, 16), + ).toEqual(0); + }); + it("should return valid starting position for FlexLayerAlignment.Center", () => { + expect( + getStartingPosition(FlexLayerAlignment.Center, 20, 24, 20, 16), + ).toEqual(24); + }); + it("should return valid starting position for FlexLayerAlignment.End", () => { + expect( + getStartingPosition(FlexLayerAlignment.End, 20, 24, 20, 16), + ).toEqual(48); + }); + }); + + describe("test getWrappedRows method", () => { + it("should segregate an alignment's children into multiple rows if combined column requirement > 64", () => { + const arr: AlignmentInfo = { + alignment: FlexLayerAlignment.Start, + columns: 80, + children: [ + { + widgetId: "1", + leftColumn: 0, + rightColumn: 16, + alignment: FlexLayerAlignment.Start, + topRow: 0, + bottomRow: 4, + type: "", + widgetName: "", + renderMode: RenderModes.CANVAS, + version: 1, + parentColumnSpace: 10, + parentRowSpace: 10, + isLoading: false, + mobileTopRow: 0, + mobileBottomRow: 4, + mobileLeftColumn: 0, + mobileRightColumn: 16, + responsiveBehavior: ResponsiveBehavior.Hug, + }, + { + widgetId: "2", + leftColumn: 16, + rightColumn: 64, + alignment: FlexLayerAlignment.Start, + topRow: 0, + bottomRow: 7, + type: "", + widgetName: "", + renderMode: RenderModes.CANVAS, + version: 1, + parentColumnSpace: 10, + parentRowSpace: 10, + isLoading: false, + mobileTopRow: 0, + mobileBottomRow: 7, + mobileLeftColumn: 16, + mobileRightColumn: 80, + responsiveBehavior: ResponsiveBehavior.Fill, + }, + ], + }; + const rows: Row[] = getWrappedRows(arr, [], true); + expect(rows.length).toEqual(2); + expect(rows[0]).toEqual({ + alignment: arr.alignment, + columns: 16, + children: [arr.children[0]], + height: 4, + }); + expect(rows[1]).toEqual({ + alignment: arr.alignment, + columns: 64, + children: [arr.children[1]], + height: 7, + }); + }); + }); + + describe("test placeWidgetsWithoutWrap method", () => { + it("should update positions of widgets - part 1", () => { + const widgets = { + "1": { + widgetId: "1", + leftColumn: 0, + rightColumn: 16, + alignment: FlexLayerAlignment.Start, + topRow: 0, + bottomRow: 4, + type: "", + widgetName: "", + renderMode: RenderModes.CANVAS, + version: 1, + parentColumnSpace: 10, + parentRowSpace: 10, + isLoading: false, + }, + "2": { + widgetId: "2", + leftColumn: 0, + rightColumn: 24, + alignment: FlexLayerAlignment.Start, + topRow: 0, + bottomRow: 7, + type: "", + widgetName: "", + renderMode: RenderModes.CANVAS, + version: 1, + parentColumnSpace: 10, + parentRowSpace: 10, + isLoading: false, + }, + "3": { + widgetId: "3", + leftColumn: 0, + rightColumn: 16, + alignment: FlexLayerAlignment.End, + topRow: 0, + bottomRow: 7, + type: "", + widgetName: "", + renderMode: RenderModes.CANVAS, + version: 1, + parentColumnSpace: 10, + parentRowSpace: 10, + isLoading: false, + }, + }; + const arr: AlignmentInfo[] = [ + { + alignment: FlexLayerAlignment.Start, + columns: 40, + children: [widgets["1"], widgets["2"]], + }, + { + alignment: FlexLayerAlignment.Center, + columns: 0, + children: [], + }, + { + alignment: FlexLayerAlignment.End, + columns: 16, + children: [widgets["3"]], + }, + ]; + const result: { + height: number; + widgets: CanvasWidgetsReduxState; + } = placeWidgetsWithoutWrap(widgets, arr, 0, 64, false, 0); + expect(result.height).toEqual(7); + expect(result.widgets["2"].leftColumn).toEqual(16); + expect(result.widgets["2"].rightColumn).toEqual(40); + expect(result.widgets["3"].leftColumn).toEqual(48); + expect(result.widgets["3"].rightColumn).toEqual(64); + }); + it("should update positions of widgets - part 2", () => { + const widgets = { + "1": { + widgetId: "1", + leftColumn: 0, + rightColumn: 16, + alignment: FlexLayerAlignment.Center, + topRow: 0, + bottomRow: 4, + type: "", + widgetName: "", + renderMode: RenderModes.CANVAS, + version: 1, + parentColumnSpace: 10, + parentRowSpace: 10, + isLoading: false, + }, + "2": { + widgetId: "2", + leftColumn: 0, + rightColumn: 24, + alignment: FlexLayerAlignment.End, + topRow: 0, + bottomRow: 7, + type: "", + widgetName: "", + renderMode: RenderModes.CANVAS, + version: 1, + parentColumnSpace: 10, + parentRowSpace: 10, + isLoading: false, + }, + "3": { + widgetId: "3", + leftColumn: 0, + rightColumn: 16, + alignment: FlexLayerAlignment.End, + topRow: 0, + bottomRow: 7, + type: "", + widgetName: "", + renderMode: RenderModes.CANVAS, + version: 1, + parentColumnSpace: 10, + parentRowSpace: 10, + isLoading: false, + }, + }; + const arr: AlignmentInfo[] = [ + { + alignment: FlexLayerAlignment.Start, + columns: 0, + children: [], + }, + { + alignment: FlexLayerAlignment.Center, + columns: 16, + children: [widgets["1"]], + }, + { + alignment: FlexLayerAlignment.End, + columns: 40, + children: [widgets["2"], widgets["3"]], + }, + ]; + const result: { + height: number; + widgets: CanvasWidgetsReduxState; + } = placeWidgetsWithoutWrap(widgets, arr, 0, 64, false, 0); + expect(result.height).toEqual(7); + expect(result.widgets["1"].leftColumn).toEqual(8); + expect(result.widgets["1"].rightColumn).toEqual(24); + expect(result.widgets["2"].leftColumn).toEqual(24); + expect(result.widgets["2"].rightColumn).toEqual(48); + expect(result.widgets["3"].leftColumn).toEqual(48); + expect(result.widgets["3"].rightColumn).toEqual(64); + }); + it("should update positions of widgets - part 3", () => { + const widgets = { + "1": { + widgetId: "1", + leftColumn: 0, + rightColumn: 16, + alignment: FlexLayerAlignment.Center, + topRow: 0, + bottomRow: 4, + type: "", + widgetName: "", + renderMode: RenderModes.CANVAS, + version: 1, + parentColumnSpace: 10, + parentRowSpace: 10, + isLoading: false, + mobileTopRow: 0, + mobileBottomRow: 4, + mobileLeftColumn: 0, + mobileRightColumn: 16, + responsiveBehavior: ResponsiveBehavior.Hug, + }, + "2": { + widgetId: "2", + leftColumn: 0, + rightColumn: 24, + alignment: FlexLayerAlignment.End, + topRow: 0, + bottomRow: 7, + type: "", + widgetName: "", + renderMode: RenderModes.CANVAS, + version: 1, + parentColumnSpace: 10, + parentRowSpace: 10, + isLoading: false, + mobileTopRow: 0, + mobileBottomRow: 7, + mobileLeftColumn: 0, + mobileRightColumn: 24, + responsiveBehavior: ResponsiveBehavior.Hug, + }, + "3": { + widgetId: "3", + leftColumn: 0, + rightColumn: 16, + alignment: FlexLayerAlignment.End, + topRow: 0, + bottomRow: 7, + type: "", + widgetName: "", + renderMode: RenderModes.CANVAS, + version: 1, + parentColumnSpace: 10, + parentRowSpace: 10, + isLoading: false, + mobileTopRow: 0, + mobileBottomRow: 7, + mobileLeftColumn: 0, + mobileRightColumn: 16, + responsiveBehavior: ResponsiveBehavior.Hug, + }, + }; + const arr: AlignmentInfo[] = [ + { + alignment: FlexLayerAlignment.Start, + columns: 0, + children: [], + }, + { + alignment: FlexLayerAlignment.Center, + columns: 16, + children: [widgets["1"]], + }, + { + alignment: FlexLayerAlignment.End, + columns: 40, + children: [widgets["2"], widgets["3"]], + }, + ]; + const result: { + height: number; + widgets: CanvasWidgetsReduxState; + } = placeWidgetsWithoutWrap(widgets, arr, 0, 64, true, 0); + expect(result.height).toEqual(7); + expect(result.widgets["1"].mobileLeftColumn).toEqual(8); + expect(result.widgets["1"].mobileRightColumn).toEqual(24); + expect(result.widgets["2"].mobileLeftColumn).toEqual(24); + expect(result.widgets["2"].mobileRightColumn).toEqual(48); + expect(result.widgets["3"].mobileLeftColumn).toEqual(48); + expect(result.widgets["3"].mobileRightColumn).toEqual(64); + }); + }); + + describe("test placeWrappedWidgets method", () => { + it("should update positions of widgets in a flex wrapped alignment", () => { + const widgets = { + "1": { + widgetId: "1", + leftColumn: 0, + rightColumn: 16, + alignment: FlexLayerAlignment.End, + topRow: 0, + bottomRow: 4, + type: "", + widgetName: "", + renderMode: RenderModes.CANVAS, + version: 1, + parentColumnSpace: 10, + parentRowSpace: 10, + isLoading: false, + mobileTopRow: 0, + mobileBottomRow: 4, + mobileLeftColumn: 0, + mobileRightColumn: 16, + responsiveBehavior: ResponsiveBehavior.Hug, + }, + "2": { + widgetId: "2", + leftColumn: 0, + rightColumn: 64, + alignment: FlexLayerAlignment.End, + topRow: 0, + bottomRow: 7, + type: "", + widgetName: "", + renderMode: RenderModes.CANVAS, + version: 1, + parentColumnSpace: 10, + parentRowSpace: 10, + isLoading: false, + mobileTopRow: 0, + mobileBottomRow: 7, + mobileLeftColumn: 0, + mobileRightColumn: 64, + responsiveBehavior: ResponsiveBehavior.Fill, + }, + "3": { + widgetId: "3", + leftColumn: 0, + rightColumn: 16, + alignment: FlexLayerAlignment.End, + topRow: 0, + bottomRow: 7, + type: "", + widgetName: "", + renderMode: RenderModes.CANVAS, + version: 1, + parentColumnSpace: 10, + parentRowSpace: 10, + isLoading: false, + mobileTopRow: 0, + mobileBottomRow: 7, + mobileLeftColumn: 0, + mobileRightColumn: 16, + responsiveBehavior: ResponsiveBehavior.Hug, + }, + }; + const result: { + height: number; + widgets: CanvasWidgetsReduxState; + } = placeWrappedWidgets( + widgets, + { + alignment: FlexLayerAlignment.End, + columns: 96, + children: [widgets["1"], widgets["2"], widgets["3"]], + }, + 0, + 64, + true, + ); + expect(result.height).toEqual(18); + expect(result.widgets["1"].mobileLeftColumn).toEqual(48); + expect(result.widgets["1"].mobileRightColumn).toEqual(64); + + expect(result.widgets["2"].mobileLeftColumn).toEqual(0); + expect(result.widgets["2"].mobileRightColumn).toEqual(64); + expect(result.widgets["2"].mobileTopRow).toEqual(4); + expect(result.widgets["2"].mobileBottomRow).toEqual(11); + + expect(result.widgets["3"].mobileLeftColumn).toEqual(48); + expect(result.widgets["3"].mobileRightColumn).toEqual(64); + expect(result.widgets["3"].mobileTopRow).toEqual(11); + expect(result.widgets["3"].mobileBottomRow).toEqual(18); + }); + }); + + describe("test updateWidgetPositions method", () => { + it("should update positions of widgets within a given parent", () => { + const widgets = { + "1": { + widgetId: "1", + leftColumn: 0, + rightColumn: 16, + alignment: FlexLayerAlignment.End, + topRow: 0, + bottomRow: 4, + type: "", + widgetName: "", + renderMode: RenderModes.CANVAS, + version: 1, + parentColumnSpace: 10, + parentRowSpace: 10, + isLoading: false, + mobileTopRow: 0, + mobileBottomRow: 4, + mobileLeftColumn: 0, + mobileRightColumn: 16, + responsiveBehavior: ResponsiveBehavior.Hug, + }, + "2": { + widgetId: "2", + leftColumn: 0, + rightColumn: 24, + alignment: FlexLayerAlignment.End, + topRow: 0, + bottomRow: 7, + type: "", + widgetName: "", + renderMode: RenderModes.CANVAS, + version: 1, + parentColumnSpace: 10, + parentRowSpace: 10, + isLoading: false, + mobileTopRow: 0, + mobileBottomRow: 7, + mobileLeftColumn: 0, + mobileRightColumn: 24, + responsiveBehavior: ResponsiveBehavior.Hug, + }, + "3": { + widgetId: "3", + leftColumn: 0, + rightColumn: 640, + alignment: FlexLayerAlignment.Start, + topRow: 0, + bottomRow: 70, + type: "CANVAS_WIDGET", + widgetName: "", + renderMode: RenderModes.CANVAS, + version: 1, + parentColumnSpace: 1, + parentRowSpace: 1, + isLoading: false, + mobileTopRow: 0, + mobileBottomRow: 70, + mobileLeftColumn: 0, + mobileRightColumn: 640, + responsiveBehavior: ResponsiveBehavior.Fill, + parentId: "4", + flexLayers: [ + { + children: [ + { id: "1", align: FlexLayerAlignment.End }, + { id: "2", align: FlexLayerAlignment.End }, + ], + }, + ], + }, + "4": { + widgetId: "3", + leftColumn: 0, + rightColumn: 64, + alignment: FlexLayerAlignment.Start, + topRow: 0, + bottomRow: 7, + type: "CANVAS_WIDGET", + widgetName: "", + renderMode: RenderModes.CANVAS, + version: 1, + parentColumnSpace: 10, + parentRowSpace: 10, + isLoading: false, + mobileTopRow: 0, + mobileBottomRow: 7, + mobileLeftColumn: 0, + mobileRightColumn: 64, + responsiveBehavior: ResponsiveBehavior.Fill, + parentId: "", + }, + }; + const result = updateWidgetPositions(widgets, "3", false); + expect(result["1"].leftColumn).toEqual(24); + expect(result["1"].rightColumn).toEqual(40); + expect(result["2"].leftColumn).toEqual(40); + expect(result["2"].rightColumn).toEqual(64); + }); + it("should update height of parents if required", () => { + const widgets = { + "1": { + widgetId: "1", + leftColumn: 0, + rightColumn: 16, + alignment: FlexLayerAlignment.End, + topRow: 0, + bottomRow: 4, + type: "", + widgetName: "", + renderMode: RenderModes.CANVAS, + version: 1, + parentColumnSpace: 10, + parentRowSpace: 10, + isLoading: false, + mobileTopRow: 0, + mobileBottomRow: 4, + mobileLeftColumn: 0, + mobileRightColumn: 16, + responsiveBehavior: ResponsiveBehavior.Hug, + parentId: "3", + }, + "2": { + widgetId: "2", + leftColumn: 0, + rightColumn: 48, + alignment: FlexLayerAlignment.End, + topRow: 0, + bottomRow: 7, + type: "", + widgetName: "", + renderMode: RenderModes.CANVAS, + version: 1, + parentColumnSpace: 10, + parentRowSpace: 10, + isLoading: false, + mobileTopRow: 0, + mobileBottomRow: 7, + mobileLeftColumn: 0, + mobileRightColumn: 64, + responsiveBehavior: ResponsiveBehavior.Fill, + parentId: "3", + }, + "3": { + widgetId: "3", + leftColumn: 0, + rightColumn: 64, + alignment: FlexLayerAlignment.Start, + topRow: 0, + bottomRow: 70, + type: "CANVAS_WIDGET", + widgetName: "Canvas1", + renderMode: RenderModes.CANVAS, + version: 1, + parentColumnSpace: 1, + parentRowSpace: 1, + isLoading: false, + mobileTopRow: 0, + mobileBottomRow: 70, + mobileLeftColumn: 0, + mobileRightColumn: 640, + responsiveBehavior: ResponsiveBehavior.Fill, + parentId: "4", + flexLayers: [ + { + children: [ + { id: "1", align: FlexLayerAlignment.End }, + { id: "2", align: FlexLayerAlignment.End }, + ], + }, + ], + children: ["1", "2"], + }, + "4": { + widgetId: "4", + leftColumn: 0, + rightColumn: 64, + alignment: FlexLayerAlignment.Start, + topRow: 0, + bottomRow: 7, + type: "CONTAINER_WIDGET", + widgetName: "Container1", + renderMode: RenderModes.CANVAS, + version: 1, + parentColumnSpace: 10, + parentRowSpace: 10, + isLoading: false, + mobileTopRow: 0, + mobileBottomRow: 7, + mobileLeftColumn: 0, + mobileRightColumn: 64, + responsiveBehavior: ResponsiveBehavior.Fill, + parentId: "0", + children: ["3"], + }, + }; + const result = updateWidgetPositions(widgets, "3", true); + expect(result["3"].mobileBottomRow).toEqual(120); + expect(result["4"].mobileBottomRow).toEqual(13); + }); + }); }); diff --git a/app/client/src/utils/autoLayout/positionUtils.ts b/app/client/src/utils/autoLayout/positionUtils.ts index 5a5cf737da78..7a3415738e2d 100644 --- a/app/client/src/utils/autoLayout/positionUtils.ts +++ b/app/client/src/utils/autoLayout/positionUtils.ts @@ -22,7 +22,7 @@ export interface AlignmentInfo { children: Widget[]; } -interface Row extends AlignmentInfo { +export interface Row extends AlignmentInfo { height: number; } @@ -81,26 +81,26 @@ export function updateWidgetPositions( isMobile, ); widgets = { ...widgets, [parent.widgetId]: updatedParent }; + + const shouldUpdateHeight = + parent.parentId && + ["CONTAINER_WIDGET", "CANVAS_WIDGET"].includes( + allWidgets[parent.parentId].type, + ); + // console.log( + // "#### update height", + // parent.widgetName, + // "parentHeight", + // parentHeight, + // "height", + // height, + // shouldUpdateHeight, + // parent.parentId ? widgets[parent.parentId].widgetName : "no parent", + // ); + if (shouldUpdateHeight && parent.parentId) + return updateWidgetPositions(widgets, parent.parentId, isMobile); } - const shouldUpdateHeight = - parent.parentId && - ["CONTAINER_WIDGET", "CANVAS_WIDGET"].includes( - allWidgets[parent.parentId].type, - ) && - parentHeight <= height; - // console.log( - // "#### update height", - // parent.widgetName, - // "parentHeight", - // parent.parent, - // "height", - // height, - // shouldUpdateHeight, - // parent.parentId ? widgets[parent.parentId].widgetName : "no parent", - // ); - if (shouldUpdateHeight && parent.parentId) - return updateWidgetPositions(widgets, parent.parentId, isMobile); return widgets; } catch (e) { // console.error(e); @@ -163,7 +163,7 @@ function calculateWidgetPositions( * @param totalHeight | number : total height assumed by the widgets in this row. * @returns { height: number; widgets: CanvasWidgetsReduxState } */ -function placeWidgetsWithoutWrap( +export function placeWidgetsWithoutWrap( allWidgets: CanvasWidgetsReduxState, arr: AlignmentInfo[], topRow: number, @@ -439,7 +439,7 @@ function updatePositionsForFlexWrap( return { height: top - topRow, widgets }; } -function getStartingPosition( +export function getStartingPosition( alignment: FlexLayerAlignment, startSize: number, centerSize: number, @@ -469,7 +469,7 @@ function getStartingPosition( * @param isMobile | boolean: is mobile viewport. * @returns { height: number; widgets: CanvasWidgetsReduxState } */ -function placeWrappedWidgets( +export function placeWrappedWidgets( allWidgets: CanvasWidgetsReduxState, alignment: AlignmentInfo, topRow: number, @@ -515,7 +515,7 @@ function placeWrappedWidgets( * @param isMobile | boolean: is mobile viewport. * @returns Row[] */ -function getWrappedRows( +export function getWrappedRows( arr: AlignmentInfo, rows: Row[], isMobile = false, From a3f30d59452695f20524a3f7432c21ff59504510 Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 10 Jan 2023 15:27:20 -0500 Subject: [PATCH 371/708] add unit tests for highlights utils --- .../utils/autoLayout/highlightUtils.test.ts | 463 ++++++++++++++++++ .../src/utils/autoLayout/highlightUtils.ts | 18 +- 2 files changed, 470 insertions(+), 11 deletions(-) create mode 100644 app/client/src/utils/autoLayout/highlightUtils.test.ts diff --git a/app/client/src/utils/autoLayout/highlightUtils.test.ts b/app/client/src/utils/autoLayout/highlightUtils.test.ts new file mode 100644 index 000000000000..dffdb0dbbd4e --- /dev/null +++ b/app/client/src/utils/autoLayout/highlightUtils.test.ts @@ -0,0 +1,463 @@ +import { FlexLayerAlignment, ResponsiveBehavior } from "components/constants"; +import { FLEXBOX_PADDING, RenderModes } from "constants/WidgetConstants"; +import { HighlightInfo } from "pages/common/CanvasArenas/hooks/useAutoLayoutHighlights"; +import { getWidgetHeight } from "./flexWidgetUtils"; +import { + deriveHighlightsFromLayers, + generateHighlightsForAlignment, + generateVerticalHighlights, + VerticalHighlightsPayload, +} from "./highlightUtils"; + +describe("test HighlightUtils methods", () => { + describe("test deriveHighlightsFromLayers method", () => { + it("should generate horizontal highlights for empty canvas", () => { + const widgets = { + "1": { + widgetId: "1", + leftColumn: 0, + rightColumn: 64, + alignment: FlexLayerAlignment.Start, + topRow: 0, + bottomRow: 70, + type: "CANVAS_WIDGET", + widgetName: "Canvas1", + renderMode: RenderModes.CANVAS, + version: 1, + parentColumnSpace: 1, + parentRowSpace: 1, + isLoading: false, + mobileTopRow: 0, + mobileBottomRow: 70, + mobileLeftColumn: 0, + mobileRightColumn: 640, + responsiveBehavior: ResponsiveBehavior.Fill, + parentId: "", + flexLayers: [], + children: [], + }, + }; + const highlights: HighlightInfo[] = deriveHighlightsFromLayers( + widgets, + "1", + 99, + ); + expect(highlights.length).toEqual(3); + expect(highlights[0].isVertical).toBeFalsy; + expect(highlights[0].width).toEqual(33); + expect(highlights[0].height).toEqual(4); + expect(highlights[1].posX).toEqual(33); + expect(highlights[2].posX).toEqual(66); + }); + it("should add horizontal heights before every layer and below the last layer", () => { + const widgets = { + "1": { + widgetId: "1", + leftColumn: 0, + rightColumn: 64, + alignment: FlexLayerAlignment.Start, + topRow: 0, + bottomRow: 70, + type: "CANVAS_WIDGET", + widgetName: "Canvas1", + renderMode: RenderModes.CANVAS, + version: 1, + parentColumnSpace: 1, + parentRowSpace: 1, + isLoading: false, + mobileTopRow: 0, + mobileBottomRow: 70, + mobileLeftColumn: 0, + mobileRightColumn: 640, + responsiveBehavior: ResponsiveBehavior.Fill, + parentId: "", + flexLayers: [ + { children: [{ id: "2", align: FlexLayerAlignment.Start }] }, + ], + children: ["2"], + }, + "2": { + widgetId: "2", + leftColumn: 0, + rightColumn: 16, + alignment: FlexLayerAlignment.Start, + topRow: 0, + bottomRow: 4, + type: "BUTTON_WIDGET", + widgetName: "Button1", + renderMode: RenderModes.CANVAS, + version: 1, + parentColumnSpace: 1.546875, + parentRowSpace: 10, + isLoading: false, + mobileTopRow: 0, + mobileBottomRow: 4, + mobileLeftColumn: 0, + mobileRightColumn: 16, + responsiveBehavior: ResponsiveBehavior.Hug, + parentId: "1", + }, + }; + const highlights: HighlightInfo[] = deriveHighlightsFromLayers( + widgets, + "1", + 99, + ); + expect(highlights.length).toEqual(10); + expect( + highlights[0].isVertical || + highlights[1].isVertical || + highlights[2].isVertical, + ).toBeFalsy; + expect( + highlights[7].isVertical || + highlights[8].isVertical || + highlights[9].isVertical, + ).toBeFalsy; + + expect(highlights[0].posY).toEqual(FLEXBOX_PADDING); + expect(highlights[7].posY).toEqual( + highlights[0].posY + + (widgets["2"].bottomRow - widgets["2"].topRow) * + widgets["2"].parentRowSpace, + ); + + expect(highlights[0].layerIndex).toEqual(0); + expect(highlights[0].isNewLayer).toBeTruthy; + expect(highlights[7].layerIndex).toEqual(1); + expect(highlights[7].isNewLayer).toBeTruthy; + }); + }); + + describe("test generateHighlightsForAlignment method", () => { + it("should add vertical highlights before every widget in the alignment, and at the end of the last widget", () => { + const children = [ + { + widgetId: "2", + leftColumn: 0, + rightColumn: 16, + alignment: FlexLayerAlignment.Start, + topRow: 0, + bottomRow: 4, + type: "BUTTON_WIDGET", + widgetName: "Button1", + renderMode: RenderModes.CANVAS, + version: 1, + parentColumnSpace: 10, + parentRowSpace: 10, + isLoading: false, + mobileTopRow: 0, + mobileBottomRow: 4, + mobileLeftColumn: 0, + mobileRightColumn: 16, + responsiveBehavior: ResponsiveBehavior.Hug, + parentId: "1", + }, + ]; + const result: HighlightInfo[] = generateHighlightsForAlignment({ + arr: children, + childCount: 0, + layerIndex: 0, + alignment: FlexLayerAlignment.Start, + maxHeight: 40, + offsetTop: 4, + parentColumnSpace: 10, + canvasWidth: 640, + avoidInitialHighlight: false, + isMobile: false, + startPosition: 0, + canvasId: "1", + }); + expect(result.length).toEqual(2); + expect(result[0].posX).toEqual(2); + expect(result[0].posY).toEqual(4); + expect(result[0].width).toEqual(4); + expect(result[0].height).toEqual( + getWidgetHeight(children[0], false) * children[0].parentRowSpace, + ); + + expect(result[1].posX).toEqual( + children[0].rightColumn * children[0].parentColumnSpace + + FLEXBOX_PADDING / 2, + ); + expect(result[1].isNewLayer).toBeFalsy; + expect(result[1].isVertical).toBeTruthy; + expect(result[1].layerIndex).toEqual(0); + }); + it("should create vertical highlights as tall as the tallest child in the row", () => { + const children = [ + { + widgetId: "2", + leftColumn: 0, + rightColumn: 16, + alignment: FlexLayerAlignment.Start, + topRow: 0, + bottomRow: 4, + type: "BUTTON_WIDGET", + widgetName: "Button1", + renderMode: RenderModes.CANVAS, + version: 1, + parentColumnSpace: 10, + parentRowSpace: 10, + isLoading: false, + mobileTopRow: 0, + mobileBottomRow: 4, + mobileLeftColumn: 0, + mobileRightColumn: 16, + responsiveBehavior: ResponsiveBehavior.Hug, + parentId: "1", + }, + { + widgetId: "3", + leftColumn: 0, + rightColumn: 16, + alignment: FlexLayerAlignment.Start, + topRow: 0, + bottomRow: 6, + type: "BUTTON_WIDGET", + widgetName: "Button2", + renderMode: RenderModes.CANVAS, + version: 1, + parentColumnSpace: 10, + parentRowSpace: 10, + isLoading: false, + mobileTopRow: 0, + mobileBottomRow: 6, + mobileLeftColumn: 0, + mobileRightColumn: 16, + responsiveBehavior: ResponsiveBehavior.Hug, + parentId: "1", + }, + ]; + const result: HighlightInfo[] = generateHighlightsForAlignment({ + arr: children, + childCount: 0, + layerIndex: 0, + alignment: FlexLayerAlignment.Start, + maxHeight: + getWidgetHeight(children[1], false) * children[1].parentRowSpace, + offsetTop: 4, + parentColumnSpace: 10, + canvasWidth: 640, + avoidInitialHighlight: false, + isMobile: false, + startPosition: 0, + canvasId: "1", + }); + expect(result.length).toEqual(3); + expect(result[0].height).toEqual( + getWidgetHeight(children[1], false) * children[1].parentRowSpace, + ); + }); + it("should not render initial highlight is avoidInitialHighlight is true", () => { + const result: HighlightInfo[] = generateHighlightsForAlignment({ + arr: [], + childCount: 0, + layerIndex: 0, + alignment: FlexLayerAlignment.Start, + maxHeight: 40, + offsetTop: 4, + parentColumnSpace: 10, + canvasWidth: 640, + avoidInitialHighlight: true, + isMobile: false, + startPosition: 0, + canvasId: "1", + }); + expect(result.length).toEqual(0); + }); + }); + + describe("test generateVerticalHighlights method", () => { + it("should not render initial highlight for an empty center alignment if one of the other alignments are encroaching its space", () => { + const widgets = { + "1": { + widgetId: "1", + leftColumn: 0, + rightColumn: 64, + alignment: FlexLayerAlignment.Start, + topRow: 0, + bottomRow: 70, + type: "CANVAS_WIDGET", + widgetName: "Canvas1", + renderMode: RenderModes.CANVAS, + version: 1, + parentColumnSpace: 1, + parentRowSpace: 1, + isLoading: false, + mobileTopRow: 0, + mobileBottomRow: 70, + mobileLeftColumn: 0, + mobileRightColumn: 640, + responsiveBehavior: ResponsiveBehavior.Fill, + parentId: "", + flexLayers: [ + { + children: [ + { id: "2", align: FlexLayerAlignment.Start }, + { id: "3", align: FlexLayerAlignment.Start }, + ], + }, + ], + children: ["2", "3"], + }, + "2": { + widgetId: "2", + leftColumn: 0, + rightColumn: 16, + alignment: FlexLayerAlignment.Start, + topRow: 0, + bottomRow: 4, + type: "BUTTON_WIDGET", + widgetName: "Button1", + renderMode: RenderModes.CANVAS, + version: 1, + parentColumnSpace: 10, + parentRowSpace: 10, + isLoading: false, + mobileTopRow: 0, + mobileBottomRow: 4, + mobileLeftColumn: 0, + mobileRightColumn: 16, + responsiveBehavior: ResponsiveBehavior.Hug, + parentId: "1", + }, + "3": { + widgetId: "3", + leftColumn: 16, + rightColumn: 26, + alignment: FlexLayerAlignment.Start, + topRow: 0, + bottomRow: 6, + type: "BUTTON_WIDGET", + widgetName: "Button2", + renderMode: RenderModes.CANVAS, + version: 1, + parentColumnSpace: 10, + parentRowSpace: 10, + isLoading: false, + mobileTopRow: 0, + mobileBottomRow: 6, + mobileLeftColumn: 16, + mobileRightColumn: 26, + responsiveBehavior: ResponsiveBehavior.Hug, + parentId: "1", + }, + }; + const result: VerticalHighlightsPayload = generateVerticalHighlights({ + widgets, + layer: widgets["1"].flexLayers[0], + childCount: 0, + layerIndex: 0, + offsetTop: 4, + canvasWidth: 640, + canvasId: "1", + columnSpace: 10, + draggedWidgets: [], + isMobile: false, + }); + expect(result.highlights.length).toEqual(4); + expect(result.childCount).toEqual(2); + }); + it("should not render highlights for flex wrapped alignments that span multiple rows", () => { + const widgets = { + "1": { + widgetId: "1", + leftColumn: 0, + rightColumn: 64, + alignment: FlexLayerAlignment.Start, + topRow: 0, + bottomRow: 70, + type: "CANVAS_WIDGET", + widgetName: "Canvas1", + renderMode: RenderModes.CANVAS, + version: 1, + parentColumnSpace: 1, + parentRowSpace: 1, + isLoading: false, + mobileTopRow: 0, + mobileBottomRow: 70, + mobileLeftColumn: 0, + mobileRightColumn: 640, + responsiveBehavior: ResponsiveBehavior.Fill, + parentId: "", + flexLayers: [ + { + children: [ + { id: "2", align: FlexLayerAlignment.Start }, + { id: "3", align: FlexLayerAlignment.Start }, + ], + }, + ], + children: ["2", "3"], + }, + "2": { + widgetId: "2", + leftColumn: 0, + rightColumn: 16, + alignment: FlexLayerAlignment.Start, + topRow: 0, + bottomRow: 4, + type: "BUTTON_WIDGET", + widgetName: "Button1", + renderMode: RenderModes.CANVAS, + version: 1, + parentColumnSpace: 10, + parentRowSpace: 10, + isLoading: false, + mobileTopRow: 0, + mobileBottomRow: 4, + mobileLeftColumn: 0, + mobileRightColumn: 16, + responsiveBehavior: ResponsiveBehavior.Hug, + parentId: "1", + }, + "3": { + widgetId: "3", + leftColumn: 16, + rightColumn: 64, + alignment: FlexLayerAlignment.Start, + topRow: 0, + bottomRow: 6, + type: "INPUT_WIDGET", + widgetName: "Button2", + renderMode: RenderModes.CANVAS, + version: 1, + parentColumnSpace: 10, + parentRowSpace: 10, + isLoading: false, + mobileTopRow: 4, + mobileBottomRow: 10, + mobileLeftColumn: 0, + mobileRightColumn: 64, + responsiveBehavior: ResponsiveBehavior.Fill, + parentId: "1", + }, + }; + const result: VerticalHighlightsPayload = generateVerticalHighlights({ + widgets, + layer: widgets["1"].flexLayers[0], + childCount: 0, + layerIndex: 0, + offsetTop: 4, + canvasWidth: 640, + canvasId: "1", + columnSpace: 10, + draggedWidgets: [], + isMobile: true, + }); + expect(result.highlights.length).toEqual(3); + expect(result.highlights[1].posY).toEqual( + widgets["3"].mobileTopRow * widgets["3"].parentRowSpace + + FLEXBOX_PADDING, + ); + expect(result.highlights[1].height).toEqual(60); + expect(result.highlights[2].posX).toEqual(636); + expect(result.highlights[2].posY).toEqual( + widgets["3"].mobileTopRow * widgets["3"].parentRowSpace + + FLEXBOX_PADDING, + ); + expect(result.childCount).toEqual(2); + }); + }); +}); diff --git a/app/client/src/utils/autoLayout/highlightUtils.ts b/app/client/src/utils/autoLayout/highlightUtils.ts index 7830c9bc2aa3..0d2b4f7f61ed 100644 --- a/app/client/src/utils/autoLayout/highlightUtils.ts +++ b/app/client/src/utils/autoLayout/highlightUtils.ts @@ -127,7 +127,7 @@ export function deriveHighlightsFromLayers( return []; } } -interface VerticalHighlightsPayload { +export interface VerticalHighlightsPayload { childCount: number; highlights: HighlightInfo[]; } @@ -137,7 +137,7 @@ interface VerticalHighlightsPayload { * - Breakdown each layer into component alignments. * - generate highlight information for each alignment. */ -function generateVerticalHighlights(data: { +export function generateVerticalHighlights(data: { widgets: CanvasWidgetsReduxState; layer: FlexLayer; childCount: number; @@ -236,7 +236,8 @@ function generateVerticalHighlights(data: { if (item.alignment === FlexLayerAlignment.Center) { const { centerSize } = getAlignmentSizeInfo(each); avoidInitialHighlight = - startColumns > 25 || endColumns > 25 || centerSize === 0; + ((startColumns > 25 || endColumns > 25) && centerColumns === 0) || + centerSize === 0; if (each.length === 2) startPosition = index === 0 @@ -258,9 +259,7 @@ function generateVerticalHighlights(data: { offsetTop, canvasId, parentColumnSpace: columnSpace, - parentRowSpace: widgets[canvasId].parentRowSpace, canvasWidth, - columnSpace, isMobile, avoidInitialHighlight, startPosition, @@ -279,7 +278,7 @@ function generateVerticalHighlights(data: { * - Add another highlight at the end position of the last widget in the alignment. * - If the alignment has no children, then add an initial highlight to mark the start of the alignment. */ -function generateHighlightsForAlignment(data: { +export function generateHighlightsForAlignment(data: { arr: Widget[]; childCount: number; layerIndex: number; @@ -288,9 +287,7 @@ function generateHighlightsForAlignment(data: { offsetTop: number; canvasId: string; parentColumnSpace: number; - parentRowSpace: number; canvasWidth: number; - columnSpace: number; avoidInitialHighlight?: boolean; isMobile: boolean; startPosition: number | undefined; @@ -302,7 +299,6 @@ function generateHighlightsForAlignment(data: { canvasId, canvasWidth, childCount, - columnSpace, isMobile, layerIndex, maxHeight, @@ -350,7 +346,7 @@ function generateHighlightsForAlignment(data: { : 0, canvasWidth, canvasId, - columnSpace, + parentColumnSpace, startPosition, ), posY: @@ -473,7 +469,7 @@ function getCanvasDimensions( return { canvasWidth: canvasWidth, columnSpace }; } -function getCanvasWidth( +export function getCanvasWidth( canvas: Widget, widgets: CanvasWidgetsReduxState, mainCanvasWidth: number, From c12469c8c81c3c17d62cc2a8730e137606a6045d Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 10 Jan 2023 18:11:06 -0500 Subject: [PATCH 372/708] make positioning a derived prop for container --- .../CanvasSagas/AutoLayoutDraggingSagas.ts | 207 +----------------- .../sagas/CanvasSagas/DraggingCanvasSagas.ts | 37 ++-- app/client/src/selectors/editorSelectors.tsx | 5 + .../autoLayout/autoLayoutDraggingUtils.ts | 204 +++++++++++++++++ app/client/src/widgets/BaseWidget.tsx | 2 + .../src/widgets/ContainerWidget/index.ts | 2 +- .../widgets/ContainerWidget/widget/index.tsx | 5 +- app/client/src/widgets/withWidgetProps.tsx | 3 + 8 files changed, 247 insertions(+), 218 deletions(-) create mode 100644 app/client/src/utils/autoLayout/autoLayoutDraggingUtils.ts diff --git a/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts b/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts index 5c46be545c7b..4a7bca3df17b 100644 --- a/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts +++ b/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts @@ -4,12 +4,8 @@ import { ReduxActionTypes, } from "ce/constants/ReduxActionConstants"; import { FlexLayerAlignment, LayoutDirection } from "components/constants"; -import { - FlexLayer, - LayerChild, -} from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; +import { FlexLayer } from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; import { GridDefaults } from "constants/WidgetConstants"; -import { isArray } from "lodash"; import log from "loglevel"; import { HighlightInfo } from "pages/common/CanvasArenas/hooks/useAutoLayoutHighlights"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; @@ -17,6 +13,13 @@ import { all, call, put, select, takeLatest } from "redux-saga/effects"; import { getWidgets } from "sagas/selectors"; import { getUpdateDslAfterCreatingChild } from "sagas/WidgetAdditionSagas"; import { getIsMobile } from "selectors/mainCanvasSelectors"; +import { + addNewLayer, + createFlexLayer, + removeWidgetsFromCurrentLayers, + updateExistingLayer, + updateRelationships, +} from "utils/autoLayout/autoLayoutDraggingUtils"; import { updateWidgetPositions } from "utils/autoLayout/positionUtils"; function* addWidgetAndReorderSaga( @@ -141,6 +144,7 @@ function* reorderAutolayoutChildren(params: { selectedWidgets, widgets, parentId, + true, isMobile, ); @@ -218,199 +222,6 @@ function* reorderAutolayoutChildren(params: { return widgetsAfterPositionUpdate; } -/** - * For all moved widgets, - * delete relationship with previous parent and - * add relationship with new parent - * @param movedWidgets - * @param widgets - * @param parentId - * @returns widgets - */ -function updateRelationships( - movedWidgets: string[], - allWidgets: CanvasWidgetsReduxState, - parentId: string, - isMobile = false, -): CanvasWidgetsReduxState { - const widgets = { ...allWidgets }; - // Check if parent has changed - const orphans = movedWidgets.filter( - (item) => widgets[item].parentId !== parentId, - ); - const prevParents: string[] = []; - if (orphans && orphans.length) { - //parent has changed - orphans.forEach((item) => { - // remove from previous parent - const prevParentId = widgets[item].parentId; - if (prevParentId !== undefined) { - prevParents.push(prevParentId); - const prevParent = Object.assign({}, widgets[prevParentId]); - if (prevParent.children && isArray(prevParent.children)) { - const updatedPrevParent = { - ...prevParent, - children: prevParent.children.filter((each) => each !== item), - flexLayers: removeWidgetsFromCurrentLayers( - widgets, - movedWidgets, - prevParent.flexLayers, - ), - }; - widgets[prevParentId] = updatedPrevParent; - } - } - - // add to new parent - widgets[item] = { - ...widgets[item], - parentId: parentId, - }; - }); - } - if (prevParents.length) { - for (const id of prevParents) { - const updatedWidgets = updateWidgetPositions(widgets, id, isMobile); - return updatedWidgets; - } - } - return widgets; -} - -function addNewLayer( - newLayer: FlexLayer, - allWidgets: CanvasWidgetsReduxState, - parentId: string, - layers: FlexLayer[], - layerIndex = 0, -): CanvasWidgetsReduxState { - const widgets: CanvasWidgetsReduxState = Object.assign({}, allWidgets); - const canvas = widgets[parentId]; - - const pos = layerIndex > layers.length ? layers.length : layerIndex; - - const updatedCanvas = { - ...canvas, - flexLayers: [...layers.slice(0, pos), newLayer, ...layers.slice(pos)], - }; - - const updatedWidgets = { - ...widgets, - [parentId]: updatedCanvas, - }; - - return updatedWidgets; -} - -function updateExistingLayer( - newLayer: FlexLayer, - allWidgets: CanvasWidgetsReduxState, - parentId: string, - layers: FlexLayer[], - index = 0, - layerIndex = 0, - rowIndex: number, -): CanvasWidgetsReduxState { - try { - const widgets: CanvasWidgetsReduxState = { ...allWidgets }; - const canvas = widgets[parentId]; - if (!canvas || !newLayer) return widgets; - - const map: { [key: string]: LayerChild[] } = { - [FlexLayerAlignment.Start]: [], - [FlexLayerAlignment.Center]: [], - [FlexLayerAlignment.End]: [], - }; - - for (const child of layers[layerIndex]?.children) { - map[child.align] = [...map[child.align], child]; - } - const alignment = newLayer.children[0].align; - map[alignment] = [ - ...map[alignment].slice(0, rowIndex), - ...newLayer?.children, - ...map[alignment].slice(rowIndex), - ]; - - // merge the selected layer with the new layer. - const selectedLayer = { - ...layers[layerIndex], - children: [ - ...map[FlexLayerAlignment.Start], - ...map[FlexLayerAlignment.Center], - ...map[FlexLayerAlignment.End], - ], - }; - - const updatedCanvas = { - ...canvas, - flexLayers: [ - ...layers.slice(0, layerIndex), - selectedLayer, - ...layers.slice(layerIndex + 1), - ], - }; - - const updatedWidgets = { ...widgets, [parentId]: updatedCanvas }; - return updatedWidgets; - } catch (e) { - console.error("#### update existing layer error", e); - return allWidgets; - } -} - -/** - * Transform movedWidgets to FlexLayer format, - * and determine if the new widgets have a fill child. - * @param movedWidgets - * @param allWidgets - * @param alignment - * @returns FlexLayer - */ -function createFlexLayer( - movedWidgets: string[], - allWidgets: CanvasWidgetsReduxState, - alignment: FlexLayerAlignment, -): FlexLayer { - const children = []; - if (movedWidgets && movedWidgets.length) { - for (const id of movedWidgets) { - const widget = allWidgets[id]; - if (!widget) continue; - children.push({ id, align: alignment }); - } - } - return { children }; -} - -/** - * Remove moved widgets from current layers. - * Return non-empty layers. - * @param allWidgets - * @param movedWidgets - * @param flexLayers - * @returns FlexLayer[] - */ -export function removeWidgetsFromCurrentLayers( - allWidgets: CanvasWidgetsReduxState, - movedWidgets: string[], - flexLayers: FlexLayer[], -): FlexLayer[] { - if (!flexLayers || !flexLayers.length) return []; - return flexLayers?.reduce((acc: FlexLayer[], layer: FlexLayer) => { - const children = layer.children.filter( - (each: LayerChild) => movedWidgets.indexOf(each.id) === -1, - ); - if (children.length) { - acc.push({ - ...layer, - children, - }); - } - return acc; - }, []); -} - export default function* autoLayoutDraggingSagas() { yield all([ takeLatest( diff --git a/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts b/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts index 8c92ad06bcda..f97e25aeb8c3 100644 --- a/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts +++ b/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts @@ -20,19 +20,22 @@ import { CanvasWidgetsReduxState, FlattenedWidgetProps, } from "reducers/entityReducers/canvasWidgetsReducer"; +import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; import { MainCanvasReduxState } from "reducers/uiReducers/mainCanvasReducer"; import { all, call, put, select, takeLatest } from "redux-saga/effects"; import { getWidget, getWidgets } from "sagas/selectors"; import { getUpdateDslAfterCreatingChild } from "sagas/WidgetAdditionSagas"; import { traverseTreeAndExecuteBlueprintChildOperations } from "sagas/WidgetBlueprintSagas"; import { + getCurrentAppPositioningType, getMainCanvasProps, getOccupiedSpacesSelectorForContainer, } from "selectors/editorSelectors"; +import { getIsMobile } from "selectors/mainCanvasSelectors"; import AnalyticsUtil from "utils/AnalyticsUtil"; +import { updateRelationships } from "utils/autoLayout/autoLayoutDraggingUtils"; import { collisionCheckPostReflow } from "utils/reflowHookUtils"; import { WidgetProps } from "widgets/BaseWidget"; -import { removeWidgetsFromCurrentLayers } from "./AutoLayoutDraggingSagas"; export type WidgetMoveParams = { widgetId: string; @@ -313,7 +316,9 @@ function* moveWidgetsSaga( const { canvasId, draggedBlocksToUpdate } = actionPayload.payload; try { const allWidgets: CanvasWidgetsReduxState = yield select(getWidgets); - + const appPositioningType: AppPositioningTypes = yield select( + getCurrentAppPositioningType, + ); const updatedWidgetsOnMove: CanvasWidgetsReduxState = yield call( moveAndUpdateWidgets, allWidgets, @@ -330,22 +335,18 @@ function* moveWidgetsSaga( ) { throw Error; } - // TODO: Put this piece of code in a proper place. - const prevParentId = - draggedBlocksToUpdate[0].updateWidgetParams.payload.parentId; - const prevParent = updatedWidgetsOnMove[prevParentId]; - - const updatedWidgets = { - ...updatedWidgetsOnMove, - [prevParent.widgetId]: { - ...prevParent, - flexLayers: removeWidgetsFromCurrentLayers( - updatedWidgetsOnMove, - draggedBlocksToUpdate.map((each) => each.widgetId), - prevParent.flexLayers, - ), - }, - }; + + let updatedWidgets: CanvasWidgetsReduxState = updatedWidgetsOnMove; + if (appPositioningType === AppPositioningTypes.AUTO) { + const isMobile: boolean = yield select(getIsMobile); + updatedWidgets = updateRelationships( + draggedBlocksToUpdate.map((block) => block.widgetId), + updatedWidgets, + canvasId, + false, + isMobile, + ); + } yield put(updateAndSaveLayout(updatedWidgets)); yield put(generateAutoHeightLayoutTreeAction(true, true)); diff --git a/app/client/src/selectors/editorSelectors.tsx b/app/client/src/selectors/editorSelectors.tsx index 2cfb065ea88d..4cd2a5f943eb 100644 --- a/app/client/src/selectors/editorSelectors.tsx +++ b/app/client/src/selectors/editorSelectors.tsx @@ -260,6 +260,11 @@ export const getCurrentApplicationLayout = createSelector( }, ); +export const isAutoLayoutEnabled = (state: AppState) => { + return true; + // state.ui.users.featureFlag.data.AUTO_LAYOUT === true; +}; + export const getCanvasWidth = (state: AppState) => state.ui.mainCanvas.width; export const getCanvasScale = (state: AppState) => state.ui.mainCanvas.scale; diff --git a/app/client/src/utils/autoLayout/autoLayoutDraggingUtils.ts b/app/client/src/utils/autoLayout/autoLayoutDraggingUtils.ts new file mode 100644 index 000000000000..effe2c5f034a --- /dev/null +++ b/app/client/src/utils/autoLayout/autoLayoutDraggingUtils.ts @@ -0,0 +1,204 @@ +import { FlexLayerAlignment } from "components/constants"; +import { + FlexLayer, + LayerChild, +} from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; +import { isArray } from "lodash"; +import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; +import { updateWidgetPositions } from "./positionUtils"; + +/** + * Transform movedWidgets to FlexLayer format, + * and determine if the new widgets have a fill child. + * @param movedWidgets + * @param allWidgets + * @param alignment + * @returns FlexLayer + */ +export function createFlexLayer( + movedWidgets: string[], + allWidgets: CanvasWidgetsReduxState, + alignment: FlexLayerAlignment, +): FlexLayer { + const children = []; + if (movedWidgets && movedWidgets.length) { + for (const id of movedWidgets) { + const widget = allWidgets[id]; + if (!widget) continue; + children.push({ id, align: alignment }); + } + } + return { children }; +} + +/** + * Remove moved widgets from current layers. + * Return non-empty layers. + * @param allWidgets + * @param movedWidgets + * @param flexLayers + * @returns FlexLayer[] + */ +export function removeWidgetsFromCurrentLayers( + allWidgets: CanvasWidgetsReduxState, + movedWidgets: string[], + flexLayers: FlexLayer[], +): FlexLayer[] { + if (!flexLayers || !flexLayers.length) return []; + return flexLayers?.reduce((acc: FlexLayer[], layer: FlexLayer) => { + const children = layer.children.filter( + (each: LayerChild) => movedWidgets.indexOf(each.id) === -1, + ); + if (children.length) { + acc.push({ + ...layer, + children, + }); + } + return acc; + }, []); +} + +/** + * For all moved widgets, + * delete relationship with previous parent and + * add relationship with new parent + * @param movedWidgets + * @param widgets + * @param parentId + * @returns widgets + */ +export function updateRelationships( + movedWidgets: string[], + allWidgets: CanvasWidgetsReduxState, + parentId: string, + addToNewParent = true, + isMobile = false, +): CanvasWidgetsReduxState { + const widgets = { ...allWidgets }; + // Check if parent has changed + const orphans = movedWidgets.filter( + (item) => widgets[item].parentId !== parentId, + ); + const prevParents: string[] = []; + if (orphans && orphans.length) { + //parent has changed + orphans.forEach((item) => { + // remove from previous parent + const prevParentId = widgets[item].parentId; + if (prevParentId !== undefined) { + prevParents.push(prevParentId); + const prevParent = Object.assign({}, widgets[prevParentId]); + if (prevParent.children && isArray(prevParent.children)) { + const updatedPrevParent = { + ...prevParent, + children: prevParent.children.filter((each) => each !== item), + flexLayers: removeWidgetsFromCurrentLayers( + widgets, + movedWidgets, + prevParent.flexLayers, + ), + }; + widgets[prevParentId] = updatedPrevParent; + } + } + + // add to new parent + if (addToNewParent) { + widgets[item] = { + ...widgets[item], + parentId: parentId, + }; + } + }); + } + if (prevParents.length) { + for (const id of prevParents) { + const updatedWidgets = updateWidgetPositions(widgets, id, isMobile); + return updatedWidgets; + } + } + return widgets; +} + +export function addNewLayer( + newLayer: FlexLayer, + allWidgets: CanvasWidgetsReduxState, + parentId: string, + layers: FlexLayer[], + layerIndex = 0, +): CanvasWidgetsReduxState { + const widgets: CanvasWidgetsReduxState = Object.assign({}, allWidgets); + const canvas = widgets[parentId]; + + const pos = layerIndex > layers.length ? layers.length : layerIndex; + + const updatedCanvas = { + ...canvas, + flexLayers: [...layers.slice(0, pos), newLayer, ...layers.slice(pos)], + }; + + const updatedWidgets = { + ...widgets, + [parentId]: updatedCanvas, + }; + + return updatedWidgets; +} + +export function updateExistingLayer( + newLayer: FlexLayer, + allWidgets: CanvasWidgetsReduxState, + parentId: string, + layers: FlexLayer[], + index = 0, + layerIndex = 0, + rowIndex: number, +): CanvasWidgetsReduxState { + try { + const widgets: CanvasWidgetsReduxState = { ...allWidgets }; + const canvas = widgets[parentId]; + if (!canvas || !newLayer) return widgets; + + const map: { [key: string]: LayerChild[] } = { + [FlexLayerAlignment.Start]: [], + [FlexLayerAlignment.Center]: [], + [FlexLayerAlignment.End]: [], + }; + + for (const child of layers[layerIndex]?.children) { + map[child.align] = [...map[child.align], child]; + } + const alignment = newLayer.children[0].align; + map[alignment] = [ + ...map[alignment].slice(0, rowIndex), + ...newLayer?.children, + ...map[alignment].slice(rowIndex), + ]; + + // merge the selected layer with the new layer. + const selectedLayer = { + ...layers[layerIndex], + children: [ + ...map[FlexLayerAlignment.Start], + ...map[FlexLayerAlignment.Center], + ...map[FlexLayerAlignment.End], + ], + }; + + const updatedCanvas = { + ...canvas, + flexLayers: [ + ...layers.slice(0, layerIndex), + selectedLayer, + ...layers.slice(layerIndex + 1), + ], + }; + + const updatedWidgets = { ...widgets, [parentId]: updatedCanvas }; + return updatedWidgets; + } catch (e) { + console.error("#### update existing layer error", e); + return allWidgets; + } +} diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index fca8b8e9e067..253fa0f70d3e 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -36,6 +36,7 @@ import { Stylesheet } from "entities/AppTheming"; import { DataTreeWidget } from "entities/DataTree/dataTreeFactory"; import { get, memoize } from "lodash"; import React, { Component, ReactNode } from "react"; +import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; import shallowequal from "shallowequal"; import { CSSProperties } from "styled-components"; import AnalyticsUtil from "utils/AnalyticsUtil"; @@ -649,6 +650,7 @@ export interface WidgetPositionProps extends WidgetRowCols { minWidth?: number; // Required to avoid squishing of widgets on mobile viewport. isMobile?: boolean; flexVerticalAlignment?: FlexVerticalAlignment; + appPositioningType?: AppPositioningTypes; } export const WIDGET_DISPLAY_PROPS = { diff --git a/app/client/src/widgets/ContainerWidget/index.ts b/app/client/src/widgets/ContainerWidget/index.ts index c3a52d9dd647..29a7983c0355 100644 --- a/app/client/src/widgets/ContainerWidget/index.ts +++ b/app/client/src/widgets/ContainerWidget/index.ts @@ -44,7 +44,7 @@ export const CONFIG = { ], }, version: 1, - positioning: Positioning.Vertical, + // positioning: Positioning.Vertical, responsiveBehavior: getDefaultResponsiveBehavior(Widget.getWidgetType()), minWidth: FILL_WIDGET_MIN_WIDTH, }, diff --git a/app/client/src/widgets/ContainerWidget/widget/index.tsx b/app/client/src/widgets/ContainerWidget/widget/index.tsx index 4169fcf1705b..c4bcd70d7982 100644 --- a/app/client/src/widgets/ContainerWidget/widget/index.tsx +++ b/app/client/src/widgets/ContainerWidget/widget/index.tsx @@ -20,6 +20,7 @@ import WidgetsMultiSelectBox from "pages/Editor/WidgetsMultiSelectBox"; import { Positioning } from "components/constants"; import { Stylesheet } from "entities/AppTheming"; import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; +import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; export class ContainerWidget extends BaseWidget< ContainerWidgetProps, @@ -139,7 +140,9 @@ export class ContainerWidget extends BaseWidget< } static getDerivedPropertiesMap(): DerivedPropertiesMap { - return {}; + return { + positioning: `{{ this.appPositioningType === ${AppPositioningTypes.AUTO} ? ${Positioning.Vertical} : ${Positioning.Fixed} }}`, + }; } static getDefaultPropertiesMap(): Record { return {}; diff --git a/app/client/src/widgets/withWidgetProps.tsx b/app/client/src/widgets/withWidgetProps.tsx index 17d67a1649df..9fefea305343 100644 --- a/app/client/src/widgets/withWidgetProps.tsx +++ b/app/client/src/widgets/withWidgetProps.tsx @@ -17,6 +17,7 @@ import { import { computeMainContainerWidget, getChildWidgets, + getCurrentAppPositioningType, getMainCanvasProps, getRenderMode, previewModeSelector, @@ -51,6 +52,7 @@ function withWidgetProps(WrappedWidget: typeof BaseWidget) { getIsWidgetLoading(state, canvasWidget?.widgetName), ); const isMobile = useSelector(getIsMobile); + const appPositioningType = useSelector(getCurrentAppPositioningType); const dispatch = useDispatch(); @@ -74,6 +76,7 @@ function withWidgetProps(WrappedWidget: typeof BaseWidget) { widgetProps = { ...canvasWidgetProps }; widgetProps.isMobile = !!isMobile; + widgetProps.appPositioningType = appPositioningType; /** * MODAL_WIDGET by default is to be hidden unless the isVisible property is found. From a52e113db7a485e180424c57d6c5cc2ad7c51343 Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 10 Jan 2023 18:21:05 -0500 Subject: [PATCH 373/708] make positioning a derived property in tabs widget --- .../src/widgets/TabsWidget/widget/index.tsx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/app/client/src/widgets/TabsWidget/widget/index.tsx b/app/client/src/widgets/TabsWidget/widget/index.tsx index 82a31a51d18b..9c727a4ce007 100644 --- a/app/client/src/widgets/TabsWidget/widget/index.tsx +++ b/app/client/src/widgets/TabsWidget/widget/index.tsx @@ -9,12 +9,10 @@ import { import { Stylesheet } from "entities/AppTheming"; import { find } from "lodash"; import React from "react"; +import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; import { WidgetProperties } from "selectors/propertyPaneSelectors"; import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService"; -import { - generatePositioningConfig, - getResponsiveLayoutConfig, -} from "utils/layoutPropertiesUtils"; +import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import WidgetFactory from "utils/WidgetFactory"; import BaseWidget, { WidgetState } from "../../BaseWidget"; import TabsComponent from "../component"; @@ -109,7 +107,6 @@ class TabsWidget extends BaseWidget< isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, - { ...generatePositioningConfig(Positioning.Vertical) }, ], }, ], @@ -363,11 +360,14 @@ class TabsWidget extends BaseWidget< const selectedTabProps = Object.values(this.props.tabsObj)?.filter( (item) => item.widgetId === selectedTabWidgetId, )[0]; - childWidgetData.positioning = selectedTabProps?.positioning; - childWidgetData.useAutoLayout = - selectedTabProps?.positioning !== Positioning.Fixed; + const positioning: Positioning = + this.props.appPositioningType == AppPositioningTypes.AUTO + ? Positioning.Vertical + : Positioning.Fixed; + childWidgetData.positioning = positioning; + childWidgetData.useAutoLayout = positioning !== Positioning.Fixed; childWidgetData.direction = - selectedTabProps?.positioning === Positioning.Vertical + positioning === Positioning.Vertical ? LayoutDirection.Vertical : LayoutDirection.Horizontal; childWidgetData.alignment = selectedTabProps?.alignment; From dc684b688f85679fc74937e10b89f21ac0bac4fa Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 11 Jan 2023 08:53:09 -0500 Subject: [PATCH 374/708] remove auto layout container --- .../component/index.tsx | 132 --------- .../AutoLayoutContainerWidget/constants.ts | 2 - .../AutoLayoutContainerWidget/icon.svg | 44 --- .../AutoLayoutContainerWidget/index.ts | 47 ---- .../widget/index.tsx | 257 ------------------ 5 files changed, 482 deletions(-) delete mode 100644 app/client/src/widgets/AutoLayoutContainerWidget/component/index.tsx delete mode 100644 app/client/src/widgets/AutoLayoutContainerWidget/constants.ts delete mode 100644 app/client/src/widgets/AutoLayoutContainerWidget/icon.svg delete mode 100644 app/client/src/widgets/AutoLayoutContainerWidget/index.ts delete mode 100644 app/client/src/widgets/AutoLayoutContainerWidget/widget/index.tsx diff --git a/app/client/src/widgets/AutoLayoutContainerWidget/component/index.tsx b/app/client/src/widgets/AutoLayoutContainerWidget/component/index.tsx deleted file mode 100644 index fb375532820b..000000000000 --- a/app/client/src/widgets/AutoLayoutContainerWidget/component/index.tsx +++ /dev/null @@ -1,132 +0,0 @@ -import React, { ReactNode, useRef, useEffect, RefObject } from "react"; -import styled, { css } from "styled-components"; -import tinycolor from "tinycolor2"; -import { invisible } from "constants/DefaultTheme"; -import { Color } from "constants/Colors"; -import { generateClassName, getCanvasClassName } from "utils/generators"; -import { useCanvasMinHeightUpdateHook } from "utils/hooks/useCanvasMinHeightUpdateHook"; -import WidgetStyleContainer, { - WidgetStyleContainerProps, -} from "components/designSystems/appsmith/WidgetStyleContainer"; -import { pick } from "lodash"; -import { ComponentProps } from "widgets/BaseComponent"; -import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; - -const scrollContents = css` - overflow-y: auto; -`; - -const StyledContainerComponent = styled.div< - AutoLayoutContainerComponentProps & { - ref: RefObject; - } ->` - height: 100%; - width: 100%; - background: ${(props) => props.backgroundColor}; - opacity: ${(props) => (props.resizeDisabled ? "0.8" : "1")}; - position: relative; - ${(props) => (!props.isVisible ? invisible : "")}; - box-shadow: ${(props) => - props.selected ? "inset 0px 0px 0px 3px rgba(59,130,246,0.5)" : "none"}; - border-radius: ${({ borderRadius }) => borderRadius}; - - display: flex; - flex-direction: row; - justify-content: flex-start; - flex-wrap: wrap; - - ${(props) => - props.shouldScrollContents === true - ? scrollContents - : props.shouldScrollContents === false - ? css` - overflow: hidden; - ` - : ""} - - &:hover { - z-index: ${(props) => (props.onClickCapture ? "2" : "1")}; - cursor: ${(props) => (props.onClickCapture ? "pointer" : "inherit")}; - background: ${(props) => { - return props.onClickCapture && props.backgroundColor - ? tinycolor(props.backgroundColor) - .darken(5) - .toString() - : props.backgroundColor; - }}; - } -`; - -function ContainerComponentWrapper(props: AutoLayoutContainerComponentProps) { - const containerStyle = props.containerStyle || "card"; - const containerRef: RefObject = useRef(null); - useEffect(() => { - if (!props.shouldScrollContents) { - const supportsNativeSmoothScroll = - "scrollBehavior" in document.documentElement.style; - if (supportsNativeSmoothScroll) { - containerRef.current?.scrollTo({ top: 0, behavior: "smooth" }); - } else { - if (containerRef.current) { - containerRef.current.scrollTop = 0; - } - } - } - }, [props.shouldScrollContents]); - return ( - - {props.children} - - ); -} - -function AutoLayoutContainerComponent( - props: AutoLayoutContainerComponentProps, -) { - useCanvasMinHeightUpdateHook(props.widgetId, props.minHeight); - return props.widgetId === MAIN_CONTAINER_WIDGET_ID ? ( - - ) : ( - - - - ); -} - -export type ContainerStyle = "border" | "card" | "rounded-border" | "none"; - -export interface AutoLayoutContainerComponentProps - extends ComponentProps, - WidgetStyleContainerProps { - children?: ReactNode; - className?: string; - backgroundColor?: Color; - shouldScrollContents?: boolean; - resizeDisabled?: boolean; - selected?: boolean; - focused?: boolean; - minHeight?: number; - useAutoLayout?: boolean; -} - -export default AutoLayoutContainerComponent; diff --git a/app/client/src/widgets/AutoLayoutContainerWidget/constants.ts b/app/client/src/widgets/AutoLayoutContainerWidget/constants.ts deleted file mode 100644 index 857d86d0e12b..000000000000 --- a/app/client/src/widgets/AutoLayoutContainerWidget/constants.ts +++ /dev/null @@ -1,2 +0,0 @@ -// This file contains common constants which can be used across the widget configuration file (index.ts), widget and component folders. -export const AUTOLAYOUTCONTAINER_WIDGET_CONSTANT = ""; diff --git a/app/client/src/widgets/AutoLayoutContainerWidget/icon.svg b/app/client/src/widgets/AutoLayoutContainerWidget/icon.svg deleted file mode 100644 index 43fda6a45216..000000000000 --- a/app/client/src/widgets/AutoLayoutContainerWidget/icon.svg +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/client/src/widgets/AutoLayoutContainerWidget/index.ts b/app/client/src/widgets/AutoLayoutContainerWidget/index.ts deleted file mode 100644 index 3119acc6446c..000000000000 --- a/app/client/src/widgets/AutoLayoutContainerWidget/index.ts +++ /dev/null @@ -1,47 +0,0 @@ -import Widget from "./widget"; -import IconSVG from "./icon.svg"; -import { ButtonBoxShadowTypes } from "components/constants"; - -export const CONFIG = { - type: Widget.getWidgetType(), - name: "AutoLayout Container", // The display name which will be made in uppercase and show in the widgets panel ( can have spaces ) - iconSVG: IconSVG, - needsMeta: false, // Defines if this widget adds any meta properties - isCanvas: true, // Defines if this widget has a canvas within in which we can drop other widgets - searchTags: ["auto layout", "flex", "div", "parent", "group"], - defaults: { - widgetName: "AutoLayoutContainer", - rows: 40, - columns: 24, - version: 1, - backgroundColor: "#FFFFFF", - containerStyle: "card", - borderColor: "transparent", - borderWidth: "0", - boxShadow: ButtonBoxShadowTypes.NONE, - animateLoading: true, - children: [], - blueprint: { - view: [ - { - type: "CANVAS_WIDGET", - position: { top: 0, left: 0 }, - props: { - containerStyle: "none", - canExtend: false, - detachFromLayout: true, - children: [], - }, - }, - ], - }, - }, - properties: { - derived: Widget.getDerivedPropertiesMap(), - default: Widget.getDefaultPropertiesMap(), - meta: Widget.getMetaPropertiesMap(), - config: Widget.getPropertyPaneConfig(), - }, -}; - -export default Widget; diff --git a/app/client/src/widgets/AutoLayoutContainerWidget/widget/index.tsx b/app/client/src/widgets/AutoLayoutContainerWidget/widget/index.tsx deleted file mode 100644 index 63c20cd66f2c..000000000000 --- a/app/client/src/widgets/AutoLayoutContainerWidget/widget/index.tsx +++ /dev/null @@ -1,257 +0,0 @@ -import React from "react"; -import { compact, map, sortBy } from "lodash"; - -import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; -import WidgetFactory, { DerivedPropertiesMap } from "utils/WidgetFactory"; - -import AutoLayoutContainerComponent from "../component"; -import { ValidationTypes } from "constants/WidgetValidation"; -import { JustifyContent, LayoutDirection } from "components/constants"; -import { ContainerStyle } from "widgets/ContainerWidget/component"; - -class AutoLayoutContainerWidget extends BaseWidget< - AutoLayoutContainerWidgetProps, - WidgetState -> { - constructor(props: AutoLayoutContainerWidgetProps) { - super(props); - this.state = { - contextValue: { - useAutoLayout: false, - direction: LayoutDirection.Horizontal, - JustifyContent: JustifyContent.FlexStart, - overflow: "wrap", - }, - }; - this.renderChildWidget = this.renderChildWidget.bind(this); - } - - static getPropertyPaneConfig() { - return [ - { - propertyName: "useAutoLayout", - label: "Use Auto Layout", - controlType: "SWITCH", - defaultValue: true, - helpText: "Controls layout of children widgets", - isJSConvertible: true, - isBindProperty: true, - isTriggerProperty: false, - validation: { type: ValidationTypes.BOOLEAN }, - }, - { - sectionName: "Layout Controls", - hidden: (props: AutoLayoutContainerWidgetProps): boolean => - !props?.useAutoLayout, - children: [ - { - helpText: "Controls the direction of layout", - propertyName: "direction", - label: "Direction", - controlType: "DROP_DOWN", - defaultValue: LayoutDirection.Horizontal, - options: [ - { label: "Horizontal", value: LayoutDirection.Horizontal }, - { label: "Vertical", value: LayoutDirection.Vertical }, - ], - isJSConvertible: false, - isBindProperty: false, - isTriggerProperty: false, - validation: { type: ValidationTypes.TEXT }, - }, - { - helpText: "Controls alignment of the content", - propertyName: "justifyContent", - label: "Align content", - controlType: "DROP_DOWN", - defaultValue: JustifyContent.FlexStart, - options: [ - { label: "Flex start", value: JustifyContent.FlexStart }, - { label: "Center", value: JustifyContent.Center }, - { label: "Space around", value: JustifyContent.SpaceAround }, - { label: "Space between", value: JustifyContent.SpaceBetween }, - { label: "Space evently", value: JustifyContent.SpaceEvenly }, - ], - isJSConvertible: false, - isBindProperty: false, - isTriggerProperty: false, - validation: { type: ValidationTypes.TEXT }, - }, - ], - }, - { - sectionName: "General", - children: [ - { - helpText: "Controls the visibility of the widget", - propertyName: "isVisible", - label: "Visible", - controlType: "SWITCH", - isJSConvertible: true, - isBindProperty: true, - isTriggerProperty: false, - validation: { type: ValidationTypes.BOOLEAN }, - }, - { - propertyName: "animateLoading", - label: "Animate Loading", - controlType: "SWITCH", - helpText: "Controls the loading of the widget", - defaultValue: true, - isJSConvertible: true, - isBindProperty: true, - isTriggerProperty: false, - validation: { type: ValidationTypes.BOOLEAN }, - }, - { - helpText: "Enables scrolling for content inside the widget", - propertyName: "shouldScrollContents", - label: "Scroll Contents", - controlType: "SWITCH", - isBindProperty: false, - isTriggerProperty: false, - }, - ], - }, - { - sectionName: "Styles", - children: [ - { - helpText: "Use a html color name, HEX, RGB or RGBA value", - placeholderText: "#FFFFFF / Gray / rgb(255, 99, 71)", - propertyName: "backgroundColor", - label: "Background Color", - controlType: "COLOR_PICKER", - isJSConvertible: true, - isBindProperty: true, - isTriggerProperty: false, - validation: { type: ValidationTypes.TEXT }, - }, - { - helpText: "Use a html color name, HEX, RGB or RGBA value", - placeholderText: "#FFFFFF / Gray / rgb(255, 99, 71)", - propertyName: "borderColor", - label: "Border Color", - controlType: "COLOR_PICKER", - isBindProperty: true, - isTriggerProperty: false, - validation: { type: ValidationTypes.TEXT }, - }, - { - helpText: "Enter value for border width", - propertyName: "borderWidth", - label: "Border Width", - placeholderText: "Enter value in px", - controlType: "INPUT_TEXT", - isBindProperty: true, - isTriggerProperty: false, - validation: { type: ValidationTypes.NUMBER }, - }, - { - propertyName: "borderRadius", - label: "Border Radius", - helpText: - "Rounds the corners of the icon button's outer border edge", - controlType: "BORDER_RADIUS_OPTIONS", - isJSConvertible: true, - isBindProperty: true, - isTriggerProperty: false, - validation: { type: ValidationTypes.TEXT }, - }, - { - propertyName: "boxShadow", - label: "Box Shadow", - helpText: - "Enables you to cast a drop shadow from the frame of the widget", - controlType: "BOX_SHADOW_OPTIONS", - isJSConvertible: true, - isBindProperty: true, - isTriggerProperty: false, - validation: { type: ValidationTypes.TEXT }, - }, - ], - }, - ]; - } - - static getDerivedPropertiesMap(): DerivedPropertiesMap { - return {}; - } - - static getDefaultPropertiesMap(): Record { - return {}; - } - - static getMetaPropertiesMap(): Record { - return {}; - } - - // componentDidUpdate(prevProps: AutoLayoutContainerWidgetProps): void { - // if (prevProps.) - // } - - renderChildWidget(childWidgetData: WidgetProps): React.ReactNode { - // For now, isVisible prop defines whether to render a detached widget - if (childWidgetData.detachFromLayout && !childWidgetData.isVisible) { - return null; - } - - const { componentHeight, componentWidth } = this.getComponentDimensions(); - - childWidgetData.rightColumn = componentWidth; - childWidgetData.bottomRow = this.props.shouldScrollContents - ? childWidgetData.bottomRow - : componentHeight; - childWidgetData.minHeight = componentHeight; - childWidgetData.isVisible = this.props.isVisible; - childWidgetData.shouldScrollContents = false; - childWidgetData.canExtend = this.props.shouldScrollContents; - - childWidgetData.parentId = this.props.widgetId; - // Pass layout controls to children - childWidgetData.useAutoLayout = this.props.useAutoLayout; - childWidgetData.direction = this.props.direction; - childWidgetData.justifyContent = this.props.justifyContent; - - return WidgetFactory.createWidget(childWidgetData, this.props.renderMode); - } - - renderChildren = () => { - return map( - // sort by row so stacking context is correct - // TODO(abhinav): This is hacky. The stacking context should increase for widgets rendered top to bottom, always. - // Figure out a way in which the stacking context is consistent. - sortBy(compact(this.props.children), (child) => child.topRow), - this.renderChildWidget, - ); - }; - - renderAsContainerComponent( - props: AutoLayoutContainerWidgetProps, - ) { - return ( - - {/* without the wrapping div onClick events are triggered twice */} - <>{this.renderChildren()} - - ); - } - - getPageView() { - return this.renderAsContainerComponent(this.props); - } - - static getWidgetType(): string { - return "AUTOLAYOUTCONTAINER_WIDGET"; - } -} - -export interface AutoLayoutContainerWidgetProps - extends WidgetProps { - children?: T[]; - containerStyle?: ContainerStyle; - shouldScrollContents?: boolean; - noPad?: boolean; -} - -export default AutoLayoutContainerWidget; From 690fa7dbf91dbf06685eefbaf5e44359f1509610 Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 11 Jan 2023 16:34:40 -0500 Subject: [PATCH 375/708] fix highlight calculation on main container edge --- .../CanvasArenas/hooks/canvasDraggingUtils.ts | 55 ++++++++++++++----- .../hooks/useBlocksToBeDraggedOnCanvas.ts | 1 + .../CanvasArenas/hooks/useCanvasDragging.ts | 20 +++++-- 3 files changed, 58 insertions(+), 18 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/canvasDraggingUtils.ts b/app/client/src/pages/common/CanvasArenas/hooks/canvasDraggingUtils.ts index e0e85d9df9ed..732ff40c72dd 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/canvasDraggingUtils.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/canvasDraggingUtils.ts @@ -8,6 +8,7 @@ import { SpaceMap, VERTICAL_RESIZE_MIN_LIMIT, } from "reflow/reflowTypes"; +import { getContainerExitEdge } from "reflow/reflowUtils"; import { getDraggingSpacesFromBlocks, getDropZoneOffsets, @@ -142,7 +143,7 @@ export function modifyDrawingRectangles( * @returns movement direction */ export function getMoveDirection( - prevPosition: OccupiedSpace, + prevPosition: OccupiedSpace | null, currentPosition: OccupiedSpace, currentDirection: ReflowDirection, ) { @@ -155,7 +156,8 @@ export function getMoveDirection( Math.abs(currentPosition.top - prevPosition.top), Math.abs(currentPosition.bottom - prevPosition.bottom), ); - if (deltaX >= deltaY) { + if (deltaX === deltaY) return currentDirection; + if (deltaX > deltaY) { if ( currentPosition.right - prevPosition.right > 0 || currentPosition.left - prevPosition.left > 0 @@ -167,18 +169,19 @@ export function getMoveDirection( currentPosition.left - prevPosition.left < 0 ) return ReflowDirection.LEFT; - } - if ( - currentPosition.bottom - prevPosition.bottom > 0 || - currentPosition.top - prevPosition.top > 0 - ) - return ReflowDirection.BOTTOM; + } else { + if ( + currentPosition.bottom - prevPosition.bottom > 0 || + currentPosition.top - prevPosition.top > 0 + ) + return ReflowDirection.BOTTOM; - if ( - currentPosition.bottom - prevPosition.bottom < 0 || - currentPosition.top - prevPosition.top < 0 - ) - return ReflowDirection.TOP; + if ( + currentPosition.bottom - prevPosition.bottom < 0 || + currentPosition.top - prevPosition.top < 0 + ) + return ReflowDirection.TOP; + } return currentDirection; } @@ -327,7 +330,18 @@ export function getInterpolatedMoveDirection( spaces: OccupiedSpace[], currentPosition: OccupiedSpace, direction: ReflowDirection, + exitContainer: OccupiedSpace | undefined, + mousePosition: OccupiedSpace, ): ReflowDirection { + if (!spaces.length) { + if (exitContainer) + return getLastCanvasExitDirection( + exitContainer, + mousePosition, + direction, + ); + return getMoveDirection(null, currentPosition, direction); + } const accumulatedPositions = spaces.reduce( (acc, curr) => { return { @@ -340,6 +354,7 @@ export function getInterpolatedMoveDirection( }, { top: 0, right: 0, bottom: 0, left: 0, id: currentPosition.id }, ); + const lastPosition = { ...accumulatedPositions, top: accumulatedPositions.top / spaces.length, @@ -347,5 +362,19 @@ export function getInterpolatedMoveDirection( bottom: accumulatedPositions.bottom / spaces.length, left: accumulatedPositions.left / spaces.length, }; + return getMoveDirection(lastPosition, currentPosition, direction); } + +export function getLastCanvasExitDirection( + exitContainer: OccupiedSpace, + mousePosition: OccupiedSpace, + currentDirection: ReflowDirection, +): ReflowDirection { + const direction: ReflowDirection | undefined = getContainerExitEdge( + exitContainer, + mousePosition, + ); + if (direction) return direction; + return currentDirection; +} diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts b/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts index 29bd3fbe77f7..fb08d6601035 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts @@ -510,5 +510,6 @@ export const useBlocksToBeDraggedOnCanvas = ({ widgetOccupiedSpace: childrenOccupiedSpaces.filter( (each) => each.id === dragCenter?.widgetId, )[0], + occupiedSpaces, }; }; diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index 1329273c4d1d..fbaec3f6cc7c 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -81,6 +81,7 @@ export const useCanvasDragging = ( isResizing, lastDraggedCanvas, occSpaces, + occupiedSpaces, onDrop, parentDiff, relativeStartPoints, @@ -197,7 +198,7 @@ export const useCanvasDragging = ( bottom: 0, id: "", }; - let lastSnappedPositionDeltas: OccupiedSpace[] = []; + let lastSnappedPositions: OccupiedSpace[] = []; const resetCanvasState = () => { throttledStopReflowing(); @@ -435,22 +436,31 @@ export const useCanvasDragging = ( canScroll.current = false; renderNewRows(delta); } else if (!isUpdatingRows) { + triggerReflow(e, firstMove); const currentSnappedPosition = getDraggingSpacesFromBlocks( currentRectanglesToDraw, snapColumnSpace, snapRowSpace, )[0]; + const currentOccupiedSpace: any[] = occupiedSpaces[widgetId]; + let exitContainer: OccupiedSpace | undefined = undefined; + if (lastDraggedCanvas.current && currentOccupiedSpace) { + exitContainer = currentOccupiedSpace.find( + (each) => each.id === lastDraggedCanvas.current, + ); + } currentDirection.current = getInterpolatedMoveDirection( - lastSnappedPositionDeltas, + lastSnappedPositions, currentSnappedPosition, currentDirection.current, + exitContainer, + getMousePositionsOnCanvas(e, gridProps), ); - lastSnappedPositionDeltas = [ + lastSnappedPositions = [ currentSnappedPosition, - ...lastSnappedPositionDeltas.slice(0, 4), + ...lastSnappedPositions.slice(0, 4), ]; - triggerReflow(e, firstMove); if ( useAutoLayout && isCurrentDraggedCanvas && From 8ddf73bbe8171aeadc47450dc6c9faceab7c4dc2 Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 11 Jan 2023 17:21:18 -0500 Subject: [PATCH 376/708] fix parent height update --- app/client/src/utils/autoLayout/positionUtils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/client/src/utils/autoLayout/positionUtils.ts b/app/client/src/utils/autoLayout/positionUtils.ts index 7a3415738e2d..fa826e9c1edd 100644 --- a/app/client/src/utils/autoLayout/positionUtils.ts +++ b/app/client/src/utils/autoLayout/positionUtils.ts @@ -65,7 +65,7 @@ export function updateWidgetPositions( const divisor = parent.parentRowSpace === 1 ? 10 : 1; const parentHeight = getWidgetRows(parent, isMobile); - if (parentHeight <= height) { + if (parentHeight - height <= 1) { /** * if children height is greater than parent height, * update the parent height to match the children height @@ -75,7 +75,7 @@ export function updateWidgetPositions( const updatedParent = setDimensions( parent, parentTopRow, - (parentTopRow + height + 1) * divisor, + parentTopRow + height * divisor + 1 * divisor, null, null, isMobile, From a41e066963398fe501c7a9df05f45f8c55ffb55e Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 11 Jan 2023 17:39:34 -0500 Subject: [PATCH 377/708] fix isStack calculation --- app/client/src/sagas/AutoLayoutUtils.ts | 14 +++++++++----- app/client/src/sagas/WidgetAdditionSagas.ts | 6 ++---- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/app/client/src/sagas/AutoLayoutUtils.ts b/app/client/src/sagas/AutoLayoutUtils.ts index ec6eaa0a739f..f7dddc080b12 100644 --- a/app/client/src/sagas/AutoLayoutUtils.ts +++ b/app/client/src/sagas/AutoLayoutUtils.ts @@ -7,8 +7,12 @@ import { FlexLayer, LayerChild, } from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; -import { FLEXBOX_PADDING } from "constants/WidgetConstants"; +import { + FLEXBOX_PADDING, + MAIN_CONTAINER_WIDGET_ID, +} from "constants/WidgetConstants"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; +import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; import { updateWidgetPositions } from "utils/autoLayout/positionUtils"; function getCanvas(widgets: CanvasWidgetsReduxState, containerId: string) { @@ -338,10 +342,10 @@ export function isStack( const parent = widget.parentId ? allWidgets[widget.parentId] : undefined; return ( widget.positioning === Positioning.Vertical || - (parent && parent.positioning === Positioning.Vertical) || - (parent !== undefined && - parent?.type === "TABS_WIDGET" && - parent?.tabsObj[widget.tabId].positioning === Positioning.Vertical) + (parent && ["CONTAINER_WIDGET", "CANVAS_WIDGET"].includes(parent.type) + ? allWidgets[MAIN_CONTAINER_WIDGET_ID].appPositioningType === + AppPositioningTypes.AUTO + : false) ); } diff --git a/app/client/src/sagas/WidgetAdditionSagas.ts b/app/client/src/sagas/WidgetAdditionSagas.ts index adc7aea7767b..e85d5de9a2b5 100644 --- a/app/client/src/sagas/WidgetAdditionSagas.ts +++ b/app/client/src/sagas/WidgetAdditionSagas.ts @@ -34,6 +34,7 @@ import WidgetFactory from "utils/WidgetFactory"; import { generateWidgetProps } from "utils/WidgetPropsUtils"; import { WidgetProps } from "widgets/BaseWidget"; import { GRID_DENSITY_MIGRATION_V1 } from "widgets/constants"; +import { isStack } from "./AutoLayoutUtils"; import { getWidget, getWidgets } from "./selectors"; import { buildWidgetBlueprint, @@ -111,10 +112,7 @@ function* getChildWidgetProps( } } - const isAutoLayout = - parent?.positioning === Positioning.Vertical || - (parent.parentId && - widgets[parent.parentId].positioning === Positioning.Vertical); + const isAutoLayout = isStack(widgets, parent); if ( isAutoLayout && restDefaultConfig?.responsiveBehavior === ResponsiveBehavior.Fill From 153d23ad8d4e028aa4b066b2cd3a96f756858c61 Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 11 Jan 2023 18:01:25 -0500 Subject: [PATCH 378/708] refactor and remove unused code --- .../appsmith/WidgetStyleContainer.tsx | 1 - .../editorComponents/ResizableComponent.tsx | 19 +- .../reducers/uiReducers/dragResizeReducer.ts | 2 - .../src/resizable/resizenreflow/index.tsx | 13 +- app/client/src/utils/autoLayoutContext.ts | 16 -- .../ContainerWidget/component/index.tsx | 1 - .../component/index.tsx | 30 --- .../HorizontalLayoutWidget/constants.ts | 2 - .../widgets/HorizontalLayoutWidget/icon.svg | 1 - .../widgets/HorizontalLayoutWidget/index.ts | 47 ---- .../HorizontalLayoutWidget/widget/index.tsx | 195 ----------------- .../VerticalLayoutWidget/component/index.tsx | 31 --- .../widgets/VerticalLayoutWidget/constants.ts | 2 - .../src/widgets/VerticalLayoutWidget/icon.svg | 1 - .../src/widgets/VerticalLayoutWidget/index.ts | 47 ---- .../VerticalLayoutWidget/widget/index.tsx | 203 ------------------ 16 files changed, 6 insertions(+), 605 deletions(-) delete mode 100644 app/client/src/utils/autoLayoutContext.ts delete mode 100644 app/client/src/widgets/HorizontalLayoutWidget/component/index.tsx delete mode 100644 app/client/src/widgets/HorizontalLayoutWidget/constants.ts delete mode 100644 app/client/src/widgets/HorizontalLayoutWidget/icon.svg delete mode 100644 app/client/src/widgets/HorizontalLayoutWidget/index.ts delete mode 100644 app/client/src/widgets/HorizontalLayoutWidget/widget/index.tsx delete mode 100644 app/client/src/widgets/VerticalLayoutWidget/component/index.tsx delete mode 100644 app/client/src/widgets/VerticalLayoutWidget/constants.ts delete mode 100644 app/client/src/widgets/VerticalLayoutWidget/icon.svg delete mode 100644 app/client/src/widgets/VerticalLayoutWidget/index.ts delete mode 100644 app/client/src/widgets/VerticalLayoutWidget/widget/index.tsx diff --git a/app/client/src/components/designSystems/appsmith/WidgetStyleContainer.tsx b/app/client/src/components/designSystems/appsmith/WidgetStyleContainer.tsx index f2b5716f6d9c..c7cf7ddbcc32 100644 --- a/app/client/src/components/designSystems/appsmith/WidgetStyleContainer.tsx +++ b/app/client/src/components/designSystems/appsmith/WidgetStyleContainer.tsx @@ -24,7 +24,6 @@ export interface WidgetStyleContainerProps { borderRadius?: number; boxShadow?: BoxShadow; className?: string; - useAutoLayout?: boolean; direction?: string; } diff --git a/app/client/src/components/editorComponents/ResizableComponent.tsx b/app/client/src/components/editorComponents/ResizableComponent.tsx index 4882c00a2c29..7bf29833eb45 100644 --- a/app/client/src/components/editorComponents/ResizableComponent.tsx +++ b/app/client/src/components/editorComponents/ResizableComponent.tsx @@ -131,7 +131,7 @@ export const ResizableComponent = memo(function ResizableComponent( let canResizeVertically = true; let canResizeHorizontally = true; - // this is required for list widget so that template have no collision + // TODO: Ashok - remove this check when reflow is reintroduced. if (props.ignoreCollision || props.isFlexChild) return { canResizeHorizontally, @@ -192,7 +192,7 @@ export const ResizableComponent = memo(function ResizableComponent( // False, if none of the rows and cols have changed. const newRowCols: WidgetRowCols | false = computeFinalRowCols( delta, - props.isFlexChild ? { x: 0, y: position.y } : position, + position, props, ); @@ -261,13 +261,6 @@ export const ResizableComponent = memo(function ResizableComponent( widgetType: props.type, }); }; - const disabledHorizontalHandles = - props.isFlexChild && - props.responsiveBehavior === ResponsiveBehavior.Fill && - props.direction === LayoutDirection.Vertical && - false - ? ["left", "right", "bottomLeft", "bottomRight", "topLeft", "topRight"] - : []; const handles = useMemo(() => { const allHandles = { left: LeftHandleStyles, @@ -279,11 +272,9 @@ export const ResizableComponent = memo(function ResizableComponent( topRight: TopRightHandleStyles, bottomLeft: BottomLeftHandleStyles, }; - let handlesToOmit = get(props, "disabledResizeHandles", []); - if (disabledHorizontalHandles && disabledHorizontalHandles.length) - handlesToOmit = [...handlesToOmit, ...disabledHorizontalHandles]; + const handlesToOmit = get(props, "disabledResizeHandles", []); return omit(allHandles, handlesToOmit); - }, [props, disabledHorizontalHandles]); + }, [props]); const isEnabled = !isDragging && @@ -313,7 +304,6 @@ export const ResizableComponent = memo(function ResizableComponent( } }; - const isAffectedByDrag: boolean = isDragging; const snapGrid = useMemo( () => ({ x: props.parentColumnSpace, @@ -339,7 +329,6 @@ export const ResizableComponent = memo(function ResizableComponent( getResizedPositions={getResizedPositions} gridProps={gridProps} handles={handles} - isAffectedByDrag={isAffectedByDrag} isFlexChild={props.isFlexChild} isMobile={props.isMobile || false} onStart={handleResizeStart} diff --git a/app/client/src/reducers/uiReducers/dragResizeReducer.ts b/app/client/src/reducers/uiReducers/dragResizeReducer.ts index 3c79af526ae3..02ae4ea05a27 100644 --- a/app/client/src/reducers/uiReducers/dragResizeReducer.ts +++ b/app/client/src/reducers/uiReducers/dragResizeReducer.ts @@ -3,7 +3,6 @@ import { ReduxActionTypes, } from "@appsmith/constants/ReduxActionConstants"; import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; -import { HighlightInfo } from "pages/common/CanvasArenas/hooks/useAutoLayoutHighlights"; import { areArraysEqual } from "utils/AppsmithUtils"; import { createImmerReducer } from "utils/ReducerUtils"; @@ -168,7 +167,6 @@ export type WidgetDragResizeState = { isDragging: boolean; dragDetails: DragDetails; autoLayoutDragDetails: any; - flexHighlight?: HighlightInfo; isResizing: boolean; lastSelectedWidget?: string; focusedWidget?: string; diff --git a/app/client/src/resizable/resizenreflow/index.tsx b/app/client/src/resizable/resizenreflow/index.tsx index 3a692fb9bf2e..dffa80bb5ba0 100644 --- a/app/client/src/resizable/resizenreflow/index.tsx +++ b/app/client/src/resizable/resizenreflow/index.tsx @@ -170,7 +170,6 @@ type ResizableProps = { responsiveBehavior?: ResponsiveBehavior; direction?: LayoutDirection; paddingOffset: number; - isAffectedByDrag: boolean; isMobile: boolean; }; @@ -316,20 +315,12 @@ export function ReflowResizable(props: ResizableProps) { ...prevDimensions, width: props.componentWidth, height: props.componentHeight, - x: - !props.isAffectedByDrag && props.isFlexChild - ? props.paddingOffset / 4 - : 0, + x: 0, y: 0, reset: true, }; }); - }, [ - props.componentHeight, - props.componentWidth, - isResizing, - props.isAffectedByDrag, - ]); + }, [props.componentHeight, props.componentWidth, isResizing]); const handles = []; diff --git a/app/client/src/utils/autoLayoutContext.ts b/app/client/src/utils/autoLayoutContext.ts deleted file mode 100644 index 31557d8e0684..000000000000 --- a/app/client/src/utils/autoLayoutContext.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Context, createContext } from "react"; -import { - AlignItems, - JustifyContent, - LayoutDirection, - Overflow, -} from "components/constants"; - -export const AutoLayoutContext: Context<{ - useAutoLayout?: boolean; - direction?: LayoutDirection; - justifyContent?: JustifyContent; - overflow?: Overflow; - disabledResizeHandles?: string[]; - alignItems?: AlignItems; -}> = createContext({}); diff --git a/app/client/src/widgets/ContainerWidget/component/index.tsx b/app/client/src/widgets/ContainerWidget/component/index.tsx index 55b507d56681..04cec9d92f7d 100644 --- a/app/client/src/widgets/ContainerWidget/component/index.tsx +++ b/app/client/src/widgets/ContainerWidget/component/index.tsx @@ -108,7 +108,6 @@ function ContainerComponent(props: ContainerComponentProps) { "borderWidth", "borderRadius", "boxShadow", - "useAutoLayout", "direction", ])} > diff --git a/app/client/src/widgets/HorizontalLayoutWidget/component/index.tsx b/app/client/src/widgets/HorizontalLayoutWidget/component/index.tsx deleted file mode 100644 index 76c48d632d2d..000000000000 --- a/app/client/src/widgets/HorizontalLayoutWidget/component/index.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import React, { ReactNode } from "react"; -import WidgetStyleContainer, { - WidgetStyleContainerProps, -} from "components/designSystems/appsmith/WidgetStyleContainer"; -import { pick } from "lodash"; - -function HorizontalLayoutComponent(props: HorizontalLayoutComponentProps) { - return ( - - {props.children} - - ); -} - -export interface HorizontalLayoutComponentProps - extends WidgetStyleContainerProps { - children?: ReactNode; -} - -export default HorizontalLayoutComponent; diff --git a/app/client/src/widgets/HorizontalLayoutWidget/constants.ts b/app/client/src/widgets/HorizontalLayoutWidget/constants.ts deleted file mode 100644 index 5f46d7c3b236..000000000000 --- a/app/client/src/widgets/HorizontalLayoutWidget/constants.ts +++ /dev/null @@ -1,2 +0,0 @@ -// This file contains common constants which can be used across the widget configuration file (index.ts), widget and component folders. -export const HORIZONTALLAYOUT_WIDGET_CONSTANT = ""; diff --git a/app/client/src/widgets/HorizontalLayoutWidget/icon.svg b/app/client/src/widgets/HorizontalLayoutWidget/icon.svg deleted file mode 100644 index e97d99915ab8..000000000000 --- a/app/client/src/widgets/HorizontalLayoutWidget/icon.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/client/src/widgets/HorizontalLayoutWidget/index.ts b/app/client/src/widgets/HorizontalLayoutWidget/index.ts deleted file mode 100644 index 36dbd6bb4e8c..000000000000 --- a/app/client/src/widgets/HorizontalLayoutWidget/index.ts +++ /dev/null @@ -1,47 +0,0 @@ -import Widget from "./widget"; -import IconSVG from "./icon.svg"; -import { ButtonBoxShadowTypes } from "components/constants"; - -export const CONFIG = { - type: Widget.getWidgetType(), - name: "Horizontal Layout", // The display name which will be made in uppercase and show in the widgets panel ( can have spaces ) - iconSVG: IconSVG, - needsMeta: false, // Defines if this widget adds any meta properties - isCanvas: true, // Defines if this widget has a canvas within in which we can drop other widgets - searchTags: ["auto layout", "flex", "row", "div", "parent", "group"], - defaults: { - widgetName: "HorizontalLayout", - rows: 16, - columns: 30, - version: 1, - containerStyle: "card", - backgroundColor: "#FFFFFF", - borderColor: "transparent", - borderWidth: "0", - boxShadow: ButtonBoxShadowTypes.NONE, - animateLoading: true, - children: [], - blueprint: { - view: [ - { - type: "CANVAS_WIDGET", - position: { top: 0, left: 0 }, - props: { - containerStyle: "none", - canExtend: false, - detachFromLayout: true, - children: [], - }, - }, - ], - }, - }, - properties: { - derived: Widget.getDerivedPropertiesMap(), - default: Widget.getDefaultPropertiesMap(), - meta: Widget.getMetaPropertiesMap(), - config: Widget.getPropertyPaneConfig(), - }, -}; - -export default Widget; diff --git a/app/client/src/widgets/HorizontalLayoutWidget/widget/index.tsx b/app/client/src/widgets/HorizontalLayoutWidget/widget/index.tsx deleted file mode 100644 index ce39162e0211..000000000000 --- a/app/client/src/widgets/HorizontalLayoutWidget/widget/index.tsx +++ /dev/null @@ -1,195 +0,0 @@ -import React from "react"; - -import WidgetFactory, { DerivedPropertiesMap } from "utils/WidgetFactory"; -import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; - -import { - AlignItems, - JustifyContent, - LayoutDirection, -} from "components/constants"; -import { - CONTAINER_GRID_PADDING, - GridDefaults, - MAIN_CONTAINER_WIDGET_ID, - WIDGET_PADDING, -} from "constants/WidgetConstants"; -import { ValidationTypes } from "constants/WidgetValidation"; -import { map } from "lodash"; -import { CanvasDraggingArena } from "pages/common/CanvasArenas/CanvasDraggingArena"; -import { AutoLayoutContext } from "utils/autoLayoutContext"; -import { getCanvasSnapRows } from "utils/WidgetPropsUtils"; -import { ContainerStyle } from "widgets/ContainerWidget/component"; -import HorizontalLayoutComponent from "../component"; - -class HorizontalLayoutWidget extends BaseWidget< - HorizontalLayoutWidgetProps, - WidgetState -> { - constructor(props: HorizontalLayoutWidgetProps) { - super(props); - this.renderChildWidget = this.renderChildWidget.bind(this); - } - - static getPropertyPaneConfig() { - return [ - { - helpText: "Controls alignment of the content", - propertyName: "justifyContent", - label: "Align content", - controlType: "DROP_DOWN", - defaultValue: JustifyContent.FlexStart, - options: [ - { label: "Flex start", value: JustifyContent.FlexStart }, - { label: "Center", value: JustifyContent.Center }, - { label: "Space around", value: JustifyContent.SpaceAround }, - { label: "Space between", value: JustifyContent.SpaceBetween }, - { label: "Space evently", value: JustifyContent.SpaceEvenly }, - ], - isJSConvertible: false, - isBindProperty: false, - isTriggerProperty: false, - validation: { type: ValidationTypes.TEXT }, - }, - ]; - } - - static getDerivedPropertiesMap(): DerivedPropertiesMap { - return {}; - } - - static getDefaultPropertiesMap(): Record { - return {}; - } - - static getMetaPropertiesMap(): Record { - return {}; - } - - getSnapSpaces = () => { - const { componentWidth } = this.getComponentDimensions(); - // For all widgets inside a container, we remove both container padding as well as widget padding from component width - let padding = (CONTAINER_GRID_PADDING + WIDGET_PADDING) * 2; - if ( - this.props.widgetId === MAIN_CONTAINER_WIDGET_ID || - this.props.type === "CONTAINER_WIDGET" - ) { - //For MainContainer and any Container Widget padding doesn't exist coz there is already container padding. - padding = CONTAINER_GRID_PADDING * 2; - } - if (this.props.noPad) { - // Widgets like ListWidget choose to have no container padding so will only have widget padding - padding = WIDGET_PADDING * 2; - } - let width = componentWidth; - width -= padding; - return { - snapRowSpace: GridDefaults.DEFAULT_GRID_ROW_HEIGHT, - snapColumnSpace: componentWidth - ? width / GridDefaults.DEFAULT_GRID_COLUMNS - : 0, - }; - }; - - renderChildWidget(childWidgetData: WidgetProps): React.ReactNode { - // For now, isVisible prop defines whether to render a detached widget - if (childWidgetData.detachFromLayout && !childWidgetData.isVisible) { - return null; - } - - const { componentHeight, componentWidth } = this.getComponentDimensions(); - - childWidgetData.rightColumn = componentWidth; - childWidgetData.bottomRow = this.props.shouldScrollContents - ? childWidgetData.bottomRow - : componentHeight; - childWidgetData.minHeight = componentHeight; - childWidgetData.isVisible = this.props.isVisible; - childWidgetData.shouldScrollContents = false; - childWidgetData.canExtend = this.props.shouldScrollContents; - - childWidgetData.parentId = this.props.widgetId; - // Pass layout controls to children - const layoutProps = { - useAutoLayout: true, - direction: LayoutDirection.Horizontal, - justifyContent: JustifyContent.FlexStart, - alignItems: AlignItems.FlexStart, - }; - return WidgetFactory.createWidget( - { ...childWidgetData, ...layoutProps }, - this.props.renderMode, - ); - } - - renderChildren = () => { - return map(this.props.children, this.renderChildWidget); - }; - - getPageView() { - const snapRows = getCanvasSnapRows( - this.props.bottomRow, - this.props.canExtend, - ); - return ( - - - {this.props.type === "CANVAS_WIDGET" && ( - <> - - {/* - // Removing Canvas Selection and grouping in the POC - */} - - )} - {/* */} - {/* without the wrapping div onClick events are triggered twice */} - <>{this.renderChildren()} - - - ); - } - - static getWidgetType(): string { - return "HORIZONTALLAYOUT_WIDGET"; - } -} - -export interface HorizontalLayoutWidgetProps - extends WidgetProps { - children?: T[]; - containerStyle?: ContainerStyle; - shouldScrollContents?: boolean; - noPad?: boolean; -} - -export default HorizontalLayoutWidget; diff --git a/app/client/src/widgets/VerticalLayoutWidget/component/index.tsx b/app/client/src/widgets/VerticalLayoutWidget/component/index.tsx deleted file mode 100644 index 9f8cc2f8fd67..000000000000 --- a/app/client/src/widgets/VerticalLayoutWidget/component/index.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import WidgetStyleContainer, { - WidgetStyleContainerProps, -} from "components/designSystems/appsmith/WidgetStyleContainer"; -import React, { ReactNode } from "react"; -import { pick } from "lodash"; - -function VerticalLayoutComponent(props: VerticalLayoutComponentProps) { - return ( - - {props.children} - - ); -} - -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface VerticalLayoutComponentProps - extends WidgetStyleContainerProps { - children?: ReactNode; -} - -export default VerticalLayoutComponent; diff --git a/app/client/src/widgets/VerticalLayoutWidget/constants.ts b/app/client/src/widgets/VerticalLayoutWidget/constants.ts deleted file mode 100644 index 2138a2fe7129..000000000000 --- a/app/client/src/widgets/VerticalLayoutWidget/constants.ts +++ /dev/null @@ -1,2 +0,0 @@ -// This file contains common constants which can be used across the widget configuration file (index.ts), widget and component folders. -export const VERTICALLAYOUT_WIDGET_CONSTANT = ""; diff --git a/app/client/src/widgets/VerticalLayoutWidget/icon.svg b/app/client/src/widgets/VerticalLayoutWidget/icon.svg deleted file mode 100644 index e97d99915ab8..000000000000 --- a/app/client/src/widgets/VerticalLayoutWidget/icon.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/client/src/widgets/VerticalLayoutWidget/index.ts b/app/client/src/widgets/VerticalLayoutWidget/index.ts deleted file mode 100644 index a3c410ba81a3..000000000000 --- a/app/client/src/widgets/VerticalLayoutWidget/index.ts +++ /dev/null @@ -1,47 +0,0 @@ -import Widget from "./widget"; -import IconSVG from "./icon.svg"; -import { ButtonBoxShadowTypes } from "components/constants"; - -export const CONFIG = { - type: Widget.getWidgetType(), - name: "Vertical Layout", // The display name which will be made in uppercase and show in the widgets panel ( can have spaces ) - iconSVG: IconSVG, - needsMeta: false, // Defines if this widget adds any meta properties - isCanvas: true, // Defines if this widget has a canvas within in which we can drop other widgets - searchTags: ["auto layout", "flex", "column", "div", "parent", "group"], - defaults: { - widgetName: "VerticalLayout", - rows: 40, - columns: 24, - version: 1, - containerStyle: "card", - backgroundColor: "#FFFFFF", - borderColor: "transparent", - borderWidth: "0", - boxShadow: ButtonBoxShadowTypes.NONE, - animateLoading: true, - children: [], - blueprint: { - view: [ - { - type: "CANVAS_WIDGET", - position: { top: 0, left: 0 }, - props: { - containerStyle: "none", - canExtend: false, - detachFromLayout: true, - children: [], - }, - }, - ], - }, - }, - properties: { - derived: Widget.getDerivedPropertiesMap(), - default: Widget.getDefaultPropertiesMap(), - meta: Widget.getMetaPropertiesMap(), - config: Widget.getPropertyPaneConfig(), - }, -}; - -export default Widget; diff --git a/app/client/src/widgets/VerticalLayoutWidget/widget/index.tsx b/app/client/src/widgets/VerticalLayoutWidget/widget/index.tsx deleted file mode 100644 index 46d3eafca84a..000000000000 --- a/app/client/src/widgets/VerticalLayoutWidget/widget/index.tsx +++ /dev/null @@ -1,203 +0,0 @@ -import { map } from "lodash"; -import React from "react"; - -import WidgetFactory, { DerivedPropertiesMap } from "utils/WidgetFactory"; -import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; - -import { - AlignItems, - JustifyContent, - LayoutDirection, -} from "components/constants"; -import { - CONTAINER_GRID_PADDING, - GridDefaults, - MAIN_CONTAINER_WIDGET_ID, - WIDGET_PADDING, -} from "constants/WidgetConstants"; -import { ValidationTypes } from "constants/WidgetValidation"; -import { CanvasDraggingArena } from "pages/common/CanvasArenas/CanvasDraggingArena"; -import { AutoLayoutContext } from "utils/autoLayoutContext"; -import { getCanvasSnapRows } from "utils/WidgetPropsUtils"; -import { ContainerStyle } from "widgets/ContainerWidget/component"; -import VerticalLayoutComponent from "../component"; - -class VerticalLayoutWidget extends BaseWidget< - VerticalLayoutWidgetProps, - WidgetState -> { - constructor(props: VerticalLayoutWidgetProps) { - super(props); - this.renderChildWidget = this.renderChildWidget.bind(this); - } - - static getPropertyPaneConfig() { - return [ - { - helpText: "Controls alignment of the content", - propertyName: "justifyContent", - label: "Align content", - controlType: "DROP_DOWN", - defaultValue: JustifyContent.FlexStart, - options: [ - { label: "Flex start", value: JustifyContent.FlexStart }, - { label: "Center", value: JustifyContent.Center }, - { label: "Space around", value: JustifyContent.SpaceAround }, - { label: "Space between", value: JustifyContent.SpaceBetween }, - { label: "Space evently", value: JustifyContent.SpaceEvenly }, - ], - isJSConvertible: false, - isBindProperty: false, - isTriggerProperty: false, - validation: { type: ValidationTypes.TEXT }, - }, - ]; - } - - static getDerivedPropertiesMap(): DerivedPropertiesMap { - return {}; - } - - static getDefaultPropertiesMap(): Record { - return {}; - } - - static getMetaPropertiesMap(): Record { - return {}; - } - - getSnapSpaces = () => { - const { componentWidth } = this.getComponentDimensions(); - // For all widgets inside a container, we remove both container padding as well as widget padding from component width - let padding = (CONTAINER_GRID_PADDING + WIDGET_PADDING) * 2; - if ( - this.props.widgetId === MAIN_CONTAINER_WIDGET_ID || - this.props.type === "CONTAINER_WIDGET" - ) { - //For MainContainer and any Container Widget padding doesn't exist coz there is already container padding. - padding = CONTAINER_GRID_PADDING * 2; - } - if (this.props.noPad) { - // Widgets like ListWidget choose to have no container padding so will only have widget padding - padding = WIDGET_PADDING * 2; - } - let width = componentWidth; - width -= padding; - return { - snapRowSpace: GridDefaults.DEFAULT_GRID_ROW_HEIGHT, - snapColumnSpace: componentWidth - ? width / GridDefaults.DEFAULT_GRID_COLUMNS - : 0, - }; - }; - - renderChildWidget(childWidgetData: WidgetProps): React.ReactNode { - // For now, isVisible prop defines whether to render a detached widget - if (childWidgetData.detachFromLayout && !childWidgetData.isVisible) { - return null; - } - - const { componentHeight, componentWidth } = this.getComponentDimensions(); - - childWidgetData.rightColumn = componentWidth; - childWidgetData.bottomRow = this.props.shouldScrollContents - ? childWidgetData.bottomRow - : componentHeight; - childWidgetData.minHeight = componentHeight; - childWidgetData.isVisible = this.props.isVisible; - childWidgetData.shouldScrollContents = false; - childWidgetData.canExtend = this.props.shouldScrollContents; - - childWidgetData.parentId = this.props.widgetId; - // Pass layout controls to children - const layoutProps = { - useAutoLayout: true, - direction: LayoutDirection.Vertical, - justifyContent: JustifyContent.FlexStart, - alignItems: AlignItems.Stretch, - }; - return WidgetFactory.createWidget( - { ...childWidgetData, ...layoutProps }, - this.props.renderMode, - ); - } - - renderChildren = () => { - return map(this.props.children, this.renderChildWidget); - }; - - getPageView() { - const snapRows = getCanvasSnapRows( - this.props.bottomRow, - this.props.canExtend, - ); - return ( - - - {this.props.type === "CANVAS_WIDGET" && ( - <> - - {/* - // Removing Canvas Selection and grouping in the POC - */} - - )} - {/* */} - {/* without the wrapping div onClick events are triggered twice */} - <>{this.renderChildren()} - - - ); - } - - static getWidgetType(): string { - return "VERTICALLAYOUT_WIDGET"; - } -} - -export interface VerticalLayoutWidgetProps - extends WidgetProps { - children?: T[]; - containerStyle?: ContainerStyle; - shouldScrollContents?: boolean; - noPad?: boolean; -} - -export default VerticalLayoutWidget; From 91f291c354f61c295cb5d26e9991d37fb52e8f1b Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 11 Jan 2023 22:53:38 -0500 Subject: [PATCH 379/708] update interpolation data --- .../src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index fbaec3f6cc7c..7e5e468dac59 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -458,7 +458,7 @@ export const useCanvasDragging = ( ); lastSnappedPositions = [ currentSnappedPosition, - ...lastSnappedPositions.slice(0, 4), + ...lastSnappedPositions.slice(0, 9), ]; if ( From 00b49c421c9a0c1b3533291766b0128cbba8b04f Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 11 Jan 2023 23:08:04 -0500 Subject: [PATCH 380/708] add feature flag checks --- .../CanvasArenas/hooks/useCanvasDragging.ts | 46 +++++++++---------- app/client/src/sagas/AutoLayoutUtils.ts | 9 +++- app/client/src/sagas/WidgetDeletionSagas.ts | 3 +- .../src/utils/autoLayout/positionUtils.ts | 11 ++++- 4 files changed, 41 insertions(+), 28 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index 7e5e468dac59..1ab6d5204818 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -437,35 +437,35 @@ export const useCanvasDragging = ( renderNewRows(delta); } else if (!isUpdatingRows) { triggerReflow(e, firstMove); - const currentSnappedPosition = getDraggingSpacesFromBlocks( - currentRectanglesToDraw, - snapColumnSpace, - snapRowSpace, - )[0]; - const currentOccupiedSpace: any[] = occupiedSpaces[widgetId]; - let exitContainer: OccupiedSpace | undefined = undefined; - if (lastDraggedCanvas.current && currentOccupiedSpace) { - exitContainer = currentOccupiedSpace.find( - (each) => each.id === lastDraggedCanvas.current, - ); - } - currentDirection.current = getInterpolatedMoveDirection( - lastSnappedPositions, - currentSnappedPosition, - currentDirection.current, - exitContainer, - getMousePositionsOnCanvas(e, gridProps), - ); - lastSnappedPositions = [ - currentSnappedPosition, - ...lastSnappedPositions.slice(0, 9), - ]; if ( useAutoLayout && isCurrentDraggedCanvas && currentDirection.current !== ReflowDirection.UNSET ) { + const currentSnappedPosition = getDraggingSpacesFromBlocks( + currentRectanglesToDraw, + snapColumnSpace, + snapRowSpace, + )[0]; + const currentOccupiedSpace: any[] = occupiedSpaces[widgetId]; + let exitContainer: OccupiedSpace | undefined = undefined; + if (lastDraggedCanvas.current && currentOccupiedSpace) { + exitContainer = currentOccupiedSpace.find( + (each) => each.id === lastDraggedCanvas.current, + ); + } + currentDirection.current = getInterpolatedMoveDirection( + lastSnappedPositions, + currentSnappedPosition, + currentDirection.current, + exitContainer, + getMousePositionsOnCanvas(e, gridProps), + ); + lastSnappedPositions = [ + currentSnappedPosition, + ...lastSnappedPositions.slice(0, 9), + ]; highlight = highlightDropPosition(e, currentDirection.current); } } diff --git a/app/client/src/sagas/AutoLayoutUtils.ts b/app/client/src/sagas/AutoLayoutUtils.ts index f7dddc080b12..b032c1b7e959 100644 --- a/app/client/src/sagas/AutoLayoutUtils.ts +++ b/app/client/src/sagas/AutoLayoutUtils.ts @@ -73,13 +73,18 @@ export function* wrapChildren( return updatedWidgets; } -export function* updateFlexLayersOnDelete( +export function updateFlexLayersOnDelete( allWidgets: CanvasWidgetsReduxState, widgetId: string, parentId: string, isMobile?: boolean, -) { +): CanvasWidgetsReduxState { const widgets = { ...allWidgets }; + if ( + widgets[MAIN_CONTAINER_WIDGET_ID].appPositioningType === + AppPositioningTypes.FIXED + ) + return widgets; let parent = widgets[parentId]; if (!parent) return widgets; diff --git a/app/client/src/sagas/WidgetDeletionSagas.ts b/app/client/src/sagas/WidgetDeletionSagas.ts index 8b30a1ae7b86..fbf3a7b274f8 100644 --- a/app/client/src/sagas/WidgetDeletionSagas.ts +++ b/app/client/src/sagas/WidgetDeletionSagas.ts @@ -245,8 +245,7 @@ function* deleteSaga(deleteAction: ReduxAction) { const { finalWidgets, otherWidgetsToDelete, widgetName } = updatedObj; const isMobile: boolean = yield select(getIsMobile); // Update flex layers of a canvas upon deletion of a widget. - const widgetsAfterUpdatingFlexLayers: CanvasWidgetsReduxState = yield call( - updateFlexLayersOnDelete, + const widgetsAfterUpdatingFlexLayers: CanvasWidgetsReduxState = updateFlexLayersOnDelete( finalWidgets, widgetId, parentId, diff --git a/app/client/src/utils/autoLayout/positionUtils.ts b/app/client/src/utils/autoLayout/positionUtils.ts index fa826e9c1edd..fb304322298c 100644 --- a/app/client/src/utils/autoLayout/positionUtils.ts +++ b/app/client/src/utils/autoLayout/positionUtils.ts @@ -1,7 +1,11 @@ import { FlexLayerAlignment, ResponsiveBehavior } from "components/constants"; import { FlexLayer } from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; -import { GridDefaults } from "constants/WidgetConstants"; +import { + GridDefaults, + MAIN_CONTAINER_WIDGET_ID, +} from "constants/WidgetConstants"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; +import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; import { WidgetProps } from "widgets/BaseWidget"; import { getBottomRow, @@ -41,6 +45,11 @@ export function updateWidgetPositions( ): CanvasWidgetsReduxState { let widgets = { ...allWidgets }; try { + if ( + widgets[MAIN_CONTAINER_WIDGET_ID].appPositioningType === + AppPositioningTypes.FIXED + ) + return widgets; const parent = widgets[parentId]; if (!parent) return widgets; From 346b686c7e478be43c482bf83218689ffeea35cf Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 11 Jan 2023 23:31:36 -0500 Subject: [PATCH 381/708] move constants to a separate file --- app/client/src/components/constants.ts | 72 ------------------- .../appsmith/autoLayout/AutoLayoutLayer.tsx | 2 +- .../appsmith/autoLayout/FlexBoxComponent.tsx | 2 +- .../appsmith/autoLayout/FlexComponent.tsx | 2 +- .../editorComponents/ResizableComponent.tsx | 2 +- .../editorComponents/ResizableUtils.ts | 2 +- .../pages/Editor/AppPositionTypeControl.tsx | 2 +- .../hooks/useAutoLayoutHighlights.ts | 2 +- .../hooks/useBlocksToBeDraggedOnCanvas.ts | 2 +- .../src/resizable/resizenreflow/index.tsx | 5 +- .../src/sagas/AutoLayoutUpdateSagas.tsx | 2 +- app/client/src/sagas/AutoLayoutUtils.ts | 2 +- .../CanvasSagas/AutoLayoutDraggingSagas.ts | 5 +- app/client/src/sagas/WidgetAdditionSagas.ts | 2 +- app/client/src/selectors/editorSelectors.tsx | 2 +- .../src/selectors/propertyPaneSelectors.tsx | 2 +- .../autoLayout/autoLayoutDraggingUtils.ts | 4 +- app/client/src/utils/autoLayout/constants.ts | 71 ++++++++++++++++++ .../utils/autoLayout/highlightUtils.test.ts | 5 +- .../src/utils/autoLayout/highlightUtils.ts | 2 +- .../utils/autoLayout/positionUtils.test.ts | 5 +- .../src/utils/autoLayout/positionUtils.ts | 5 +- app/client/src/utils/layoutPropertiesUtils.ts | 2 +- app/client/src/widgets/BaseWidget.tsx | 2 +- app/client/src/widgets/CameraWidget/index.ts | 2 +- app/client/src/widgets/CanvasWidget.tsx | 6 +- .../src/widgets/CategorySliderWidget/index.ts | 3 +- .../src/widgets/ContainerWidget/index.ts | 2 +- .../widgets/ContainerWidget/widget/index.tsx | 2 +- app/client/src/widgets/FormWidget/index.ts | 7 +- app/client/src/widgets/ListWidget/index.ts | 2 +- .../src/widgets/ListWidget/widget/index.tsx | 2 +- .../src/widgets/ProgressBarWidget/index.ts | 2 +- app/client/src/widgets/StatboxWidget/index.ts | 3 +- app/client/src/widgets/TabsWidget/index.ts | 2 +- .../src/widgets/TabsWidget/widget/index.tsx | 2 +- 36 files changed, 128 insertions(+), 111 deletions(-) create mode 100644 app/client/src/utils/autoLayout/constants.ts diff --git a/app/client/src/components/constants.ts b/app/client/src/components/constants.ts index b5c6ccf7069b..edc3525cfdda 100644 --- a/app/client/src/components/constants.ts +++ b/app/client/src/components/constants.ts @@ -110,75 +110,3 @@ export const SELECT_DEFAULT_HEIGHT = "32px"; * Default margin bottom value for old select widgets */ export const LABEL_MARGIN_OLD_SELECT = "5px"; - -export enum LayoutDirection { - Horizontal = "Horizontal", - Vertical = "Vertical", -} - -export enum JustifyContent { - FlexStart = "flex-start", - Center = "center", - SpaceAround = "space-around", - SpaceBetween = "space-between", - SpaceEvenly = "space-evenly", - FlexEnd = "flex-end", -} - -export enum AlignItems { - FlexStart = "flex-start", - Center = "center", - Stretch = "stretch", - FlexEnd = "flex-end", -} - -export enum Positioning { - Fixed = "fixed", - Horizontal = "horizontal", - Vertical = "vertical", -} - -export enum ResponsiveBehavior { - Fill = "fill", - Hug = "hug", -} - -export enum FlexDirection { - Row = "row", - RowReverse = "row-reverse", - Column = "column", - ColumnReverse = "column-reverse", -} - -export enum Alignment { - Top = "top", - Bottom = "bottom", - Left = "left", - Right = "right", -} - -export enum Spacing { - None = "none", - Equal = "equal", - SpaceBetween = "space-between", -} - -export enum Overflow { - Wrap = "wrap", - NoWrap = "nowrap", - Hidden = "hidden", - Scroll = "scroll", - Auto = "auto", -} - -export enum FlexLayerAlignment { - Start = "start", - Center = "center", - End = "end", -} - -export enum FlexVerticalAlignment { - Top = "start", - Center = "center", - Bottom = "end", -} diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx index fe8a045d8b00..b2ca659e85a7 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx @@ -1,7 +1,7 @@ import React, { ReactNode } from "react"; import styled from "styled-components"; -import { FlexDirection, LayoutDirection } from "components/constants"; +import { FlexDirection, LayoutDirection } from "utils/autoLayout/constants"; /** * 1. Given a direction if should employ flex in perpendicular direction. diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx index 4599bc72a444..9d338420bdf3 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx @@ -6,7 +6,7 @@ import { FlexLayerAlignment, LayoutDirection, Overflow, -} from "components/constants"; +} from "utils/autoLayout/constants"; import { APP_MODE } from "entities/App"; import { useSelector } from "react-redux"; import { getWidgets } from "sagas/selectors"; diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx index 8f0a48ce0188..15e2c0cf1f9e 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx @@ -5,7 +5,7 @@ import { FlexVerticalAlignment, LayoutDirection, ResponsiveBehavior, -} from "components/constants"; +} from "utils/autoLayout/constants"; import { WidgetType, widgetTypeClassname, diff --git a/app/client/src/components/editorComponents/ResizableComponent.tsx b/app/client/src/components/editorComponents/ResizableComponent.tsx index 7bf29833eb45..67ab464e050f 100644 --- a/app/client/src/components/editorComponents/ResizableComponent.tsx +++ b/app/client/src/components/editorComponents/ResizableComponent.tsx @@ -1,7 +1,7 @@ import { AppState } from "@appsmith/reducers"; import { batchUpdateMultipleWidgetProperties } from "actions/controlActions"; import { focusWidget } from "actions/widgetActions"; -import { LayoutDirection, ResponsiveBehavior } from "components/constants"; +import { ResponsiveBehavior } from "utils/autoLayout/constants"; import { EditorContext } from "components/editorComponents/EditorContextProvider"; import { GridDefaults } from "constants/WidgetConstants"; import { get, omit } from "lodash"; diff --git a/app/client/src/components/editorComponents/ResizableUtils.ts b/app/client/src/components/editorComponents/ResizableUtils.ts index b3ee7ed81cda..8f99e743ba76 100644 --- a/app/client/src/components/editorComponents/ResizableUtils.ts +++ b/app/client/src/components/editorComponents/ResizableUtils.ts @@ -2,7 +2,7 @@ import { WidgetProps, WidgetRowCols } from "widgets/BaseWidget"; import { GridDefaults } from "constants/WidgetConstants"; import { XYCord } from "pages/common/CanvasArenas/hooks/useRenderBlocksOnCanvas"; import { ReflowDirection } from "reflow/reflowTypes"; -import { ResponsiveBehavior } from "components/constants"; +import { ResponsiveBehavior } from "utils/autoLayout/constants"; export type UIElementSize = { height: number; width: number }; diff --git a/app/client/src/pages/Editor/AppPositionTypeControl.tsx b/app/client/src/pages/Editor/AppPositionTypeControl.tsx index fa9df13b02e5..62d125edcdf1 100644 --- a/app/client/src/pages/Editor/AppPositionTypeControl.tsx +++ b/app/client/src/pages/Editor/AppPositionTypeControl.tsx @@ -3,7 +3,7 @@ import React, { useMemo } from "react"; import { useDispatch, useSelector } from "react-redux"; import { batchUpdateMultipleWidgetProperties } from "actions/controlActions"; -import { LayoutDirection, Positioning } from "components/constants"; +import { LayoutDirection, Positioning } from "utils/autoLayout/constants"; import { Colors } from "constants/Colors"; import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; import { Icon, IconName, IconSize, TooltipComponent } from "design-system"; diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index f0f689762a0c..024de0676e39 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -2,7 +2,7 @@ import { FlexLayerAlignment, LayoutDirection, ResponsiveBehavior, -} from "components/constants"; +} from "utils/autoLayout/constants"; import { useSelector } from "react-redux"; import { ReflowDirection } from "reflow/reflowTypes"; import { getWidgets } from "sagas/selectors"; diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts b/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts index fb08d6601035..e432d1516953 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts @@ -1,7 +1,7 @@ import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants"; import { AppState } from "@appsmith/reducers"; import { stopReflowAction } from "actions/reflowActions"; -import { AlignItems, LayoutDirection } from "components/constants"; +import { AlignItems, LayoutDirection } from "utils/autoLayout/constants"; import { DropTargetContext } from "components/editorComponents/DropTargetComponent"; import { EditorContext } from "components/editorComponents/EditorContextProvider"; import { OccupiedSpace, WidgetSpace } from "constants/CanvasEditorConstants"; diff --git a/app/client/src/resizable/resizenreflow/index.tsx b/app/client/src/resizable/resizenreflow/index.tsx index dffa80bb5ba0..5d9c1cda29c7 100644 --- a/app/client/src/resizable/resizenreflow/index.tsx +++ b/app/client/src/resizable/resizenreflow/index.tsx @@ -1,4 +1,7 @@ -import { LayoutDirection, ResponsiveBehavior } from "components/constants"; +import { + LayoutDirection, + ResponsiveBehavior, +} from "utils/autoLayout/constants"; import { isHandleResizeAllowed } from "components/editorComponents/ResizableUtils"; import { OccupiedSpace } from "constants/CanvasEditorConstants"; import { WIDGET_PADDING } from "constants/WidgetConstants"; diff --git a/app/client/src/sagas/AutoLayoutUpdateSagas.tsx b/app/client/src/sagas/AutoLayoutUpdateSagas.tsx index 13f24b0bd0f9..d94e25291d8e 100644 --- a/app/client/src/sagas/AutoLayoutUpdateSagas.tsx +++ b/app/client/src/sagas/AutoLayoutUpdateSagas.tsx @@ -4,7 +4,7 @@ import { ReduxActionErrorTypes, ReduxActionTypes, } from "ce/constants/ReduxActionConstants"; -import { ResponsiveBehavior } from "components/constants"; +import { ResponsiveBehavior } from "utils/autoLayout/constants"; import log from "loglevel"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; import { all, call, put, select, takeLatest } from "redux-saga/effects"; diff --git a/app/client/src/sagas/AutoLayoutUtils.ts b/app/client/src/sagas/AutoLayoutUtils.ts index b032c1b7e959..33c49b139e7d 100644 --- a/app/client/src/sagas/AutoLayoutUtils.ts +++ b/app/client/src/sagas/AutoLayoutUtils.ts @@ -2,7 +2,7 @@ import { FlexLayerAlignment, Positioning, ResponsiveBehavior, -} from "components/constants"; +} from "utils/autoLayout/constants"; import { FlexLayer, LayerChild, diff --git a/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts b/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts index 4a7bca3df17b..f908e8c64419 100644 --- a/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts +++ b/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts @@ -3,7 +3,10 @@ import { ReduxAction, ReduxActionTypes, } from "ce/constants/ReduxActionConstants"; -import { FlexLayerAlignment, LayoutDirection } from "components/constants"; +import { + FlexLayerAlignment, + LayoutDirection, +} from "utils/autoLayout/constants"; import { FlexLayer } from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; import { GridDefaults } from "constants/WidgetConstants"; import log from "loglevel"; diff --git a/app/client/src/sagas/WidgetAdditionSagas.ts b/app/client/src/sagas/WidgetAdditionSagas.ts index e85d5de9a2b5..c99595e91353 100644 --- a/app/client/src/sagas/WidgetAdditionSagas.ts +++ b/app/client/src/sagas/WidgetAdditionSagas.ts @@ -6,7 +6,7 @@ import { } from "@appsmith/constants/ReduxActionConstants"; import { generateAutoHeightLayoutTreeAction } from "actions/autoHeightActions"; import { updateAndSaveLayout, WidgetAddChild } from "actions/pageActions"; -import { Positioning, ResponsiveBehavior } from "components/constants"; +import { ResponsiveBehavior } from "utils/autoLayout/constants"; import { MAIN_CONTAINER_WIDGET_ID, RenderModes, diff --git a/app/client/src/selectors/editorSelectors.tsx b/app/client/src/selectors/editorSelectors.tsx index 4cd2a5f943eb..e08367076ac5 100644 --- a/app/client/src/selectors/editorSelectors.tsx +++ b/app/client/src/selectors/editorSelectors.tsx @@ -14,7 +14,7 @@ import { WidgetCardProps, WidgetProps } from "widgets/BaseWidget"; import { Page } from "@appsmith/constants/ReduxActionConstants"; import { ApplicationVersion } from "actions/applicationActions"; -import { Positioning } from "components/constants"; +import { Positioning } from "utils/autoLayout/constants"; import { OccupiedSpace, WidgetSpace } from "constants/CanvasEditorConstants"; import { PLACEHOLDER_APP_SLUG, PLACEHOLDER_PAGE_SLUG } from "constants/routes"; import { diff --git a/app/client/src/selectors/propertyPaneSelectors.tsx b/app/client/src/selectors/propertyPaneSelectors.tsx index 95b9df123058..015e75260497 100644 --- a/app/client/src/selectors/propertyPaneSelectors.tsx +++ b/app/client/src/selectors/propertyPaneSelectors.tsx @@ -25,7 +25,7 @@ import { getCanvasWidgets } from "./entitiesSelector"; import { getLastSelectedWidget, getSelectedWidgets } from "./ui"; import { getCurrentAppPositioningType } from "./editorSelectors"; import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; -import { Positioning } from "components/constants"; +import { Positioning } from "utils/autoLayout/constants"; import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; export type WidgetProperties = WidgetProps & { diff --git a/app/client/src/utils/autoLayout/autoLayoutDraggingUtils.ts b/app/client/src/utils/autoLayout/autoLayoutDraggingUtils.ts index effe2c5f034a..d02cc2ccce4a 100644 --- a/app/client/src/utils/autoLayout/autoLayoutDraggingUtils.ts +++ b/app/client/src/utils/autoLayout/autoLayoutDraggingUtils.ts @@ -1,4 +1,4 @@ -import { FlexLayerAlignment } from "components/constants"; +import { FlexLayerAlignment } from "utils/autoLayout/constants"; import { FlexLayer, LayerChild, @@ -198,7 +198,7 @@ export function updateExistingLayer( const updatedWidgets = { ...widgets, [parentId]: updatedCanvas }; return updatedWidgets; } catch (e) { - console.error("#### update existing layer error", e); + // console.error("#### update existing layer error", e); return allWidgets; } } diff --git a/app/client/src/utils/autoLayout/constants.ts b/app/client/src/utils/autoLayout/constants.ts new file mode 100644 index 000000000000..5a2df5b8da4e --- /dev/null +++ b/app/client/src/utils/autoLayout/constants.ts @@ -0,0 +1,71 @@ +export enum LayoutDirection { + Horizontal = "Horizontal", + Vertical = "Vertical", +} + +export enum JustifyContent { + FlexStart = "flex-start", + Center = "center", + SpaceAround = "space-around", + SpaceBetween = "space-between", + SpaceEvenly = "space-evenly", + FlexEnd = "flex-end", +} + +export enum AlignItems { + FlexStart = "flex-start", + Center = "center", + Stretch = "stretch", + FlexEnd = "flex-end", +} + +export enum Positioning { + Fixed = "fixed", + Horizontal = "horizontal", + Vertical = "vertical", +} + +export enum ResponsiveBehavior { + Fill = "fill", + Hug = "hug", +} + +export enum FlexDirection { + Row = "row", + RowReverse = "row-reverse", + Column = "column", + ColumnReverse = "column-reverse", +} + +export enum Alignment { + Top = "top", + Bottom = "bottom", + Left = "left", + Right = "right", +} + +export enum Spacing { + None = "none", + Equal = "equal", + SpaceBetween = "space-between", +} + +export enum Overflow { + Wrap = "wrap", + NoWrap = "nowrap", + Hidden = "hidden", + Scroll = "scroll", + Auto = "auto", +} + +export enum FlexLayerAlignment { + Start = "start", + Center = "center", + End = "end", +} + +export enum FlexVerticalAlignment { + Top = "start", + Center = "center", + Bottom = "end", +} diff --git a/app/client/src/utils/autoLayout/highlightUtils.test.ts b/app/client/src/utils/autoLayout/highlightUtils.test.ts index dffdb0dbbd4e..e914afbd5fd1 100644 --- a/app/client/src/utils/autoLayout/highlightUtils.test.ts +++ b/app/client/src/utils/autoLayout/highlightUtils.test.ts @@ -1,4 +1,7 @@ -import { FlexLayerAlignment, ResponsiveBehavior } from "components/constants"; +import { + FlexLayerAlignment, + ResponsiveBehavior, +} from "utils/autoLayout/constants"; import { FLEXBOX_PADDING, RenderModes } from "constants/WidgetConstants"; import { HighlightInfo } from "pages/common/CanvasArenas/hooks/useAutoLayoutHighlights"; import { getWidgetHeight } from "./flexWidgetUtils"; diff --git a/app/client/src/utils/autoLayout/highlightUtils.ts b/app/client/src/utils/autoLayout/highlightUtils.ts index 0d2b4f7f61ed..62c1d463cbd9 100644 --- a/app/client/src/utils/autoLayout/highlightUtils.ts +++ b/app/client/src/utils/autoLayout/highlightUtils.ts @@ -1,4 +1,4 @@ -import { FlexLayerAlignment } from "components/constants"; +import { FlexLayerAlignment } from "utils/autoLayout/constants"; import { DEFAULT_HIGHLIGHT_SIZE, FlexLayer, diff --git a/app/client/src/utils/autoLayout/positionUtils.test.ts b/app/client/src/utils/autoLayout/positionUtils.test.ts index 38e0dbacd3e2..5155a6863944 100644 --- a/app/client/src/utils/autoLayout/positionUtils.test.ts +++ b/app/client/src/utils/autoLayout/positionUtils.test.ts @@ -1,4 +1,7 @@ -import { FlexLayerAlignment, ResponsiveBehavior } from "components/constants"; +import { + FlexLayerAlignment, + ResponsiveBehavior, +} from "utils/autoLayout/constants"; import { FlexLayer } from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; import { RenderModes } from "constants/WidgetConstants"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; diff --git a/app/client/src/utils/autoLayout/positionUtils.ts b/app/client/src/utils/autoLayout/positionUtils.ts index fb304322298c..295a2a80d6d6 100644 --- a/app/client/src/utils/autoLayout/positionUtils.ts +++ b/app/client/src/utils/autoLayout/positionUtils.ts @@ -1,4 +1,7 @@ -import { FlexLayerAlignment, ResponsiveBehavior } from "components/constants"; +import { + FlexLayerAlignment, + ResponsiveBehavior, +} from "utils/autoLayout/constants"; import { FlexLayer } from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; import { GridDefaults, diff --git a/app/client/src/utils/layoutPropertiesUtils.ts b/app/client/src/utils/layoutPropertiesUtils.ts index ac3904ed8664..0dc84f799757 100644 --- a/app/client/src/utils/layoutPropertiesUtils.ts +++ b/app/client/src/utils/layoutPropertiesUtils.ts @@ -9,7 +9,7 @@ import { Positioning, ResponsiveBehavior, Spacing, -} from "components/constants"; +} from "utils/autoLayout/constants"; import { ValidationTypes } from "constants/WidgetValidation"; export interface LayoutProperties { diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index 253fa0f70d3e..556ccd2d5822 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -11,7 +11,7 @@ import { FlexVerticalAlignment, LayoutDirection, ResponsiveBehavior, -} from "components/constants"; +} from "utils/autoLayout/constants"; import FlexComponent from "components/designSystems/appsmith/autoLayout/FlexComponent"; import PositionedContainer from "components/designSystems/appsmith/PositionedContainer"; import DraggableComponent from "components/editorComponents/DraggableComponent"; diff --git a/app/client/src/widgets/CameraWidget/index.ts b/app/client/src/widgets/CameraWidget/index.ts index 9275404c63e5..6c4db078bf71 100644 --- a/app/client/src/widgets/CameraWidget/index.ts +++ b/app/client/src/widgets/CameraWidget/index.ts @@ -1,4 +1,4 @@ -import { ResponsiveBehavior } from "components/constants"; +import { ResponsiveBehavior } from "utils/autoLayout/constants"; import { CameraModeTypes } from "./constants"; import IconSVG from "./icon.svg"; import Widget from "./widget"; diff --git a/app/client/src/widgets/CanvasWidget.tsx b/app/client/src/widgets/CanvasWidget.tsx index 8aedc9d1e376..a36910bd5f0c 100644 --- a/app/client/src/widgets/CanvasWidget.tsx +++ b/app/client/src/widgets/CanvasWidget.tsx @@ -1,4 +1,8 @@ -import { LayoutDirection, Overflow, Positioning } from "components/constants"; +import { + LayoutDirection, + Overflow, + Positioning, +} from "utils/autoLayout/constants"; import FlexBoxComponent from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; import DropTargetComponent from "components/editorComponents/DropTargetComponent"; import { CANVAS_DEFAULT_MIN_HEIGHT_PX } from "constants/AppConstants"; diff --git a/app/client/src/widgets/CategorySliderWidget/index.ts b/app/client/src/widgets/CategorySliderWidget/index.ts index 73a6b832c028..55260a0e5149 100644 --- a/app/client/src/widgets/CategorySliderWidget/index.ts +++ b/app/client/src/widgets/CategorySliderWidget/index.ts @@ -1,8 +1,9 @@ -import { LabelPosition, ResponsiveBehavior } from "components/constants"; +import { LabelPosition } from "components/constants"; import { Alignment } from "@blueprintjs/core"; import IconSVG from "./icon.svg"; import Widget from "./widget"; +import { ResponsiveBehavior } from "utils/autoLayout/constants"; export const CONFIG = { type: Widget.getWidgetType(), diff --git a/app/client/src/widgets/ContainerWidget/index.ts b/app/client/src/widgets/ContainerWidget/index.ts index 29a7983c0355..c3c5dd10b28d 100644 --- a/app/client/src/widgets/ContainerWidget/index.ts +++ b/app/client/src/widgets/ContainerWidget/index.ts @@ -1,4 +1,4 @@ -import { ButtonBoxShadowTypes, Positioning } from "components/constants"; +import { ButtonBoxShadowTypes } from "components/constants"; import { Colors } from "constants/Colors"; import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; import { WidgetHeightLimits } from "constants/WidgetConstants"; diff --git a/app/client/src/widgets/ContainerWidget/widget/index.tsx b/app/client/src/widgets/ContainerWidget/widget/index.tsx index c4bcd70d7982..30cf15845ad8 100644 --- a/app/client/src/widgets/ContainerWidget/widget/index.tsx +++ b/app/client/src/widgets/ContainerWidget/widget/index.tsx @@ -17,7 +17,7 @@ import { ValidationTypes } from "constants/WidgetValidation"; import { compact, map, sortBy } from "lodash"; import WidgetsMultiSelectBox from "pages/Editor/WidgetsMultiSelectBox"; -import { Positioning } from "components/constants"; +import { Positioning } from "utils/autoLayout/constants"; import { Stylesheet } from "entities/AppTheming"; import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; diff --git a/app/client/src/widgets/FormWidget/index.ts b/app/client/src/widgets/FormWidget/index.ts index efb8b7a88c80..fcb46f4c71da 100644 --- a/app/client/src/widgets/FormWidget/index.ts +++ b/app/client/src/widgets/FormWidget/index.ts @@ -1,10 +1,7 @@ -import { - ButtonVariantTypes, - Positioning, - RecaptchaTypes, -} from "components/constants"; +import { ButtonVariantTypes, RecaptchaTypes } from "components/constants"; import { Colors } from "constants/Colors"; import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; +import { Positioning } from "utils/autoLayout/constants"; import { getDefaultResponsiveBehavior } from "utils/layoutPropertiesUtils"; import IconSVG from "./icon.svg"; import Widget from "./widget"; diff --git a/app/client/src/widgets/ListWidget/index.ts b/app/client/src/widgets/ListWidget/index.ts index de8a5df29512..c467db9a58c7 100644 --- a/app/client/src/widgets/ListWidget/index.ts +++ b/app/client/src/widgets/ListWidget/index.ts @@ -1,4 +1,4 @@ -import { Positioning } from "components/constants"; +import { Positioning } from "utils/autoLayout/constants"; import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; import { cloneDeep, get, indexOf, isString } from "lodash"; import { diff --git a/app/client/src/widgets/ListWidget/widget/index.tsx b/app/client/src/widgets/ListWidget/widget/index.tsx index 81c85b4a29a5..a29f5c99806b 100644 --- a/app/client/src/widgets/ListWidget/widget/index.tsx +++ b/app/client/src/widgets/ListWidget/widget/index.tsx @@ -1,5 +1,5 @@ import { entityDefinitions } from "ce/utils/autocomplete/EntityDefinitions"; -import { Positioning } from "components/constants"; +import { Positioning } from "utils/autoLayout/constants"; import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; import { GridDefaults, diff --git a/app/client/src/widgets/ProgressBarWidget/index.ts b/app/client/src/widgets/ProgressBarWidget/index.ts index 48af511b9978..9031bdf35281 100644 --- a/app/client/src/widgets/ProgressBarWidget/index.ts +++ b/app/client/src/widgets/ProgressBarWidget/index.ts @@ -1,4 +1,4 @@ -import { ResponsiveBehavior } from "components/constants"; +import { ResponsiveBehavior } from "utils/autoLayout/constants"; import { BarType } from "./constants"; import IconSVG from "./icon.svg"; import Widget from "./widget"; diff --git a/app/client/src/widgets/StatboxWidget/index.ts b/app/client/src/widgets/StatboxWidget/index.ts index 324e7a837411..8cacf84423d9 100644 --- a/app/client/src/widgets/StatboxWidget/index.ts +++ b/app/client/src/widgets/StatboxWidget/index.ts @@ -1,5 +1,6 @@ -import { ButtonVariantTypes, Positioning } from "components/constants"; +import { ButtonVariantTypes } from "components/constants"; import { Colors } from "constants/Colors"; +import { Positioning } from "utils/autoLayout/constants"; import IconSVG from "./icon.svg"; import Widget from "./widget"; diff --git a/app/client/src/widgets/TabsWidget/index.ts b/app/client/src/widgets/TabsWidget/index.ts index 3595f8538a32..119ee7637147 100644 --- a/app/client/src/widgets/TabsWidget/index.ts +++ b/app/client/src/widgets/TabsWidget/index.ts @@ -1,4 +1,4 @@ -import { Positioning } from "components/constants"; +import { Positioning } from "utils/autoLayout/constants"; import { Colors } from "constants/Colors"; import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; import { WidgetHeightLimits } from "constants/WidgetConstants"; diff --git a/app/client/src/widgets/TabsWidget/widget/index.tsx b/app/client/src/widgets/TabsWidget/widget/index.tsx index 9c727a4ce007..17463b1de8a9 100644 --- a/app/client/src/widgets/TabsWidget/widget/index.tsx +++ b/app/client/src/widgets/TabsWidget/widget/index.tsx @@ -1,5 +1,5 @@ import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants"; -import { LayoutDirection, Positioning } from "components/constants"; +import { LayoutDirection, Positioning } from "utils/autoLayout/constants"; import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; import { WIDGET_PADDING } from "constants/WidgetConstants"; import { From 1e16639a784d863f186958fb5aaabeba4c00fcfc Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 11 Jan 2023 23:45:32 -0500 Subject: [PATCH 382/708] minor fix --- .../common/CanvasArenas/hooks/useCanvasDragging.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index 1ab6d5204818..804516c069cf 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -438,11 +438,7 @@ export const useCanvasDragging = ( } else if (!isUpdatingRows) { triggerReflow(e, firstMove); - if ( - useAutoLayout && - isCurrentDraggedCanvas && - currentDirection.current !== ReflowDirection.UNSET - ) { + if (useAutoLayout && isCurrentDraggedCanvas) { const currentSnappedPosition = getDraggingSpacesFromBlocks( currentRectanglesToDraw, snapColumnSpace, @@ -466,7 +462,11 @@ export const useCanvasDragging = ( currentSnappedPosition, ...lastSnappedPositions.slice(0, 9), ]; - highlight = highlightDropPosition(e, currentDirection.current); + if (currentDirection.current !== ReflowDirection.UNSET) + highlight = highlightDropPosition( + e, + currentDirection.current, + ); } } isUpdatingRows = renderBlocks( From 5454827497ee547a30dbbc237af7b1172d286ebe Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Thu, 12 Jan 2023 12:24:50 +0530 Subject: [PATCH 383/708] fixing bad imports. --- .../pages/common/CanvasArenas/CanvasDraggingArena.tsx | 2 +- app/client/src/widgets/constants.ts | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/CanvasDraggingArena.tsx b/app/client/src/pages/common/CanvasArenas/CanvasDraggingArena.tsx index 32f2a52a31f4..fbede3186a81 100644 --- a/app/client/src/pages/common/CanvasArenas/CanvasDraggingArena.tsx +++ b/app/client/src/pages/common/CanvasArenas/CanvasDraggingArena.tsx @@ -1,7 +1,7 @@ -import { LayoutDirection } from "components/constants"; import { theme } from "constants/DefaultTheme"; import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; import React, { useMemo } from "react"; +import { LayoutDirection } from "utils/autoLayout/constants"; import { getNearestParentCanvas } from "utils/generators"; import { useCanvasDragging } from "./hooks/useCanvasDragging"; import { StickyCanvasArena } from "./StickyCanvasArena"; diff --git a/app/client/src/widgets/constants.ts b/app/client/src/widgets/constants.ts index 802cdf41a64c..c93181bc9a4c 100644 --- a/app/client/src/widgets/constants.ts +++ b/app/client/src/widgets/constants.ts @@ -1,9 +1,4 @@ import { IconNames } from "@blueprintjs/icons"; -import { - LayoutDirection, - Positioning, - ResponsiveBehavior, -} from "components/constants"; import { Theme } from "constants/DefaultTheme"; import { PropertyPaneConfig } from "constants/PropertyControlConstants"; import { WIDGET_STATIC_PROPS } from "constants/WidgetConstants"; @@ -11,6 +6,11 @@ import { Stylesheet } from "entities/AppTheming"; import { omit } from "lodash"; import moment from "moment"; import { WidgetConfigProps } from "reducers/entityReducers/widgetConfigReducer"; +import { + LayoutDirection, + Positioning, + ResponsiveBehavior, +} from "utils/autoLayout/constants"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; import { WidgetFeatures } from "utils/WidgetFeatures"; import { WidgetProps } from "./BaseWidget"; From 4a4e87c60d9954f97496271f74867e5837f09893 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Thu, 12 Jan 2023 12:29:26 +0530 Subject: [PATCH 384/708] fixing bad imports. --- app/client/src/widgets/ModalWidget/widget/index.tsx | 2 +- app/client/src/widgets/TabsWidget/constants.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/client/src/widgets/ModalWidget/widget/index.tsx b/app/client/src/widgets/ModalWidget/widget/index.tsx index b9eda2fd14c3..5642f89b0f09 100644 --- a/app/client/src/widgets/ModalWidget/widget/index.tsx +++ b/app/client/src/widgets/ModalWidget/widget/index.tsx @@ -5,13 +5,13 @@ import { connect } from "react-redux"; import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants"; import { AppState } from "@appsmith/reducers"; import { deselectModalWidgetAction } from "actions/widgetSelectionActions"; -import { Alignment, Positioning, Spacing } from "components/constants"; import { UIElementSize } from "components/editorComponents/ResizableUtils"; import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; import { RenderMode, WIDGET_PADDING } from "constants/WidgetConstants"; import { ValidationTypes } from "constants/WidgetValidation"; import { CanvasWidgetsStructureReduxState } from "reducers/entityReducers/canvasWidgetsStructureReducer"; import { getCanvasWidth, snipingModeSelector } from "selectors/editorSelectors"; +import { Alignment, Positioning, Spacing } from "utils/autoLayout/constants"; import { generateClassName } from "utils/generators"; import { ClickContentToOpenPropPane } from "utils/hooks/useClickToSelectWidget"; import WidgetFactory from "utils/WidgetFactory"; diff --git a/app/client/src/widgets/TabsWidget/constants.ts b/app/client/src/widgets/TabsWidget/constants.ts index 51810a031254..7f68d4ffe9f1 100644 --- a/app/client/src/widgets/TabsWidget/constants.ts +++ b/app/client/src/widgets/TabsWidget/constants.ts @@ -1,4 +1,4 @@ -import { Alignment, Positioning, Spacing } from "components/constants"; +import { Alignment, Positioning, Spacing } from "utils/autoLayout/constants"; import { WidgetProps } from "widgets/BaseWidget"; export interface TabContainerWidgetProps extends WidgetProps { From e62eccd22afb42723cfd2231d024ad36057d5b93 Mon Sep 17 00:00:00 2001 From: Aswath K Date: Thu, 12 Jan 2023 13:10:39 +0530 Subject: [PATCH 385/708] Makes the style changes apply only for auto-layout --- .../appsmith/PositionedContainer.tsx | 2 -- .../BaseInputWidget/component/index.tsx | 6 ++--- .../component/styles.module.css | 27 ++++++++++--------- .../ContainerWidget/component/index.tsx | 8 +++++- .../widgets/components/LabelWithTooltip.tsx | 3 +-- 5 files changed, 25 insertions(+), 21 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/PositionedContainer.tsx b/app/client/src/components/designSystems/appsmith/PositionedContainer.tsx index 762d52cc24cf..5afd77afaf60 100644 --- a/app/client/src/components/designSystems/appsmith/PositionedContainer.tsx +++ b/app/client/src/components/designSystems/appsmith/PositionedContainer.tsx @@ -27,8 +27,6 @@ const PositionedWidget = styled.div<{ zIndexOnHover: number; disabled?: boolean; }>` - container-name: widget-container; - container-type: inline-size; &:hover { z-index: ${(props) => props.zIndexOnHover} !important; } diff --git a/app/client/src/widgets/BaseInputWidget/component/index.tsx b/app/client/src/widgets/BaseInputWidget/component/index.tsx index f011cb3ff94d..06c8d8c41fd5 100644 --- a/app/client/src/widgets/BaseInputWidget/component/index.tsx +++ b/app/client/src/widgets/BaseInputWidget/component/index.tsx @@ -365,9 +365,9 @@ const TextInputWrapper = styled.div<{ }}; border-radius: ${({ borderRadius }) => borderRadius} !important; box-shadow: ${({ boxShadow }) => `${boxShadow}`} !important; - min-height: 40px; - &&& { - flex: 0 40px; + min-height: 32px; + .auto-layout & { + min-height: 40px; } &:hover { diff --git a/app/client/src/widgets/BaseInputWidget/component/styles.module.css b/app/client/src/widgets/BaseInputWidget/component/styles.module.css index ce7b2b999523..8d03102aa7c4 100644 --- a/app/client/src/widgets/BaseInputWidget/component/styles.module.css +++ b/app/client/src/widgets/BaseInputWidget/component/styles.module.css @@ -1,23 +1,24 @@ -.inputComponentWrapper { - /* background: blue; */ +:global(.auto-layout) .inputComponentWrapper { min-width: 160px; } -@container (max-width: 360px) { - .inputComponentWrapper { - /* background: green; */ - } - .label { +:global(.auto-layout) .textInputWrapper { + flex: 0 40px !important; +} + +@container canvas-container (max-width: 360px) { + :global(.auto-layout) .label { margin-bottom: 4px !important; } - .textInputWrapper { - flex-basis: 36px; - min-height: 36px; + :global(.auto-layout) .textInputWrapper { + min-height: 36px !important; + flex-basis: 36px !important; } } -@container (min-width: 768px) { - .inputComponentWrapper { - /* background: red; */ +@container canvas-container (min-width: 361px) { + :global(.auto-layout) .label { + margin-bottom: 10px !important; + font-size: 15px !important; } } diff --git a/app/client/src/widgets/ContainerWidget/component/index.tsx b/app/client/src/widgets/ContainerWidget/component/index.tsx index 55b507d56681..308777260c6a 100644 --- a/app/client/src/widgets/ContainerWidget/component/index.tsx +++ b/app/client/src/widgets/ContainerWidget/component/index.tsx @@ -20,6 +20,10 @@ const StyledContainerComponent = styled.div< ref: RefObject; } >` + &.auto-layout { + container-name: canvas-container; + container-type: inline-size; + } height: 100%; width: 100%; background: ${(props) => props.backgroundColor}; @@ -85,7 +89,9 @@ function ContainerComponentWrapper(props: ContainerComponentProps) { // getCanvasClassName is used to add a scrollable parent. className={`${ props.shouldScrollContents ? getCanvasClassName() : "" - } ${generateClassName(props.widgetId)}`} + } ${generateClassName(props.widgetId)} ${ + props.useAutoLayout && props.widgetId === "0" ? "auto-layout" : "" + }`} containerStyle={containerStyle} ref={containerRef} tabIndex={props.shouldScrollContents ? undefined : 0} diff --git a/app/client/src/widgets/components/LabelWithTooltip.tsx b/app/client/src/widgets/components/LabelWithTooltip.tsx index b523bedce72e..7ff2d3277127 100644 --- a/app/client/src/widgets/components/LabelWithTooltip.tsx +++ b/app/client/src/widgets/components/LabelWithTooltip.tsx @@ -68,7 +68,7 @@ export const LABEL_MAX_WIDTH_RATE = 70; /** * Default margin-top or margin-right value between label, help text and input */ -export const LABEL_DEFAULT_GAP = "10px"; +export const LABEL_DEFAULT_GAP = "5px"; /** * The amount of time in milliseconds the popover on the label with ellipsis @@ -103,7 +103,6 @@ export const labelLayoutStyles = css<{ return "flex-start"; }}; justify-content: flex-start; - min-width: 160px; `; export const multiSelectInputContainerStyles = css<{ From 1b1a2bffdc59b0f037b35dedfc62695bf4f86b01 Mon Sep 17 00:00:00 2001 From: Aswath K Date: Thu, 12 Jan 2023 17:02:58 +0530 Subject: [PATCH 386/708] hide certain widget props for auto-layout --- app/client/src/selectors/mainCanvasSelectors.tsx | 10 ++++++++++ .../src/widgets/BaseInputWidget/widget/index.tsx | 3 +++ .../src/widgets/ContainerWidget/component/index.tsx | 4 +++- 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/app/client/src/selectors/mainCanvasSelectors.tsx b/app/client/src/selectors/mainCanvasSelectors.tsx index e79e6c436731..e42f72f528ad 100644 --- a/app/client/src/selectors/mainCanvasSelectors.tsx +++ b/app/client/src/selectors/mainCanvasSelectors.tsx @@ -1,7 +1,17 @@ import { AppState } from "@appsmith/reducers"; +import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; +import store from "store"; export const getIsCanvasInitialized = (state: AppState) => { return state.ui.mainCanvas.initialized; }; export const getIsMobile = (state: AppState) => state.ui.mainCanvas.isMobile; + +export const getUseAutoLayout = (state: AppState) => + state.entities.canvasWidgets[MAIN_CONTAINER_WIDGET_ID].useAutoLayout; + +export function isAutoLayout() { + const appState = store.getState(); + return !!getUseAutoLayout(appState); +} diff --git a/app/client/src/widgets/BaseInputWidget/widget/index.tsx b/app/client/src/widgets/BaseInputWidget/widget/index.tsx index ae5385e19661..fafab6785fd1 100644 --- a/app/client/src/widgets/BaseInputWidget/widget/index.tsx +++ b/app/client/src/widgets/BaseInputWidget/widget/index.tsx @@ -8,6 +8,7 @@ import { import { WidgetType } from "constants/WidgetConstants"; import { ValidationTypes } from "constants/WidgetValidation"; import React from "react"; +import { isAutoLayout } from "selectors/mainCanvasSelectors"; import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; @@ -43,6 +44,7 @@ class BaseInputWidget< label: "Position", controlType: "ICON_TABS", fullWidth: true, + hidden: isAutoLayout, options: [ { label: "Auto", value: LabelPosition.Auto }, { label: "Left", value: LabelPosition.Left }, @@ -337,6 +339,7 @@ class BaseInputWidget< helpText: "Control the font size of the label associated", controlType: "DROP_DOWN", defaultValue: "0.875rem", + hidden: isAutoLayout, options: [ { label: "S", diff --git a/app/client/src/widgets/ContainerWidget/component/index.tsx b/app/client/src/widgets/ContainerWidget/component/index.tsx index b6a920ac5738..793c707a3094 100644 --- a/app/client/src/widgets/ContainerWidget/component/index.tsx +++ b/app/client/src/widgets/ContainerWidget/component/index.tsx @@ -90,7 +90,9 @@ function ContainerComponentWrapper(props: ContainerComponentProps) { className={`${ props.shouldScrollContents ? getCanvasClassName() : "" } ${generateClassName(props.widgetId)} ${ - props.useAutoLayout && props.widgetId === "0" ? "auto-layout" : "" + props.useAutoLayout && props.widgetId === MAIN_CONTAINER_WIDGET_ID + ? "auto-layout" + : "" }`} containerStyle={containerStyle} ref={containerRef} From 3a06be790d9c09bc78f2ebd305e6e0e118da8999 Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 12 Jan 2023 08:07:34 -0500 Subject: [PATCH 387/708] fix statbox and form positioning --- app/client/src/widgets/FormWidget/widget/index.tsx | 6 ++++++ app/client/src/widgets/StatboxWidget/widget/index.tsx | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/app/client/src/widgets/FormWidget/widget/index.tsx b/app/client/src/widgets/FormWidget/widget/index.tsx index 4fa4da2e9f5c..016e10c63522 100644 --- a/app/client/src/widgets/FormWidget/widget/index.tsx +++ b/app/client/src/widgets/FormWidget/widget/index.tsx @@ -8,6 +8,8 @@ import { ContainerWidgetProps, } from "widgets/ContainerWidget/widget"; import { ContainerComponentProps } from "widgets/ContainerWidget/component"; +import { DerivedPropertiesMap } from "utils/WidgetFactory"; +import { Positioning } from "utils/autoLayout/constants"; class FormWidget extends ContainerWidget { checkInvalidChildren = (children: WidgetProps[]): boolean => { @@ -121,6 +123,10 @@ class FormWidget extends ContainerWidget { boxShadow: "{{appsmith.theme.boxShadow.appBoxShadow}}", }; } + + static getDerivedPropertiesMap(): DerivedPropertiesMap { + return { positioning: Positioning.Fixed }; + } } export interface FormWidgetProps extends ContainerComponentProps { diff --git a/app/client/src/widgets/StatboxWidget/widget/index.tsx b/app/client/src/widgets/StatboxWidget/widget/index.tsx index 1a34607c325e..7b0768e2a76d 100644 --- a/app/client/src/widgets/StatboxWidget/widget/index.tsx +++ b/app/client/src/widgets/StatboxWidget/widget/index.tsx @@ -4,6 +4,8 @@ import { ContainerWidget } from "widgets/ContainerWidget/widget"; import { ValidationTypes } from "constants/WidgetValidation"; import { Stylesheet } from "entities/AppTheming"; import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; +import { DerivedPropertiesMap } from "utils/WidgetFactory"; +import { Positioning } from "utils/autoLayout/constants"; class StatboxWidget extends ContainerWidget { static getPropertyPaneContentConfig() { @@ -125,6 +127,10 @@ class StatboxWidget extends ContainerWidget { static getWidgetType(): WidgetType { return "STATBOX_WIDGET"; } + + static getDerivedPropertiesMap(): DerivedPropertiesMap { + return { positioning: Positioning.Fixed }; + } } export interface StatboxWidgetProps { From 85c9e8e5e6d6d807dc68a408af0283885589a30e Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 12 Jan 2023 08:27:09 -0500 Subject: [PATCH 388/708] fix highlight positions --- app/client/src/utils/autoLayout/highlightUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/client/src/utils/autoLayout/highlightUtils.ts b/app/client/src/utils/autoLayout/highlightUtils.ts index 62c1d463cbd9..e928b2aaa1dd 100644 --- a/app/client/src/utils/autoLayout/highlightUtils.ts +++ b/app/client/src/utils/autoLayout/highlightUtils.ts @@ -485,7 +485,7 @@ export function getCanvasWidth( while (widget.parentId) { columns = getWidgetWidth(widget, isMobile); padding += getPadding(widget); - width *= columns / GridDefaults.DEFAULT_GRID_COLUMNS; + width *= columns > 64 ? 1 : columns / GridDefaults.DEFAULT_GRID_COLUMNS; widget = widgets[widget.parentId]; } const totalWidth = width * mainCanvasWidth; From 6520a85ab09dd8f719e44cec8ff5d0c1eb55158f Mon Sep 17 00:00:00 2001 From: Aswath K Date: Thu, 12 Jan 2023 19:22:20 +0530 Subject: [PATCH 389/708] sets final min-width & height for Input --- .../src/widgets/BaseInputWidget/component/styles.module.css | 2 +- app/client/src/widgets/InputWidgetV2/index.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/client/src/widgets/BaseInputWidget/component/styles.module.css b/app/client/src/widgets/BaseInputWidget/component/styles.module.css index 8d03102aa7c4..d857a40bc9fa 100644 --- a/app/client/src/widgets/BaseInputWidget/component/styles.module.css +++ b/app/client/src/widgets/BaseInputWidget/component/styles.module.css @@ -1,5 +1,5 @@ :global(.auto-layout) .inputComponentWrapper { - min-width: 160px; + min-width: 60px; } :global(.auto-layout) .textInputWrapper { diff --git a/app/client/src/widgets/InputWidgetV2/index.ts b/app/client/src/widgets/InputWidgetV2/index.ts index 2e3fab21fccf..6c37ba19cd99 100644 --- a/app/client/src/widgets/InputWidgetV2/index.ts +++ b/app/client/src/widgets/InputWidgetV2/index.ts @@ -10,7 +10,7 @@ export const CONFIG = { features: { dynamicHeight: { sectionIndex: 3, - defaultValue: DynamicHeight.AUTO_HEIGHT, + defaultValue: DynamicHeight.FIXED, active: true, }, }, @@ -21,7 +21,7 @@ export const CONFIG = { searchTags: ["form", "text input", "number", "textarea"], defaults: { ...BaseConfig.defaults, - rows: 7, + rows: 8, labelPosition: LabelPosition.Top, inputType: "TEXT", widgetName: "Input", From bc70aa0ea5f3f1d63c23e06f308fca77593f1216 Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 12 Jan 2023 09:19:52 -0500 Subject: [PATCH 390/708] add unit test for calculating canvas width --- .../utils/autoLayout/highlightUtils.test.ts | 11 + app/client/src/utils/autoLayout/testData.ts | 305 ++++++++++++++++++ 2 files changed, 316 insertions(+) create mode 100644 app/client/src/utils/autoLayout/testData.ts diff --git a/app/client/src/utils/autoLayout/highlightUtils.test.ts b/app/client/src/utils/autoLayout/highlightUtils.test.ts index e914afbd5fd1..32b793fc0120 100644 --- a/app/client/src/utils/autoLayout/highlightUtils.test.ts +++ b/app/client/src/utils/autoLayout/highlightUtils.test.ts @@ -9,8 +9,11 @@ import { deriveHighlightsFromLayers, generateHighlightsForAlignment, generateVerticalHighlights, + getCanvasWidth, VerticalHighlightsPayload, } from "./highlightUtils"; +import { data } from "./testData"; +import { Widget } from "./positionUtils"; describe("test HighlightUtils methods", () => { describe("test deriveHighlightsFromLayers method", () => { @@ -463,4 +466,12 @@ describe("test HighlightUtils methods", () => { expect(result.childCount).toEqual(2); }); }); + + describe("test getCanvasWidth method", () => { + it("should measure the width of canvas and account for paddings", () => { + expect(getCanvasWidth(data["0"], data, 1174, false)).toEqual(1166); + expect(getCanvasWidth(data["mglfryj65c"], data, 1174, true)).toEqual(569); + expect(getCanvasWidth(data["a3lldg1wg9"], data, 1174, true)).toEqual(569); + }); + }); }); diff --git a/app/client/src/utils/autoLayout/testData.ts b/app/client/src/utils/autoLayout/testData.ts new file mode 100644 index 000000000000..5eeb48e8ee42 --- /dev/null +++ b/app/client/src/utils/autoLayout/testData.ts @@ -0,0 +1,305 @@ +import { RenderModes } from "constants/WidgetConstants"; +import { LayoutDirection, ResponsiveBehavior } from "./constants"; + +export const data = { + "0": { + widgetName: "MainContainer", + backgroundColor: "none", + rightColumn: 1224, + snapColumns: 64, + detachFromLayout: true, + widgetId: "0", + topRow: 0, + bottomRow: 460, + containerStyle: "none", + snapRows: 46, + parentRowSpace: 1, + type: "CANVAS_WIDGET", + canExtend: true, + version: 75, + minHeight: 470, + useAutoLayout: true, + parentColumnSpace: 1, + dynamicTriggerPathList: [], + dynamicBindingPathList: [], + leftColumn: 0, + children: ["mglfryj65c", "vaqw5v85cs"], + positioning: "vertical", + flexLayers: [ + { + children: [ + { + id: "mglfryj65c", + align: "start", + }, + { + id: "vaqw5v85cs", + align: "start", + }, + ], + }, + ], + direction: LayoutDirection.Vertical, + renderMode: RenderModes.CANVAS, + isLoading: false, + }, + pt32jvs72k: { + resetFormOnClick: false, + boxShadow: "none", + widgetName: "Button7", + buttonColor: "{{appsmith.theme.colors.primaryColor}}", + displayName: "Button", + iconSVG: "/static/media/icon.cca026338f1c8eb6df8ba03d084c2fca.svg", + searchTags: ["click", "submit"], + topRow: 0, + bottomRow: 4, + parentRowSpace: 10, + type: "BUTTON_WIDGET", + hideCard: false, + animateLoading: true, + parentColumnSpace: 8.796875, + leftColumn: 0, + dynamicBindingPathList: [ + { + key: "buttonColor", + }, + { + key: "borderRadius", + }, + ], + text: "Submit", + isDisabled: false, + key: "jvfayx7kel", + isDeprecated: false, + rightColumn: 16, + isDefaultClickDisabled: true, + widgetId: "pt32jvs72k", + minWidth: 120, + isVisible: true, + recaptchaType: "V3", + version: 1, + parentId: "a3lldg1wg9", + renderMode: RenderModes.CANVAS, + isLoading: false, + responsiveBehavior: ResponsiveBehavior.Hug, + disabledWhenInvalid: false, + borderRadius: "{{appsmith.theme.borderRadius.appBorderRadius}}", + buttonVariant: "PRIMARY", + placement: "CENTER", + alignment: "start", + }, + tg6jcd1kjp: { + resetFormOnClick: false, + boxShadow: "none", + widgetName: "Button8", + buttonColor: "{{appsmith.theme.colors.primaryColor}}", + displayName: "Button", + iconSVG: "/static/media/icon.cca026338f1c8eb6df8ba03d084c2fca.svg", + searchTags: ["click", "submit"], + topRow: 0, + bottomRow: 4, + parentRowSpace: 10, + type: "BUTTON_WIDGET", + hideCard: false, + animateLoading: true, + parentColumnSpace: 8.796875, + leftColumn: 48, + dynamicBindingPathList: [ + { + key: "buttonColor", + }, + { + key: "borderRadius", + }, + ], + text: "Submit", + isDisabled: false, + key: "jvfayx7kel", + isDeprecated: false, + rightColumn: 64, + isDefaultClickDisabled: true, + widgetId: "tg6jcd1kjp", + minWidth: 120, + isVisible: true, + recaptchaType: "V3", + version: 1, + parentId: "a3lldg1wg9", + renderMode: RenderModes.CANVAS, + isLoading: false, + responsiveBehavior: ResponsiveBehavior.Hug, + originalTopRow: 11, + disabledWhenInvalid: false, + borderRadius: "{{appsmith.theme.borderRadius.appBorderRadius}}", + originalBottomRow: 15, + buttonVariant: "PRIMARY", + placement: "CENTER", + alignment: "start", + }, + a3lldg1wg9: { + widgetName: "Canvas6", + displayName: "Canvas", + topRow: 0, + bottomRow: 100, + parentRowSpace: 1, + type: "CANVAS_WIDGET", + canExtend: false, + hideCard: true, + minHeight: 100, + parentColumnSpace: 1, + leftColumn: 0, + dynamicBindingPathList: [], + children: ["pt32jvs72k", "tg6jcd1kjp"], + key: "7gw94mobie", + isDeprecated: false, + rightColumn: 1166, + detachFromLayout: true, + widgetId: "a3lldg1wg9", + containerStyle: "none", + minWidth: 450, + isVisible: true, + version: 1, + parentId: "mglfryj65c", + renderMode: RenderModes.CANVAS, + isLoading: false, + responsiveBehavior: ResponsiveBehavior.Fill, + flexLayers: [ + { + children: [ + { + id: "pt32jvs72k", + align: "start", + }, + { + id: "tg6jcd1kjp", + align: "end", + }, + ], + }, + ], + }, + mglfryj65c: { + boxShadow: "{{appsmith.theme.boxShadow.appBoxShadow}}", + widgetName: "Container2", + borderColor: "#E0DEDE", + isCanvas: true, + displayName: "Container", + iconSVG: "/static/media/icon.1977dca3370505e2db3a8e44cfd54907.svg", + searchTags: ["div", "parent", "group"], + topRow: 0, + bottomRow: 10, + parentRowSpace: 10, + type: "CONTAINER_WIDGET", + hideCard: false, + shouldScrollContents: true, + animateLoading: true, + parentColumnSpace: 18.21875, + leftColumn: 0, + dynamicBindingPathList: [ + { + key: "borderRadius", + }, + { + key: "boxShadow", + }, + ], + children: ["a3lldg1wg9"], + borderWidth: "1", + key: "bxkxveofyb", + backgroundColor: "#FFFFFF", + isDeprecated: false, + rightColumn: 32, + dynamicHeight: "AUTO_HEIGHT", + widgetId: "mglfryj65c", + containerStyle: "card", + minWidth: 450, + isVisible: true, + version: 1, + parentId: "0", + renderMode: RenderModes.CANVAS, + isLoading: false, + responsiveBehavior: ResponsiveBehavior.Fill, + originalTopRow: 0, + borderRadius: "{{appsmith.theme.borderRadius.appBorderRadius}}", + maxDynamicHeight: 9000, + originalBottomRow: 11, + alignment: "start", + minDynamicHeight: 10, + }, + "2ydfwnmayi": { + widgetName: "Canvas5", + displayName: "Canvas", + topRow: 0, + bottomRow: 100, + parentRowSpace: 1, + type: "CANVAS_WIDGET", + canExtend: false, + hideCard: true, + minHeight: 100, + parentColumnSpace: 1, + leftColumn: 0, + dynamicBindingPathList: [], + children: [], + key: "7gw94mobie", + isDeprecated: false, + rightColumn: 1166, + detachFromLayout: true, + widgetId: "2ydfwnmayi", + containerStyle: "none", + minWidth: 450, + isVisible: true, + version: 1, + parentId: "vaqw5v85cs", + renderMode: RenderModes.CANVAS, + isLoading: false, + responsiveBehavior: ResponsiveBehavior.Fill, + flexLayers: [], + }, + vaqw5v85cs: { + boxShadow: "{{appsmith.theme.boxShadow.appBoxShadow}}", + widgetName: "Container1", + borderColor: "#E0DEDE", + isCanvas: true, + displayName: "Container", + iconSVG: "/static/media/icon.1977dca3370505e2db3a8e44cfd54907.svg", + searchTags: ["div", "parent", "group"], + topRow: 0, + bottomRow: 11, + parentRowSpace: 10, + type: "CONTAINER_WIDGET", + hideCard: false, + shouldScrollContents: true, + animateLoading: true, + parentColumnSpace: 18.21875, + leftColumn: 32, + dynamicBindingPathList: [ + { + key: "borderRadius", + }, + { + key: "boxShadow", + }, + ], + children: ["2ydfwnmayi"], + borderWidth: "1", + key: "bxkxveofyb", + backgroundColor: "#FFFFFF", + isDeprecated: false, + rightColumn: 64, + dynamicHeight: "AUTO_HEIGHT", + widgetId: "vaqw5v85cs", + containerStyle: "card", + minWidth: 450, + isVisible: true, + version: 1, + parentId: "0", + renderMode: RenderModes.CANVAS, + isLoading: false, + responsiveBehavior: ResponsiveBehavior.Fill, + originalTopRow: 0, + borderRadius: "{{appsmith.theme.borderRadius.appBorderRadius}}", + maxDynamicHeight: 9000, + originalBottomRow: 11, + alignment: "start", + minDynamicHeight: 10, + }, +}; From 83d46e02de4e5b368a4f52f9ad889e9bd2c55acf Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 12 Jan 2023 12:47:43 -0500 Subject: [PATCH 391/708] fix drag from auto to fixed --- .../CanvasSagas/AutoLayoutDraggingSagas.ts | 3 +- .../sagas/CanvasSagas/DraggingCanvasSagas.ts | 31 +++-- .../autoLayoutDraggingUtils.test.ts | 123 ++++++++++++++++++ .../autoLayout/autoLayoutDraggingUtils.ts | 22 ++-- .../utils/autoLayout/highlightUtils.test.ts | 1 - 5 files changed, 153 insertions(+), 27 deletions(-) create mode 100644 app/client/src/utils/autoLayout/autoLayoutDraggingUtils.test.ts diff --git a/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts b/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts index f908e8c64419..3f3b6d5545f9 100644 --- a/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts +++ b/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts @@ -147,7 +147,7 @@ function* reorderAutolayoutChildren(params: { selectedWidgets, widgets, parentId, - true, + false, isMobile, ); @@ -159,7 +159,6 @@ function* reorderAutolayoutChildren(params: { // Remove moved widgets from the flex layers. const filteredLayers = removeWidgetsFromCurrentLayers( - widgets, selectedWidgets, flexLayers, ); diff --git a/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts b/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts index f97e25aeb8c3..7ba99ca01831 100644 --- a/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts +++ b/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts @@ -319,9 +319,24 @@ function* moveWidgetsSaga( const appPositioningType: AppPositioningTypes = yield select( getCurrentAppPositioningType, ); + let updatedWidgets: CanvasWidgetsReduxState = { ...allWidgets }; + if (appPositioningType === AppPositioningTypes.AUTO) { + /** + * If previous parent is an auto layout container, + * then update the flex layers. + */ + const isMobile: boolean = yield select(getIsMobile); + updatedWidgets = updateRelationships( + draggedBlocksToUpdate.map((block) => block.widgetId), + updatedWidgets, + canvasId, + true, + isMobile, + ); + } const updatedWidgetsOnMove: CanvasWidgetsReduxState = yield call( moveAndUpdateWidgets, - allWidgets, + updatedWidgets, draggedBlocksToUpdate, canvasId, ); @@ -336,19 +351,7 @@ function* moveWidgetsSaga( throw Error; } - let updatedWidgets: CanvasWidgetsReduxState = updatedWidgetsOnMove; - if (appPositioningType === AppPositioningTypes.AUTO) { - const isMobile: boolean = yield select(getIsMobile); - updatedWidgets = updateRelationships( - draggedBlocksToUpdate.map((block) => block.widgetId), - updatedWidgets, - canvasId, - false, - isMobile, - ); - } - - yield put(updateAndSaveLayout(updatedWidgets)); + yield put(updateAndSaveLayout(updatedWidgetsOnMove)); yield put(generateAutoHeightLayoutTreeAction(true, true)); const block = draggedBlocksToUpdate[0]; diff --git a/app/client/src/utils/autoLayout/autoLayoutDraggingUtils.test.ts b/app/client/src/utils/autoLayout/autoLayoutDraggingUtils.test.ts new file mode 100644 index 000000000000..850fce1351a5 --- /dev/null +++ b/app/client/src/utils/autoLayout/autoLayoutDraggingUtils.test.ts @@ -0,0 +1,123 @@ +import { + FlexLayer, + LayerChild, +} from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; +import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; +import { + createFlexLayer, + removeWidgetsFromCurrentLayers, + updateRelationships, +} from "./autoLayoutDraggingUtils"; +import { FlexLayerAlignment } from "./constants"; +import { data } from "./testData"; + +describe("test AutoLayoutDraggingUtils methods", () => { + describe("test createFlexLayer method", () => { + it("should return a new FlexLayer for given widgets and alignment", () => { + const widgets = { ...data }; + const layer: FlexLayer = createFlexLayer( + [widgets["tg6jcd1kjp"].widgetId], + widgets, + FlexLayerAlignment.Start, + ); + expect(layer).toEqual({ + children: [{ id: "tg6jcd1kjp", align: FlexLayerAlignment.Start }], + }); + }); + it("should exclude widgets that don't exist", () => { + const widgets = { ...data }; + const layer: FlexLayer = createFlexLayer( + ["33"], + widgets, + FlexLayerAlignment.Start, + ); + expect(layer).toEqual({ + children: [], + }); + }); + }); + describe("test removeWidgetsFromCurrentLayers method", () => { + it("should remove the widget from given flex layers", () => { + const widgets = { ...data }; + const layers: any[] = widgets["a3lldg1wg9"].flexLayers; + const result: FlexLayer[] = removeWidgetsFromCurrentLayers( + ["pt32jvs72k"], + layers, + ); + const layerIndex = result.findIndex((layer: FlexLayer) => { + return ( + layer.children.findIndex( + (child: LayerChild) => child.id === "pt32jvs72k", + ) !== -1 + ); + }); + expect(result[0].children.length).toEqual(1); + expect(layerIndex).toEqual(-1); + }); + it("should return an empty array if the input layers are empty", () => { + expect(removeWidgetsFromCurrentLayers(["pt32jvs72k"], [])).toEqual([]); + }); + it("should return input flexLayers as is if moved widgets are not found amongst them", () => { + const widgets = { ...data }; + const layers: any[] = widgets["a3lldg1wg9"].flexLayers; + const result: FlexLayer[] = removeWidgetsFromCurrentLayers( + ["33", "44"], + layers, + ); + expect(result[0].children.length).toEqual(2); + expect(result).toEqual(layers); + }); + }); + + describe("test updateRelationships method", () => { + it("should remove moved widgets from old parent's layers and assign new parent to the widgets", () => { + const widgets = { ...data }; + const movedWidget = "pt32jvs72k"; + const oldParent = widgets[movedWidget].parentId; + const result: CanvasWidgetsReduxState = updateRelationships( + [movedWidget], + widgets, + "0", + ); + expect(result[movedWidget].parentId === "0").toBeTruthy; + if (result[oldParent]) { + expect(result[oldParent]?.children?.includes(movedWidget)).toBeFalsy; + const layerIndex = result[oldParent]?.flexLayers?.findIndex( + (layer: FlexLayer) => { + return ( + layer.children.findIndex( + (child: LayerChild) => child.id === "pt32jvs72k", + ) !== -1 + ); + }, + ); + expect(layerIndex).toEqual(-1); + } + }); + it("should not update previous parent's children or moved widget's parentId id onlyUpdateFlexLayers is set to true", () => { + const widgets = { ...data }; + const movedWidget = "pt32jvs72k"; + const oldParent = widgets[movedWidget].parentId; + const result: CanvasWidgetsReduxState = updateRelationships( + [movedWidget], + widgets, + "0", + true, + ); + expect(result[movedWidget].parentId === "0").toBeFalsy; + if (result[oldParent]) { + expect(result[oldParent]?.children?.includes(movedWidget)).toBeTruthy; + const layerIndex = result[oldParent]?.flexLayers?.findIndex( + (layer: FlexLayer) => { + return ( + layer.children.findIndex( + (child: LayerChild) => child.id === "pt32jvs72k", + ) !== -1 + ); + }, + ); + expect(layerIndex).toEqual(-1); + } + }); + }); +}); diff --git a/app/client/src/utils/autoLayout/autoLayoutDraggingUtils.ts b/app/client/src/utils/autoLayout/autoLayoutDraggingUtils.ts index d02cc2ccce4a..b68a1ecb2d60 100644 --- a/app/client/src/utils/autoLayout/autoLayoutDraggingUtils.ts +++ b/app/client/src/utils/autoLayout/autoLayoutDraggingUtils.ts @@ -40,7 +40,6 @@ export function createFlexLayer( * @returns FlexLayer[] */ export function removeWidgetsFromCurrentLayers( - allWidgets: CanvasWidgetsReduxState, movedWidgets: string[], flexLayers: FlexLayer[], ): FlexLayer[] { @@ -72,7 +71,7 @@ export function updateRelationships( movedWidgets: string[], allWidgets: CanvasWidgetsReduxState, parentId: string, - addToNewParent = true, + onlyUpdateFlexLayers = false, isMobile = false, ): CanvasWidgetsReduxState { const widgets = { ...allWidgets }; @@ -89,22 +88,25 @@ export function updateRelationships( if (prevParentId !== undefined) { prevParents.push(prevParentId); const prevParent = Object.assign({}, widgets[prevParentId]); - if (prevParent.children && isArray(prevParent.children)) { + if (prevParent && prevParent.children && isArray(prevParent.children)) { const updatedPrevParent = { ...prevParent, - children: prevParent.children.filter((each) => each !== item), - flexLayers: removeWidgetsFromCurrentLayers( - widgets, - movedWidgets, - prevParent.flexLayers, - ), + children: onlyUpdateFlexLayers + ? prevParent.children + : prevParent.children.filter((each) => each !== item), + flexLayers: + prevParent.flexLayers !== undefined && + removeWidgetsFromCurrentLayers( + movedWidgets, + prevParent.flexLayers, + ), }; widgets[prevParentId] = updatedPrevParent; } } // add to new parent - if (addToNewParent) { + if (!onlyUpdateFlexLayers) { widgets[item] = { ...widgets[item], parentId: parentId, diff --git a/app/client/src/utils/autoLayout/highlightUtils.test.ts b/app/client/src/utils/autoLayout/highlightUtils.test.ts index 32b793fc0120..56b4b718ca30 100644 --- a/app/client/src/utils/autoLayout/highlightUtils.test.ts +++ b/app/client/src/utils/autoLayout/highlightUtils.test.ts @@ -13,7 +13,6 @@ import { VerticalHighlightsPayload, } from "./highlightUtils"; import { data } from "./testData"; -import { Widget } from "./positionUtils"; describe("test HighlightUtils methods", () => { describe("test deriveHighlightsFromLayers method", () => { From a652ac07f647e768b93630e844e0ba735da2ff94 Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 12 Jan 2023 13:06:17 -0500 Subject: [PATCH 392/708] add unit tests for AutoLayoutDraggingUtils --- .../autoLayoutDraggingUtils.test.ts | 90 +++++++++++++++++++ .../autoLayout/autoLayoutDraggingUtils.ts | 22 ++++- 2 files changed, 111 insertions(+), 1 deletion(-) diff --git a/app/client/src/utils/autoLayout/autoLayoutDraggingUtils.test.ts b/app/client/src/utils/autoLayout/autoLayoutDraggingUtils.test.ts index 850fce1351a5..238e83f191bf 100644 --- a/app/client/src/utils/autoLayout/autoLayoutDraggingUtils.test.ts +++ b/app/client/src/utils/autoLayout/autoLayoutDraggingUtils.test.ts @@ -4,8 +4,10 @@ import { } from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; import { + addNewLayer, createFlexLayer, removeWidgetsFromCurrentLayers, + updateExistingLayer, updateRelationships, } from "./autoLayoutDraggingUtils"; import { FlexLayerAlignment } from "./constants"; @@ -120,4 +122,92 @@ describe("test AutoLayoutDraggingUtils methods", () => { } }); }); + describe("test addNewLayer method", () => { + it("should add a new layer to flexLayers array at the given layerIndex", () => { + const widgets = { ...data }; + const movedWidget = "pt32jvs72k"; + const newParentId = "0"; + const newLayer: FlexLayer = { + children: [{ id: movedWidget, align: FlexLayerAlignment.Center }], + }; + const result: CanvasWidgetsReduxState = addNewLayer( + newLayer, + widgets, + newParentId, + widgets[newParentId].flexLayers as FlexLayer[], + 0, + ); + const updatedParent = result[newParentId]; + expect(updatedParent.flexLayers.length).toEqual(2); + const layerIndex = updatedParent?.flexLayers?.findIndex( + (layer: FlexLayer) => { + return ( + layer.children.findIndex( + (child: LayerChild) => child.id === "pt32jvs72k", + ) !== -1 + ); + }, + ); + expect(layerIndex).toEqual(0); + }); + it("should add new layer at the end of the flex layers if layerIndex is invalid", () => { + const widgets = { ...data }; + const movedWidget = "pt32jvs72k"; + const newParentId = "0"; + const newLayer: FlexLayer = { + children: [{ id: movedWidget, align: FlexLayerAlignment.Center }], + }; + const result: CanvasWidgetsReduxState = addNewLayer( + newLayer, + widgets, + newParentId, + widgets[newParentId].flexLayers as FlexLayer[], + 5, + ); + const updatedParent = result[newParentId]; + expect(updatedParent.flexLayers.length).toEqual(2); + const layerIndex = updatedParent?.flexLayers?.findIndex( + (layer: FlexLayer) => { + return ( + layer.children.findIndex( + (child: LayerChild) => child.id === "pt32jvs72k", + ) !== -1 + ); + }, + ); + expect(layerIndex).toEqual(1); + }); + }); + + describe("test updateExistingLayer method", () => { + it("should update existing layer by merging the new layer at the given layerIndex", () => { + const widgets = { ...data }; + const movedWidget = "pt32jvs72k"; + const newParentId = "0"; + const newLayer: FlexLayer = { + children: [{ id: movedWidget, align: FlexLayerAlignment.Center }], + }; + const result: CanvasWidgetsReduxState = updateExistingLayer( + newLayer, + widgets, + newParentId, + widgets[newParentId].flexLayers as FlexLayer[], + 0, + 0, + ); + const updatedParent = result[newParentId]; + expect(updatedParent.flexLayers.length).toEqual(1); + expect(updatedParent.flexLayers[0].children.length).toEqual(3); + const layerIndex = updatedParent?.flexLayers?.findIndex( + (layer: FlexLayer) => { + return ( + layer.children.findIndex( + (child: LayerChild) => child.id === "pt32jvs72k", + ) !== -1 + ); + }, + ); + expect(layerIndex).toEqual(0); + }); + }); }); diff --git a/app/client/src/utils/autoLayout/autoLayoutDraggingUtils.ts b/app/client/src/utils/autoLayout/autoLayoutDraggingUtils.ts index b68a1ecb2d60..70e63a239ec9 100644 --- a/app/client/src/utils/autoLayout/autoLayoutDraggingUtils.ts +++ b/app/client/src/utils/autoLayout/autoLayoutDraggingUtils.ts @@ -123,6 +123,17 @@ export function updateRelationships( return widgets; } +/** + * If widgets are dropped into a new vertical position in an auto layout canvas, + * then add a new FlexLayer to contain the new widgets. + * Use the layerIndex to add the new layer at the right position. + * @param newLayer | FlexLayer + * @param allWidgets | CanvasWidgetsReduxState + * @param parentId | string + * @param layers | FlexLayer[] + * @param layerIndex | number + * @returns CanvasWidgetsReduxState + */ export function addNewLayer( newLayer: FlexLayer, allWidgets: CanvasWidgetsReduxState, @@ -148,12 +159,21 @@ export function addNewLayer( return updatedWidgets; } +/** + * + * @param newLayer | FlexLayer : FlexLayer comprised of the moved widgets and selected alignment. + * @param allWidgets | CanvasWidgetsReduxState + * @param parentId | string : new parentId + * @param layers | FlexLayer[] : flexLayers of new parent. + * @param layerIndex | number : index of existing layer to add the moved widgets to. + * @param rowIndex | number : starting index of the moved widgets within an alignment in the selected layer. + * @returns CanvasWidgetsReduxState + */ export function updateExistingLayer( newLayer: FlexLayer, allWidgets: CanvasWidgetsReduxState, parentId: string, layers: FlexLayer[], - index = 0, layerIndex = 0, rowIndex: number, ): CanvasWidgetsReduxState { From c0ed402c949851dcfbaab5cdfd86a89d00ab8020 Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 12 Jan 2023 13:43:34 -0500 Subject: [PATCH 393/708] fix widget deletion if flexLayers are corrupted --- app/client/src/sagas/AutoLayoutUtils.ts | 12 ++++++++---- .../src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts | 1 - 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/app/client/src/sagas/AutoLayoutUtils.ts b/app/client/src/sagas/AutoLayoutUtils.ts index 33c49b139e7d..10deab15479b 100644 --- a/app/client/src/sagas/AutoLayoutUtils.ts +++ b/app/client/src/sagas/AutoLayoutUtils.ts @@ -91,21 +91,26 @@ export function updateFlexLayersOnDelete( let flexLayers = [...(parent.flexLayers || [])]; if (!flexLayers.length) return widgets; let layerIndex = -1; // Find the layer in which the deleted widget exists. + let index = 0; let updatedChildren: LayerChild[] = []; for (const layer of flexLayers) { - layerIndex += 1; const children = layer.children || []; if (!children.length) continue; - const index = children.findIndex( + const childIndex = children.findIndex( (each: LayerChild) => each.id === widgetId, ); - if (index === -1) continue; + if (childIndex === -1) { + index += 1; + continue; + } // children.splice(index, 1); updatedChildren = children.filter( (each: LayerChild) => each.id !== widgetId, ); + layerIndex += 1; break; } + if (layerIndex === -1) return widgets; flexLayers = [ ...flexLayers.slice(0, layerIndex), @@ -123,7 +128,6 @@ export function updateFlexLayersOnDelete( }; widgets[parentId] = parent; - if (layerIndex === -1) return widgets; return updateWidgetPositions(widgets, parentId, isMobile); } diff --git a/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts b/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts index 3f3b6d5545f9..55d757932ea1 100644 --- a/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts +++ b/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts @@ -184,7 +184,6 @@ function* reorderAutolayoutChildren(params: { updatedWidgets, parentId, filteredLayers, - index, layerIndex, rowIndex, ); From 1e980fc4aa2d719d71d10af2d93d273aefce679f Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 12 Jan 2023 13:49:46 -0500 Subject: [PATCH 394/708] fix multi widget delete --- app/client/src/sagas/AutoLayoutUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/client/src/sagas/AutoLayoutUtils.ts b/app/client/src/sagas/AutoLayoutUtils.ts index 10deab15479b..d04427a7fa61 100644 --- a/app/client/src/sagas/AutoLayoutUtils.ts +++ b/app/client/src/sagas/AutoLayoutUtils.ts @@ -107,7 +107,7 @@ export function updateFlexLayersOnDelete( updatedChildren = children.filter( (each: LayerChild) => each.id !== widgetId, ); - layerIndex += 1; + layerIndex = index; break; } if (layerIndex === -1) return widgets; From 0c917103699034857e74a310580b7fa2b3ee63db Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 12 Jan 2023 16:37:10 -0500 Subject: [PATCH 395/708] fix paste of child widgets --- app/client/src/sagas/AutoLayoutUtils.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/app/client/src/sagas/AutoLayoutUtils.ts b/app/client/src/sagas/AutoLayoutUtils.ts index d04427a7fa61..0bbedd747387 100644 --- a/app/client/src/sagas/AutoLayoutUtils.ts +++ b/app/client/src/sagas/AutoLayoutUtils.ts @@ -325,8 +325,8 @@ export function addChildToPastedFlexLayers( { id: widget.widgetId, align: child.align }, ...children.slice(childIndex + 1), ]; - childIndex += 1; } + childIndex += 1; } flexLayers[index] = { children, @@ -348,12 +348,15 @@ export function isStack( allWidgets: CanvasWidgetsReduxState, widget: any, ): boolean { - const parent = widget.parentId ? allWidgets[widget.parentId] : undefined; + let parent = widget.parentId ? allWidgets[widget.parentId] : undefined; + if (parent && parent.type === "CANVAS_WIDGET" && parent.parentId) + parent = allWidgets[parent.parentId]; return ( widget.positioning === Positioning.Vertical || - (parent && ["CONTAINER_WIDGET", "CANVAS_WIDGET"].includes(parent.type) - ? allWidgets[MAIN_CONTAINER_WIDGET_ID].appPositioningType === - AppPositioningTypes.AUTO + ((parent && ["CONTAINER_WIDGET", "TABS_WIDGET"].includes(parent.type)) || + parent?.widgetId === MAIN_CONTAINER_WIDGET_ID + ? allWidgets[MAIN_CONTAINER_WIDGET_ID].positioning === + Positioning.Vertical : false) ); } From b233b5f76f937cc4b8d9310d0ec138ad97723e55 Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 12 Jan 2023 17:16:55 -0500 Subject: [PATCH 396/708] consider parent offset top in drawing highlight during scroll --- .../CanvasArenas/hooks/useCanvasDragging.ts | 3 +++ .../CanvasArenas/hooks/useRenderBlocksOnCanvas.ts | 9 +++++++-- app/client/src/selectors/autoLayoutSelectors.tsx | 15 +++++++++++++++ 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index 804516c069cf..22f7714391f4 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -46,6 +46,7 @@ import { useAutoLayoutHighlights, } from "./useAutoLayoutHighlights"; import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; +import { getParentOffsetTop } from "selectors/autoLayoutSelectors"; export const useCanvasDragging = ( slidingArenaRef: React.RefObject, @@ -68,6 +69,7 @@ export const useCanvasDragging = ( let { devicePixelRatio: scale = 1 } = window; scale *= canvasScale; const appPositioningType = useSelector(getCurrentAppPositioningType); + const parentOffsetTop = useSelector(getParentOffsetTop(widgetId)); const { blocksToDraw, defaultHandlePositions, @@ -477,6 +479,7 @@ export const useCanvasDragging = ( scrollParent, highlight, widgetId === MAIN_CONTAINER_WIDGET_ID, + parentOffsetTop, ); scrollObj.lastMouseMoveEvent = { offsetX: e.offsetX, diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useRenderBlocksOnCanvas.ts b/app/client/src/pages/common/CanvasArenas/hooks/useRenderBlocksOnCanvas.ts index 2f8e47a3b95f..67b445890d52 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useRenderBlocksOnCanvas.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useRenderBlocksOnCanvas.ts @@ -123,6 +123,7 @@ export const useRenderBlocksOnCanvas = ( scrollParent: Element | null, highlight?: HighlightInfo | undefined, isMainContainer?: boolean, + parentOffsetTop?: number, ) => { let isCurrUpdatingRows = isUpdatingRows; const modifiedRectanglesToDraw = modifyDrawingRectangles( @@ -156,8 +157,12 @@ export const useRenderBlocksOnCanvas = ( canvasCtx.fillStyle = "rgba(196, 139, 181, 1)"; const { height, posX, posY, width } = highlight; let val = 0; - if (isMainContainer && scrollParent?.scrollTop) - val = scrollParent.scrollTop; + if (scrollParent?.scrollTop) + val = isMainContainer + ? scrollParent.scrollTop + : parentOffsetTop && scrollParent.scrollTop > parentOffsetTop + ? scrollParent.scrollTop - parentOffsetTop + : 0; canvasCtx.fillRect(posX, posY - val, width, height); canvasCtx.save(); } diff --git a/app/client/src/selectors/autoLayoutSelectors.tsx b/app/client/src/selectors/autoLayoutSelectors.tsx index b3255fbb6ef7..f7d759585679 100644 --- a/app/client/src/selectors/autoLayoutSelectors.tsx +++ b/app/client/src/selectors/autoLayoutSelectors.tsx @@ -3,8 +3,11 @@ import { FlexLayer, LayerChild, } from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; +import { FLEXBOX_PADDING, GridDefaults } from "constants/WidgetConstants"; +import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; import { createSelector } from "reselect"; import { getWidgets } from "sagas/selectors"; +import { getIsMobile } from "./mainCanvasSelectors"; export const getFlexLayers = (parentId: string) => { return createSelector(getWidgets, (widgets): FlexLayer[] => { @@ -49,3 +52,15 @@ export const isCurrentCanvasDragging = (widgetId: string) => { }, ); }; + +export const getParentOffsetTop = (widgetId: string) => + createSelector(getWidgets, getIsMobile, (widgets, isMobile): number => { + const widget = widgets[widgetId]; + if (!widget || !widget.parentId) return 0; + const parent = widgets[widget.parentId]; + const top = + isMobile && parent.mobileTopRow !== undefined + ? parent.mobileTopRow + : parent.topRow; + return top * GridDefaults.DEFAULT_GRID_ROW_HEIGHT + FLEXBOX_PADDING; + }); From d5827c9f1f94a917d4e261e2c11c0b35bf4d298a Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 12 Jan 2023 19:46:26 -0500 Subject: [PATCH 397/708] fix container positioning on new fixed page --- app/client/src/utils/autoLayout/positionUtils.ts | 1 + app/client/src/widgets/ContainerWidget/widget/index.tsx | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/app/client/src/utils/autoLayout/positionUtils.ts b/app/client/src/utils/autoLayout/positionUtils.ts index 295a2a80d6d6..28353a0ec25d 100644 --- a/app/client/src/utils/autoLayout/positionUtils.ts +++ b/app/client/src/utils/autoLayout/positionUtils.ts @@ -288,6 +288,7 @@ export function extractAlignmentInfo( // Calculate the number of columns occupied by hug widgets in each alignment. for (const child of layer.children) { const widget = widgets[child.id]; + if (!widget) continue; const isFillWidget = widget.responsiveBehavior === ResponsiveBehavior.Fill; if (isFillWidget) fillChildren.push(child); if (child.align === FlexLayerAlignment.Start) { diff --git a/app/client/src/widgets/ContainerWidget/widget/index.tsx b/app/client/src/widgets/ContainerWidget/widget/index.tsx index 30cf15845ad8..f0dae2578d58 100644 --- a/app/client/src/widgets/ContainerWidget/widget/index.tsx +++ b/app/client/src/widgets/ContainerWidget/widget/index.tsx @@ -207,7 +207,9 @@ export class ContainerWidget extends BaseWidget< // Pass layout controls to children childWidget.positioning = childWidget?.positioning || this.props.positioning; - childWidget.useAutoLayout = this.props.positioning !== Positioning.Fixed; + childWidget.useAutoLayout = this.props.positioning + ? this.props.positioning !== Positioning.Fixed + : false; return WidgetFactory.createWidget(childWidget, this.props.renderMode); } From 3e9d67638b138e9cc901aeee4d5c37376863f4b7 Mon Sep 17 00:00:00 2001 From: Aswath K Date: Fri, 13 Jan 2023 11:52:08 +0530 Subject: [PATCH 398/708] makes the default widget rows/columns separate for autoLayout --- app/client/src/pages/Editor/WidgetCard.tsx | 6 ++++++ app/client/src/selectors/editorSelectors.tsx | 2 ++ app/client/src/utils/WidgetRegisterHelpers.tsx | 1 + app/client/src/widgets/InputWidgetV2/index.ts | 7 ++++++- app/client/src/widgets/constants.ts | 7 +++++++ 5 files changed, 22 insertions(+), 1 deletion(-) diff --git a/app/client/src/pages/Editor/WidgetCard.tsx b/app/client/src/pages/Editor/WidgetCard.tsx index 6923c38af3de..e9aad39071b9 100644 --- a/app/client/src/pages/Editor/WidgetCard.tsx +++ b/app/client/src/pages/Editor/WidgetCard.tsx @@ -7,6 +7,7 @@ import { generateReactKey } from "utils/generators"; import { Colors } from "constants/Colors"; import { useWidgetSelection } from "utils/hooks/useWidgetSelection"; import { IconWrapper } from "constants/IconConstants"; +import { isAutoLayout } from "selectors/mainCanvasSelectors"; type CardProps = { details: WidgetCardProps; @@ -68,6 +69,10 @@ function WidgetCard(props: CardProps) { const { deselectAll } = useWidgetSelection(); const onDragStart = (e: any) => { + let rows = (props.details as any).rows; + if (isAutoLayout()) { + rows = (props.details as any).autoLayout?.defaults?.rows ?? rows; + } e.preventDefault(); e.stopPropagation(); deselectAll(); @@ -78,6 +83,7 @@ function WidgetCard(props: CardProps) { setDraggingNewWidget && setDraggingNewWidget(true, { ...props.details, + rows: rows, widgetId: generateReactKey(), }); }; diff --git a/app/client/src/selectors/editorSelectors.tsx b/app/client/src/selectors/editorSelectors.tsx index e08367076ac5..84a329385922 100644 --- a/app/client/src/selectors/editorSelectors.tsx +++ b/app/client/src/selectors/editorSelectors.tsx @@ -313,6 +313,7 @@ export const getWidgetCards = createSelector( const _cards: WidgetCardProps[] = cards.map((config) => { const { + autoLayout, columns, detachFromLayout = false, displayName, @@ -323,6 +324,7 @@ export const getWidgetCards = createSelector( type, } = config; return { + autoLayout, key, type, rows, diff --git a/app/client/src/utils/WidgetRegisterHelpers.tsx b/app/client/src/utils/WidgetRegisterHelpers.tsx index c9cc30e3f7e9..72f262159c3d 100644 --- a/app/client/src/utils/WidgetRegisterHelpers.tsx +++ b/app/client/src/utils/WidgetRegisterHelpers.tsx @@ -71,6 +71,7 @@ export const configureWidget = (config: WidgetConfiguration) => { const _config = { ...config.defaults, ...features, + autoLayout: config.autoLayout, searchTags: config.searchTags, type: config.type, hideCard: !!config.hideCard || !config.iconSVG, diff --git a/app/client/src/widgets/InputWidgetV2/index.ts b/app/client/src/widgets/InputWidgetV2/index.ts index 6c37ba19cd99..404d43794c20 100644 --- a/app/client/src/widgets/InputWidgetV2/index.ts +++ b/app/client/src/widgets/InputWidgetV2/index.ts @@ -21,7 +21,7 @@ export const CONFIG = { searchTags: ["form", "text input", "number", "textarea"], defaults: { ...BaseConfig.defaults, - rows: 8, + rows: 7, labelPosition: LabelPosition.Top, inputType: "TEXT", widgetName: "Input", @@ -39,6 +39,11 @@ export const CONFIG = { styleConfig: Widget.getPropertyPaneStyleConfig(), stylesheetConfig: Widget.getStylesheetConfig(), }, + autoLayout: { + defaults: { + rows: 8, + }, + }, }; export default Widget; diff --git a/app/client/src/widgets/constants.ts b/app/client/src/widgets/constants.ts index c93181bc9a4c..7bceddd34385 100644 --- a/app/client/src/widgets/constants.ts +++ b/app/client/src/widgets/constants.ts @@ -15,7 +15,14 @@ import { DerivedPropertiesMap } from "utils/WidgetFactory"; import { WidgetFeatures } from "utils/WidgetFeatures"; import { WidgetProps } from "./BaseWidget"; +interface AutoLayoutWidgetConfiguration { + defaults: { + rows?: number; + }; +} + export interface WidgetConfiguration { + autoLayout?: AutoLayoutWidgetConfiguration; type: string; name: string; iconSVG?: string; From cd91ce34c9ab22a66c1dc7927cad8d51ea9132fc Mon Sep 17 00:00:00 2001 From: Aswath K Date: Fri, 13 Jan 2023 15:29:02 +0530 Subject: [PATCH 399/708] futureproof way to identify autolayout --- .../src/widgets/ContainerWidget/component/index.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/client/src/widgets/ContainerWidget/component/index.tsx b/app/client/src/widgets/ContainerWidget/component/index.tsx index 793c707a3094..d3d12b25c653 100644 --- a/app/client/src/widgets/ContainerWidget/component/index.tsx +++ b/app/client/src/widgets/ContainerWidget/component/index.tsx @@ -10,6 +10,9 @@ import WidgetStyleContainer, { } from "components/designSystems/appsmith/WidgetStyleContainer"; import { ComponentProps } from "widgets/BaseComponent"; import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; +import { useSelector } from "react-redux"; +import { getCurrentAppPositioningType } from "selectors/editorSelectors"; +import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; const scrollContents = css` overflow-y: auto; @@ -68,6 +71,8 @@ const StyledContainerComponent = styled.div< function ContainerComponentWrapper(props: ContainerComponentProps) { const containerStyle = props.containerStyle || "card"; const containerRef: RefObject = useRef(null); + const appPositioningType = useSelector(getCurrentAppPositioningType); + useEffect(() => { if (!props.shouldScrollContents) { const supportsNativeSmoothScroll = @@ -90,7 +95,8 @@ function ContainerComponentWrapper(props: ContainerComponentProps) { className={`${ props.shouldScrollContents ? getCanvasClassName() : "" } ${generateClassName(props.widgetId)} ${ - props.useAutoLayout && props.widgetId === MAIN_CONTAINER_WIDGET_ID + appPositioningType === AppPositioningTypes.AUTO && + props.widgetId === MAIN_CONTAINER_WIDGET_ID ? "auto-layout" : "" }`} From fcfea59a1ec0e43c8b787d70c1ba792117cd2979 Mon Sep 17 00:00:00 2001 From: Aswath K Date: Fri, 13 Jan 2023 17:56:16 +0530 Subject: [PATCH 400/708] set rows for mobile viewport --- app/client/src/sagas/AutoLayoutUtils.ts | 4 ++++ app/client/src/widgets/InputWidgetV2/index.ts | 3 +++ 2 files changed, 7 insertions(+) diff --git a/app/client/src/sagas/AutoLayoutUtils.ts b/app/client/src/sagas/AutoLayoutUtils.ts index 0bbedd747387..874833822592 100644 --- a/app/client/src/sagas/AutoLayoutUtils.ts +++ b/app/client/src/sagas/AutoLayoutUtils.ts @@ -190,6 +190,10 @@ export function alterLayoutForMobile( } widget.mobileTopRow = widget.topRow; widget.mobileBottomRow = widget.bottomRow; + if (widget.autoLayout?.mobile?.rows) { + widget.mobileBottomRow = + widget.mobileTopRow + widget.autoLayout.mobile.rows; + } widgets = alterLayoutForMobile( widgets, child, diff --git a/app/client/src/widgets/InputWidgetV2/index.ts b/app/client/src/widgets/InputWidgetV2/index.ts index 404d43794c20..e94ece8d17e2 100644 --- a/app/client/src/widgets/InputWidgetV2/index.ts +++ b/app/client/src/widgets/InputWidgetV2/index.ts @@ -43,6 +43,9 @@ export const CONFIG = { defaults: { rows: 8, }, + mobile: { + rows: 7, + }, }, }; From d744734b91462a45b1d61078660ad0c8340f9d91 Mon Sep 17 00:00:00 2001 From: Preet Date: Fri, 13 Jan 2023 10:40:36 -0500 Subject: [PATCH 401/708] add unit tests for AutoLayoutUtils --- .../src/sagas/AutoLayoutUpdateSagas.tsx | 2 +- app/client/src/sagas/WidgetAdditionSagas.ts | 2 +- app/client/src/sagas/WidgetDeletionSagas.ts | 2 +- app/client/src/sagas/WidgetOperationSagas.tsx | 2 +- .../autoLayout}/AutoLayoutUtils.ts | 21 ++- .../autoLayoutDraggingUtils.test.ts | 64 ++------ .../utils/autoLayout/autoLayoutUtils.test.ts | 153 ++++++++++++++++++ 7 files changed, 194 insertions(+), 52 deletions(-) rename app/client/src/{sagas => utils/autoLayout}/AutoLayoutUtils.ts (94%) create mode 100644 app/client/src/utils/autoLayout/autoLayoutUtils.test.ts diff --git a/app/client/src/sagas/AutoLayoutUpdateSagas.tsx b/app/client/src/sagas/AutoLayoutUpdateSagas.tsx index d94e25291d8e..42fa64c56c16 100644 --- a/app/client/src/sagas/AutoLayoutUpdateSagas.tsx +++ b/app/client/src/sagas/AutoLayoutUpdateSagas.tsx @@ -15,7 +15,7 @@ import { removeChildLayers, updateFillChildStatus, wrapChildren, -} from "./AutoLayoutUtils"; +} from "../utils/autoLayout/AutoLayoutUtils"; import { getWidgets } from "./selectors"; type LayoutUpdatePayload = { diff --git a/app/client/src/sagas/WidgetAdditionSagas.ts b/app/client/src/sagas/WidgetAdditionSagas.ts index c99595e91353..8c7ad317434f 100644 --- a/app/client/src/sagas/WidgetAdditionSagas.ts +++ b/app/client/src/sagas/WidgetAdditionSagas.ts @@ -34,7 +34,7 @@ import WidgetFactory from "utils/WidgetFactory"; import { generateWidgetProps } from "utils/WidgetPropsUtils"; import { WidgetProps } from "widgets/BaseWidget"; import { GRID_DENSITY_MIGRATION_V1 } from "widgets/constants"; -import { isStack } from "./AutoLayoutUtils"; +import { isStack } from "../utils/autoLayout/AutoLayoutUtils"; import { getWidget, getWidgets } from "./selectors"; import { buildWidgetBlueprint, diff --git a/app/client/src/sagas/WidgetDeletionSagas.ts b/app/client/src/sagas/WidgetDeletionSagas.ts index fbf3a7b274f8..1207fedb6ea8 100644 --- a/app/client/src/sagas/WidgetDeletionSagas.ts +++ b/app/client/src/sagas/WidgetDeletionSagas.ts @@ -35,7 +35,7 @@ import AppsmithConsole from "utils/AppsmithConsole"; import { showUndoRedoToast } from "utils/replayHelpers"; import WidgetFactory from "utils/WidgetFactory"; import { WidgetProps } from "widgets/BaseWidget"; -import { updateFlexLayersOnDelete } from "./AutoLayoutUtils"; +import { updateFlexLayersOnDelete } from "../utils/autoLayout/AutoLayoutUtils"; import { getSelectedWidget, getWidget, getWidgets } from "./selectors"; import { getAllWidgetsInTree, diff --git a/app/client/src/sagas/WidgetOperationSagas.tsx b/app/client/src/sagas/WidgetOperationSagas.tsx index 0c135f298ff7..8337a104b214 100644 --- a/app/client/src/sagas/WidgetOperationSagas.tsx +++ b/app/client/src/sagas/WidgetOperationSagas.tsx @@ -152,7 +152,7 @@ import { addChildToPastedFlexLayers, isStack, pasteWidgetInFlexLayers, -} from "./AutoLayoutUtils"; +} from "../utils/autoLayout/AutoLayoutUtils"; export function* updateAllChildCanvasHeights( currentContainerLikeWidgetId: string, diff --git a/app/client/src/sagas/AutoLayoutUtils.ts b/app/client/src/utils/autoLayout/AutoLayoutUtils.ts similarity index 94% rename from app/client/src/sagas/AutoLayoutUtils.ts rename to app/client/src/utils/autoLayout/AutoLayoutUtils.ts index 0bbedd747387..909ca9f8c7f7 100644 --- a/app/client/src/sagas/AutoLayoutUtils.ts +++ b/app/client/src/utils/autoLayout/AutoLayoutUtils.ts @@ -103,7 +103,7 @@ export function updateFlexLayersOnDelete( index += 1; continue; } - // children.splice(index, 1); + updatedChildren = children.filter( (each: LayerChild) => each.id !== widgetId, ); @@ -304,6 +304,12 @@ export function pasteWidgetInFlexLayers( return updateWidgetPositions(widgets, parentId, isMobile); } +/** + * Add nested children to flex layers of the new pasted canvas. + * The flexLayers get copied from the original canvas. + * This method matches the copied widgetId with the original widgetId + * and replaces them in position. + */ export function addChildToPastedFlexLayers( allWidgets: CanvasWidgetsReduxState, widget: any, @@ -364,3 +370,16 @@ export function isStack( /** * END: copy paste utils */ + +export function getLayerIndexOfWidget( + flexLayers: FlexLayer[], + widgetId: string, +): number { + if (!flexLayers) return -1; + return flexLayers.findIndex((layer: FlexLayer) => { + return ( + layer.children.findIndex((child: LayerChild) => child.id === widgetId) !== + -1 + ); + }); +} diff --git a/app/client/src/utils/autoLayout/autoLayoutDraggingUtils.test.ts b/app/client/src/utils/autoLayout/autoLayoutDraggingUtils.test.ts index 238e83f191bf..dfde6ed92ff5 100644 --- a/app/client/src/utils/autoLayout/autoLayoutDraggingUtils.test.ts +++ b/app/client/src/utils/autoLayout/autoLayoutDraggingUtils.test.ts @@ -10,6 +10,7 @@ import { updateExistingLayer, updateRelationships, } from "./autoLayoutDraggingUtils"; +import { getLayerIndexOfWidget } from "./AutoLayoutUtils"; import { FlexLayerAlignment } from "./constants"; import { data } from "./testData"; @@ -46,13 +47,7 @@ describe("test AutoLayoutDraggingUtils methods", () => { ["pt32jvs72k"], layers, ); - const layerIndex = result.findIndex((layer: FlexLayer) => { - return ( - layer.children.findIndex( - (child: LayerChild) => child.id === "pt32jvs72k", - ) !== -1 - ); - }); + const layerIndex = getLayerIndexOfWidget(result, "pt32jvs72k"); expect(result[0].children.length).toEqual(1); expect(layerIndex).toEqual(-1); }); @@ -84,14 +79,9 @@ describe("test AutoLayoutDraggingUtils methods", () => { expect(result[movedWidget].parentId === "0").toBeTruthy; if (result[oldParent]) { expect(result[oldParent]?.children?.includes(movedWidget)).toBeFalsy; - const layerIndex = result[oldParent]?.flexLayers?.findIndex( - (layer: FlexLayer) => { - return ( - layer.children.findIndex( - (child: LayerChild) => child.id === "pt32jvs72k", - ) !== -1 - ); - }, + const layerIndex = getLayerIndexOfWidget( + result[oldParent]?.flexLayers, + "pt32jvs72k", ); expect(layerIndex).toEqual(-1); } @@ -109,14 +99,9 @@ describe("test AutoLayoutDraggingUtils methods", () => { expect(result[movedWidget].parentId === "0").toBeFalsy; if (result[oldParent]) { expect(result[oldParent]?.children?.includes(movedWidget)).toBeTruthy; - const layerIndex = result[oldParent]?.flexLayers?.findIndex( - (layer: FlexLayer) => { - return ( - layer.children.findIndex( - (child: LayerChild) => child.id === "pt32jvs72k", - ) !== -1 - ); - }, + const layerIndex = getLayerIndexOfWidget( + result[oldParent]?.flexLayers, + "pt32jvs72k", ); expect(layerIndex).toEqual(-1); } @@ -139,14 +124,9 @@ describe("test AutoLayoutDraggingUtils methods", () => { ); const updatedParent = result[newParentId]; expect(updatedParent.flexLayers.length).toEqual(2); - const layerIndex = updatedParent?.flexLayers?.findIndex( - (layer: FlexLayer) => { - return ( - layer.children.findIndex( - (child: LayerChild) => child.id === "pt32jvs72k", - ) !== -1 - ); - }, + const layerIndex = getLayerIndexOfWidget( + updatedParent?.flexLayers, + "pt32jvs72k", ); expect(layerIndex).toEqual(0); }); @@ -166,14 +146,9 @@ describe("test AutoLayoutDraggingUtils methods", () => { ); const updatedParent = result[newParentId]; expect(updatedParent.flexLayers.length).toEqual(2); - const layerIndex = updatedParent?.flexLayers?.findIndex( - (layer: FlexLayer) => { - return ( - layer.children.findIndex( - (child: LayerChild) => child.id === "pt32jvs72k", - ) !== -1 - ); - }, + const layerIndex = getLayerIndexOfWidget( + updatedParent?.flexLayers, + "pt32jvs72k", ); expect(layerIndex).toEqual(1); }); @@ -198,14 +173,9 @@ describe("test AutoLayoutDraggingUtils methods", () => { const updatedParent = result[newParentId]; expect(updatedParent.flexLayers.length).toEqual(1); expect(updatedParent.flexLayers[0].children.length).toEqual(3); - const layerIndex = updatedParent?.flexLayers?.findIndex( - (layer: FlexLayer) => { - return ( - layer.children.findIndex( - (child: LayerChild) => child.id === "pt32jvs72k", - ) !== -1 - ); - }, + const layerIndex = getLayerIndexOfWidget( + updatedParent?.flexLayers, + "pt32jvs72k", ); expect(layerIndex).toEqual(0); }); diff --git a/app/client/src/utils/autoLayout/autoLayoutUtils.test.ts b/app/client/src/utils/autoLayout/autoLayoutUtils.test.ts new file mode 100644 index 000000000000..15525c6413ff --- /dev/null +++ b/app/client/src/utils/autoLayout/autoLayoutUtils.test.ts @@ -0,0 +1,153 @@ +import { + FlexLayer, + LayerChild, +} from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; +import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; +import { + getLayerIndexOfWidget, + pasteWidgetInFlexLayers, + updateFlexLayersOnDelete, +} from "./AutoLayoutUtils"; +import { data } from "./testData"; + +describe("test AutoLayoutUtils methods", () => { + describe("test updateFlexLayersOnDelete method", () => { + it("should remove deleted widgets from flex layers of the parent", () => { + const widgets = { ...data }; + const deletedWidgetId = "pt32jvs72k"; + const parentId = "a3lldg1wg9"; + const result: CanvasWidgetsReduxState = updateFlexLayersOnDelete( + widgets, + deletedWidgetId, + parentId, + false, + ); + expect(result[parentId].flexLayers?.length).toEqual(1); + const layerIndex = getLayerIndexOfWidget( + result[parentId]?.flexLayers, + deletedWidgetId, + ); + expect(layerIndex).toEqual(-1); + }); + it("should return the layers as is, if the deleted widget does not exist in them", () => { + const widgets = { ...data }; + const deletedWidgetId = "33"; + const parentId = "a3lldg1wg9"; + const result: CanvasWidgetsReduxState = updateFlexLayersOnDelete( + widgets, + deletedWidgetId, + parentId, + false, + ); + expect(result[parentId].flexLayers?.length).toEqual(1); + expect(result[parentId].flexLayers[0]?.children.length).toEqual(2); + }); + it("should discard empty layers after removing deleted widgets", () => { + const widgets = { ...data }; + const parentId = "a3lldg1wg9"; + const updatedWidgets: CanvasWidgetsReduxState = updateFlexLayersOnDelete( + widgets, + "pt32jvs72k", + parentId, + false, + ); + const result: CanvasWidgetsReduxState = updateFlexLayersOnDelete( + updatedWidgets, + "tg6jcd1kjp", + parentId, + false, + ); + expect(result[parentId].flexLayers?.length).toEqual(0); + }); + }); + + describe("test pasteWidgetInFlexLayers method", () => { + it("should add the pasted widget to a new layer at the bottom of the parent, if the new parent is different from the original", () => { + let widgets = { ...data }; + const originalWidgetId = "pt32jvs72k"; + const newParentId = "2ydfwnmayi"; + expect(widgets[newParentId].flexLayers?.length).toEqual(0); + + const copiedWidget = { + ...widgets["pt32jvs72k"], + widgetId: "abcdef123", + widgetName: widgets["pt32jvs72k"].widgetName + "Copy", + key: + widgets["pt32jvs72k"].key.slice( + 0, + widgets["pt32jvs72k"].key.length - 2, + ) + "yz", + parentId: newParentId, + }; + widgets = { ...widgets, [copiedWidget.widgetId]: copiedWidget }; + const result: CanvasWidgetsReduxState = pasteWidgetInFlexLayers( + widgets, + newParentId, + copiedWidget, + originalWidgetId, + false, + ); + + expect(result[newParentId].flexLayers?.length).toEqual(1); + expect( + getLayerIndexOfWidget( + result[newParentId]?.flexLayers, + copiedWidget.widgetId, + ), + ).toEqual(0); + }); + it("should paste the copied widget in the same layer and to the right of the original widget, if the parentId remains the same", () => { + let widgets = { ...data }; + const originalWidgetId = "pt32jvs72k"; + const parentId = "a3lldg1wg9"; + /** + * Update original parent's flexLayers to ramp up the layer count. + * => split each child into a new layer. + */ + const layers: FlexLayer[] = []; + for (const layer of widgets[parentId].flexLayers) { + for (const child of layer.children) { + layers.push({ + children: [child as LayerChild], + }); + } + } + widgets = { + ...widgets, + [parentId]: { ...widgets[parentId], flexLayers: layers }, + }; + expect(widgets[parentId].flexLayers?.length).toEqual(2); + + const copiedWidget = { + ...widgets["pt32jvs72k"], + widgetId: "abcdef123", + widgetName: widgets["pt32jvs72k"].widgetName + "Copy", + key: + widgets["pt32jvs72k"].key.slice( + 0, + widgets["pt32jvs72k"].key.length - 2, + ) + "yz", + parentId: parentId, + }; + widgets = { ...widgets, [copiedWidget.widgetId]: copiedWidget }; + const result: CanvasWidgetsReduxState = pasteWidgetInFlexLayers( + widgets, + parentId, + copiedWidget, + originalWidgetId, + false, + ); + // layer count should remain the same. => no new layer is created. + expect(result[parentId].flexLayers?.length).toEqual(2); + // new widget should be pasted in the same layer as the original + expect( + getLayerIndexOfWidget( + result[parentId]?.flexLayers, + copiedWidget.widgetId, + ), + ).toEqual( + getLayerIndexOfWidget(result[parentId]?.flexLayers, originalWidgetId), + ); + }); + }); +}); From 51436b18499cbb7677fddd3349781fdd299f3ce3 Mon Sep 17 00:00:00 2001 From: Preet Date: Fri, 13 Jan 2023 10:50:03 -0500 Subject: [PATCH 402/708] merge fix --- app/client/src/components/propertyControls/ButtonControl.tsx | 2 -- app/client/src/selectors/autoLayoutSelectors.tsx | 1 - 2 files changed, 3 deletions(-) diff --git a/app/client/src/components/propertyControls/ButtonControl.tsx b/app/client/src/components/propertyControls/ButtonControl.tsx index a4b38def6d61..5e08ad5c5991 100644 --- a/app/client/src/components/propertyControls/ButtonControl.tsx +++ b/app/client/src/components/propertyControls/ButtonControl.tsx @@ -4,8 +4,6 @@ import styled from "styled-components"; import BaseControl, { ControlProps } from "./BaseControl"; import { StyledPropertyPaneButton } from "./StyledControls"; import { Category, Size } from "design-system"; -import BaseControl, { ControlProps } from "./BaseControl"; -import { StyledPropertyPaneButton } from "./StyledControls"; export type OnButtonClickProps = { props: ControlProps; diff --git a/app/client/src/selectors/autoLayoutSelectors.tsx b/app/client/src/selectors/autoLayoutSelectors.tsx index f7d759585679..46b4f7b1e1ca 100644 --- a/app/client/src/selectors/autoLayoutSelectors.tsx +++ b/app/client/src/selectors/autoLayoutSelectors.tsx @@ -4,7 +4,6 @@ import { LayerChild, } from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; import { FLEXBOX_PADDING, GridDefaults } from "constants/WidgetConstants"; -import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; import { createSelector } from "reselect"; import { getWidgets } from "sagas/selectors"; import { getIsMobile } from "./mainCanvasSelectors"; From 4e27229fc721637a7ce2ca252c67c2b502ccf3f9 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Mon, 16 Jan 2023 15:12:15 +0530 Subject: [PATCH 403/708] 1d widget resize enable in fixed. --- .../editorComponents/ResizableComponent.tsx | 14 ++-- .../CanvasArenas/hooks/canvasDraggingUtils.ts | 64 ++++++++++--------- .../CanvasArenas/hooks/useCanvasDragging.ts | 31 +++++---- 3 files changed, 63 insertions(+), 46 deletions(-) diff --git a/app/client/src/components/editorComponents/ResizableComponent.tsx b/app/client/src/components/editorComponents/ResizableComponent.tsx index 67ab464e050f..d523f440d191 100644 --- a/app/client/src/components/editorComponents/ResizableComponent.tsx +++ b/app/client/src/components/editorComponents/ResizableComponent.tsx @@ -1,15 +1,16 @@ import { AppState } from "@appsmith/reducers"; import { batchUpdateMultipleWidgetProperties } from "actions/controlActions"; import { focusWidget } from "actions/widgetActions"; -import { ResponsiveBehavior } from "utils/autoLayout/constants"; import { EditorContext } from "components/editorComponents/EditorContextProvider"; import { GridDefaults } from "constants/WidgetConstants"; import { get, omit } from "lodash"; import { XYCord } from "pages/common/CanvasArenas/hooks/useRenderBlocksOnCanvas"; import React, { memo, useContext, useMemo } from "react"; import { useDispatch, useSelector } from "react-redux"; +import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; import Resizable from "resizable/resizenreflow"; import { + getCurrentAppPositioningType, previewModeSelector, snipingModeSelector, } from "selectors/editorSelectors"; @@ -21,6 +22,7 @@ import { isWidgetSelected, } from "selectors/widgetSelectors"; import AnalyticsUtil from "utils/AnalyticsUtil"; +import { ResponsiveBehavior } from "utils/autoLayout/constants"; import { useShowPropertyPane, useShowTableFilterPane, @@ -66,7 +68,9 @@ export const ResizableComponent = memo(function ResizableComponent( const isSnipingMode = useSelector(snipingModeSelector); const isPreviewMode = useSelector(previewModeSelector); - + const currentAppPositioningType = useSelector(getCurrentAppPositioningType); + const isAutoLayoutMode = + currentAppPositioningType === AppPositioningTypes.AUTO; const showPropertyPane = useShowPropertyPane(); const showTableFilterPane = useShowTableFilterPane(); const { selectWidget } = useWidgetSelection(); @@ -316,8 +320,10 @@ export const ResizableComponent = memo(function ResizableComponent( return !isAutoHeightEnabledForWidget(props) && isEnabled; }, [props, isAutoHeightEnabledForWidget, isEnabled]); const allowResize: boolean = - !(NonResizableWidgets.includes(props.type) || isMultiSelected) || - !props.isFlexChild; + !( + (isAutoLayoutMode && NonResizableWidgets.includes(props.type)) || + isMultiSelected + ) || !(isAutoLayoutMode && props.isFlexChild); return ( { const { columnWidth, @@ -225,43 +226,44 @@ export const modifyBlockDimension = ( y: 0, }, ); - let leftOffset = 0, rightOffset = 0, topOffset = 0, bottomOffset = 0; + if (!modifyBlock) { + // calculate offsets based on collisions and limits + if (leftColumn < 0) { + leftOffset = + leftColumn + columnWidth > HORIZONTAL_RESIZE_MIN_LIMIT + ? leftColumn + : HORIZONTAL_RESIZE_MIN_LIMIT - columnWidth; + } else if (leftColumn + columnWidth > GridDefaults.DEFAULT_GRID_COLUMNS) { + rightOffset = + GridDefaults.DEFAULT_GRID_COLUMNS - leftColumn - columnWidth; + rightOffset = + columnWidth + rightOffset >= HORIZONTAL_RESIZE_MIN_LIMIT + ? rightOffset + : HORIZONTAL_RESIZE_MIN_LIMIT - columnWidth; + } - // calculate offsets based on collisions and limits - if (leftColumn < 0) { - leftOffset = - leftColumn + columnWidth > HORIZONTAL_RESIZE_MIN_LIMIT - ? leftColumn - : HORIZONTAL_RESIZE_MIN_LIMIT - columnWidth; - } else if (leftColumn + columnWidth > GridDefaults.DEFAULT_GRID_COLUMNS) { - rightOffset = GridDefaults.DEFAULT_GRID_COLUMNS - leftColumn - columnWidth; - rightOffset = - columnWidth + rightOffset >= HORIZONTAL_RESIZE_MIN_LIMIT - ? rightOffset - : HORIZONTAL_RESIZE_MIN_LIMIT - columnWidth; - } - - if (topRow < 0 && fixedHeight === undefined) { - topOffset = - topRow + rowHeight > VERTICAL_RESIZE_MIN_LIMIT - ? topRow - : VERTICAL_RESIZE_MIN_LIMIT - rowHeight; - } + if (topRow < 0 && fixedHeight === undefined) { + topOffset = + topRow + rowHeight > VERTICAL_RESIZE_MIN_LIMIT + ? topRow + : VERTICAL_RESIZE_MIN_LIMIT - rowHeight; + } - if ( - topRow + rowHeight > parentBottomRow && - !canExtend && - fixedHeight === undefined - ) { - bottomOffset = parentBottomRow - topRow - rowHeight; - bottomOffset = - rowHeight + bottomOffset >= VERTICAL_RESIZE_MIN_LIMIT - ? bottomOffset - : VERTICAL_RESIZE_MIN_LIMIT - rowHeight; + if ( + topRow + rowHeight > parentBottomRow && + !canExtend && + fixedHeight === undefined + ) { + bottomOffset = parentBottomRow - topRow - rowHeight; + bottomOffset = + rowHeight + bottomOffset >= VERTICAL_RESIZE_MIN_LIMIT + ? bottomOffset + : VERTICAL_RESIZE_MIN_LIMIT - rowHeight; + } } return { diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index 22f7714391f4..a53f4251a796 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -1,4 +1,3 @@ -import React from "react"; import { OccupiedSpace } from "constants/CanvasEditorConstants"; import { GridDefaults, @@ -6,13 +5,16 @@ import { } from "constants/WidgetConstants"; import { debounce, isEmpty, throttle } from "lodash"; import { CanvasDraggingArenaProps } from "pages/common/CanvasArenas/CanvasDraggingArena"; -import { useEffect, useRef } from "react"; +import React, { useEffect, useRef } from "react"; +import { useSelector } from "react-redux"; +import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; import { MovementLimitMap, ReflowDirection, ReflowedSpaceMap, SpaceMap, } from "reflow/reflowTypes"; +import { getParentOffsetTop } from "selectors/autoLayoutSelectors"; import { getCanvasScale, getCurrentAppPositioningType, @@ -34,19 +36,16 @@ import { modifyDrawingRectangles, updateRectanglesPostReflow, } from "./canvasDraggingUtils"; +import { + HighlightInfo, + useAutoLayoutHighlights, +} from "./useAutoLayoutHighlights"; import { useBlocksToBeDraggedOnCanvas, WidgetDraggingBlock, } from "./useBlocksToBeDraggedOnCanvas"; import { useCanvasDragToScroll } from "./useCanvasDragToScroll"; import { useRenderBlocksOnCanvas } from "./useRenderBlocksOnCanvas"; -import { useSelector } from "react-redux"; -import { - HighlightInfo, - useAutoLayoutHighlights, -} from "./useAutoLayoutHighlights"; -import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; -import { getParentOffsetTop } from "selectors/autoLayoutSelectors"; export const useCanvasDragging = ( slidingArenaRef: React.RefObject, @@ -303,12 +302,13 @@ export const useCanvasDragging = ( onMouseMove(e, over); } }; + const isAutoLayout = appPositioningType === AppPositioningTypes.AUTO; const triggerReflow = (e: any, firstMove: boolean) => { const canReflow = !currentRectanglesToDraw[0].detachFromLayout && !dropDisabled && - appPositioningType === AppPositioningTypes.FIXED; + !isAutoLayout; const isReflowing = !isEmpty(currentReflowParams.movementMap) || (!isEmpty(currentReflowParams.movementLimitMap) && @@ -408,6 +408,7 @@ export const useCanvasDragging = ( snapRowSpace, rowRef.current - 1, canExtend, + isAutoLayout, ), ); const newRows = updateRelativeRows(drawingBlocks, rowRef.current); @@ -509,6 +510,7 @@ export const useCanvasDragging = ( snapRowSpace, rowRef.current - 1, canExtend, + isAutoLayout, ); return { ...block, @@ -682,7 +684,14 @@ export const useCanvasDragging = ( resetCanvasState(); } } - }, [isDragging, isResizing, blocksToDraw, snapRows, canExtend]); + }, [ + isDragging, + isResizing, + blocksToDraw, + snapRows, + canExtend, + appPositioningType, + ]); return { showCanvas: isDragging && !isResizing, }; From 4178bd0c562957c79d94c00db3e960955ef78e54 Mon Sep 17 00:00:00 2001 From: Aswath K Date: Mon, 16 Jan 2023 18:02:50 +0530 Subject: [PATCH 404/708] PhoneInput & CurrencyInput added --- .../component/CurrencyCodeDropdown.tsx | 2 +- app/client/src/widgets/CurrencyInputWidget/index.ts | 8 ++++++++ .../PhoneInputWidget/component/ISDCodeDropdown.tsx | 2 +- app/client/src/widgets/PhoneInputWidget/index.ts | 8 ++++++++ 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/app/client/src/widgets/CurrencyInputWidget/component/CurrencyCodeDropdown.tsx b/app/client/src/widgets/CurrencyInputWidget/component/CurrencyCodeDropdown.tsx index c236983f609d..be705afb8959 100644 --- a/app/client/src/widgets/CurrencyInputWidget/component/CurrencyCodeDropdown.tsx +++ b/app/client/src/widgets/CurrencyInputWidget/component/CurrencyCodeDropdown.tsx @@ -251,7 +251,7 @@ export default function CurrencyTypeDropdown(props: CurrencyDropdownProps) { enableSearch height="36px" onSelect={props.onCurrencyTypeChange} - optionWidth="340px" + optionWidth="360px" options={props.options} portalClassName={`country-type-filter-dropdown-${props.widgetId}`} portalContainer={document.getElementById("art-board") || undefined} diff --git a/app/client/src/widgets/CurrencyInputWidget/index.ts b/app/client/src/widgets/CurrencyInputWidget/index.ts index e54c87fe08b7..6cb136e7a038 100644 --- a/app/client/src/widgets/CurrencyInputWidget/index.ts +++ b/app/client/src/widgets/CurrencyInputWidget/index.ts @@ -42,6 +42,14 @@ export const CONFIG = { styleConfig: Widget.getPropertyPaneStyleConfig(), stylesheetConfig: Widget.getStylesheetConfig(), }, + autoLayout: { + defaults: { + rows: 8, + }, + mobile: { + rows: 7, + }, + }, }; export default Widget; diff --git a/app/client/src/widgets/PhoneInputWidget/component/ISDCodeDropdown.tsx b/app/client/src/widgets/PhoneInputWidget/component/ISDCodeDropdown.tsx index 078e7c57dfdf..5978bf0368dd 100644 --- a/app/client/src/widgets/PhoneInputWidget/component/ISDCodeDropdown.tsx +++ b/app/client/src/widgets/PhoneInputWidget/component/ISDCodeDropdown.tsx @@ -277,7 +277,7 @@ export default function ISDCodeDropdown(props: ISDCodeDropdownProps) { enableSearch height="36px" onSelect={props.onISDCodeChange} - optionWidth="340px" + optionWidth="360px" options={props.options} portalClassName={`country-type-filter-dropdown-${props.widgetId}`} portalContainer={document.getElementById("art-board") || undefined} diff --git a/app/client/src/widgets/PhoneInputWidget/index.ts b/app/client/src/widgets/PhoneInputWidget/index.ts index 263deb603c60..99a773d63087 100644 --- a/app/client/src/widgets/PhoneInputWidget/index.ts +++ b/app/client/src/widgets/PhoneInputWidget/index.ts @@ -41,6 +41,14 @@ export const CONFIG = { styleConfig: Widget.getPropertyPaneStyleConfig(), stylesheetConfig: Widget.getStylesheetConfig(), }, + autoLayout: { + defaults: { + rows: 8, + }, + mobile: { + rows: 7, + }, + }, }; export default Widget; From b96c5fcac1010394748be977d8beadafffd03ca0 Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 16 Jan 2023 08:50:50 -0500 Subject: [PATCH 405/708] fix unit test --- .../hooks/canvasDraggingUtils.test.ts | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/canvasDraggingUtils.test.ts b/app/client/src/pages/common/CanvasArenas/hooks/canvasDraggingUtils.test.ts index 725c1430a50e..c651e6414fdf 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/canvasDraggingUtils.test.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/canvasDraggingUtils.test.ts @@ -187,9 +187,9 @@ describe("test canvasDraggingUtils Methods", () => { widgetId: "id", isNotColliding: true, }; - expect(modifyBlockDimension(draggingBlock, 10, 10, 100, true)).toEqual( - modifiedBlock, - ); + expect( + modifyBlockDimension(draggingBlock, 10, 10, 100, true, false), + ).toEqual(modifiedBlock); }); it("should return resized dragging blocks while colliding with canvas edges to it's limits, for top Left", () => { @@ -214,9 +214,9 @@ describe("test canvasDraggingUtils Methods", () => { widgetId: "id", isNotColliding: true, }; - expect(modifyBlockDimension(draggingBlock, 10, 10, 100, true)).toEqual( - modifiedBlock, - ); + expect( + modifyBlockDimension(draggingBlock, 10, 10, 100, true, false), + ).toEqual(modifiedBlock); }); it("should return resized dragging blocks while colliding with canvas edges, for bottom right", () => { @@ -241,9 +241,9 @@ describe("test canvasDraggingUtils Methods", () => { widgetId: "id", isNotColliding: true, }; - expect(modifyBlockDimension(draggingBlock, 10, 10, 100, false)).toEqual( - modifiedBlock, - ); + expect( + modifyBlockDimension(draggingBlock, 10, 10, 100, false, false), + ).toEqual(modifiedBlock); }); it("should return resized dragging blocks while colliding with canvas edges to it's limits, for bottom right", () => { @@ -270,9 +270,9 @@ describe("test canvasDraggingUtils Methods", () => { isNotColliding: true, fixedHeight: 90, }; - expect(modifyBlockDimension(draggingBlock, 10, 10, 100, false)).toEqual( - modifiedBlock, - ); + expect( + modifyBlockDimension(draggingBlock, 10, 10, 100, false, false), + ).toEqual(modifiedBlock); }); }); From 780e813e4239fe44457dd2b267657b71bb287c3c Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 16 Jan 2023 10:54:09 -0500 Subject: [PATCH 406/708] fix position of the right resize indicator --- app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx index b6c3e04dc031..a23a5b38af8b 100644 --- a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx +++ b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx @@ -241,7 +241,7 @@ function CanvasContainer() { cursor: "col-resize", width: "16px", height: "0px", - left: isPreviewMode ? "100%" : "calc(100% - 32px)", + right: isPreviewMode ? "100%" : "16px", top: "50%", zIndex: isPreviewMode ? 2 : undefined, float: "right", From 947ff47c1a52b8193f0bc32c0dfa6a61385f9cc0 Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 16 Jan 2023 10:56:38 -0500 Subject: [PATCH 407/708] fix position of right resize indicator in preview mode --- app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx index a23a5b38af8b..b4c869be798d 100644 --- a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx +++ b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx @@ -241,7 +241,7 @@ function CanvasContainer() { cursor: "col-resize", width: "16px", height: "0px", - right: isPreviewMode ? "100%" : "16px", + right: isPreviewMode ? "0px" : "16px", top: "50%", zIndex: isPreviewMode ? 2 : undefined, float: "right", From 857582f9f9856ed5aa4798e9adc9ed3d45225527 Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 16 Jan 2023 16:01:57 -0500 Subject: [PATCH 408/708] fix drop besides list widget --- .../CanvasArenas/hooks/canvasDraggingUtils.ts | 24 +++++++++++++++++++ .../CanvasArenas/hooks/useCanvasDragging.ts | 18 ++++++++++++-- app/client/src/reflow/reflowUtils.ts | 12 ++++++++++ 3 files changed, 52 insertions(+), 2 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/canvasDraggingUtils.ts b/app/client/src/pages/common/CanvasArenas/hooks/canvasDraggingUtils.ts index 8d1d50174233..a2eeef2aaef2 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/canvasDraggingUtils.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/canvasDraggingUtils.ts @@ -1,5 +1,6 @@ import { OccupiedSpace } from "constants/CanvasEditorConstants"; import { GridDefaults } from "constants/WidgetConstants"; +import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; import { HORIZONTAL_RESIZE_MIN_LIMIT, MovementLimitMap, @@ -380,3 +381,26 @@ export function getLastCanvasExitDirection( if (direction) return direction; return currentDirection; } + +export function getLastDraggedCanvasSpace( + allWidgets: CanvasWidgetsReduxState, + currentCanvasId: string, + lastCanvasId: string | undefined, + occupiedSpaceMap: { [key: string]: OccupiedSpace }, +): OccupiedSpace | undefined { + if (!allWidgets || !lastCanvasId || !currentCanvasId || !occupiedSpaceMap) + return undefined; + if (currentCanvasId === lastCanvasId) return undefined; + if (occupiedSpaceMap[lastCanvasId]) return occupiedSpaceMap[lastCanvasId]; + else if (allWidgets[lastCanvasId].parentId) { + /** + * Needed for list widget. + */ + return getLastDraggedCanvasSpace( + allWidgets, + currentCanvasId, + allWidgets[lastCanvasId].parentId, + occupiedSpaceMap, + ); + } +} diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index a53f4251a796..4ff7b5e8a5de 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -7,6 +7,7 @@ import { debounce, isEmpty, throttle } from "lodash"; import { CanvasDraggingArenaProps } from "pages/common/CanvasArenas/CanvasDraggingArena"; import React, { useEffect, useRef } from "react"; import { useSelector } from "react-redux"; +import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; import { MovementLimitMap, @@ -19,6 +20,7 @@ import { getCanvasScale, getCurrentAppPositioningType, } from "selectors/editorSelectors"; +import { getCanvasWidgets } from "selectors/entitiesSelector"; import { getNearestParentCanvas } from "utils/generators"; import { useWidgetDragResize } from "utils/hooks/dragResizeHooks"; import { ReflowInterface, useReflow } from "utils/hooks/useReflow"; @@ -30,6 +32,7 @@ import { import { getEdgeDirection, getInterpolatedMoveDirection, + getLastDraggedCanvasSpace, getMoveDirection, getReflowedSpaces, modifyBlockDimension, @@ -69,6 +72,7 @@ export const useCanvasDragging = ( scale *= canvasScale; const appPositioningType = useSelector(getCurrentAppPositioningType); const parentOffsetTop = useSelector(getParentOffsetTop(widgetId)); + const allWidgets: CanvasWidgetsReduxState = useSelector(getCanvasWidgets); const { blocksToDraw, defaultHandlePositions, @@ -450,8 +454,18 @@ export const useCanvasDragging = ( const currentOccupiedSpace: any[] = occupiedSpaces[widgetId]; let exitContainer: OccupiedSpace | undefined = undefined; if (lastDraggedCanvas.current && currentOccupiedSpace) { - exitContainer = currentOccupiedSpace.find( - (each) => each.id === lastDraggedCanvas.current, + const occupiedSpaceMap = currentOccupiedSpace.reduce( + (acc, curr) => { + acc[curr.id] = curr; + return acc; + }, + {}, + ); + exitContainer = getLastDraggedCanvasSpace( + allWidgets, + widgetId, + lastDraggedCanvas.current, + occupiedSpaceMap, ); } currentDirection.current = getInterpolatedMoveDirection( diff --git a/app/client/src/reflow/reflowUtils.ts b/app/client/src/reflow/reflowUtils.ts index addad6489e3d..6ae8d76ca53a 100644 --- a/app/client/src/reflow/reflowUtils.ts +++ b/app/client/src/reflow/reflowUtils.ts @@ -1994,6 +1994,12 @@ export function getContainerExitEdge( ) { if (mousePosition.left >= exitContainer.right) return ReflowDirection.RIGHT; if (mousePosition.left <= exitContainer.left) return ReflowDirection.LEFT; + if ( + Math.abs(mousePosition.left - exitContainer.left) < + Math.abs(mousePosition.left - exitContainer.right) + ) + return ReflowDirection.LEFT; + else return ReflowDirection.RIGHT; } if ( @@ -2003,6 +2009,12 @@ export function getContainerExitEdge( if (mousePosition.top >= exitContainer.bottom) return ReflowDirection.BOTTOM; if (mousePosition.top <= exitContainer.top) return ReflowDirection.TOP; + if ( + Math.abs(mousePosition.top - exitContainer.top) < + Math.abs(mousePosition.top - exitContainer.bottom) + ) + return ReflowDirection.TOP; + else return ReflowDirection.BOTTOM; } } From dd871a7092674250cc352e603e78c06320b3fd04 Mon Sep 17 00:00:00 2001 From: Arsalan Date: Tue, 17 Jan 2023 12:50:42 +0530 Subject: [PATCH 409/708] fix: remove container query polyfill. --- app/client/package.json | 1 - app/client/src/index.tsx | 7 ------- app/client/yarn.lock | 5 ----- 3 files changed, 13 deletions(-) diff --git a/app/client/package.json b/app/client/package.json index c35c61aa8737..a58d084ba00f 100644 --- a/app/client/package.json +++ b/app/client/package.json @@ -147,7 +147,6 @@ "remixicon-react": "^1.0.0", "reselect": "^4.0.0", "scroll-into-view-if-needed": "^2.2.26", - "shadow-container-query-polyfill": "^1.1.7", "shallowequal": "^1.1.0", "showdown": "^1.9.1", "smartlook-client": "^8.0.0", diff --git a/app/client/src/index.tsx b/app/client/src/index.tsx index c10a717fac02..efc07c6bec71 100755 --- a/app/client/src/index.tsx +++ b/app/client/src/index.tsx @@ -23,13 +23,6 @@ import AppErrorBoundary from "AppErrorBoundry"; const shouldAutoFreeze = process.env.NODE_ENV === "development"; setAutoFreeze(shouldAutoFreeze); -const supportsContainerQueries = "container" in document.documentElement.style; - -if (!supportsContainerQueries) { - // @ts-expect-error: polyfill type declarations not found - import("shadow-container-query-polyfill"); -} - runSagaMiddleware(); appInitializer(); diff --git a/app/client/yarn.lock b/app/client/yarn.lock index ff78de87f1db..03031d171985 100644 --- a/app/client/yarn.lock +++ b/app/client/yarn.lock @@ -13743,11 +13743,6 @@ setprototypeof@1.2.0: resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== -shadow-container-query-polyfill@^1.1.7: - version "1.1.7" - resolved "https://registry.yarnpkg.com/shadow-container-query-polyfill/-/shadow-container-query-polyfill-1.1.7.tgz#3c75473e27f3a6acdc3b9200da54194a414a0d4d" - integrity sha512-XflRiqZvcTpXRTg60Tvq5Z4qCGgEmAHLNbSd/HJoS7HaGxpglJVntrwZ4L3IdS0ZsS2DzuMUP/6Q3E9ovDP+iA== - shallow-clone@^3.0.0: version "3.0.1" resolved "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz" From 7d7bd35faca6c6473ea1876b4835faf39513c129 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Tue, 17 Jan 2023 15:13:19 +0530 Subject: [PATCH 410/708] reszinig fixes. --- .../appsmith/autoLayout/FlexComponent.tsx | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx index 15e2c0cf1f9e..686c377e1d6d 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx @@ -1,11 +1,6 @@ import React, { CSSProperties, ReactNode, useCallback, useMemo } from "react"; import styled from "styled-components"; -import { - FlexVerticalAlignment, - LayoutDirection, - ResponsiveBehavior, -} from "utils/autoLayout/constants"; import { WidgetType, widgetTypeClassname, @@ -13,6 +8,12 @@ import { } from "constants/WidgetConstants"; import { useSelector } from "react-redux"; import { snipingModeSelector } from "selectors/editorSelectors"; +import { getIsResizing } from "selectors/widgetSelectors"; +import { + FlexVerticalAlignment, + LayoutDirection, + ResponsiveBehavior, +} from "utils/autoLayout/constants"; import { useClickToSelectWidget } from "utils/hooks/useClickToSelectWidget"; import { usePositionedContainerZIndex } from "utils/hooks/usePositionedContainerZIndex"; import { checkIsDropTarget } from "../PositionedContainer"; @@ -65,12 +66,18 @@ export function FlexComponent(props: AutoLayoutProps) { props.widgetId } ${widgetTypeClassname(props.widgetType)}`; + const isResizing = useSelector(getIsResizing); + const flexComponentStyle: CSSProperties = useMemo(() => { return { display: "flex", zIndex, - width: `${props.componentWidth - WIDGET_PADDING * 2}px`, - height: props.componentHeight - WIDGET_PADDING * 2 + "px", + width: isResizing + ? "100%" + : `${props.componentWidth - WIDGET_PADDING * 2}px`, + height: isResizing + ? "100%" + : props.componentHeight - WIDGET_PADDING * 2 + "px", minHeight: "30px", margin: WIDGET_PADDING + "px", alignSelf: props.flexVerticalAlignment, @@ -84,6 +91,7 @@ export function FlexComponent(props: AutoLayoutProps) { props.componentHeight, props.flexVerticalAlignment, zIndex, + isResizing, onHoverZIndex, ]); From ac919f8e19f08103585261387ff0e547d3aa263c Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Tue, 17 Jan 2023 15:46:23 +0530 Subject: [PATCH 411/708] fixing resizing. --- .../designSystems/appsmith/autoLayout/FlexComponent.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx index 686c377e1d6d..02341d72442c 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx @@ -73,10 +73,10 @@ export function FlexComponent(props: AutoLayoutProps) { display: "flex", zIndex, width: isResizing - ? "100%" + ? "auto" : `${props.componentWidth - WIDGET_PADDING * 2}px`, height: isResizing - ? "100%" + ? "auto" : props.componentHeight - WIDGET_PADDING * 2 + "px", minHeight: "30px", margin: WIDGET_PADDING + "px", From b33bcf334c7538724bcda32ca0c5b7ec06850263 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Tue, 17 Jan 2023 16:35:09 +0530 Subject: [PATCH 412/708] Bounding Box design updates. --- .../appsmith/autoLayout/FlexComponent.tsx | 1 + .../editorComponents/ResizableComponent.tsx | 6 +- .../ResizeStyledComponents.tsx | 58 +++++-- .../WidgetNameComponent/SettingsControl.tsx | 16 +- .../WidgetNameComponent/index.tsx | 142 +++++++++++++++--- app/client/src/constants/Colors.tsx | 4 +- app/client/src/pages/Editor/Popper.tsx | 20 ++- .../src/resizable/resizenreflow/index.tsx | 6 + app/client/src/widgets/BaseWidget.tsx | 1 + 9 files changed, 206 insertions(+), 48 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx index 02341d72442c..57ea86704d6d 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx @@ -98,6 +98,7 @@ export function FlexComponent(props: AutoLayoutProps) { return ( ` &::after { position: absolute; content: ""; width: 6px; + height: 16px; + border-radius: 50%/16%; + background: white; + top: calc(50% - 8px); + left: calc(50% - 2.5px); + border: ${(props) => { + return `1px solid ${props.isHovered ? Colors.WATUSI : "#F86A2B"}`; + }}; + } + &:hover::after { + background: #f86a2b; + } +`; + +const HorizontalResizeIndicators = css<{ + showLightBorder: boolean; + isHovered: boolean; +}>` + &::after { + position: absolute; + content: ""; + width: 16px; height: 6px; - border-radius: 50%; - background: ${(props) => - props.showLightBorder - ? theme.colors.widgetLightBorder - : theme.colors.widgetBorder}; - top: calc(50% - 2px); - left: calc(50% - 2px); + border-radius: 16%/50%; + border: ${(props) => { + return `1px solid ${props.isHovered ? Colors.WATUSI : "#F86A2B"}`; + }}; + background: white; + top: calc(50% - 2.5px); + left: calc(50% - 8px); + } + &:hover::after { + background: #f86a2b; } `; @@ -36,6 +63,7 @@ export const EdgeHandleStyles = css<{ showAsBorder: boolean; showLightBorder: boolean; disableDot: boolean; + isHovered: boolean; }>` position: absolute; width: ${EDGE_RESIZE_HANDLE_WIDTH}px; @@ -45,22 +73,25 @@ export const EdgeHandleStyles = css<{ background: ${(props) => { if (props.showLightBorder) return theme.colors.widgetLightBorder; - if (props.showAsBorder) return theme.colors.widgetMultiSelectBorder; + if (props.isHovered) { + return Colors.WATUSI; + } - return theme.colors.widgetBorder; + return "#F86A2B"; }}; content: ""; } - ${(props) => - props.showAsBorder || props.disableDot ? "" : ResizeIndicatorStyle} `; export const VerticalHandleStyles = css<{ showAsBorder: boolean; showLightBorder: boolean; disableDot: boolean; + isHovered: boolean; }>` ${EdgeHandleStyles} + ${(props) => + props.showAsBorder || props.disableDot ? "" : VerticalResizeIndicators} top:${~(WIDGET_PADDING - 1) + 1}px; height: calc(100% + ${2 * WIDGET_PADDING - 1}px); ${(props) => @@ -77,8 +108,11 @@ export const HorizontalHandleStyles = css<{ showAsBorder: boolean; showLightBorder: boolean; disableDot: boolean; + isHovered: boolean; }>` ${EdgeHandleStyles} + ${(props) => + props.showAsBorder || props.disableDot ? "" : HorizontalResizeIndicators} left: ${~WIDGET_PADDING + 1}px; width: calc(100% + ${2 * WIDGET_PADDING}px); ${(props) => diff --git a/app/client/src/components/editorComponents/WidgetNameComponent/SettingsControl.tsx b/app/client/src/components/editorComponents/WidgetNameComponent/SettingsControl.tsx index f4caa5a126f4..cfec858e2181 100644 --- a/app/client/src/components/editorComponents/WidgetNameComponent/SettingsControl.tsx +++ b/app/client/src/components/editorComponents/WidgetNameComponent/SettingsControl.tsx @@ -1,11 +1,11 @@ -import React, { CSSProperties } from "react"; -import { ControlIcons } from "icons/ControlIcons"; -import { Icon, IconSize } from "design-system"; +import { Classes, Tooltip } from "@blueprintjs/core"; import { Colors } from "constants/Colors"; -import styled from "styled-components"; -import { Tooltip, Classes } from "@blueprintjs/core"; +import { Icon, IconSize } from "design-system"; +import { ControlIcons } from "icons/ControlIcons"; +import React, { CSSProperties } from "react"; import { useSelector } from "react-redux"; import { snipingModeSelector } from "selectors/editorSelectors"; +import styled from "styled-components"; // I honestly can't think of a better name for this enum export enum Activities { HOVERING, @@ -35,7 +35,8 @@ const SettingsWrapper = styled.div` line-height: ${(props) => props.theme.fontSizes[3] - 1}px; } } - border-radius: 2px; + border-top-left-radius: 4px; + border-top-right-radius: 4px; `; const WidgetName = styled.span` @@ -91,7 +92,7 @@ const getStyles = ( case Activities.HOVERING: return { background: Colors.WATUSI, - color: Colors.BLACK_PEARL, + color: Colors.WHITE, }; case Activities.SELECTED: return { @@ -152,7 +153,6 @@ export function SettingsControl(props: SettingsControlProps) { {isSnipingMode ? `Bind to ${props.name}` : props.name} - {!isSnipingMode && settingsIcon} ); diff --git a/app/client/src/components/editorComponents/WidgetNameComponent/index.tsx b/app/client/src/components/editorComponents/WidgetNameComponent/index.tsx index e1d7c7062cee..399bc6e03fb7 100644 --- a/app/client/src/components/editorComponents/WidgetNameComponent/index.tsx +++ b/app/client/src/components/editorComponents/WidgetNameComponent/index.tsx @@ -1,36 +1,44 @@ import { AppState } from "@appsmith/reducers"; import { bindDataToWidget } from "actions/propertyPaneActions"; import { Layers } from "constants/Layers"; -import { WidgetType } from "constants/WidgetConstants"; +import { WidgetType, WIDGET_PADDING } from "constants/WidgetConstants"; +import Popper from "pages/Editor/Popper"; +import { Data } from "popper.js"; import React from "react"; import { useDispatch, useSelector } from "react-redux"; +import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; import { hideErrors } from "selectors/debuggerSelectors"; import { + getCurrentAppPositioningType, previewModeSelector, snipingModeSelector, } from "selectors/editorSelectors"; import { getIsPropertyPaneVisible } from "selectors/propertyPaneSelectors"; import { getIsTableFilterPaneVisible } from "selectors/tableFilterSelectors"; +import { + isCurrentWidgetFocused, + isWidgetSelected, +} from "selectors/widgetSelectors"; import styled from "styled-components"; import AnalyticsUtil from "utils/AnalyticsUtil"; -import { useShowTableFilterPane } from "utils/hooks/dragResizeHooks"; +import { + useShowTableFilterPane, + useWidgetDragResize, +} from "utils/hooks/dragResizeHooks"; import { useWidgetSelection } from "utils/hooks/useWidgetSelection"; import PerformanceTracker, { PerformanceTransactionName, } from "utils/PerformanceTracker"; import WidgetFactory from "utils/WidgetFactory"; +import { canDrag } from "../DraggableComponent"; import SettingsControl, { Activities } from "./SettingsControl"; const WidgetTypes = WidgetFactory.widgetTypes; const PositionStyle = styled.div<{ topRow: number; isSnipingMode: boolean }>` - position: absolute; - top: ${(props) => - props.topRow > 2 ? `${-1 * props.theme.spaces[10]}px` : "calc(100%)"}; height: ${(props) => props.theme.spaces[10]}px; - ${(props) => (props.isSnipingMode ? "left: -7px" : "right: 0")}; + ${(props) => (props.isSnipingMode ? "left: -7px" : "left: 0px")}; display: flex; - padding: 0 4px; cursor: pointer; z-index: ${Layers.widgetName}; `; @@ -55,6 +63,7 @@ type WidgetNameComponentProps = { topRow: number; errorCount: number; isFlexChild: boolean; + widgetProps: any; }; export function WidgetNameComponent(props: WidgetNameComponentProps) { @@ -161,23 +170,114 @@ export function WidgetNameComponent(props: WidgetNameComponentProps) { propertyPaneWidgetId === props.widgetId ) currentActivity = Activities.ACTIVE; + const targetNode: any = document.getElementById(`${props.widgetId}`); + + // This state tells us to disable dragging, + // This is usually true when widgets themselves implement drag/drop + // This flag resolves conflicting drag/drop triggers. + const isDraggingDisabled: boolean = useSelector( + (state: AppState) => state.ui.widgetDragResize.isDraggingDisabled, + ); + // True when any widget is dragging or resizing, including this one + const isResizingOrDragging = !!isResizing || !!isDragging; + const allowDrag = canDrag( + isResizingOrDragging, + isDraggingDisabled, + props.widgetProps, + isSnipingMode, + isPreviewMode, + ); + const isSelected = useSelector(isWidgetSelected(props.widgetId)); + // This state tels us which widget is focused + // The value is the widgetId of the focused widget. + const isFocused = useSelector(isCurrentWidgetFocused(props.widgetId)); + const { setDraggingState } = useWidgetDragResize(); + + const onDragStart = (e: any) => { + e.preventDefault(); + e.stopPropagation(); + // allowDrag check is added as react jest test simulation is not respecting default behaviour + // of draggable=false and triggering onDragStart. allowDrag condition check is purely for the test cases. + if (allowDrag && targetNode && !(e.metaKey || e.ctrlKey)) { + if (!isFocused) return; + + if (!isSelected) { + selectWidget(props.widgetId); + } + const widgetHeight = + props.widgetProps.bottomRow - props.widgetProps.topRow; + const widgetWidth = + props.widgetProps.rightColumn - props.widgetProps.leftColumn; + const bounds = targetNode.getBoundingClientRect(); + const startPoints = { + top: Math.min( + Math.max( + (e.clientY - bounds.top) / props.widgetProps.parentRowSpace, + 0, + ), + widgetHeight - 1, + ), + left: Math.min( + Math.max( + (e.clientX - bounds.left) / props.widgetProps.parentColumnSpace, + 0, + ), + widgetWidth - 1, + ), + }; + showTableFilterPane(); + setDraggingState({ + isDragging: true, + dragGroupActualParent: props.widgetProps.parentId || "", + draggingGroupCenter: { widgetId: props.widgetProps.widgetId }, + startPoints, + draggedOn: props.widgetProps.parentId, + }); + } + }; + const currentAppPositioningType = useSelector(getCurrentAppPositioningType); + const isAutoLayout = currentAppPositioningType === AppPositioningTypes.AUTO; + const popperOffset = { + left: isAutoLayout ? WIDGET_PADDING : 0, + top: isAutoLayout ? WIDGET_PADDING : 0, + }; return showWidgetName ? ( - { + const left = data.offsets.popper.left - popperOffset.left; + const top = data.offsets.popper.top - popperOffset.top; + data.styles.transform = `translate3d(${left}px,${top}px , 0px)`; + return data; + }, + }, + }} + placement="top-start" + targetNode={targetNode} + zIndex={Layers.widgetName - 1} > - - - - + + + + + + ) : null; } diff --git a/app/client/src/constants/Colors.tsx b/app/client/src/constants/Colors.tsx index 13ebf1a216dd..79873f2edb9a 100644 --- a/app/client/src/constants/Colors.tsx +++ b/app/client/src/constants/Colors.tsx @@ -69,13 +69,13 @@ export const Colors = { BUTTER_CUP: "#F7AF22", BLUE_CHARCOAL: "#23292E", TROUT: "#4C565E", - JAFFA_DARK: "#EF7541", + JAFFA_DARK: "#F86A2B", BURNING_ORANGE: "#FF7742", TIA_MARIA: "#CB4810", SOLID_MERCURY: "#E5E5E5", TROUT_DARK: "#535B62", ALABASTER: "#F9F8F8", - WATUSI: "#FFE0D2", + WATUSI: "#FFA67E", GRAY: "#858282", GRAY2: "#939090", DOVE_GRAY2: "#716e6e", diff --git a/app/client/src/pages/Editor/Popper.tsx b/app/client/src/pages/Editor/Popper.tsx index dea8f8f6c618..f90d215fecd1 100644 --- a/app/client/src/pages/Editor/Popper.tsx +++ b/app/client/src/pages/Editor/Popper.tsx @@ -1,9 +1,9 @@ +import { AppState } from "@appsmith/reducers"; import { ReactComponent as DragHandleIcon } from "assets/icons/ads/app-icons/draghandler.svg"; import { Colors } from "constants/Colors"; import PopperJS, { Placement, PopperOptions } from "popper.js"; import React, { useEffect, useMemo, useRef } from "react"; import { createPortal } from "react-dom"; -import { AppState } from "@appsmith/reducers"; import { getThemeDetails, ThemeMode } from "selectors/themeSelectors"; import styled, { ThemeProvider } from "styled-components"; import { noop } from "utils/AppsmithUtils"; @@ -27,6 +27,7 @@ export type PopperProps = { zIndex?: string; position?: string; }; + style?: React.CSSProperties; placement: Placement; modifiers?: Partial; isDraggable?: boolean; @@ -44,8 +45,7 @@ export type PopperProps = { const PopperWrapper = styled.div<{ zIndex: number; borderRadius?: string }>` z-index: ${(props) => props.zIndex}; position: absolute; - border-radius: ${(props) => props.borderRadius || "0"}; - box-shadow: 0 6px 20px 0px rgba(0, 0, 0, 0.15); + overflow: hidden; `; @@ -126,6 +126,7 @@ export default (props: PopperProps) => { elementRef.style.top = initPositon.top + "px"; elementRef.style.left = initPositon.left + "px"; } + _popper.scheduleUpdate(); }, modifiers: { flip: { @@ -149,6 +150,18 @@ export default (props: PopperProps) => { }, }, ); + if (props.targetNode) { + const config = { attributes: true }; + const callback = (mutationList: any, observer: any) => { + for (const mutation of mutationList) { + if (["attributes", "childList"].includes(mutation.type)) { + _popper.scheduleUpdate(); + } + } + }; + const observer = new MutationObserver(callback); + observer.observe(props.targetNode, config); + } if (isDraggable) { disablePopperEvents && _popper.disableEventListeners(); draggableElement( @@ -186,6 +199,7 @@ export default (props: PopperProps) => { {props.children} diff --git a/app/client/src/resizable/resizenreflow/index.tsx b/app/client/src/resizable/resizenreflow/index.tsx index 5d9c1cda29c7..a7e0f1ed30db 100644 --- a/app/client/src/resizable/resizenreflow/index.tsx +++ b/app/client/src/resizable/resizenreflow/index.tsx @@ -60,6 +60,7 @@ type ResizableHandleProps = { allowResize: boolean; scrollParent: HTMLDivElement | null; disableDot: boolean; + isHovered: boolean; checkForCollision: (widgetNewSize: { left: number; top: number; @@ -120,6 +121,7 @@ function ResizableHandle(props: ResizableHandleProps) { ...bind(), showAsBorder: !props.allowResize, disableDot: props.disableDot, + isHovered: props.isHovered, }; return ( @@ -170,6 +172,7 @@ type ResizableProps = { zWidgetType?: string; zWidgetId?: string; isFlexChild?: boolean; + isHovered: boolean; responsiveBehavior?: ResponsiveBehavior; direction?: LayoutDirection; paddingOffset: number; @@ -265,6 +268,7 @@ export function ReflowResizable(props: ResizableProps) { movementLimitMap: MovementLimitMap | undefined = {}; if (resizedPositions) { + console.log({ resizedPositions }); //calling reflow to update movements of reflowing widgets and get movementLimit of current resizing widget ({ bottomMostRow, movementLimitMap } = reflow.reflowSpaces( [resizedPositions], @@ -502,6 +506,7 @@ export function ReflowResizable(props: ResizableProps) { checkForCollision={checkForCollision} direction={handle.handleDirection} disableDot={disableDot} + isHovered={props.isHovered} key={index} onStart={() => { togglePointerEvents(false); @@ -545,6 +550,7 @@ export function ReflowResizable(props: ResizableProps) { diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index 556ccd2d5822..6067a98d01bc 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -343,6 +343,7 @@ abstract class BaseWidget< type={this.props.type} widgetId={this.props.widgetId} widgetName={this.props.widgetName} + widgetProps={this.props} /> )} {content} From d5829cf0e7cc1c6473faf4162076e94bf5e122e7 Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 17 Jan 2023 13:24:13 -0500 Subject: [PATCH 413/708] add feature flag to canvas property control --- app/client/src/entities/FeatureFlags.ts | 1 + .../pages/Editor/AppPositionTypeControl.tsx | 26 ++++++++++++++++--- .../pages/Editor/CanvasPropertyPane/index.tsx | 8 ++---- app/client/src/selectors/editorSelectors.tsx | 5 ++-- 4 files changed, 28 insertions(+), 12 deletions(-) diff --git a/app/client/src/entities/FeatureFlags.ts b/app/client/src/entities/FeatureFlags.ts index 2975150e3ee7..9dde0374a420 100644 --- a/app/client/src/entities/FeatureFlags.ts +++ b/app/client/src/entities/FeatureFlags.ts @@ -10,6 +10,7 @@ type FeatureFlags = { DATASOURCE_ENVIRONMENTS?: boolean; CUSTOM_JS_LIBRARY?: boolean; MULTIPLE_PANES?: boolean; + AUTO_LAYOUT?: boolean; }; export default FeatureFlags; diff --git a/app/client/src/pages/Editor/AppPositionTypeControl.tsx b/app/client/src/pages/Editor/AppPositionTypeControl.tsx index 62d125edcdf1..967de7b4d7e7 100644 --- a/app/client/src/pages/Editor/AppPositionTypeControl.tsx +++ b/app/client/src/pages/Editor/AppPositionTypeControl.tsx @@ -1,6 +1,7 @@ import classNames from "classnames"; -import React, { useMemo } from "react"; +import React, { useEffect, useMemo } from "react"; import { useDispatch, useSelector } from "react-redux"; +import styled from "styled-components"; import { batchUpdateMultipleWidgetProperties } from "actions/controlActions"; import { LayoutDirection, Positioning } from "utils/autoLayout/constants"; @@ -11,8 +12,10 @@ import { AppPositioningTypeConfig, AppPositioningTypes, } from "reducers/entityReducers/pageListReducer"; -import { getCurrentAppPositioningType } from "selectors/editorSelectors"; -import { Title } from "./CanvasPropertyPane"; +import { + getCurrentAppPositioningType, + isAutoLayoutEnabled, +} from "selectors/editorSelectors"; import { MainContainerLayoutControl } from "./MainContainerLayoutControl"; interface ApplicationPositionTypeConfigOption { @@ -38,10 +41,15 @@ const AppsmithLayoutTypes: ApplicationPositionTypeConfigOption[] = [ }, ]; +export const Title = styled.p` + color: ${Colors.GRAY_800}; +`; + export function AppPositionTypeControl() { const dispatch = useDispatch(); const buttonRefs: Array = []; const selectedOption = useSelector(getCurrentAppPositioningType); + const isAutoLayoutActive = useSelector(isAutoLayoutEnabled); /** * return selected layout index. if there is no app * layout, use the default one ( fluid ) @@ -55,6 +63,15 @@ export function AppPositionTypeControl() { const [focusedIndex, setFocusedIndex] = React.useState(selectedIndex); + useEffect(() => { + if (!isAutoLayoutActive) { + /** + * if feature flag is disabled, set the layout to fixed. + */ + updateAppPositioningLayout(AppsmithLayoutTypes[0]); + } + }, [isAutoLayoutActive]); + const updateAppPositioningLayout = ( layoutOption: ApplicationPositionTypeConfigOption, ) => { @@ -97,8 +114,11 @@ export function AppPositionTypeControl() { } }; + if (!isAutoLayoutActive) return null; + return ( <> + App Positioning Type
- App Positioning Type { - return true; - // state.ui.users.featureFlag.data.AUTO_LAYOUT === true; +export const isAutoLayoutEnabled = (state: AppState): boolean => { + return state.ui.users.featureFlag.data.AUTO_LAYOUT === true; }; export const getCanvasWidth = (state: AppState) => state.ui.mainCanvas.width; From 834ba084f74f1ee371f2032ed7b0b5062f3d13b5 Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 17 Jan 2023 14:07:37 -0500 Subject: [PATCH 414/708] update container and canvas --- .../editorComponents/DropTargetComponent.tsx | 4 +++- .../editorComponents/ResizableComponent.tsx | 12 +++--------- .../CanvasArenas/hooks/useCanvasDragging.ts | 16 +++++----------- app/client/src/utils/WidgetPropsUtils.tsx | 5 ++++- .../src/utils/autoLayout/AutoLayoutUtils.ts | 3 ++- app/client/src/utils/autoLayout/constants.ts | 2 ++ .../src/widgets/ContainerWidget/widget/index.tsx | 5 +---- app/client/src/widgets/withWidgetProps.tsx | 12 ++++++++++++ 8 files changed, 32 insertions(+), 27 deletions(-) diff --git a/app/client/src/components/editorComponents/DropTargetComponent.tsx b/app/client/src/components/editorComponents/DropTargetComponent.tsx index 9969ea82a298..eede0134092e 100644 --- a/app/client/src/components/editorComponents/DropTargetComponent.tsx +++ b/app/client/src/components/editorComponents/DropTargetComponent.tsx @@ -18,6 +18,7 @@ import { useSelector } from "react-redux"; import { getDragDetails } from "sagas/selectors"; import { getOccupiedSpacesSelectorForContainer, + isAutoLayoutEnabled, previewModeSelector, } from "selectors/editorSelectors"; import styled from "styled-components"; @@ -117,7 +118,7 @@ export function DropTargetComponent(props: DropTargetComponentProps) { ); // Are we changing the auto height limits by dragging the signifiers? const { isAutoHeightWithLimitsChanging } = useAutoHeightUIState(); - + const isAutoLayoutActive = useSelector(isAutoLayoutEnabled); // dragDetails contains of info needed for a container jump: // which parent the dragging widget belongs, // which canvas is active(being dragged on), @@ -162,6 +163,7 @@ export function DropTargetComponent(props: DropTargetComponentProps) { props.canExtend && !isPreviewMode, props.mobileBottomRow, props.isMobile, + isAutoLayoutActive, ); // If the current ref is not set to the new snaprows we've received (based on bottomRow) if (rowRef.current !== snapRows) { diff --git a/app/client/src/components/editorComponents/ResizableComponent.tsx b/app/client/src/components/editorComponents/ResizableComponent.tsx index 5f8a18326441..c96919f4f8d7 100644 --- a/app/client/src/components/editorComponents/ResizableComponent.tsx +++ b/app/client/src/components/editorComponents/ResizableComponent.tsx @@ -7,10 +7,8 @@ import { get, omit } from "lodash"; import { XYCord } from "pages/common/CanvasArenas/hooks/useRenderBlocksOnCanvas"; import React, { memo, useContext, useMemo } from "react"; import { useDispatch, useSelector } from "react-redux"; -import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; import Resizable from "resizable/resizenreflow"; import { - getCurrentAppPositioningType, previewModeSelector, snipingModeSelector, } from "selectors/editorSelectors"; @@ -68,9 +66,7 @@ export const ResizableComponent = memo(function ResizableComponent( const isSnipingMode = useSelector(snipingModeSelector); const isPreviewMode = useSelector(previewModeSelector); - const currentAppPositioningType = useSelector(getCurrentAppPositioningType); - const isAutoLayoutMode = - currentAppPositioningType === AppPositioningTypes.AUTO; + const showPropertyPane = useShowPropertyPane(); const showTableFilterPane = useShowTableFilterPane(); const { selectWidget } = useWidgetSelection(); @@ -320,10 +316,8 @@ export const ResizableComponent = memo(function ResizableComponent( return !isAutoHeightEnabledForWidget(props) && isEnabled; }, [props, isAutoHeightEnabledForWidget, isEnabled]); const allowResize: boolean = - !( - (isAutoLayoutMode && NonResizableWidgets.includes(props.type)) || - isMultiSelected - ) || !(isAutoLayoutMode && props.isFlexChild); + !(NonResizableWidgets.includes(props.type) || isMultiSelected) || + !props.isFlexChild; const isHovered = isFocused && !isSelected; return ( (ReflowDirection.UNSET); let { devicePixelRatio: scale = 1 } = window; scale *= canvasScale; - const appPositioningType = useSelector(getCurrentAppPositioningType); const parentOffsetTop = useSelector(getParentOffsetTop(widgetId)); const allWidgets: CanvasWidgetsReduxState = useSelector(getCanvasWidgets); const { @@ -306,13 +301,12 @@ export const useCanvasDragging = ( onMouseMove(e, over); } }; - const isAutoLayout = appPositioningType === AppPositioningTypes.AUTO; const triggerReflow = (e: any, firstMove: boolean) => { const canReflow = !currentRectanglesToDraw[0].detachFromLayout && !dropDisabled && - !isAutoLayout; + !useAutoLayout; const isReflowing = !isEmpty(currentReflowParams.movementMap) || (!isEmpty(currentReflowParams.movementLimitMap) && @@ -412,7 +406,7 @@ export const useCanvasDragging = ( snapRowSpace, rowRef.current - 1, canExtend, - isAutoLayout, + useAutoLayout || false, ), ); const newRows = updateRelativeRows(drawingBlocks, rowRef.current); @@ -524,7 +518,7 @@ export const useCanvasDragging = ( snapRowSpace, rowRef.current - 1, canExtend, - isAutoLayout, + useAutoLayout || false, ); return { ...block, @@ -704,7 +698,7 @@ export const useCanvasDragging = ( blocksToDraw, snapRows, canExtend, - appPositioningType, + useAutoLayout, ]); return { showCanvas: isDragging && !isResizing, diff --git a/app/client/src/utils/WidgetPropsUtils.tsx b/app/client/src/utils/WidgetPropsUtils.tsx index 260dfcf0f742..5f24790b1317 100644 --- a/app/client/src/utils/WidgetPropsUtils.tsx +++ b/app/client/src/utils/WidgetPropsUtils.tsx @@ -280,9 +280,12 @@ export const getCanvasSnapRows = ( canExtend: boolean, mobileBottomRow?: number, isMobile?: boolean, + isAutoLayoutActive?: boolean, ): number => { const bottom = - isMobile && mobileBottomRow !== undefined ? mobileBottomRow : bottomRow; + isMobile && mobileBottomRow !== undefined && isAutoLayoutActive + ? mobileBottomRow + : bottomRow; const totalRows = Math.floor(bottom / GridDefaults.DEFAULT_GRID_ROW_HEIGHT); // Canvas Widgets do not need to accommodate for widget and container padding. diff --git a/app/client/src/utils/autoLayout/AutoLayoutUtils.ts b/app/client/src/utils/autoLayout/AutoLayoutUtils.ts index 909ca9f8c7f7..cfdc21624f15 100644 --- a/app/client/src/utils/autoLayout/AutoLayoutUtils.ts +++ b/app/client/src/utils/autoLayout/AutoLayoutUtils.ts @@ -1,4 +1,5 @@ import { + defaultAutoLayoutWidgets, FlexLayerAlignment, Positioning, ResponsiveBehavior, @@ -359,7 +360,7 @@ export function isStack( parent = allWidgets[parent.parentId]; return ( widget.positioning === Positioning.Vertical || - ((parent && ["CONTAINER_WIDGET", "TABS_WIDGET"].includes(parent.type)) || + ((parent && defaultAutoLayoutWidgets.includes(parent.type)) || parent?.widgetId === MAIN_CONTAINER_WIDGET_ID ? allWidgets[MAIN_CONTAINER_WIDGET_ID].positioning === Positioning.Vertical diff --git a/app/client/src/utils/autoLayout/constants.ts b/app/client/src/utils/autoLayout/constants.ts index 5a2df5b8da4e..2b1cf2663b57 100644 --- a/app/client/src/utils/autoLayout/constants.ts +++ b/app/client/src/utils/autoLayout/constants.ts @@ -69,3 +69,5 @@ export enum FlexVerticalAlignment { Center = "center", Bottom = "end", } + +export const defaultAutoLayoutWidgets = ["CONTAINER_WIDGET", "TABS_WIDGET"]; diff --git a/app/client/src/widgets/ContainerWidget/widget/index.tsx b/app/client/src/widgets/ContainerWidget/widget/index.tsx index f0dae2578d58..00392be0f6e2 100644 --- a/app/client/src/widgets/ContainerWidget/widget/index.tsx +++ b/app/client/src/widgets/ContainerWidget/widget/index.tsx @@ -20,7 +20,6 @@ import WidgetsMultiSelectBox from "pages/Editor/WidgetsMultiSelectBox"; import { Positioning } from "utils/autoLayout/constants"; import { Stylesheet } from "entities/AppTheming"; import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; -import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; export class ContainerWidget extends BaseWidget< ContainerWidgetProps, @@ -140,9 +139,7 @@ export class ContainerWidget extends BaseWidget< } static getDerivedPropertiesMap(): DerivedPropertiesMap { - return { - positioning: `{{ this.appPositioningType === ${AppPositioningTypes.AUTO} ? ${Positioning.Vertical} : ${Positioning.Fixed} }}`, - }; + return {}; } static getDefaultPropertiesMap(): Record { return {}; diff --git a/app/client/src/widgets/withWidgetProps.tsx b/app/client/src/widgets/withWidgetProps.tsx index 9fefea305343..3d1d5d4e4aeb 100644 --- a/app/client/src/widgets/withWidgetProps.tsx +++ b/app/client/src/widgets/withWidgetProps.tsx @@ -28,6 +28,11 @@ import { createLoadingWidget, } from "utils/widgetRenderUtils"; import BaseWidget, { WidgetProps } from "./BaseWidget"; +import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; +import { + defaultAutoLayoutWidgets, + Positioning, +} from "utils/autoLayout/constants"; const WIDGETS_WITH_CHILD_WIDGETS = ["LIST_WIDGET", "FORM_WIDGET"]; @@ -114,6 +119,13 @@ function withWidgetProps(WrappedWidget: typeof BaseWidget) { if ("isFormValid" in props) widgetProps.isFormValid = props.isFormValid; } + if (defaultAutoLayoutWidgets.includes(props.type)) { + widgetProps.positioning = + appPositioningType && appPositioningType === AppPositioningTypes.AUTO + ? Positioning.Vertical + : Positioning.Fixed; + } + widgetProps.children = children; widgetProps.isLoading = isLoading; From 6ab11ebab5163175305a0fc1e03e3ab4a6bb0999 Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 17 Jan 2023 15:39:11 -0500 Subject: [PATCH 415/708] add feature flag to operation sagas --- .../CanvasArenas/hooks/useCanvasDragging.ts | 12 +++++++----- app/client/src/sagas/WidgetOperationSagas.tsx | 19 +++++++++++++------ app/client/src/widgets/WidgetUtils.ts | 2 +- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index 4aac0bde2d1b..c4be60a08e54 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -130,12 +130,14 @@ export const useCanvasDragging = ( useAutoLayout, }); - setTimeout(() => { - calculateHighlights(); - }, 0); + if (useAutoLayout) { + setTimeout(() => { + calculateHighlights(); + }, 0); - if (!isDragging || !isCurrentDraggedCanvas) { - cleanUpTempStyles(); + if (!isDragging || !isCurrentDraggedCanvas) { + cleanUpTempStyles(); + } } const { diff --git a/app/client/src/sagas/WidgetOperationSagas.tsx b/app/client/src/sagas/WidgetOperationSagas.tsx index 8337a104b214..ebaf92f047a9 100644 --- a/app/client/src/sagas/WidgetOperationSagas.tsx +++ b/app/client/src/sagas/WidgetOperationSagas.tsx @@ -42,6 +42,7 @@ import { import { getCanvasHeightOffset, getContainerWidgetSpacesSelector, + getCurrentAppPositioningType, getCurrentPageId, } from "selectors/editorSelectors"; import AnalyticsUtil from "utils/AnalyticsUtil"; @@ -153,6 +154,7 @@ import { isStack, pasteWidgetInFlexLayers, } from "../utils/autoLayout/AutoLayoutUtils"; +import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; export function* updateAllChildCanvasHeights( currentContainerLikeWidgetId: string, @@ -220,7 +222,9 @@ export function* resizeSaga(resizeAction: ReduxAction) { let widget = { ...stateWidget }; const stateWidgets: CanvasWidgetsReduxState = yield select(getWidgets); const widgets = { ...stateWidgets }; - + const appPositioningType: AppPositioningTypes = yield select( + getCurrentAppPositioningType, + ); widget = { ...widget, leftColumn, rightColumn, topRow, bottomRow }; let movedWidgets: { [widgetId: string]: FlattenedWidgetProps; @@ -253,11 +257,14 @@ export function* resizeSaga(resizeAction: ReduxAction) { }; } const isMobile: boolean = yield select(getIsMobile); - const updatedWidgetsAfterResizing = updateWidgetPositions( - movedWidgets, - parentId, - isMobile, - ); + let updatedWidgetsAfterResizing = movedWidgets; + if (appPositioningType === AppPositioningTypes.AUTO) { + updatedWidgetsAfterResizing = updateWidgetPositions( + movedWidgets, + parentId, + isMobile, + ); + } log.debug("resize computations took", performance.now() - start, "ms"); yield put(stopReflowAction()); yield put(updateAndSaveLayout(updatedWidgetsAfterResizing)); diff --git a/app/client/src/widgets/WidgetUtils.ts b/app/client/src/widgets/WidgetUtils.ts index 5d4c08198629..e8cf50979f50 100644 --- a/app/client/src/widgets/WidgetUtils.ts +++ b/app/client/src/widgets/WidgetUtils.ts @@ -742,7 +742,7 @@ export const isAutoHeightEnabledForWidget = ( props: WidgetProps, shouldCheckIfEnabledWithLimits = false, ) => { - if (props.useAutoLayout) { + if (props.isFlexChild) { return false; } if (shouldCheckIfEnabledWithLimits) { From 4cfde3c1dd0e3cdf3fd25d0f2ab0ce0fbb09a33b Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 17 Jan 2023 16:13:17 -0500 Subject: [PATCH 416/708] disable auto height calc --- app/client/src/sagas/WidgetOperationSagas.tsx | 7 ++++++- app/client/src/selectors/editorSelectors.tsx | 13 +++++++------ app/client/src/utils/autoLayout/AutoLayoutUtils.ts | 3 ++- app/client/src/utils/autoLayout/positionUtils.ts | 8 ++------ 4 files changed, 17 insertions(+), 14 deletions(-) diff --git a/app/client/src/sagas/WidgetOperationSagas.tsx b/app/client/src/sagas/WidgetOperationSagas.tsx index ebaf92f047a9..09801adb26ec 100644 --- a/app/client/src/sagas/WidgetOperationSagas.tsx +++ b/app/client/src/sagas/WidgetOperationSagas.tsx @@ -268,7 +268,12 @@ export function* resizeSaga(resizeAction: ReduxAction) { log.debug("resize computations took", performance.now() - start, "ms"); yield put(stopReflowAction()); yield put(updateAndSaveLayout(updatedWidgetsAfterResizing)); - yield put(generateAutoHeightLayoutTreeAction(true, true)); + yield put( + generateAutoHeightLayoutTreeAction( + appPositioningType !== AppPositioningTypes.AUTO, + true, + ), + ); } catch (error) { yield put({ type: ReduxActionErrorTypes.WIDGET_OPERATION_ERROR, diff --git a/app/client/src/selectors/editorSelectors.tsx b/app/client/src/selectors/editorSelectors.tsx index 2a32df8a49b0..d549605e0995 100644 --- a/app/client/src/selectors/editorSelectors.tsx +++ b/app/client/src/selectors/editorSelectors.tsx @@ -247,10 +247,15 @@ export const getMainCanvasPositioning = createSelector( }, ); +export const isAutoLayoutEnabled = (state: AppState): boolean => { + return state.ui.users.featureFlag.data.AUTO_LAYOUT === true; +}; + export const getCurrentAppPositioningType = createSelector( getMainCanvasPositioning, - (positioning: any): AppPositioningTypes => { - return positioning && positioning !== Positioning.Fixed + isAutoLayoutEnabled, + (positioning: any, autoLayoutEnabled: boolean): AppPositioningTypes => { + return positioning && positioning !== Positioning.Fixed && autoLayoutEnabled ? AppPositioningTypes.AUTO : AppPositioningTypes.FIXED; }, @@ -266,10 +271,6 @@ export const getCurrentApplicationLayout = createSelector( }, ); -export const isAutoLayoutEnabled = (state: AppState): boolean => { - return state.ui.users.featureFlag.data.AUTO_LAYOUT === true; -}; - export const getCanvasWidth = (state: AppState) => state.ui.mainCanvas.width; export const getCanvasScale = (state: AppState) => state.ui.mainCanvas.scale; diff --git a/app/client/src/utils/autoLayout/AutoLayoutUtils.ts b/app/client/src/utils/autoLayout/AutoLayoutUtils.ts index cfdc21624f15..b8591a70e172 100644 --- a/app/client/src/utils/autoLayout/AutoLayoutUtils.ts +++ b/app/client/src/utils/autoLayout/AutoLayoutUtils.ts @@ -83,7 +83,8 @@ export function updateFlexLayersOnDelete( const widgets = { ...allWidgets }; if ( widgets[MAIN_CONTAINER_WIDGET_ID].appPositioningType === - AppPositioningTypes.FIXED + AppPositioningTypes.FIXED || + widgets[MAIN_CONTAINER_WIDGET_ID].positioning === Positioning.Fixed ) return widgets; let parent = widgets[parentId]; diff --git a/app/client/src/utils/autoLayout/positionUtils.ts b/app/client/src/utils/autoLayout/positionUtils.ts index 28353a0ec25d..fcc62fac917b 100644 --- a/app/client/src/utils/autoLayout/positionUtils.ts +++ b/app/client/src/utils/autoLayout/positionUtils.ts @@ -1,5 +1,6 @@ import { FlexLayerAlignment, + Positioning, ResponsiveBehavior, } from "utils/autoLayout/constants"; import { FlexLayer } from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; @@ -8,7 +9,6 @@ import { MAIN_CONTAINER_WIDGET_ID, } from "constants/WidgetConstants"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; -import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; import { WidgetProps } from "widgets/BaseWidget"; import { getBottomRow, @@ -48,10 +48,7 @@ export function updateWidgetPositions( ): CanvasWidgetsReduxState { let widgets = { ...allWidgets }; try { - if ( - widgets[MAIN_CONTAINER_WIDGET_ID].appPositioningType === - AppPositioningTypes.FIXED - ) + if (widgets[MAIN_CONTAINER_WIDGET_ID].positioning === Positioning.Fixed) return widgets; const parent = widgets[parentId]; if (!parent) return widgets; @@ -112,7 +109,6 @@ export function updateWidgetPositions( if (shouldUpdateHeight && parent.parentId) return updateWidgetPositions(widgets, parent.parentId, isMobile); } - return widgets; } catch (e) { // console.error(e); From 594226b0eb67efd2002aba879ac04ea8e62d9631 Mon Sep 17 00:00:00 2001 From: Aswath K Date: Wed, 18 Jan 2023 11:10:30 +0530 Subject: [PATCH 417/708] feat: Widget responsiveness without css container query (#19857) CSS container query polyfill that we were making use of was found to be breaking the application styles on Firefox. This POC shows how we can avoid the usage of CSS container queries to implement widget responsiveness --- .../appsmith/autoLayout/FlexComponent.tsx | 2 -- app/client/src/pages/Editor/Canvas.tsx | 5 +++- .../src/utils/autoLayout/AutoLayoutUtils.ts | 8 +++++++ .../BaseInputWidget/component/index.tsx | 19 +++++++++++---- .../component/styles.module.css | 24 ------------------- .../ContainerWidget/component/index.tsx | 4 ---- .../widgets/components/LabelWithTooltip.tsx | 10 ++++++++ 7 files changed, 36 insertions(+), 36 deletions(-) delete mode 100644 app/client/src/widgets/BaseInputWidget/component/styles.module.css diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx index cc76c7f3c0d1..57ea86704d6d 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx @@ -37,8 +37,6 @@ export type AutoLayoutProps = { const FlexWidget = styled.div` position: relative; - container-name: widget-container; - container-type: inline-size; `; export function FlexComponent(props: AutoLayoutProps) { diff --git a/app/client/src/pages/Editor/Canvas.tsx b/app/client/src/pages/Editor/Canvas.tsx index caff6dd4fcfd..2b778701044d 100644 --- a/app/client/src/pages/Editor/Canvas.tsx +++ b/app/client/src/pages/Editor/Canvas.tsx @@ -21,6 +21,7 @@ import { previewModeSelector } from "selectors/editorSelectors"; import { getCurrentGitBranch } from "selectors/gitSyncSelectors"; import { getIsPageLevelSocketConnected } from "selectors/websocketSelectors"; import useWidgetFocus from "utils/hooks/useWidgetFocus"; +import { getViewportClassName } from "utils/autoLayout/AutoLayoutUtils"; interface CanvasProps { widgetsStructure: CanvasWidgetStructure; @@ -109,7 +110,9 @@ const Canvas = memo((props: CanvasProps) => { return ( { diff --git a/app/client/src/utils/autoLayout/AutoLayoutUtils.ts b/app/client/src/utils/autoLayout/AutoLayoutUtils.ts index a5c8c97b38cc..55f87415e215 100644 --- a/app/client/src/utils/autoLayout/AutoLayoutUtils.ts +++ b/app/client/src/utils/autoLayout/AutoLayoutUtils.ts @@ -389,3 +389,11 @@ export function getLayerIndexOfWidget( ); }); } + +export function getViewportClassName(viewportWidth: number) { + if (viewportWidth > 360) { + return "desktop-view"; + } else { + return "mobile-view"; + } +} diff --git a/app/client/src/widgets/BaseInputWidget/component/index.tsx b/app/client/src/widgets/BaseInputWidget/component/index.tsx index 4fa29f0f962a..9fd8b3924cd6 100644 --- a/app/client/src/widgets/BaseInputWidget/component/index.tsx +++ b/app/client/src/widgets/BaseInputWidget/component/index.tsx @@ -20,7 +20,6 @@ import { INPUT_WIDGET_DEFAULT_VALIDATION_ERROR, } from "@appsmith/constants/messages"; import { InputTypes, NumberInputStepButtonPosition } from "../constants"; -import styles from "./styles.module.css"; // TODO(abhinav): All of the following imports should not be in widgets. import ErrorTooltip from "components/editorComponents/ErrorTooltip"; @@ -81,6 +80,10 @@ const InputComponentWrapper = styled((props) => ( }>` ${labelLayoutStyles} + .auto-layout & { + min-width: 60px; + } + cursor: ${({ disabled }) => (disabled ? "not-allowed" : "auto")}; .${Classes.INPUT_GROUP} { display: flex; @@ -368,8 +371,15 @@ const TextInputWrapper = styled.div<{ border-radius: ${({ borderRadius }) => borderRadius} !important; box-shadow: ${({ boxShadow }) => `${boxShadow}`} !important; min-height: 32px; - .auto-layout & { + + .auto-layout && { min-height: 40px; + flex: 0 40px; + } + + .mobile-view .auto-layout && { + min-height: 36px; + flex: 0 36px; } &:hover { @@ -670,7 +680,6 @@ class BaseInputComponent extends React.Component< return ( ; } >` - &.auto-layout { - container-name: canvas-container; - container-type: inline-size; - } height: 100%; width: 100%; background: ${(props) => props.backgroundColor}; diff --git a/app/client/src/widgets/components/LabelWithTooltip.tsx b/app/client/src/widgets/components/LabelWithTooltip.tsx index 7ff2d3277127..cdb6db33e4fe 100644 --- a/app/client/src/widgets/components/LabelWithTooltip.tsx +++ b/app/client/src/widgets/components/LabelWithTooltip.tsx @@ -187,6 +187,16 @@ export const StyledLabel = styled(Label)` `} } + .auto-layout && { + margin-bottom: 10px; + font-size: 15px; + } + + .mobile-view .auto-layout && { + margin-bottom: 4px; + font-size: 14px; + } + ${({ $isDynamicHeightEnabled }) => $isDynamicHeightEnabled ? "&& { text-overflow: initial; white-space: initial; }" From 9f96502e6eb52e0c71c2f19a45201157c21e6800 Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 18 Jan 2023 08:13:47 -0500 Subject: [PATCH 418/708] fix canvas properties visibility --- .../pages/Editor/AppPositionTypeControl.tsx | 93 ++++++++++--------- 1 file changed, 48 insertions(+), 45 deletions(-) diff --git a/app/client/src/pages/Editor/AppPositionTypeControl.tsx b/app/client/src/pages/Editor/AppPositionTypeControl.tsx index 967de7b4d7e7..d348e89cfc30 100644 --- a/app/client/src/pages/Editor/AppPositionTypeControl.tsx +++ b/app/client/src/pages/Editor/AppPositionTypeControl.tsx @@ -114,53 +114,56 @@ export function AppPositionTypeControl() { } }; - if (!isAutoLayoutActive) return null; - return ( <> - App Positioning Type -
-
setFocusedIndex(selectedIndex)} - > - {AppsmithLayoutTypes.map((layoutOption: any, index: number) => { - return ( - - - - ); - })} -
-
+ {isAutoLayoutActive ? ( + <> + App Positioning Type +
+
setFocusedIndex(selectedIndex)} + > + {AppsmithLayoutTypes.map((layoutOption: any, index: number) => { + return ( + + + + ); + })} +
+
+ + ) : null} {selectedOption === AppPositioningTypes.FIXED && ( <> Canvas Size From c94da4e3d6f364135b068eb49578318522c62c80 Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 18 Jan 2023 11:11:24 -0500 Subject: [PATCH 419/708] use mouse positions for direction calculation --- .../CanvasArenas/hooks/canvasDraggingUtils.ts | 167 +++++++----------- .../hooks/useBlocksToBeDraggedOnCanvas.ts | 1 - .../CanvasArenas/hooks/useCanvasDragging.ts | 44 +---- app/client/src/reflow/reflowUtils.ts | 12 -- 4 files changed, 76 insertions(+), 148 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/canvasDraggingUtils.ts b/app/client/src/pages/common/CanvasArenas/hooks/canvasDraggingUtils.ts index a2eeef2aaef2..749fa8a603fe 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/canvasDraggingUtils.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/canvasDraggingUtils.ts @@ -1,6 +1,5 @@ import { OccupiedSpace } from "constants/CanvasEditorConstants"; import { GridDefaults } from "constants/WidgetConstants"; -import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; import { HORIZONTAL_RESIZE_MIN_LIMIT, MovementLimitMap, @@ -9,7 +8,6 @@ import { SpaceMap, VERTICAL_RESIZE_MIN_LIMIT, } from "reflow/reflowTypes"; -import { getContainerExitEdge } from "reflow/reflowUtils"; import { getDraggingSpacesFromBlocks, getDropZoneOffsets, @@ -149,40 +147,30 @@ export function getMoveDirection( currentDirection: ReflowDirection, ) { if (!prevPosition || !currentPosition) return currentDirection; - const deltaX = Math.max( - Math.abs(currentPosition.left - prevPosition.left), - Math.abs(currentPosition.right - prevPosition.right), - ); - const deltaY = Math.max( - Math.abs(currentPosition.top - prevPosition.top), - Math.abs(currentPosition.bottom - prevPosition.bottom), - ); - if (deltaX === deltaY) return currentDirection; - if (deltaX > deltaY) { - if ( - currentPosition.right - prevPosition.right > 0 || - currentPosition.left - prevPosition.left > 0 - ) - return ReflowDirection.RIGHT; - if ( - currentPosition.right - prevPosition.right < 0 || - currentPosition.left - prevPosition.left < 0 - ) - return ReflowDirection.LEFT; - } else { - if ( - currentPosition.bottom - prevPosition.bottom > 0 || - currentPosition.top - prevPosition.top > 0 - ) - return ReflowDirection.BOTTOM; + if ( + currentPosition.right - prevPosition.right > 0 || + currentPosition.left - prevPosition.left > 0 + ) + return ReflowDirection.RIGHT; - if ( - currentPosition.bottom - prevPosition.bottom < 0 || - currentPosition.top - prevPosition.top < 0 - ) - return ReflowDirection.TOP; - } + if ( + currentPosition.right - prevPosition.right < 0 || + currentPosition.left - prevPosition.left < 0 + ) + return ReflowDirection.LEFT; + + if ( + currentPosition.bottom - prevPosition.bottom > 0 || + currentPosition.top - prevPosition.top > 0 + ) + return ReflowDirection.BOTTOM; + + if ( + currentPosition.bottom - prevPosition.bottom < 0 || + currentPosition.top - prevPosition.top < 0 + ) + return ReflowDirection.TOP; return currentDirection; } @@ -329,78 +317,59 @@ export const updateRectanglesPostReflow = ( return rectanglesToDraw; }; +/** + * Get mouse move direction using an average of last five mouse positions. + * @param lastMousePositions | { x: number; y: number }[] : array of last five mouse positions. + * @param currentPosition | { x: number; y: number } : current mouse position. + * @param direction | ReflowDirection : current direction. + * @param updateMousePosition | ({ x, y }: { x: number; y: number }) => void : function to update mouse position. + * @returns ReflowDirection + */ export function getInterpolatedMoveDirection( - spaces: OccupiedSpace[], - currentPosition: OccupiedSpace, + lastMousePositions: { x: number; y: number }[], + currentPosition: { x: number; y: number }, direction: ReflowDirection, - exitContainer: OccupiedSpace | undefined, - mousePosition: OccupiedSpace, + updateMousePosition: ({ x, y }: { x: number; y: number }) => void, ): ReflowDirection { - if (!spaces.length) { - if (exitContainer) - return getLastCanvasExitDirection( - exitContainer, - mousePosition, - direction, - ); - return getMoveDirection(null, currentPosition, direction); + if (!lastMousePositions.length) { + updateMousePosition(currentPosition); + return direction; } - const accumulatedPositions = spaces.reduce( - (acc, curr) => { - return { - ...acc, - top: acc.top + curr.top, - right: acc.right + curr.right, - bottom: acc.bottom + curr.bottom, - left: acc.left + curr.left, - }; - }, - { top: 0, right: 0, bottom: 0, left: 0, id: currentPosition.id }, - ); - - const lastPosition = { - ...accumulatedPositions, - top: accumulatedPositions.top / spaces.length, - right: accumulatedPositions.right / spaces.length, - bottom: accumulatedPositions.bottom / spaces.length, - left: accumulatedPositions.left / spaces.length, - }; - - return getMoveDirection(lastPosition, currentPosition, direction); -} -export function getLastCanvasExitDirection( - exitContainer: OccupiedSpace, - mousePosition: OccupiedSpace, - currentDirection: ReflowDirection, -): ReflowDirection { - const direction: ReflowDirection | undefined = getContainerExitEdge( - exitContainer, - mousePosition, - ); - if (direction) return direction; - return currentDirection; -} + const averagePosition = getAverageMousePosition(lastMousePositions); + updateMousePosition(currentPosition); + const deltaX = currentPosition.x - averagePosition.x; + const deltaY = currentPosition.y - averagePosition.y; -export function getLastDraggedCanvasSpace( - allWidgets: CanvasWidgetsReduxState, - currentCanvasId: string, - lastCanvasId: string | undefined, - occupiedSpaceMap: { [key: string]: OccupiedSpace }, -): OccupiedSpace | undefined { - if (!allWidgets || !lastCanvasId || !currentCanvasId || !occupiedSpaceMap) - return undefined; - if (currentCanvasId === lastCanvasId) return undefined; - if (occupiedSpaceMap[lastCanvasId]) return occupiedSpaceMap[lastCanvasId]; - else if (allWidgets[lastCanvasId].parentId) { + if (Math.abs(deltaY) > Math.abs(deltaX)) + return deltaY > 0 ? ReflowDirection.BOTTOM : ReflowDirection.TOP; + if (Math.abs(deltaX) > Math.abs(deltaY)) + return deltaX > 0 ? ReflowDirection.RIGHT : ReflowDirection.LEFT; + if ( + Math.abs(deltaX) === Math.abs(deltaY) && + direction === ReflowDirection.UNSET + ) { /** - * Needed for list widget. + * If the direction is unset and mouse position is perfectly diagonal. + * Then set the direction vertically (random choice). */ - return getLastDraggedCanvasSpace( - allWidgets, - currentCanvasId, - allWidgets[lastCanvasId].parentId, - occupiedSpaceMap, - ); + return deltaY > 0 ? ReflowDirection.BOTTOM : ReflowDirection.TOP; } + return direction; +} + +function getAverageMousePosition( + lastMousePositions: { x: number; y: number }[], +) { + const accumulatedPositions = lastMousePositions.reduce( + (acc, curr) => { + return { x: acc.x + curr.x, y: acc.y + curr.y }; + }, + { x: 0, y: 0 }, + ); + const averagePosition = { + x: accumulatedPositions.x / lastMousePositions.length, + y: accumulatedPositions.y / lastMousePositions.length, + }; + return averagePosition; } diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts b/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts index e432d1516953..9072fc49100b 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts @@ -510,6 +510,5 @@ export const useBlocksToBeDraggedOnCanvas = ({ widgetOccupiedSpace: childrenOccupiedSpaces.filter( (each) => each.id === dragCenter?.widgetId, )[0], - occupiedSpaces, }; }; diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index 5bb75fff868d..e50f2370e6f5 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -7,7 +7,6 @@ import { debounce, isEmpty, throttle } from "lodash"; import { CanvasDraggingArenaProps } from "pages/common/CanvasArenas/CanvasDraggingArena"; import React, { useEffect, useRef } from "react"; import { useSelector } from "react-redux"; -import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; import { MovementLimitMap, ReflowDirection, @@ -16,7 +15,6 @@ import { } from "reflow/reflowTypes"; import { getParentOffsetTop } from "selectors/autoLayoutSelectors"; import { getCanvasScale } from "selectors/editorSelectors"; -import { getCanvasWidgets } from "selectors/entitiesSelector"; import { getNearestParentCanvas } from "utils/generators"; import { useWidgetDragResize } from "utils/hooks/dragResizeHooks"; import { ReflowInterface, useReflow } from "utils/hooks/useReflow"; @@ -28,7 +26,6 @@ import { import { getEdgeDirection, getInterpolatedMoveDirection, - getLastDraggedCanvasSpace, getMoveDirection, getReflowedSpaces, modifyBlockDimension, @@ -67,7 +64,6 @@ export const useCanvasDragging = ( let { devicePixelRatio: scale = 1 } = window; scale *= canvasScale; const parentOffsetTop = useSelector(getParentOffsetTop(widgetId)); - const allWidgets: CanvasWidgetsReduxState = useSelector(getCanvasWidgets); const { blocksToDraw, defaultHandlePositions, @@ -81,7 +77,6 @@ export const useCanvasDragging = ( isResizing, lastDraggedCanvas, occSpaces, - occupiedSpaces, onDrop, parentDiff, relativeStartPoints, @@ -200,7 +195,8 @@ export const useCanvasDragging = ( bottom: 0, id: "", }; - let lastSnappedPositions: OccupiedSpace[] = []; + + let lastMousePositions: { x: number; y: number }[] = []; const resetCanvasState = () => { throttledStopReflowing(); @@ -389,6 +385,9 @@ export const useCanvasDragging = ( ); rowRef.current = newRows ? newRows : rowRef.current; }; + const updateMousePosition = ({ x, y }: { x: number; y: number }) => { + lastMousePositions = [{ x, y }, ...lastMousePositions.slice(0, 4)]; + }; const onMouseMove = (e: any, firstMove = false) => { if (isDragging && canvasIsDragging && slidingArenaRef.current) { @@ -442,39 +441,12 @@ export const useCanvasDragging = ( triggerReflow(e, firstMove); if (useAutoLayout && isCurrentDraggedCanvas) { - const currentSnappedPosition = getDraggingSpacesFromBlocks( - currentRectanglesToDraw, - snapColumnSpace, - snapRowSpace, - )[0]; - const currentOccupiedSpace: any[] = occupiedSpaces[widgetId]; - let exitContainer: OccupiedSpace | undefined = undefined; - if (lastDraggedCanvas.current && currentOccupiedSpace) { - const occupiedSpaceMap = currentOccupiedSpace.reduce( - (acc, curr) => { - acc[curr.id] = curr; - return acc; - }, - {}, - ); - exitContainer = getLastDraggedCanvasSpace( - allWidgets, - widgetId, - lastDraggedCanvas.current, - occupiedSpaceMap, - ); - } currentDirection.current = getInterpolatedMoveDirection( - lastSnappedPositions, - currentSnappedPosition, + lastMousePositions, + { x: e.clientX, y: e.clientY }, currentDirection.current, - exitContainer, - getMousePositionsOnCanvas(e, gridProps), + updateMousePosition, ); - lastSnappedPositions = [ - currentSnappedPosition, - ...lastSnappedPositions.slice(0, 9), - ]; if (currentDirection.current !== ReflowDirection.UNSET) highlight = highlightDropPosition( e, diff --git a/app/client/src/reflow/reflowUtils.ts b/app/client/src/reflow/reflowUtils.ts index 6ae8d76ca53a..addad6489e3d 100644 --- a/app/client/src/reflow/reflowUtils.ts +++ b/app/client/src/reflow/reflowUtils.ts @@ -1994,12 +1994,6 @@ export function getContainerExitEdge( ) { if (mousePosition.left >= exitContainer.right) return ReflowDirection.RIGHT; if (mousePosition.left <= exitContainer.left) return ReflowDirection.LEFT; - if ( - Math.abs(mousePosition.left - exitContainer.left) < - Math.abs(mousePosition.left - exitContainer.right) - ) - return ReflowDirection.LEFT; - else return ReflowDirection.RIGHT; } if ( @@ -2009,12 +2003,6 @@ export function getContainerExitEdge( if (mousePosition.top >= exitContainer.bottom) return ReflowDirection.BOTTOM; if (mousePosition.top <= exitContainer.top) return ReflowDirection.TOP; - if ( - Math.abs(mousePosition.top - exitContainer.top) < - Math.abs(mousePosition.top - exitContainer.bottom) - ) - return ReflowDirection.TOP; - else return ReflowDirection.BOTTOM; } } From 7c12e52fefc035afc0aacc1db6196eae6340c914 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Thu, 19 Jan 2023 18:33:13 +0530 Subject: [PATCH 420/708] Fixing container scrollability in fixed mode. --- app/client/src/widgets/ContainerWidget/widget/index.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/app/client/src/widgets/ContainerWidget/widget/index.tsx b/app/client/src/widgets/ContainerWidget/widget/index.tsx index 00392be0f6e2..69a33b393ecb 100644 --- a/app/client/src/widgets/ContainerWidget/widget/index.tsx +++ b/app/client/src/widgets/ContainerWidget/widget/index.tsx @@ -17,8 +17,8 @@ import { ValidationTypes } from "constants/WidgetValidation"; import { compact, map, sortBy } from "lodash"; import WidgetsMultiSelectBox from "pages/Editor/WidgetsMultiSelectBox"; -import { Positioning } from "utils/autoLayout/constants"; import { Stylesheet } from "entities/AppTheming"; +import { Positioning } from "utils/autoLayout/constants"; import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; export class ContainerWidget extends BaseWidget< @@ -224,8 +224,13 @@ export class ContainerWidget extends BaseWidget< }; renderAsContainerComponent(props: ContainerWidgetProps) { + //ToDo: Ashok Need a better way of doing this. + const useAutoLayout = this.props.positioning + ? this.props.positioning !== Positioning.Fixed + : false; + const shouldScroll = props.shouldScrollContents && !useAutoLayout; return ( - + Date: Thu, 19 Jan 2023 11:52:24 -0500 Subject: [PATCH 421/708] fix check for vertical stack before altering layout --- .../pages/Editor/WidgetsEditor/CanvasContainer.tsx | 1 - app/client/src/utils/autoLayout/AutoLayoutUtils.ts | 12 ++---------- app/client/src/utils/autoLayout/positionUtils.ts | 5 ++++- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx index b4c869be798d..18a958b5d764 100644 --- a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx +++ b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx @@ -95,7 +95,6 @@ function CanvasContainer() { } if (!isPageInitializing && widgetsStructure) { - // TODO: Temporary workaround for positioning. To be removed after testing. node = ( Date: Thu, 19 Jan 2023 12:34:48 -0500 Subject: [PATCH 422/708] add feature flag to widget dimension calculation --- .../editorComponents/ResizableComponent.tsx | 14 ++++-- app/client/src/widgets/BaseWidget.tsx | 48 ++++++++++++------- 2 files changed, 42 insertions(+), 20 deletions(-) diff --git a/app/client/src/components/editorComponents/ResizableComponent.tsx b/app/client/src/components/editorComponents/ResizableComponent.tsx index c96919f4f8d7..910a0ae6f80f 100644 --- a/app/client/src/components/editorComponents/ResizableComponent.tsx +++ b/app/client/src/components/editorComponents/ResizableComponent.tsx @@ -99,19 +99,25 @@ export const ResizableComponent = memo(function ResizableComponent( // The ResizableContainer's size prop is controlled const dimensions: UIElementSize = { width: - ((props.isMobile && props.mobileRightColumn !== undefined + ((props.isFlexChild && + props.isMobile && + props.mobileRightColumn !== undefined ? props.mobileRightColumn : props.rightColumn) - - (props.isMobile && props.mobileLeftColumn !== undefined + (props.isFlexChild && + props.isMobile && + props.mobileLeftColumn !== undefined ? props.mobileLeftColumn : props.leftColumn)) * props.parentColumnSpace - 2 * props.paddingOffset, height: - ((props.isMobile && props.mobileBottomRow !== undefined + ((props.isFlexChild && + props.isMobile && + props.mobileBottomRow !== undefined ? props.mobileBottomRow : props.bottomRow) - - (props.isMobile && props.mobileTopRow !== undefined + (props.isFlexChild && props.isMobile && props.mobileTopRow !== undefined ? props.mobileTopRow : props.topRow)) * props.parentRowSpace - diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index 6067a98d01bc..3e799ae0afb9 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -248,6 +248,7 @@ abstract class BaseWidget< this.props.mobileTopRow, this.props.mobileBottomRow, this.props.isMobile, + this.props.isFlexChild, ); }; @@ -263,26 +264,41 @@ abstract class BaseWidget< mobileTopRow?: number, mobileBottomRow?: number, isMobile?: boolean, + isFlexChild?: boolean, ): { componentWidth: number; componentHeight: number; } { - const right = - isMobile && mobileRightColumn !== undefined && parentColumnSpace !== 1 - ? mobileRightColumn - : rightColumn; - const left = - isMobile && mobileLeftColumn !== undefined && parentColumnSpace !== 1 - ? mobileLeftColumn - : leftColumn; - const top = - isMobile && mobileTopRow !== undefined && parentRowSpace !== 1 - ? mobileTopRow - : topRow; - const bottom = - isMobile && mobileBottomRow !== undefined && parentRowSpace !== 1 - ? mobileBottomRow - : bottomRow; + let left = leftColumn; + let right = rightColumn; + let top = topRow; + let bottom = bottomRow; + if (isFlexChild && isMobile) { + if (mobileLeftColumn !== undefined && parentColumnSpace !== 1) { + left = mobileLeftColumn; + } + if (mobileRightColumn !== undefined && parentColumnSpace !== 1) { + right = mobileRightColumn; + } + if (mobileTopRow !== undefined && parentRowSpace !== 1) { + top = mobileTopRow; + } + if (mobileBottomRow !== undefined && parentRowSpace !== 1) { + bottom = mobileBottomRow; + } + } + console.log( + "#### left", + left, + "right", + right, + "top", + top, + "bottom", + bottom, + "widget", + this.props.widgetName, + ); return { componentWidth: (right - left) * parentColumnSpace, componentHeight: (bottom - top) * parentRowSpace, From 1215978fd0a21ef24466bfb9a3612d089a17f362 Mon Sep 17 00:00:00 2001 From: Preet Date: Fri, 20 Jan 2023 07:36:56 -0500 Subject: [PATCH 423/708] remove log --- app/client/src/widgets/BaseWidget.tsx | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index 3e799ae0afb9..924df17504f3 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -287,18 +287,7 @@ abstract class BaseWidget< bottom = mobileBottomRow; } } - console.log( - "#### left", - left, - "right", - right, - "top", - top, - "bottom", - bottom, - "widget", - this.props.widgetName, - ); + return { componentWidth: (right - left) * parentColumnSpace, componentHeight: (bottom - top) * parentRowSpace, From a6d6f6969b492a161771941d428bcd66896db477 Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 23 Jan 2023 10:27:33 -0500 Subject: [PATCH 424/708] select closest highlight before accounting for drag direction. --- .../hooks/useAutoLayoutHighlights.ts | 181 ++----------- .../autoLayout/highlightSelectionUtils.ts | 237 ++++++++++++++++++ 2 files changed, 260 insertions(+), 158 deletions(-) create mode 100644 app/client/src/utils/autoLayout/highlightSelectionUtils.ts diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index 024de0676e39..563f3ba14cb7 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -11,11 +11,10 @@ import { getIsMobile } from "selectors/mainCanvasSelectors"; import { deriveHighlightsFromLayers } from "utils/autoLayout/highlightUtils"; import WidgetFactory from "utils/WidgetFactory"; import { WidgetDraggingBlock } from "./useBlocksToBeDraggedOnCanvas"; - -interface Point { - x: number; - y: number; -} +import { + getHighlightPayload, + Point, +} from "utils/autoLayout/highlightSelectionUtils"; export interface HighlightInfo { isNewLayer: boolean; // determines if a new layer / child has been added directly to the container. @@ -126,7 +125,20 @@ export const useAutoLayoutHighlights = ({ e: any, moveDirection: ReflowDirection, ): HighlightInfo | undefined => { + if (!highlights || !highlights.length) + highlights = deriveHighlightsFromLayers( + allWidgets, + canvasId, + canvasWidth, + blocksToDraw.map((block) => block?.widgetId), + isFillWidget, + isMobile, + ); + const highlight: HighlightInfo | undefined = getHighlightPayload( + highlights, + direction, + isFillWidget, e, moveDirection, ); @@ -136,12 +148,9 @@ export const useAutoLayoutHighlights = ({ return highlight; }; - const getHighlightPayload = ( - e: any, - moveDirection?: ReflowDirection, - val?: Point, - ): HighlightInfo | undefined => { - let base: HighlightInfo[] = []; // all highlight for the current canvas. + const getDropInfo = (val: Point): HighlightInfo | undefined => { + if (lastActiveHighlight) return lastActiveHighlight; + if (!highlights || !highlights.length) highlights = deriveHighlightsFromLayers( allWidgets, @@ -151,155 +160,11 @@ export const useAutoLayoutHighlights = ({ isFillWidget, isMobile, ); - base = highlights; - // Current mouse coordinates. - const pos: Point = { - x: e?.offsetX || val?.x, - y: e?.offsetY || val?.y, - }; - - let filteredHighlights: HighlightInfo[] = []; - filteredHighlights = getViableDropPositions(base, pos, moveDirection); - if (!filteredHighlights || !filteredHighlights?.length) return; - // Sort filtered highlights in ascending order of distance from mouse position. - const arr = [...filteredHighlights]?.sort((a, b) => { - return ( - calculateDistance(a, pos, moveDirection) - - calculateDistance(b, pos, moveDirection) - ); - }); - - // console.log("#### arr", arr, base, moveDirection); - - // Return the closest highlight. - return arr[0]; - }; - - function getViableDropPositions( - arr: HighlightInfo[], - pos: Point, - moveDirection?: ReflowDirection, - ): HighlightInfo[] { - if (!moveDirection || !arr) return arr || []; - const isVerticalDrag = [ - ReflowDirection.TOP, - ReflowDirection.BOTTOM, - ].includes(moveDirection); - return direction === LayoutDirection.Vertical - ? getVerticalStackDropPositions(arr, pos, isVerticalDrag) - : getHorizontalStackDropPositions(arr, pos); - } - - function getVerticalStackDropPositions( - arr: HighlightInfo[], - pos: Point, - isVerticalDrag: boolean, - ): HighlightInfo[] { - // For vertical stacks, filter out the highlights based on drag direction and y position. - let filteredHighlights: HighlightInfo[] = arr.filter( - (highlight: HighlightInfo) => { - // Return only horizontal highlights for vertical drag. - if (isVerticalDrag) - return ( - !highlight.isVertical && - highlight.width > 0 && - pos.x > 0 && - pos.y > 0 && - pos.x >= highlight.posX && - pos.x <= highlight.posX + highlight.width - ); - // Return only vertical highlights for horizontal drag, if they lie in the same x plane. - return ( - highlight.isVertical && - pos.y >= highlight.posY && - pos.y <= highlight.posY + highlight.height - ); - }, - ); - /** - * For horizontal drag, if no vertical highlight exists in the same x plane, - * return the horizontal highlights for the last layer. - * In case of a dragged Fill widget, only return the Start alignment as it will span the entire width. - */ - if (!isVerticalDrag && !filteredHighlights.length) - filteredHighlights = arr - .slice(arr.length - 3) - .filter((highlight: HighlightInfo) => - !highlight.isVertical && isFillWidget - ? highlight.alignment === FlexLayerAlignment.Start - : true, - ); - - return filteredHighlights; - } - - function getHorizontalStackDropPositions( - arr: HighlightInfo[], - pos: Point, - ): HighlightInfo[] { - // For horizontal stack, return the highlights that lie in the same x plane. - let filteredHighlights = arr.filter( - (highlight) => - pos.y >= highlight.posY && pos.y <= highlight.posY + highlight.height, - ); - // If no highlight exists in the same x plane, return the last highlight. - if (!filteredHighlights.length) filteredHighlights = [arr[arr.length - 1]]; - return filteredHighlights; - } - - /** - * Calculate distance between the mouse position and the closest point on the highlight. - * - * @param a | HighlightInfo : current highlight. - * @param b | Point : current mouse position. - * @param moveDirection | ReflowDirection : current drag direction. - * @returns number - */ - function calculateDistance( - a: HighlightInfo, - b: Point, - moveDirection?: ReflowDirection, - ): number { - let distX = 0, - distY = 0; - if (a.isVertical) { - distX = b.x - a.posX; - if (b.y < a.posY) { - distY = b.y - a.posY; - } else if (b.y > a.posY + a.height) { - distY = b.y - (a.posY + a.height); - } else { - distY = 0; - } - } else { - distY = b.y - a.posY; - if (b.x < a.posX) { - distX = b.x - a.posX; - } else if (b.x > a.posX + a.width) { - distX = b.x - (a.posX + a.width); - } else { - distX = 0; - } - } - - /** - * Emphasize move direction over actual distance. - * - * If the point is close to a highlight. However, it is moving in the opposite direction, - * then increase the appropriate distance to ensure that this highlight is discounted. - */ - if (moveDirection === ReflowDirection.RIGHT && distX > 20) distX += 2000; - if (moveDirection === ReflowDirection.LEFT && distX < -20) distX -= 2000; - if (moveDirection === ReflowDirection.BOTTOM && distY > 20) distY += 2000; - if (moveDirection === ReflowDirection.TOP && distY < -20) distY -= 2000; - - return Math.abs(Math.sqrt(distX * distX + distY * distY)); - } - - const getDropInfo = (val: Point): HighlightInfo | undefined => { - if (lastActiveHighlight) return lastActiveHighlight; const payload: HighlightInfo | undefined = getHighlightPayload( + highlights, + direction, + isFillWidget, null, undefined, val, diff --git a/app/client/src/utils/autoLayout/highlightSelectionUtils.ts b/app/client/src/utils/autoLayout/highlightSelectionUtils.ts new file mode 100644 index 000000000000..9134fe91fc0b --- /dev/null +++ b/app/client/src/utils/autoLayout/highlightSelectionUtils.ts @@ -0,0 +1,237 @@ +import { HighlightInfo } from "pages/common/CanvasArenas/hooks/useAutoLayoutHighlights"; +import { ReflowDirection } from "reflow/reflowTypes"; +import { FlexLayerAlignment, LayoutDirection } from "./constants"; + +export interface Point { + x: number; + y: number; +} + +/** + * Select the closest highlight to the mouse position (in the direction of the). + * @param highlights | HighlightInfo[] : all highlights for the current canvas. + * @param direction | LayoutDirection | undefined : direction of the stacking. + * @param isFillWidget | boolean : determines if the dragged widgets contain a fill widget. + * @param e | any : mouse event. + * @param moveDirection | ReflowDirection : direction of the drag. + * @param val | Point : mouse coordinates. + * @returns HighlightInfo | undefined + */ +export const getHighlightPayload = ( + highlights: HighlightInfo[], + direction: LayoutDirection | undefined, + isFillWidget: boolean, + e: any, + moveDirection?: ReflowDirection, + val?: Point, +): HighlightInfo | undefined => { + if (!highlights || !highlights.length) return; + + // Current mouse coordinates. + const pos: Point = { + x: e?.offsetX || val?.x, + y: e?.offsetY || val?.y, + }; + + /** + * If the mouse is within 10px of a highlight, return that highlight. + */ + const closestHighlight: HighlightInfo | undefined = getClosestHighlight( + highlights, + pos, + ); + if (closestHighlight) return closestHighlight; + + /** + * Filter highlights that are in the direction of the drag + * and span the current mouse position. + */ + let filteredHighlights: HighlightInfo[] = []; + filteredHighlights = getViableDropPositions( + highlights, + pos, + isFillWidget, + moveDirection, + direction, + ); + if (!filteredHighlights || !filteredHighlights?.length) return; + + // Sort filtered highlights in ascending order of distance from mouse position. + const arr = [...filteredHighlights]?.sort((a, b) => { + return ( + calculateDirectionalDistance(a, pos, moveDirection) - + calculateDirectionalDistance(b, pos, moveDirection) + ); + }); + + // console.log("#### arr", arr, highlights, moveDirection); + + // Return the closest highlight. + return arr[0]; +}; + +/** + * Filter highlights based on direction of drag. + * @param arr | HighlightInfo[] : all highlights for the current canvas. + * @param pos | Point : current mouse coordinates. + * @param isFillWidget | boolean : determines if the dragged widgets contain a fill widget. + * @param moveDirection | ReflowDirection : direction of the drag. + * @param direction | LayoutDirection | undefined : direction of the stacking. + * @returns HighlightInfo | undefined + */ +function getViableDropPositions( + arr: HighlightInfo[], + pos: Point, + isFillWidget: boolean, + moveDirection?: ReflowDirection, + direction?: LayoutDirection, +): HighlightInfo[] { + if (!moveDirection || !arr) return arr || []; + const isVerticalDrag = [ReflowDirection.TOP, ReflowDirection.BOTTOM].includes( + moveDirection, + ); + return direction === LayoutDirection.Vertical + ? getVerticalStackDropPositions(arr, pos, isVerticalDrag, isFillWidget) + : getHorizontalStackDropPositions(arr, pos); +} + +/** + * Calculate the distance between the mouse position and the highlight. + * Return the closest highlight if distance <= 10px. + * @param arr | HighlightInfo[] : all highlights for the current canvas. + * @param pos | Point : current mouse coordinates. + * @returns HighlightInfo | undefined + */ +function getClosestHighlight( + arr: HighlightInfo[], + pos: Point, +): HighlightInfo | undefined { + if (!arr || !pos) return; + const res: HighlightInfo[] = arr.filter((highlight: HighlightInfo) => { + const distance = calculateActualDistance(highlight, pos); + return distance <= 10; + }); + if (!res.length) return; + return res.sort((a, b) => { + return calculateActualDistance(a, pos) - calculateActualDistance(b, pos); + })[0]; +} + +function getVerticalStackDropPositions( + arr: HighlightInfo[], + pos: Point, + isVerticalDrag: boolean, + isFillWidget: boolean, +): HighlightInfo[] { + // For vertical stacks, filter out the highlights based on drag direction and y position. + let filteredHighlights: HighlightInfo[] = arr.filter( + (highlight: HighlightInfo) => { + // Return only horizontal highlights for vertical drag. + if (isVerticalDrag) + return ( + !highlight.isVertical && + highlight.width > 0 && + pos.x > 0 && + pos.y > 0 && + pos.x >= highlight.posX && + pos.x <= highlight.posX + highlight.width + ); + // Return only vertical highlights for horizontal drag, if they lie in the same x plane. + return ( + highlight.isVertical && + pos.y >= highlight.posY && + pos.y <= highlight.posY + highlight.height + ); + }, + ); + /** + * For horizontal drag, if no vertical highlight exists in the same x plane, + * return the horizontal highlights for the last layer. + * In case of a dragged Fill widget, only return the Start alignment as it will span the entire width. + */ + if (!isVerticalDrag && !filteredHighlights.length) + filteredHighlights = arr + .slice(arr.length - 3) + .filter((highlight: HighlightInfo) => + !highlight.isVertical && isFillWidget + ? highlight.alignment === FlexLayerAlignment.Start + : true, + ); + + return filteredHighlights; +} + +function getHorizontalStackDropPositions( + arr: HighlightInfo[], + pos: Point, +): HighlightInfo[] { + // For horizontal stack, return the highlights that lie in the same x plane. + let filteredHighlights = arr.filter( + (highlight) => + pos.y >= highlight.posY && pos.y <= highlight.posY + highlight.height, + ); + // If no highlight exists in the same x plane, return the last highlight. + if (!filteredHighlights.length) filteredHighlights = [arr[arr.length - 1]]; + return filteredHighlights; +} + +/** + * Calculate distance between the mouse position and the closest point on the highlight. + * + * @param a | HighlightInfo : current highlight. + * @param b | Point : current mouse position. + * @param moveDirection | ReflowDirection : current drag direction. + * @returns number + */ +function calculateDirectionalDistance( + a: HighlightInfo, + b: Point, + moveDirection?: ReflowDirection, +): number { + let { distX, distY } = getXYDistance(a, b); + /** + * Emphasize move direction over actual distance. + * + * If the point is close to a highlight. However, it is moving in the opposite direction, + * then increase the appropriate distance to ensure that this highlight is discounted. + */ + if (moveDirection === ReflowDirection.RIGHT && distX > 20) distX += 2000; + if (moveDirection === ReflowDirection.LEFT && distX < -20) distX -= 2000; + if (moveDirection === ReflowDirection.BOTTOM && distY > 20) distY += 2000; + if (moveDirection === ReflowDirection.TOP && distY < -20) distY -= 2000; + + return Math.abs(Math.sqrt(distX * distX + distY * distY)); +} + +function getXYDistance( + a: HighlightInfo, + b: Point, +): { distX: number; distY: number } { + let distX = 0, + distY = 0; + if (a.isVertical) { + distX = b.x - a.posX; + if (b.y < a.posY) { + distY = b.y - a.posY; + } else if (b.y > a.posY + a.height) { + distY = b.y - (a.posY + a.height); + } else { + distY = 0; + } + } else { + distY = b.y - a.posY; + if (b.x < a.posX) { + distX = b.x - a.posX; + } else if (b.x > a.posX + a.width) { + distX = b.x - (a.posX + a.width); + } else { + distX = 0; + } + } + return { distX, distY }; +} + +function calculateActualDistance(a: HighlightInfo, b: Point): number { + const { distX, distY } = getXYDistance(a, b); + return Math.abs(Math.sqrt(distX * distX + distY * distY)); +} From 9e99bc994a29d2fa0708884f69f52eadd18bd22a Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 23 Jan 2023 17:01:01 -0500 Subject: [PATCH 425/708] use debounce for highlight selection --- .../CanvasArenas/hooks/useCanvasDragging.ts | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index e50f2370e6f5..7dadcb50b22f 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -124,6 +124,7 @@ export const useCanvasDragging = ( isDragging, useAutoLayout, }); + let selectedHighlight: HighlightInfo | undefined; if (useAutoLayout) { setTimeout(() => { @@ -432,7 +433,6 @@ export const useCanvasDragging = ( each.detachFromLayout, )), })); - let highlight: HighlightInfo | undefined; if (rowDelta && slidingArenaRef.current && !useAutoLayout) { isUpdatingRows = true; canScroll.current = false; @@ -447,11 +447,13 @@ export const useCanvasDragging = ( currentDirection.current, updateMousePosition, ); - if (currentDirection.current !== ReflowDirection.UNSET) - highlight = highlightDropPosition( - e, - currentDirection.current, - ); + setTimeout(() => { + if (currentDirection.current !== ReflowDirection.UNSET) + selectedHighlight = highlightDropPosition( + e, + currentDirection.current, + ); + }, 100); } } isUpdatingRows = renderBlocks( @@ -460,7 +462,7 @@ export const useCanvasDragging = ( isUpdatingRows, canvasIsDragging, scrollParent, - highlight, + selectedHighlight, widgetId === MAIN_CONTAINER_WIDGET_ID, parentOffsetTop, ); From 4c0079a9e88136ada93da7a824e6e555e45604c4 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Tue, 24 Jan 2023 11:24:31 +0530 Subject: [PATCH 426/708] fixing modals. --- .../pages/common/CanvasArenas/StickyCanvasArena.tsx | 10 +++++++--- app/client/src/widgets/ModalWidget/widget/index.tsx | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/StickyCanvasArena.tsx b/app/client/src/pages/common/CanvasArenas/StickyCanvasArena.tsx index 149bdc500028..a25f6bf2a1c7 100644 --- a/app/client/src/pages/common/CanvasArenas/StickyCanvasArena.tsx +++ b/app/client/src/pages/common/CanvasArenas/StickyCanvasArena.tsx @@ -1,8 +1,8 @@ import React, { forwardRef, RefObject, useEffect, useRef } from "react"; import styled from "styled-components"; -import ResizeObserver from "resize-observer-polyfill"; import { useSelector } from "react-redux"; +import ResizeObserver from "resize-observer-polyfill"; import { getCanvasScale } from "selectors/editorSelectors"; import { isMultiPaneActive } from "selectors/multiPaneSelectors"; @@ -127,7 +127,9 @@ export const StickyCanvasArena = forwardRef( }; useEffect(() => { - observeSlider(); + if (slidingArenaRef.current) { + observeSlider(); + } }, [ showCanvas, snapRows, @@ -148,7 +150,9 @@ export const StickyCanvasArena = forwardRef( return () => { parentCanvas?.removeEventListener("scroll", observeSlider); parentCanvas?.removeEventListener("mouseover", observeSlider); - resizeObserver.current.unobserve(slidingArenaRef.current); + if (slidingArenaRef.current) { + resizeObserver.current.unobserve(slidingArenaRef.current); + } }; }, []); return ( diff --git a/app/client/src/widgets/ModalWidget/widget/index.tsx b/app/client/src/widgets/ModalWidget/widget/index.tsx index 5642f89b0f09..51d799c7d0b2 100644 --- a/app/client/src/widgets/ModalWidget/widget/index.tsx +++ b/app/client/src/widgets/ModalWidget/widget/index.tsx @@ -149,7 +149,7 @@ export class ModalWidget extends BaseWidget { childData.positioning = this.props.positioning; childData.alignment = this.props.alignment; childData.spacing = this.props.spacing; - return WidgetFactory.createWidget(childWidgetData, this.props.renderMode); + return WidgetFactory.createWidget(childData, this.props.renderMode); }; onModalClose = () => { From eda1e7f7e976a5ecf4e6563ab0d3ae2a71aaa214 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Tue, 24 Jan 2023 11:35:31 +0530 Subject: [PATCH 427/708] release merge bugs. --- .../WidgetNameComponent/SettingsControl.tsx | 14 -------------- .../src/pages/Editor/AppPositionTypeControl.tsx | 4 ++-- app/client/src/pages/Editor/Popper.tsx | 2 +- app/client/src/resizable/resizenreflow/index.tsx | 9 ++++----- app/client/src/selectors/propertyPaneSelectors.tsx | 7 +++---- 5 files changed, 10 insertions(+), 26 deletions(-) diff --git a/app/client/src/components/editorComponents/WidgetNameComponent/SettingsControl.tsx b/app/client/src/components/editorComponents/WidgetNameComponent/SettingsControl.tsx index f692a8e9d565..46d4dd116501 100644 --- a/app/client/src/components/editorComponents/WidgetNameComponent/SettingsControl.tsx +++ b/app/client/src/components/editorComponents/WidgetNameComponent/SettingsControl.tsx @@ -64,7 +64,6 @@ type SettingsControlProps = { }; const BindDataIcon = ControlIcons.BIND_DATA_CONTROL; -const SettingsIcon = ControlIcons.SETTINGS_CONTROL; const getStyles = ( activity: Activities, @@ -104,19 +103,6 @@ const getStyles = ( export function SettingsControl(props: SettingsControlProps) { const isSnipingMode = useSelector(snipingModeSelector); - const settingsIcon = ( - - ); const errorIcon = ( { ); if (props.targetNode) { const config = { attributes: true }; - const callback = (mutationList: any, observer: any) => { + const callback = (mutationList: any) => { for (const mutation of mutationList) { if (["attributes", "childList"].includes(mutation.type)) { _popper.scheduleUpdate(); diff --git a/app/client/src/resizable/resizenreflow/index.tsx b/app/client/src/resizable/resizenreflow/index.tsx index a7e0f1ed30db..b20c11ced5ae 100644 --- a/app/client/src/resizable/resizenreflow/index.tsx +++ b/app/client/src/resizable/resizenreflow/index.tsx @@ -1,7 +1,3 @@ -import { - LayoutDirection, - ResponsiveBehavior, -} from "utils/autoLayout/constants"; import { isHandleResizeAllowed } from "components/editorComponents/ResizableUtils"; import { OccupiedSpace } from "constants/CanvasEditorConstants"; import { WIDGET_PADDING } from "constants/WidgetConstants"; @@ -18,6 +14,10 @@ import { import { getContainerOccupiedSpacesSelectorWhileResizing } from "selectors/editorSelectors"; import { getReflowSelector } from "selectors/widgetReflowSelectors"; import styled, { StyledComponent } from "styled-components"; +import { + LayoutDirection, + ResponsiveBehavior, +} from "utils/autoLayout/constants"; import { getNearestParentCanvas } from "utils/generators"; import { useReflow } from "utils/hooks/useReflow"; import PerformanceTracker, { @@ -268,7 +268,6 @@ export function ReflowResizable(props: ResizableProps) { movementLimitMap: MovementLimitMap | undefined = {}; if (resizedPositions) { - console.log({ resizedPositions }); //calling reflow to update movements of reflowing widgets and get movementLimit of current resizing widget ({ bottomMostRow, movementLimitMap } = reflow.reflowSpaces( [resizedPositions], diff --git a/app/client/src/selectors/propertyPaneSelectors.tsx b/app/client/src/selectors/propertyPaneSelectors.tsx index 015e75260497..a3bd0b7a7d24 100644 --- a/app/client/src/selectors/propertyPaneSelectors.tsx +++ b/app/client/src/selectors/propertyPaneSelectors.tsx @@ -2,18 +2,21 @@ import { AppState } from "@appsmith/reducers"; import { find, get, pick, set } from "lodash"; import { createSelector } from "reselect"; +import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; import { DataTree, DataTreeEntity, DataTreeWidget, } from "entities/DataTree/dataTreeFactory"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; +import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; import { PropertyPaneReduxState, SelectedPropertyPanel, } from "reducers/uiReducers/propertyPaneReducer"; import { getWidgets } from "sagas/selectors"; import { getDataTree } from "selectors/dataTreeSelectors"; +import { Positioning } from "utils/autoLayout/constants"; import { EVALUATION_PATH, isPathDynamicProperty, @@ -23,10 +26,6 @@ import { generateClassName } from "utils/generators"; import { WidgetProps } from "widgets/BaseWidget"; import { getCanvasWidgets } from "./entitiesSelector"; import { getLastSelectedWidget, getSelectedWidgets } from "./ui"; -import { getCurrentAppPositioningType } from "./editorSelectors"; -import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; -import { Positioning } from "utils/autoLayout/constants"; -import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; export type WidgetProperties = WidgetProps & { [EVALUATION_PATH]?: DataTreeEntity; From e0c1f5bab413693e614d80feacc44f2b7373725b Mon Sep 17 00:00:00 2001 From: Aswath K Date: Tue, 24 Jan 2023 12:31:01 +0530 Subject: [PATCH 428/708] makes use of layoutConfig constant instead of hardcoded viewport value --- app/client/src/utils/autoLayout/AutoLayoutUtils.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/client/src/utils/autoLayout/AutoLayoutUtils.ts b/app/client/src/utils/autoLayout/AutoLayoutUtils.ts index 11055048c560..2e556d0d83a8 100644 --- a/app/client/src/utils/autoLayout/AutoLayoutUtils.ts +++ b/app/client/src/utils/autoLayout/AutoLayoutUtils.ts @@ -10,6 +10,7 @@ import { } from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; import { FLEXBOX_PADDING, + layoutConfigurations, MAIN_CONTAINER_WIDGET_ID, } from "constants/WidgetConstants"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; @@ -383,7 +384,7 @@ export function getLayerIndexOfWidget( } export function getViewportClassName(viewportWidth: number) { - if (viewportWidth > 360) { + if (viewportWidth > layoutConfigurations.MOBILE.maxWidth) { return "desktop-view"; } else { return "mobile-view"; From c916076eb84f3a0f04024ef994a1f60a81d556fb Mon Sep 17 00:00:00 2001 From: Aswath K Date: Tue, 24 Jan 2023 13:58:30 +0530 Subject: [PATCH 429/708] Adds min-width config --- .../src/widgets/CurrencyInputWidget/index.ts | 21 +++++++++++++++++++ app/client/src/widgets/InputWidgetV2/index.ts | 21 +++++++++++++++++++ .../src/widgets/PhoneInputWidget/index.ts | 21 +++++++++++++++++++ 3 files changed, 63 insertions(+) diff --git a/app/client/src/widgets/CurrencyInputWidget/index.ts b/app/client/src/widgets/CurrencyInputWidget/index.ts index 6cb136e7a038..0124331f2a9e 100644 --- a/app/client/src/widgets/CurrencyInputWidget/index.ts +++ b/app/client/src/widgets/CurrencyInputWidget/index.ts @@ -1,5 +1,6 @@ import { LabelPosition } from "components/constants"; import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; +import { layoutConfigurations } from "constants/WidgetConstants"; import { getDefaultResponsiveBehavior } from "utils/layoutPropertiesUtils"; import { DynamicHeight } from "utils/WidgetFeatures"; import { CONFIG as BaseConfig } from "widgets/BaseInputWidget"; @@ -50,6 +51,26 @@ export const CONFIG = { rows: 7, }, }, + widgetSize: [ + { + viewportMinWidth: 0, + configuration: () => { + return { + minWidth: "60px", + minHeight: "70px", + }; + }, + }, + { + viewportMinWidth: layoutConfigurations.MOBILE.maxWidth, + configuration: () => { + return { + minWidth: "60px", + minHeight: "80px", + }; + }, + }, + ], }; export default Widget; diff --git a/app/client/src/widgets/InputWidgetV2/index.ts b/app/client/src/widgets/InputWidgetV2/index.ts index e94ece8d17e2..5bb80a859346 100644 --- a/app/client/src/widgets/InputWidgetV2/index.ts +++ b/app/client/src/widgets/InputWidgetV2/index.ts @@ -1,5 +1,6 @@ import { LabelPosition } from "components/constants"; import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; +import { layoutConfigurations } from "constants/WidgetConstants"; import { getDefaultResponsiveBehavior } from "utils/layoutPropertiesUtils"; import { DynamicHeight } from "utils/WidgetFeatures"; import { CONFIG as BaseConfig } from "widgets/BaseInputWidget"; @@ -47,6 +48,26 @@ export const CONFIG = { rows: 7, }, }, + widgetSize: [ + { + viewportMinWidth: 0, + configuration: () => { + return { + minWidth: "60px", + minHeight: "70px", + }; + }, + }, + { + viewportMinWidth: layoutConfigurations.MOBILE.maxWidth, + configuration: () => { + return { + minWidth: "60px", + minHeight: "80px", + }; + }, + }, + ], }; export default Widget; diff --git a/app/client/src/widgets/PhoneInputWidget/index.ts b/app/client/src/widgets/PhoneInputWidget/index.ts index 99a773d63087..64b0cfbe733d 100644 --- a/app/client/src/widgets/PhoneInputWidget/index.ts +++ b/app/client/src/widgets/PhoneInputWidget/index.ts @@ -1,5 +1,6 @@ import { LabelPosition } from "components/constants"; import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; +import { layoutConfigurations } from "constants/WidgetConstants"; import { getDefaultResponsiveBehavior } from "utils/layoutPropertiesUtils"; import { DynamicHeight } from "utils/WidgetFeatures"; import { CONFIG as BaseConfig } from "widgets/BaseInputWidget"; @@ -49,6 +50,26 @@ export const CONFIG = { rows: 7, }, }, + widgetSize: [ + { + viewportMinWidth: 0, + configuration: () => { + return { + minWidth: "60px", + minHeight: "70px", + }; + }, + }, + { + viewportMinWidth: layoutConfigurations.MOBILE.maxWidth, + configuration: () => { + return { + minWidth: "60px", + minHeight: "80px", + }; + }, + }, + ], }; export default Widget; From 06f3ad95c675fdb00b49e6c83daf1de628822b7b Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 24 Jan 2023 13:13:55 -0500 Subject: [PATCH 430/708] add drop zones for highlights --- .../hooks/useAutoLayoutHighlights.ts | 31 +-- .../CanvasArenas/hooks/useCanvasDragging.ts | 7 +- .../autoLayout/highlightSelectionUtils.ts | 197 +++---------- .../src/utils/autoLayout/highlightUtils.ts | 259 +++++++++++++----- 4 files changed, 235 insertions(+), 259 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index 563f3ba14cb7..ecdd87b1d0c1 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -1,14 +1,13 @@ -import { - FlexLayerAlignment, - LayoutDirection, - ResponsiveBehavior, -} from "utils/autoLayout/constants"; +import { ResponsiveBehavior } from "utils/autoLayout/constants"; import { useSelector } from "react-redux"; import { ReflowDirection } from "reflow/reflowTypes"; import { getWidgets } from "sagas/selectors"; import { getCanvasWidth } from "selectors/editorSelectors"; import { getIsMobile } from "selectors/mainCanvasSelectors"; -import { deriveHighlightsFromLayers } from "utils/autoLayout/highlightUtils"; +import { + deriveHighlightsFromLayers, + HighlightInfo, +} from "utils/autoLayout/highlightUtils"; import WidgetFactory from "utils/WidgetFactory"; import { WidgetDraggingBlock } from "./useBlocksToBeDraggedOnCanvas"; import { @@ -16,24 +15,9 @@ import { Point, } from "utils/autoLayout/highlightSelectionUtils"; -export interface HighlightInfo { - isNewLayer: boolean; // determines if a new layer / child has been added directly to the container. - index: number; // index of the child in props.children. - layerIndex?: number; // index of layer in props.flexLayers. - rowIndex: number; // index of highlight within a horizontal layer. - alignment: FlexLayerAlignment; // alignment of the child in the layer. - posX: number; // x position of the highlight. - posY: number; // y position of the highlight. - width: number; // width of the highlight. - height: number; // height of the highlight. - isVertical: boolean; // determines if the highlight is vertical or horizontal. - canvasId: string; // widgetId of the canvas to which the highlight belongs. -} - export interface AutoLayoutHighlightProps { blocksToDraw: WidgetDraggingBlock[]; canvasId: string; - direction?: LayoutDirection; isCurrentDraggedCanvas: boolean; isDragging: boolean; useAutoLayout?: boolean; @@ -48,7 +32,6 @@ export interface HighlightSelectionPayload { export const useAutoLayoutHighlights = ({ blocksToDraw, canvasId, - direction, isCurrentDraggedCanvas, isDragging, useAutoLayout, @@ -137,8 +120,6 @@ export const useAutoLayoutHighlights = ({ const highlight: HighlightInfo | undefined = getHighlightPayload( highlights, - direction, - isFillWidget, e, moveDirection, ); @@ -163,8 +144,6 @@ export const useAutoLayoutHighlights = ({ const payload: HighlightInfo | undefined = getHighlightPayload( highlights, - direction, - isFillWidget, null, undefined, val, diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index 7dadcb50b22f..3bcae3f72e8f 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -15,6 +15,7 @@ import { } from "reflow/reflowTypes"; import { getParentOffsetTop } from "selectors/autoLayoutSelectors"; import { getCanvasScale } from "selectors/editorSelectors"; +import { HighlightInfo } from "utils/autoLayout/highlightUtils"; import { getNearestParentCanvas } from "utils/generators"; import { useWidgetDragResize } from "utils/hooks/dragResizeHooks"; import { ReflowInterface, useReflow } from "utils/hooks/useReflow"; @@ -32,10 +33,7 @@ import { modifyDrawingRectangles, updateRectanglesPostReflow, } from "./canvasDraggingUtils"; -import { - HighlightInfo, - useAutoLayoutHighlights, -} from "./useAutoLayoutHighlights"; +import { useAutoLayoutHighlights } from "./useAutoLayoutHighlights"; import { useBlocksToBeDraggedOnCanvas, WidgetDraggingBlock, @@ -119,7 +117,6 @@ export const useCanvasDragging = ( } = useAutoLayoutHighlights({ blocksToDraw, canvasId: widgetId, - direction, isCurrentDraggedCanvas, isDragging, useAutoLayout, diff --git a/app/client/src/utils/autoLayout/highlightSelectionUtils.ts b/app/client/src/utils/autoLayout/highlightSelectionUtils.ts index 9134fe91fc0b..34337b682847 100644 --- a/app/client/src/utils/autoLayout/highlightSelectionUtils.ts +++ b/app/client/src/utils/autoLayout/highlightSelectionUtils.ts @@ -1,6 +1,5 @@ -import { HighlightInfo } from "pages/common/CanvasArenas/hooks/useAutoLayoutHighlights"; import { ReflowDirection } from "reflow/reflowTypes"; -import { FlexLayerAlignment, LayoutDirection } from "./constants"; +import { HighlightInfo } from "./highlightUtils"; export interface Point { x: number; @@ -10,8 +9,6 @@ export interface Point { /** * Select the closest highlight to the mouse position (in the direction of the). * @param highlights | HighlightInfo[] : all highlights for the current canvas. - * @param direction | LayoutDirection | undefined : direction of the stacking. - * @param isFillWidget | boolean : determines if the dragged widgets contain a fill widget. * @param e | any : mouse event. * @param moveDirection | ReflowDirection : direction of the drag. * @param val | Point : mouse coordinates. @@ -19,8 +16,6 @@ export interface Point { */ export const getHighlightPayload = ( highlights: HighlightInfo[], - direction: LayoutDirection | undefined, - isFillWidget: boolean, e: any, moveDirection?: ReflowDirection, val?: Point, @@ -34,34 +29,15 @@ export const getHighlightPayload = ( }; /** - * If the mouse is within 10px of a highlight, return that highlight. - */ - const closestHighlight: HighlightInfo | undefined = getClosestHighlight( - highlights, - pos, - ); - if (closestHighlight) return closestHighlight; - - /** - * Filter highlights that are in the direction of the drag - * and span the current mouse position. + * Filter highlights that span the current mouse position. */ let filteredHighlights: HighlightInfo[] = []; - filteredHighlights = getViableDropPositions( - highlights, - pos, - isFillWidget, - moveDirection, - direction, - ); + filteredHighlights = getViableDropPositions(highlights, pos, moveDirection); if (!filteredHighlights || !filteredHighlights?.length) return; // Sort filtered highlights in ascending order of distance from mouse position. const arr = [...filteredHighlights]?.sort((a, b) => { - return ( - calculateDirectionalDistance(a, pos, moveDirection) - - calculateDirectionalDistance(b, pos, moveDirection) - ); + return calculateDistance(a, pos) - calculateDistance(b, pos); }); // console.log("#### arr", arr, highlights, moveDirection); @@ -74,139 +50,59 @@ export const getHighlightPayload = ( * Filter highlights based on direction of drag. * @param arr | HighlightInfo[] : all highlights for the current canvas. * @param pos | Point : current mouse coordinates. - * @param isFillWidget | boolean : determines if the dragged widgets contain a fill widget. * @param moveDirection | ReflowDirection : direction of the drag. - * @param direction | LayoutDirection | undefined : direction of the stacking. * @returns HighlightInfo | undefined */ function getViableDropPositions( arr: HighlightInfo[], pos: Point, - isFillWidget: boolean, moveDirection?: ReflowDirection, - direction?: LayoutDirection, ): HighlightInfo[] { if (!moveDirection || !arr) return arr || []; - const isVerticalDrag = [ReflowDirection.TOP, ReflowDirection.BOTTOM].includes( - moveDirection, - ); - return direction === LayoutDirection.Vertical - ? getVerticalStackDropPositions(arr, pos, isVerticalDrag, isFillWidget) - : getHorizontalStackDropPositions(arr, pos); -} - -/** - * Calculate the distance between the mouse position and the highlight. - * Return the closest highlight if distance <= 10px. - * @param arr | HighlightInfo[] : all highlights for the current canvas. - * @param pos | Point : current mouse coordinates. - * @returns HighlightInfo | undefined - */ -function getClosestHighlight( - arr: HighlightInfo[], - pos: Point, -): HighlightInfo | undefined { - if (!arr || !pos) return; - const res: HighlightInfo[] = arr.filter((highlight: HighlightInfo) => { - const distance = calculateActualDistance(highlight, pos); - return distance <= 10; - }); - if (!res.length) return; - return res.sort((a, b) => { - return calculateActualDistance(a, pos) - calculateActualDistance(b, pos); - })[0]; -} - -function getVerticalStackDropPositions( - arr: HighlightInfo[], - pos: Point, - isVerticalDrag: boolean, - isFillWidget: boolean, -): HighlightInfo[] { - // For vertical stacks, filter out the highlights based on drag direction and y position. - let filteredHighlights: HighlightInfo[] = arr.filter( - (highlight: HighlightInfo) => { - // Return only horizontal highlights for vertical drag. - if (isVerticalDrag) - return ( - !highlight.isVertical && - highlight.width > 0 && - pos.x > 0 && - pos.y > 0 && - pos.x >= highlight.posX && - pos.x <= highlight.posX + highlight.width - ); - // Return only vertical highlights for horizontal drag, if they lie in the same x plane. - return ( - highlight.isVertical && - pos.y >= highlight.posY && - pos.y <= highlight.posY + highlight.height - ); - }, + const DEFAULT_DROP_RANGE = 10; + const verticalHighlights = arr.filter( + (highlight: HighlightInfo) => highlight.isVertical, ); - /** - * For horizontal drag, if no vertical highlight exists in the same x plane, - * return the horizontal highlights for the last layer. - * In case of a dragged Fill widget, only return the Start alignment as it will span the entire width. - */ - if (!isVerticalDrag && !filteredHighlights.length) - filteredHighlights = arr - .slice(arr.length - 3) - .filter((highlight: HighlightInfo) => - !highlight.isVertical && isFillWidget - ? highlight.alignment === FlexLayerAlignment.Start - : true, - ); - - return filteredHighlights; -} - -function getHorizontalStackDropPositions( - arr: HighlightInfo[], - pos: Point, -): HighlightInfo[] { - // For horizontal stack, return the highlights that lie in the same x plane. - let filteredHighlights = arr.filter( - (highlight) => - pos.y >= highlight.posY && pos.y <= highlight.posY + highlight.height, + const horizontalHighlights = arr.filter( + (highlight: HighlightInfo) => !highlight.isVertical, ); - // If no highlight exists in the same x plane, return the last highlight. - if (!filteredHighlights.length) filteredHighlights = [arr[arr.length - 1]]; - return filteredHighlights; -} - -/** - * Calculate distance between the mouse position and the closest point on the highlight. - * - * @param a | HighlightInfo : current highlight. - * @param b | Point : current mouse position. - * @param moveDirection | ReflowDirection : current drag direction. - * @returns number - */ -function calculateDirectionalDistance( - a: HighlightInfo, - b: Point, - moveDirection?: ReflowDirection, -): number { - let { distX, distY } = getXYDistance(a, b); - /** - * Emphasize move direction over actual distance. - * - * If the point is close to a highlight. However, it is moving in the opposite direction, - * then increase the appropriate distance to ensure that this highlight is discounted. - */ - if (moveDirection === ReflowDirection.RIGHT && distX > 20) distX += 2000; - if (moveDirection === ReflowDirection.LEFT && distX < -20) distX -= 2000; - if (moveDirection === ReflowDirection.BOTTOM && distY > 20) distY += 2000; - if (moveDirection === ReflowDirection.TOP && distY < -20) distY -= 2000; - - return Math.abs(Math.sqrt(distX * distX + distY * distY)); + const selection: HighlightInfo[] = []; + verticalHighlights.forEach((highlight: HighlightInfo) => { + if (pos.y >= highlight.posY && pos.y <= highlight.posY + highlight.height) + if ( + (pos.x >= highlight.posX && + pos.x <= + highlight.posX + + (highlight.dropZone?.right || DEFAULT_DROP_RANGE)) || + (pos.x < highlight.posX && + pos.x >= + highlight.posX - (highlight.dropZone?.left || DEFAULT_DROP_RANGE)) + ) + selection.push(highlight); + }); + const hasVerticalSelection = selection.length > 0; + horizontalHighlights.forEach((highlight: HighlightInfo) => { + if (pos.x >= highlight.posX && pos.x <= highlight.posX + highlight.width) + if ( + (pos.y >= highlight.posY && + pos.y <= + highlight.posY + + (highlight.dropZone?.bottom !== undefined + ? highlight.dropZone?.bottom * (hasVerticalSelection ? 0.2 : 1) + : DEFAULT_DROP_RANGE)) || + (pos.y < highlight.posY && + pos.y >= + highlight.posY - + (highlight.dropZone?.top !== undefined + ? highlight.dropZone?.top * (hasVerticalSelection ? 0.3 : 1) + : DEFAULT_DROP_RANGE)) + ) + selection.push(highlight); + }); + return selection; } -function getXYDistance( - a: HighlightInfo, - b: Point, -): { distX: number; distY: number } { +function calculateDistance(a: HighlightInfo, b: Point): number { let distX = 0, distY = 0; if (a.isVertical) { @@ -228,10 +124,5 @@ function getXYDistance( distX = 0; } } - return { distX, distY }; -} - -function calculateActualDistance(a: HighlightInfo, b: Point): number { - const { distX, distY } = getXYDistance(a, b); return Math.abs(Math.sqrt(distX * distX + distY * distY)); } diff --git a/app/client/src/utils/autoLayout/highlightUtils.ts b/app/client/src/utils/autoLayout/highlightUtils.ts index e928b2aaa1dd..9b14ebaeded8 100644 --- a/app/client/src/utils/autoLayout/highlightUtils.ts +++ b/app/client/src/utils/autoLayout/highlightUtils.ts @@ -10,7 +10,6 @@ import { MAIN_CONTAINER_WIDGET_ID, WIDGET_PADDING, } from "constants/WidgetConstants"; -import { HighlightInfo } from "pages/common/CanvasArenas/hooks/useAutoLayoutHighlights"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; import { getLeftColumn, @@ -27,6 +26,28 @@ import { Widget, } from "./positionUtils"; +export interface DropZone { + top?: number; + bottom?: number; + left?: number; + right?: number; +} + +export interface HighlightInfo { + isNewLayer: boolean; // determines if a new layer / child has been added directly to the container. + index: number; // index of the child in props.children. + layerIndex?: number; // index of layer in props.flexLayers. + rowIndex: number; // index of highlight within a horizontal layer. + alignment: FlexLayerAlignment; // alignment of the child in the layer. + posX: number; // x position of the highlight. + posY: number; // y position of the highlight. + width: number; // width of the highlight. + height: number; // height of the highlight. + isVertical: boolean; // determines if the highlight is vertical or horizontal. + canvasId: string; // widgetId of the canvas to which the highlight belongs. + dropZone: DropZone; // size of the drop zone of this highlight. +} + /** * @param allWidgets : CanvasWidgetsReduxState * @param canvasId : string @@ -44,88 +65,87 @@ export function deriveHighlightsFromLayers( isMobile = false, ): HighlightInfo[] { const widgets = { ...allWidgets }; - try { - const canvas = widgets[canvasId]; - if (!canvas) return []; + const canvas = widgets[canvasId]; + if (!canvas) return []; + + const { canvasWidth, columnSpace } = getCanvasDimensions( + canvas, + widgets, + mainCanvasWidth, + isMobile, + ); + const layers: FlexLayer[] = canvas.flexLayers || []; + let highlights: HighlightInfo[] = []; + let childCount = 0; + let layerIndex = 0; - const { canvasWidth, columnSpace } = getCanvasDimensions( - canvas, + let offsetTop = FLEXBOX_PADDING; // used to calculate distance of a highlight from parents's top. + for (const layer of layers) { + /** + * If the layer is empty, after discounting the dragged widgets, + * then don't process it for vertical highlights. + */ + const isEmpty: boolean = + layer?.children?.filter( + (child: LayerChild) => draggedWidgets.indexOf(child.id) === -1, + ).length === 0; + const childrenRows = getTotalRowsOfAllChildren( widgets, - mainCanvasWidth, + layer.children?.map((child) => child.id) || [], isMobile, ); - const layers: FlexLayer[] = canvas.flexLayers || []; - const highlights: HighlightInfo[] = []; - let childCount = 0; - let layerIndex = 0; - // TODO: remove offsetTop and use child positions after widget positioning on grid is solved. - let offsetTop = FLEXBOX_PADDING; // used to calculate distance of a highlight from parents's top. - for (const layer of layers) { + + const payload: VerticalHighlightsPayload = generateVerticalHighlights({ + widgets, + layer, + childCount, + layerIndex, + offsetTop, + canvasWidth, + canvasId, + columnSpace, + draggedWidgets, + isMobile, + }); + + if (!isEmpty) { /** - * If the layer is empty, after discounting the dragged widgets, - * then don't process it for vertical highlights. + * Add a layer of horizontal highlights before each flex layer + * to account for new vertical drop positions. */ - const isEmpty: boolean = - layer?.children?.filter( - (child: LayerChild) => draggedWidgets.indexOf(child.id) === -1, - ).length === 0; - const childrenRows = getTotalRowsOfAllChildren( - widgets, - layer.children?.map((child) => child.id) || [], - isMobile, + highlights.push( + ...generateHorizontalHighlights( + childCount, + layerIndex, + offsetTop, + canvasWidth, + canvasId, + hasFillWidget, + childrenRows * GridDefaults.DEFAULT_GRID_ROW_HEIGHT, + getPreviousOffsetTop(highlights), + ), ); - const payload: VerticalHighlightsPayload = generateVerticalHighlights({ - widgets, - layer, - childCount, - layerIndex, - offsetTop, - canvasWidth, - canvasId, - columnSpace, - draggedWidgets, - isMobile, - }); - - if (!isEmpty) { - /** - * Add a layer of horizontal highlights before each flex layer - * to account for new vertical drop positions. - */ - highlights.push( - ...generateHorizontalHighlights( - childCount, - layerIndex, - offsetTop, - canvasWidth, - canvasId, - hasFillWidget, - ), - ); - - highlights.push(...payload.highlights); - layerIndex += 1; - } - offsetTop += childrenRows * GridDefaults.DEFAULT_GRID_ROW_HEIGHT || 0; - childCount += payload.childCount; - } - // Add a layer of horizontal highlights for the empty space at the bottom of a stack. - highlights.push( - ...generateHorizontalHighlights( - childCount, - layerIndex, - offsetTop, - canvasWidth, - canvasId, - hasFillWidget, - ), - ); - return highlights; - } catch (e) { - // console.error(e); - return []; + highlights.push(...payload.highlights); + layerIndex += 1; + } else highlights = updateHorizontalDropZone(highlights); + offsetTop += childrenRows * GridDefaults.DEFAULT_GRID_ROW_HEIGHT || 0; + childCount += payload.childCount; } + // Add a layer of horizontal highlights for the empty space at the bottom of a stack. + highlights.push( + ...generateHorizontalHighlights( + childCount, + layerIndex, + offsetTop, + canvasWidth, + canvasId, + hasFillWidget, + -1, + getPreviousOffsetTop(highlights), + ), + ); + return highlights; } export interface VerticalHighlightsPayload { childCount: number; @@ -214,7 +234,7 @@ export function generateVerticalHighlights(data: { ? getWrappedAlignmentInfo(alignmentInfo) : [alignmentInfo]; - const highlights: HighlightInfo[] = []; + let highlights: HighlightInfo[] = []; for (const each of wrappingInfo) { if (!each.length) continue; /** @@ -268,6 +288,7 @@ export function generateVerticalHighlights(data: { index += 1; } } + highlights = updateVerticalHighlightDropZone(highlights, canvasWidth); return { highlights, childCount: count }; } @@ -324,6 +345,7 @@ export function generateHighlightsForAlignment(data: { : maxHeight, isVertical: true, canvasId, + dropZone: {}, }); count += 1; } @@ -361,6 +383,7 @@ export function generateHighlightsForAlignment(data: { : maxHeight, isVertical: true, canvasId, + dropZone: {}, }); } return res; @@ -418,9 +441,15 @@ function generateHorizontalHighlights( containerWidth: number, canvasId: string, hasFillWidget: boolean, + rowHeight: number, + previousOffset: number, ): HighlightInfo[] { const width = containerWidth / 3; const arr: HighlightInfo[] = []; + const dropZone: DropZone = { + top: previousOffset === -1 ? offsetTop : (offsetTop - previousOffset) * 0.5, + bottom: rowHeight === -1 ? 10000 : rowHeight * 0.5, + }; [ FlexLayerAlignment.Start, FlexLayerAlignment.Center, @@ -446,6 +475,7 @@ function generateHorizontalHighlights( height: DEFAULT_HIGHLIGHT_SIZE, isVertical: false, canvasId, + dropZone, }); }); return arr; @@ -511,3 +541,82 @@ function getPadding(canvas: Widget): number { padding += canvas.type === "CONTAINER_WIDGET" ? 2 : 0; return padding; } + +/** + * Calculate drop zones for vertical highlights. + * Drop zone of vertical highlights span 35% of the distance between two consecutive highlights. + * @param highlights | HighlightInfo[] : array of highlights to be updated. + * @param canvasWidth | number : width of the canvas. + * @returns HighlightInfo[] : updated highlights. + */ +function updateVerticalHighlightDropZone( + highlights: HighlightInfo[], + canvasWidth: number, +): HighlightInfo[] { + for (const [index, highlight] of highlights.entries()) { + const nextHighlight: HighlightInfo | undefined = highlights[index + 1]; + const previousHighlight: HighlightInfo | undefined = highlights[index - 1]; + const leftZone = previousHighlight + ? (highlight.posX - previousHighlight.posX) * 0.35 + : highlight.posX + DEFAULT_HIGHLIGHT_SIZE; + const rightZone = nextHighlight + ? (nextHighlight.posX - highlight.posX) * 0.35 + : canvasWidth - highlight.posX; + highlights[index] = { + ...highlight, + dropZone: { + left: leftZone, + right: rightZone, + }, + }; + } + return highlights; +} + +/** + * Update drop zones for horizontal highlights of the last row. + * Normally, bottom drop zone of a horizontal highlights spans 50% of the row height. + * However, if the next row of horizontal highlights is omitted on account of the dragged widgets, + * then update the previous row's bottom drop zone to span 100% of the row height. + * @param highlights | HighlightInfo[] : array of highlights to be updated. + * @returns HighlightInfo[] : updated highlights. + */ +function updateHorizontalDropZone( + highlights: HighlightInfo[], +): HighlightInfo[] { + let index = highlights.length - 1; + while (index >= 0 && highlights[index].isVertical) { + index -= 1; + } + if (index < 0) return highlights; + const dropZone = { + top: highlights[index].dropZone.top, + bottom: (highlights[index]?.dropZone?.bottom || 5) * 2, + }; + const updatedHighlights: HighlightInfo[] = [ + ...highlights.slice(0, index - 2), + { + ...highlights[index - 2], + dropZone, + }, + { + ...highlights[index - 1], + dropZone, + }, + { + ...highlights[index], + dropZone, + }, + ...highlights.slice(index + 1), + ]; + return updatedHighlights; +} + +function getPreviousOffsetTop(highlights: HighlightInfo[]): number { + if (!highlights.length) return -1; + let index = highlights.length - 1; + while (highlights[index].isVertical) { + index--; + } + return highlights[index].posY + highlights[index].height; +} From 363dc53019a5decaaff447310aee45d446897a26 Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 24 Jan 2023 13:40:28 -0500 Subject: [PATCH 431/708] reduced opacity of dragged widget --- .../src/components/editorComponents/DraggableComponent.tsx | 5 +++-- .../src/components/editorComponents/ResizableComponent.tsx | 4 +++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/app/client/src/components/editorComponents/DraggableComponent.tsx b/app/client/src/components/editorComponents/DraggableComponent.tsx index 4d8b71788926..88e6d9ae3591 100644 --- a/app/client/src/components/editorComponents/DraggableComponent.tsx +++ b/app/client/src/components/editorComponents/DraggableComponent.tsx @@ -115,10 +115,11 @@ function DraggableComponent(props: DraggableComponentProps) { focusWidget(props.widgetId); e.stopPropagation(); }; - const shouldRenderComponent = !(isSelected && isDragging); + const shouldRenderComponent = + props.isFlexChild || !(isSelected && isDragging); // Display this draggable based on the current drag state const dragWrapperStyle: CSSProperties = { - display: isCurrentWidgetDragging ? "none" : "block", + display: !props.isFlexChild && isCurrentWidgetDragging ? "none" : "block", }; const dragBoundariesStyle: React.CSSProperties = useMemo(() => { return { diff --git a/app/client/src/components/editorComponents/ResizableComponent.tsx b/app/client/src/components/editorComponents/ResizableComponent.tsx index 910a0ae6f80f..944a63a7eaa1 100644 --- a/app/client/src/components/editorComponents/ResizableComponent.tsx +++ b/app/client/src/components/editorComponents/ResizableComponent.tsx @@ -354,7 +354,9 @@ export const ResizableComponent = memo(function ResizableComponent( > {props.children} From 36b38009166cee30140c6314eb19e950f82698b4 Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 24 Jan 2023 13:44:21 -0500 Subject: [PATCH 432/708] fix imports --- .../common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts | 2 +- .../pages/common/CanvasArenas/hooks/useRenderBlocksOnCanvas.ts | 2 +- app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts | 2 +- app/client/src/utils/autoLayout/highlightUtils.test.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts b/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts index 9072fc49100b..ca9d5bb848af 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts @@ -29,8 +29,8 @@ import { WidgetOperationParams, widgetOperationParams, } from "utils/WidgetPropsUtils"; -import { HighlightInfo } from "./useAutoLayoutHighlights"; import { XYCord } from "./useRenderBlocksOnCanvas"; +import { HighlightInfo } from "utils/autoLayout/highlightUtils"; export interface WidgetDraggingUpdateParams extends WidgetDraggingBlock { updateWidgetParams: WidgetOperationParams; diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useRenderBlocksOnCanvas.ts b/app/client/src/pages/common/CanvasArenas/hooks/useRenderBlocksOnCanvas.ts index 67b445890d52..79aa97fd38eb 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useRenderBlocksOnCanvas.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useRenderBlocksOnCanvas.ts @@ -2,9 +2,9 @@ import { CONTAINER_GRID_PADDING } from "constants/WidgetConstants"; import { useSelector } from "react-redux"; import { SpaceMap } from "reflow/reflowTypes"; import { getZoomLevel } from "selectors/editorSelectors"; +import { HighlightInfo } from "utils/autoLayout/highlightUtils"; import { getAbsolutePixels } from "utils/helpers"; import { modifyDrawingRectangles } from "./canvasDraggingUtils"; -import { HighlightInfo } from "./useAutoLayoutHighlights"; import { WidgetDraggingBlock } from "./useBlocksToBeDraggedOnCanvas"; export interface XYCord { diff --git a/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts b/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts index 55d757932ea1..9394e3480b42 100644 --- a/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts +++ b/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts @@ -10,7 +10,6 @@ import { import { FlexLayer } from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; import { GridDefaults } from "constants/WidgetConstants"; import log from "loglevel"; -import { HighlightInfo } from "pages/common/CanvasArenas/hooks/useAutoLayoutHighlights"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; import { all, call, put, select, takeLatest } from "redux-saga/effects"; import { getWidgets } from "sagas/selectors"; @@ -24,6 +23,7 @@ import { updateRelationships, } from "utils/autoLayout/autoLayoutDraggingUtils"; import { updateWidgetPositions } from "utils/autoLayout/positionUtils"; +import { HighlightInfo } from "utils/autoLayout/highlightUtils"; function* addWidgetAndReorderSaga( actionPayload: ReduxAction<{ diff --git a/app/client/src/utils/autoLayout/highlightUtils.test.ts b/app/client/src/utils/autoLayout/highlightUtils.test.ts index 56b4b718ca30..3a622a6c073a 100644 --- a/app/client/src/utils/autoLayout/highlightUtils.test.ts +++ b/app/client/src/utils/autoLayout/highlightUtils.test.ts @@ -3,13 +3,13 @@ import { ResponsiveBehavior, } from "utils/autoLayout/constants"; import { FLEXBOX_PADDING, RenderModes } from "constants/WidgetConstants"; -import { HighlightInfo } from "pages/common/CanvasArenas/hooks/useAutoLayoutHighlights"; import { getWidgetHeight } from "./flexWidgetUtils"; import { deriveHighlightsFromLayers, generateHighlightsForAlignment, generateVerticalHighlights, getCanvasWidth, + HighlightInfo, VerticalHighlightsPayload, } from "./highlightUtils"; import { data } from "./testData"; From 44753c375568152a5e89ed159b213675ad13fc19 Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 24 Jan 2023 16:31:06 -0500 Subject: [PATCH 433/708] rounded rect highlight --- .../editorComponents/ResizableComponent.tsx | 5 ++--- .../editorComponents/ResizeStyledComponents.tsx | 6 ++++++ .../CanvasArenas/hooks/useRenderBlocksOnCanvas.ts | 11 +++++++++-- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/app/client/src/components/editorComponents/ResizableComponent.tsx b/app/client/src/components/editorComponents/ResizableComponent.tsx index 944a63a7eaa1..88a24af9b5a2 100644 --- a/app/client/src/components/editorComponents/ResizableComponent.tsx +++ b/app/client/src/components/editorComponents/ResizableComponent.tsx @@ -354,9 +354,8 @@ export const ResizableComponent = memo(function ResizableComponent( > {props.children} diff --git a/app/client/src/components/editorComponents/ResizeStyledComponents.tsx b/app/client/src/components/editorComponents/ResizeStyledComponents.tsx index 1066712f1028..6cd055b9d118 100644 --- a/app/client/src/components/editorComponents/ResizeStyledComponents.tsx +++ b/app/client/src/components/editorComponents/ResizeStyledComponents.tsx @@ -9,10 +9,16 @@ const CORNER_RESIZE_HANDLE_WIDTH = 10; export const VisibilityContainer = styled.div<{ visible: boolean; padding: number; + reduceOpacity: boolean; }>` ${(props) => (!props.visible ? invisible : "")} height: 100%; width: 100%; + ${({ reduceOpacity }) => + reduceOpacity && + css` + opacity: 0.25; + `} `; const VerticalResizeIndicators = css<{ diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useRenderBlocksOnCanvas.ts b/app/client/src/pages/common/CanvasArenas/hooks/useRenderBlocksOnCanvas.ts index 79aa97fd38eb..523854fbc008 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useRenderBlocksOnCanvas.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useRenderBlocksOnCanvas.ts @@ -146,6 +146,7 @@ export const useRenderBlocksOnCanvas = ( stickyCanvasRef.current.width, stickyCanvasRef.current.height, ); + canvasCtx.beginPath(); isCurrUpdatingRows = false; canvasCtx.transform(canvasZoomLevel, 0, 0, canvasZoomLevel, 0, 0); if (canvasIsDragging) { @@ -154,7 +155,11 @@ export const useRenderBlocksOnCanvas = ( }); } if (highlight) { - canvasCtx.fillStyle = "rgba(196, 139, 181, 1)"; + const highlightColor = "rgba(196, 139, 181, 1)"; + canvasCtx.fillStyle = highlightColor; + canvasCtx.lineWidth = 1; + canvasCtx.strokeStyle = highlightColor; + canvasCtx.setLineDash([]); const { height, posX, posY, width } = highlight; let val = 0; if (scrollParent?.scrollTop) @@ -163,7 +168,9 @@ export const useRenderBlocksOnCanvas = ( : parentOffsetTop && scrollParent.scrollTop > parentOffsetTop ? scrollParent.scrollTop - parentOffsetTop : 0; - canvasCtx.fillRect(posX, posY - val, width, height); + canvasCtx.roundRect(posX, posY - val, width, height, 4); + canvasCtx.fill(); + canvasCtx.stroke(); canvasCtx.save(); } canvasCtx.restore(); From f23f0dd348518ddf170858edfd636cd9488bc994 Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 24 Jan 2023 17:23:15 -0500 Subject: [PATCH 434/708] remove dashed rect around the dragged block --- .../CanvasArenas/hooks/useCanvasDragging.ts | 1 + .../hooks/useRenderBlocksOnCanvas.ts | 40 ++++++++++--------- 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index 3bcae3f72e8f..d1f14f47522b 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -462,6 +462,7 @@ export const useCanvasDragging = ( selectedHighlight, widgetId === MAIN_CONTAINER_WIDGET_ID, parentOffsetTop, + useAutoLayout, ); scrollObj.lastMouseMoveEvent = { offsetX: e.offsetX, diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useRenderBlocksOnCanvas.ts b/app/client/src/pages/common/CanvasArenas/hooks/useRenderBlocksOnCanvas.ts index 523854fbc008..5c2e0941e85c 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useRenderBlocksOnCanvas.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useRenderBlocksOnCanvas.ts @@ -50,6 +50,7 @@ export const useRenderBlocksOnCanvas = ( const drawBlockOnCanvas = ( blockDimensions: WidgetDraggingBlock, scrollParent: Element | null, + useAutoLayout?: boolean, ) => { if ( stickyCanvasRef.current && @@ -86,23 +87,25 @@ export const useRenderBlocksOnCanvas = ( blockDimensions.width, blockDimensions.height, ); - const strokeWidth = 1; - canvasCtx.setLineDash([3]); - canvasCtx.strokeStyle = blockDimensions.isNotColliding - ? "rgb(104, 113, 239)" - : "red"; - canvasCtx.strokeRect( - snappedXY.X - - leftOffset + - strokeWidth + - (noPad ? 0 : CONTAINER_GRID_PADDING), - snappedXY.Y - - topOffset + - strokeWidth + - (noPad ? 0 : CONTAINER_GRID_PADDING), - blockDimensions.width - strokeWidth, - blockDimensions.height - strokeWidth, - ); + if (!useAutoLayout) { + const strokeWidth = 1; + canvasCtx.setLineDash([3]); + canvasCtx.strokeStyle = blockDimensions.isNotColliding + ? "rgb(104, 113, 239)" + : "red"; + canvasCtx.strokeRect( + snappedXY.X - + leftOffset + + strokeWidth + + (noPad ? 0 : CONTAINER_GRID_PADDING), + snappedXY.Y - + topOffset + + strokeWidth + + (noPad ? 0 : CONTAINER_GRID_PADDING), + blockDimensions.width - strokeWidth, + blockDimensions.height - strokeWidth, + ); + } } }; @@ -124,6 +127,7 @@ export const useRenderBlocksOnCanvas = ( highlight?: HighlightInfo | undefined, isMainContainer?: boolean, parentOffsetTop?: number, + useAutoLayout?: boolean, ) => { let isCurrUpdatingRows = isUpdatingRows; const modifiedRectanglesToDraw = modifyDrawingRectangles( @@ -151,7 +155,7 @@ export const useRenderBlocksOnCanvas = ( canvasCtx.transform(canvasZoomLevel, 0, 0, canvasZoomLevel, 0, 0); if (canvasIsDragging) { modifiedRectanglesToDraw.forEach((each) => { - drawBlockOnCanvas(each, scrollParent); + drawBlockOnCanvas(each, scrollParent, useAutoLayout); }); } if (highlight) { From 43c4ed0e15a6bf067559ae48e9b0d9a3dbc916ec Mon Sep 17 00:00:00 2001 From: Aswath K Date: Thu, 26 Jan 2023 20:34:41 +0530 Subject: [PATCH 435/708] fix: Modal dropped inside a widget getting limited to parent size (#20092) Regardless of where we drop the Modal widget, its parent should be MAIN_CONTAINER_WIDGET_ID fixes: #19601 --- .../common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts b/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts index 9072fc49100b..85f58051969b 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts @@ -274,7 +274,9 @@ export const useBlocksToBeDraggedOnCanvas = ({ payload: { dropPayload, newWidget: widgetPayload, - parentId: widgetId, + parentId: newWidget.detachFromLayout + ? MAIN_CONTAINER_WIDGET_ID + : widgetId, direction, }, }); From 6f42a55296b5dde7c00bcad3099a2f093c30ba82 Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 26 Jan 2023 10:21:03 -0500 Subject: [PATCH 436/708] fix start and end highlight positioning --- app/client/src/utils/autoLayout/highlightUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/client/src/utils/autoLayout/highlightUtils.ts b/app/client/src/utils/autoLayout/highlightUtils.ts index 9b14ebaeded8..d865039ac07b 100644 --- a/app/client/src/utils/autoLayout/highlightUtils.ts +++ b/app/client/src/utils/autoLayout/highlightUtils.ts @@ -408,7 +408,7 @@ function getPositionForInitialHighlight( startPosition: number | undefined, ): number { const endPosition = - 64 * columnSpace - (canvasId !== MAIN_CONTAINER_WIDGET_ID ? 4 : 0); + 64 * columnSpace - (canvasId !== MAIN_CONTAINER_WIDGET_ID ? 6 : -2); if (alignment === FlexLayerAlignment.End) { return endPosition; } else if (alignment === FlexLayerAlignment.Center) { From 0c42b6efcf113f401d4c8aa97cd773882f2c1667 Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 26 Jan 2023 10:23:18 -0500 Subject: [PATCH 437/708] fix horizontal highlight positioning --- app/client/src/utils/autoLayout/highlightUtils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/client/src/utils/autoLayout/highlightUtils.ts b/app/client/src/utils/autoLayout/highlightUtils.ts index d865039ac07b..4d4e01427989 100644 --- a/app/client/src/utils/autoLayout/highlightUtils.ts +++ b/app/client/src/utils/autoLayout/highlightUtils.ts @@ -463,9 +463,9 @@ function generateHorizontalHighlights( alignment, posX: hasFillWidget ? alignment === FlexLayerAlignment.Start - ? 0 + ? FLEXBOX_PADDING : containerWidth - : width * index, + : width * index + FLEXBOX_PADDING, posY: offsetTop, width: hasFillWidget ? alignment === FlexLayerAlignment.Start From 168c6d2ca8255b6847fa7cacae8bdaff97048e22 Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 26 Jan 2023 21:52:59 -0500 Subject: [PATCH 438/708] add highlight colors --- app/client/src/constants/Colors.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/client/src/constants/Colors.tsx b/app/client/src/constants/Colors.tsx index 79873f2edb9a..3cbd0e9a366b 100644 --- a/app/client/src/constants/Colors.tsx +++ b/app/client/src/constants/Colors.tsx @@ -228,6 +228,9 @@ export const Colors = { NOTIFICATION_BANNER_ERROR_TEXT: "#C91818", CTA_PURPLE: "#5E5DC1", + + HIGHLIGHT_FILL: "#C273EF", + HIGHLIGHT_OUTLINE: "rgba(255, 255, 255, 0.5)", }; export type Color = typeof Colors[keyof typeof Colors]; From 23699af2744ca2e0d8e598b1f25c9c731ebf2413 Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 26 Jan 2023 23:02:36 -0500 Subject: [PATCH 439/708] update highlight colors --- .../common/CanvasArenas/hooks/useRenderBlocksOnCanvas.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useRenderBlocksOnCanvas.ts b/app/client/src/pages/common/CanvasArenas/hooks/useRenderBlocksOnCanvas.ts index 5c2e0941e85c..94e2aead83f0 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useRenderBlocksOnCanvas.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useRenderBlocksOnCanvas.ts @@ -1,3 +1,4 @@ +import { Colors } from "constants/Colors"; import { CONTAINER_GRID_PADDING } from "constants/WidgetConstants"; import { useSelector } from "react-redux"; import { SpaceMap } from "reflow/reflowTypes"; @@ -159,10 +160,9 @@ export const useRenderBlocksOnCanvas = ( }); } if (highlight) { - const highlightColor = "rgba(196, 139, 181, 1)"; - canvasCtx.fillStyle = highlightColor; + canvasCtx.fillStyle = Colors.HIGHLIGHT_FILL; canvasCtx.lineWidth = 1; - canvasCtx.strokeStyle = highlightColor; + canvasCtx.strokeStyle = Colors.HIGHLIGHT_OUTLINE; canvasCtx.setLineDash([]); const { height, posX, posY, width } = highlight; let val = 0; From f4ba880f6f1b87c7702b7c1c97a2d94126fec791 Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 26 Jan 2023 23:20:15 -0500 Subject: [PATCH 440/708] add temp local storage variables for testing --- .../src/pages/Editor/WidgetsEditor/CanvasContainer.tsx | 4 ++++ .../src/utils/autoLayout/highlightSelectionUtils.ts | 8 ++++++-- app/client/src/utils/autoLayout/highlightUtils.ts | 6 ++++-- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx index 5e18ca8a9ff2..7a88932bda55 100644 --- a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx +++ b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx @@ -76,6 +76,10 @@ function CanvasContainer() { const isPageInitializing = isFetchingPage || !isLayoutingInitialized; useEffect(() => { + if (!localStorage.getItem("verticalHighlightDropArea")) + localStorage.setItem("verticalHighlightDropArea", "0.35"); + if (!localStorage.getItem("horizontalHighlightDropArea")) + localStorage.setItem("horizontalHighlightDropArea", "0.2"); return () => { dispatch(forceOpenWidgetPanel(false)); }; diff --git a/app/client/src/utils/autoLayout/highlightSelectionUtils.ts b/app/client/src/utils/autoLayout/highlightSelectionUtils.ts index 34337b682847..dcfe4cf7ecd2 100644 --- a/app/client/src/utils/autoLayout/highlightSelectionUtils.ts +++ b/app/client/src/utils/autoLayout/highlightSelectionUtils.ts @@ -81,6 +81,8 @@ function getViableDropPositions( selection.push(highlight); }); const hasVerticalSelection = selection.length > 0; + const dropArea = localStorage.getItem("horizontalHighlightDropArea"); + const zoneSize = dropArea ? parseFloat(dropArea) : 0; horizontalHighlights.forEach((highlight: HighlightInfo) => { if (pos.x >= highlight.posX && pos.x <= highlight.posX + highlight.width) if ( @@ -88,13 +90,15 @@ function getViableDropPositions( pos.y <= highlight.posY + (highlight.dropZone?.bottom !== undefined - ? highlight.dropZone?.bottom * (hasVerticalSelection ? 0.2 : 1) + ? highlight.dropZone?.bottom * + (hasVerticalSelection ? zoneSize : 1) : DEFAULT_DROP_RANGE)) || (pos.y < highlight.posY && pos.y >= highlight.posY - (highlight.dropZone?.top !== undefined - ? highlight.dropZone?.top * (hasVerticalSelection ? 0.3 : 1) + ? highlight.dropZone?.top * + (hasVerticalSelection ? zoneSize + 0.1 : 1) : DEFAULT_DROP_RANGE)) ) selection.push(highlight); diff --git a/app/client/src/utils/autoLayout/highlightUtils.ts b/app/client/src/utils/autoLayout/highlightUtils.ts index 4d4e01427989..b9c6aec5f611 100644 --- a/app/client/src/utils/autoLayout/highlightUtils.ts +++ b/app/client/src/utils/autoLayout/highlightUtils.ts @@ -553,14 +553,16 @@ function updateVerticalHighlightDropZone( highlights: HighlightInfo[], canvasWidth: number, ): HighlightInfo[] { + const dropArea = localStorage.getItem("verticalHighlightDropArea"); + const zoneSize = dropArea !== null ? parseFloat(dropArea) : 0.35; for (const [index, highlight] of highlights.entries()) { const nextHighlight: HighlightInfo | undefined = highlights[index + 1]; const previousHighlight: HighlightInfo | undefined = highlights[index - 1]; const leftZone = previousHighlight - ? (highlight.posX - previousHighlight.posX) * 0.35 + ? (highlight.posX - previousHighlight.posX) * zoneSize : highlight.posX + DEFAULT_HIGHLIGHT_SIZE; const rightZone = nextHighlight - ? (nextHighlight.posX - highlight.posX) * 0.35 + ? (nextHighlight.posX - highlight.posX) * zoneSize : canvasWidth - highlight.posX; highlights[index] = { ...highlight, From 999da099988df85e03b49e7030fef03ab9eea96d Mon Sep 17 00:00:00 2001 From: Preet Date: Fri, 27 Jan 2023 11:06:56 -0500 Subject: [PATCH 441/708] remove moveDirection usage --- .../Editor/WidgetsEditor/CanvasContainer.tsx | 4 -- .../CanvasArenas/hooks/canvasDraggingUtils.ts | 57 ------------------- .../hooks/useAutoLayoutHighlights.ts | 9 +-- .../CanvasArenas/hooks/useCanvasDragging.ts | 20 +------ .../autoLayout/highlightSelectionUtils.ts | 16 ++---- .../src/utils/autoLayout/highlightUtils.ts | 3 +- 6 files changed, 8 insertions(+), 101 deletions(-) diff --git a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx index 7a88932bda55..5e18ca8a9ff2 100644 --- a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx +++ b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx @@ -76,10 +76,6 @@ function CanvasContainer() { const isPageInitializing = isFetchingPage || !isLayoutingInitialized; useEffect(() => { - if (!localStorage.getItem("verticalHighlightDropArea")) - localStorage.setItem("verticalHighlightDropArea", "0.35"); - if (!localStorage.getItem("horizontalHighlightDropArea")) - localStorage.setItem("horizontalHighlightDropArea", "0.2"); return () => { dispatch(forceOpenWidgetPanel(false)); }; diff --git a/app/client/src/pages/common/CanvasArenas/hooks/canvasDraggingUtils.ts b/app/client/src/pages/common/CanvasArenas/hooks/canvasDraggingUtils.ts index 749fa8a603fe..590669b388ab 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/canvasDraggingUtils.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/canvasDraggingUtils.ts @@ -316,60 +316,3 @@ export const updateRectanglesPostReflow = ( return rectanglesToDraw; }; - -/** - * Get mouse move direction using an average of last five mouse positions. - * @param lastMousePositions | { x: number; y: number }[] : array of last five mouse positions. - * @param currentPosition | { x: number; y: number } : current mouse position. - * @param direction | ReflowDirection : current direction. - * @param updateMousePosition | ({ x, y }: { x: number; y: number }) => void : function to update mouse position. - * @returns ReflowDirection - */ -export function getInterpolatedMoveDirection( - lastMousePositions: { x: number; y: number }[], - currentPosition: { x: number; y: number }, - direction: ReflowDirection, - updateMousePosition: ({ x, y }: { x: number; y: number }) => void, -): ReflowDirection { - if (!lastMousePositions.length) { - updateMousePosition(currentPosition); - return direction; - } - - const averagePosition = getAverageMousePosition(lastMousePositions); - updateMousePosition(currentPosition); - const deltaX = currentPosition.x - averagePosition.x; - const deltaY = currentPosition.y - averagePosition.y; - - if (Math.abs(deltaY) > Math.abs(deltaX)) - return deltaY > 0 ? ReflowDirection.BOTTOM : ReflowDirection.TOP; - if (Math.abs(deltaX) > Math.abs(deltaY)) - return deltaX > 0 ? ReflowDirection.RIGHT : ReflowDirection.LEFT; - if ( - Math.abs(deltaX) === Math.abs(deltaY) && - direction === ReflowDirection.UNSET - ) { - /** - * If the direction is unset and mouse position is perfectly diagonal. - * Then set the direction vertically (random choice). - */ - return deltaY > 0 ? ReflowDirection.BOTTOM : ReflowDirection.TOP; - } - return direction; -} - -function getAverageMousePosition( - lastMousePositions: { x: number; y: number }[], -) { - const accumulatedPositions = lastMousePositions.reduce( - (acc, curr) => { - return { x: acc.x + curr.x, y: acc.y + curr.y }; - }, - { x: 0, y: 0 }, - ); - const averagePosition = { - x: accumulatedPositions.x / lastMousePositions.length, - y: accumulatedPositions.y / lastMousePositions.length, - }; - return averagePosition; -} diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index ecdd87b1d0c1..67a2471df9bb 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -1,6 +1,5 @@ import { ResponsiveBehavior } from "utils/autoLayout/constants"; import { useSelector } from "react-redux"; -import { ReflowDirection } from "reflow/reflowTypes"; import { getWidgets } from "sagas/selectors"; import { getCanvasWidth } from "selectors/editorSelectors"; import { getIsMobile } from "selectors/mainCanvasSelectors"; @@ -101,13 +100,9 @@ export const useAutoLayoutHighlights = ({ /** * Highlight a drop position based on mouse position and move direction. * @param e | MouseMoveEvent - * @param moveDirection | ReflowDirection * @returns HighlightInfo | undefined */ - const highlightDropPosition = ( - e: any, - moveDirection: ReflowDirection, - ): HighlightInfo | undefined => { + const highlightDropPosition = (e: any): HighlightInfo | undefined => { if (!highlights || !highlights.length) highlights = deriveHighlightsFromLayers( allWidgets, @@ -121,7 +116,6 @@ export const useAutoLayoutHighlights = ({ const highlight: HighlightInfo | undefined = getHighlightPayload( highlights, e, - moveDirection, ); if (!highlight) return; // console.log("#### selection", highlight); @@ -145,7 +139,6 @@ export const useAutoLayoutHighlights = ({ const payload: HighlightInfo | undefined = getHighlightPayload( highlights, null, - undefined, val, ); if (!payload) return; diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index d1f14f47522b..6c9413cf135c 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -26,7 +26,6 @@ import { } from "utils/WidgetPropsUtils"; import { getEdgeDirection, - getInterpolatedMoveDirection, getMoveDirection, getReflowedSpaces, modifyBlockDimension, @@ -194,8 +193,6 @@ export const useCanvasDragging = ( id: "", }; - let lastMousePositions: { x: number; y: number }[] = []; - const resetCanvasState = () => { throttledStopReflowing(); reflow.current?.resetReflow(); @@ -383,9 +380,6 @@ export const useCanvasDragging = ( ); rowRef.current = newRows ? newRows : rowRef.current; }; - const updateMousePosition = ({ x, y }: { x: number; y: number }) => { - lastMousePositions = [{ x, y }, ...lastMousePositions.slice(0, 4)]; - }; const onMouseMove = (e: any, firstMove = false) => { if (isDragging && canvasIsDragging && slidingArenaRef.current) { @@ -438,19 +432,9 @@ export const useCanvasDragging = ( triggerReflow(e, firstMove); if (useAutoLayout && isCurrentDraggedCanvas) { - currentDirection.current = getInterpolatedMoveDirection( - lastMousePositions, - { x: e.clientX, y: e.clientY }, - currentDirection.current, - updateMousePosition, - ); setTimeout(() => { - if (currentDirection.current !== ReflowDirection.UNSET) - selectedHighlight = highlightDropPosition( - e, - currentDirection.current, - ); - }, 100); + selectedHighlight = highlightDropPosition(e); + }, 50); } } isUpdatingRows = renderBlocks( diff --git a/app/client/src/utils/autoLayout/highlightSelectionUtils.ts b/app/client/src/utils/autoLayout/highlightSelectionUtils.ts index dcfe4cf7ecd2..5eca37bb030e 100644 --- a/app/client/src/utils/autoLayout/highlightSelectionUtils.ts +++ b/app/client/src/utils/autoLayout/highlightSelectionUtils.ts @@ -1,4 +1,3 @@ -import { ReflowDirection } from "reflow/reflowTypes"; import { HighlightInfo } from "./highlightUtils"; export interface Point { @@ -10,14 +9,12 @@ export interface Point { * Select the closest highlight to the mouse position (in the direction of the). * @param highlights | HighlightInfo[] : all highlights for the current canvas. * @param e | any : mouse event. - * @param moveDirection | ReflowDirection : direction of the drag. * @param val | Point : mouse coordinates. * @returns HighlightInfo | undefined */ export const getHighlightPayload = ( highlights: HighlightInfo[], e: any, - moveDirection?: ReflowDirection, val?: Point, ): HighlightInfo | undefined => { if (!highlights || !highlights.length) return; @@ -32,7 +29,7 @@ export const getHighlightPayload = ( * Filter highlights that span the current mouse position. */ let filteredHighlights: HighlightInfo[] = []; - filteredHighlights = getViableDropPositions(highlights, pos, moveDirection); + filteredHighlights = getViableDropPositions(highlights, pos); if (!filteredHighlights || !filteredHighlights?.length) return; // Sort filtered highlights in ascending order of distance from mouse position. @@ -56,9 +53,8 @@ export const getHighlightPayload = ( function getViableDropPositions( arr: HighlightInfo[], pos: Point, - moveDirection?: ReflowDirection, ): HighlightInfo[] { - if (!moveDirection || !arr) return arr || []; + if (!arr) return arr || []; const DEFAULT_DROP_RANGE = 10; const verticalHighlights = arr.filter( (highlight: HighlightInfo) => highlight.isVertical, @@ -81,8 +77,6 @@ function getViableDropPositions( selection.push(highlight); }); const hasVerticalSelection = selection.length > 0; - const dropArea = localStorage.getItem("horizontalHighlightDropArea"); - const zoneSize = dropArea ? parseFloat(dropArea) : 0; horizontalHighlights.forEach((highlight: HighlightInfo) => { if (pos.x >= highlight.posX && pos.x <= highlight.posX + highlight.width) if ( @@ -90,15 +84,13 @@ function getViableDropPositions( pos.y <= highlight.posY + (highlight.dropZone?.bottom !== undefined - ? highlight.dropZone?.bottom * - (hasVerticalSelection ? zoneSize : 1) + ? highlight.dropZone?.bottom * (hasVerticalSelection ? 0.2 : 1) : DEFAULT_DROP_RANGE)) || (pos.y < highlight.posY && pos.y >= highlight.posY - (highlight.dropZone?.top !== undefined - ? highlight.dropZone?.top * - (hasVerticalSelection ? zoneSize + 0.1 : 1) + ? highlight.dropZone?.top * (hasVerticalSelection ? 0.3 : 1) : DEFAULT_DROP_RANGE)) ) selection.push(highlight); diff --git a/app/client/src/utils/autoLayout/highlightUtils.ts b/app/client/src/utils/autoLayout/highlightUtils.ts index b9c6aec5f611..11a0d3cb821a 100644 --- a/app/client/src/utils/autoLayout/highlightUtils.ts +++ b/app/client/src/utils/autoLayout/highlightUtils.ts @@ -553,8 +553,7 @@ function updateVerticalHighlightDropZone( highlights: HighlightInfo[], canvasWidth: number, ): HighlightInfo[] { - const dropArea = localStorage.getItem("verticalHighlightDropArea"); - const zoneSize = dropArea !== null ? parseFloat(dropArea) : 0.35; + const zoneSize = 0.35; for (const [index, highlight] of highlights.entries()) { const nextHighlight: HighlightInfo | undefined = highlights[index + 1]; const previousHighlight: HighlightInfo | undefined = highlights[index - 1]; From 936c84a083cb113ba065f568792c48497584f6cf Mon Sep 17 00:00:00 2001 From: Preet Date: Fri, 27 Jan 2023 11:06:56 -0500 Subject: [PATCH 442/708] Revert "remove moveDirection usage" This reverts commit 999da099988df85e03b49e7030fef03ab9eea96d. --- .../Editor/WidgetsEditor/CanvasContainer.tsx | 4 ++ .../CanvasArenas/hooks/canvasDraggingUtils.ts | 57 +++++++++++++++++++ .../hooks/useAutoLayoutHighlights.ts | 9 ++- .../CanvasArenas/hooks/useCanvasDragging.ts | 20 ++++++- .../autoLayout/highlightSelectionUtils.ts | 16 ++++-- .../src/utils/autoLayout/highlightUtils.ts | 3 +- 6 files changed, 101 insertions(+), 8 deletions(-) diff --git a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx index 5e18ca8a9ff2..7a88932bda55 100644 --- a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx +++ b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx @@ -76,6 +76,10 @@ function CanvasContainer() { const isPageInitializing = isFetchingPage || !isLayoutingInitialized; useEffect(() => { + if (!localStorage.getItem("verticalHighlightDropArea")) + localStorage.setItem("verticalHighlightDropArea", "0.35"); + if (!localStorage.getItem("horizontalHighlightDropArea")) + localStorage.setItem("horizontalHighlightDropArea", "0.2"); return () => { dispatch(forceOpenWidgetPanel(false)); }; diff --git a/app/client/src/pages/common/CanvasArenas/hooks/canvasDraggingUtils.ts b/app/client/src/pages/common/CanvasArenas/hooks/canvasDraggingUtils.ts index 590669b388ab..749fa8a603fe 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/canvasDraggingUtils.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/canvasDraggingUtils.ts @@ -316,3 +316,60 @@ export const updateRectanglesPostReflow = ( return rectanglesToDraw; }; + +/** + * Get mouse move direction using an average of last five mouse positions. + * @param lastMousePositions | { x: number; y: number }[] : array of last five mouse positions. + * @param currentPosition | { x: number; y: number } : current mouse position. + * @param direction | ReflowDirection : current direction. + * @param updateMousePosition | ({ x, y }: { x: number; y: number }) => void : function to update mouse position. + * @returns ReflowDirection + */ +export function getInterpolatedMoveDirection( + lastMousePositions: { x: number; y: number }[], + currentPosition: { x: number; y: number }, + direction: ReflowDirection, + updateMousePosition: ({ x, y }: { x: number; y: number }) => void, +): ReflowDirection { + if (!lastMousePositions.length) { + updateMousePosition(currentPosition); + return direction; + } + + const averagePosition = getAverageMousePosition(lastMousePositions); + updateMousePosition(currentPosition); + const deltaX = currentPosition.x - averagePosition.x; + const deltaY = currentPosition.y - averagePosition.y; + + if (Math.abs(deltaY) > Math.abs(deltaX)) + return deltaY > 0 ? ReflowDirection.BOTTOM : ReflowDirection.TOP; + if (Math.abs(deltaX) > Math.abs(deltaY)) + return deltaX > 0 ? ReflowDirection.RIGHT : ReflowDirection.LEFT; + if ( + Math.abs(deltaX) === Math.abs(deltaY) && + direction === ReflowDirection.UNSET + ) { + /** + * If the direction is unset and mouse position is perfectly diagonal. + * Then set the direction vertically (random choice). + */ + return deltaY > 0 ? ReflowDirection.BOTTOM : ReflowDirection.TOP; + } + return direction; +} + +function getAverageMousePosition( + lastMousePositions: { x: number; y: number }[], +) { + const accumulatedPositions = lastMousePositions.reduce( + (acc, curr) => { + return { x: acc.x + curr.x, y: acc.y + curr.y }; + }, + { x: 0, y: 0 }, + ); + const averagePosition = { + x: accumulatedPositions.x / lastMousePositions.length, + y: accumulatedPositions.y / lastMousePositions.length, + }; + return averagePosition; +} diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index 67a2471df9bb..ecdd87b1d0c1 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -1,5 +1,6 @@ import { ResponsiveBehavior } from "utils/autoLayout/constants"; import { useSelector } from "react-redux"; +import { ReflowDirection } from "reflow/reflowTypes"; import { getWidgets } from "sagas/selectors"; import { getCanvasWidth } from "selectors/editorSelectors"; import { getIsMobile } from "selectors/mainCanvasSelectors"; @@ -100,9 +101,13 @@ export const useAutoLayoutHighlights = ({ /** * Highlight a drop position based on mouse position and move direction. * @param e | MouseMoveEvent + * @param moveDirection | ReflowDirection * @returns HighlightInfo | undefined */ - const highlightDropPosition = (e: any): HighlightInfo | undefined => { + const highlightDropPosition = ( + e: any, + moveDirection: ReflowDirection, + ): HighlightInfo | undefined => { if (!highlights || !highlights.length) highlights = deriveHighlightsFromLayers( allWidgets, @@ -116,6 +121,7 @@ export const useAutoLayoutHighlights = ({ const highlight: HighlightInfo | undefined = getHighlightPayload( highlights, e, + moveDirection, ); if (!highlight) return; // console.log("#### selection", highlight); @@ -139,6 +145,7 @@ export const useAutoLayoutHighlights = ({ const payload: HighlightInfo | undefined = getHighlightPayload( highlights, null, + undefined, val, ); if (!payload) return; diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index 6c9413cf135c..d1f14f47522b 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -26,6 +26,7 @@ import { } from "utils/WidgetPropsUtils"; import { getEdgeDirection, + getInterpolatedMoveDirection, getMoveDirection, getReflowedSpaces, modifyBlockDimension, @@ -193,6 +194,8 @@ export const useCanvasDragging = ( id: "", }; + let lastMousePositions: { x: number; y: number }[] = []; + const resetCanvasState = () => { throttledStopReflowing(); reflow.current?.resetReflow(); @@ -380,6 +383,9 @@ export const useCanvasDragging = ( ); rowRef.current = newRows ? newRows : rowRef.current; }; + const updateMousePosition = ({ x, y }: { x: number; y: number }) => { + lastMousePositions = [{ x, y }, ...lastMousePositions.slice(0, 4)]; + }; const onMouseMove = (e: any, firstMove = false) => { if (isDragging && canvasIsDragging && slidingArenaRef.current) { @@ -432,9 +438,19 @@ export const useCanvasDragging = ( triggerReflow(e, firstMove); if (useAutoLayout && isCurrentDraggedCanvas) { + currentDirection.current = getInterpolatedMoveDirection( + lastMousePositions, + { x: e.clientX, y: e.clientY }, + currentDirection.current, + updateMousePosition, + ); setTimeout(() => { - selectedHighlight = highlightDropPosition(e); - }, 50); + if (currentDirection.current !== ReflowDirection.UNSET) + selectedHighlight = highlightDropPosition( + e, + currentDirection.current, + ); + }, 100); } } isUpdatingRows = renderBlocks( diff --git a/app/client/src/utils/autoLayout/highlightSelectionUtils.ts b/app/client/src/utils/autoLayout/highlightSelectionUtils.ts index 5eca37bb030e..dcfe4cf7ecd2 100644 --- a/app/client/src/utils/autoLayout/highlightSelectionUtils.ts +++ b/app/client/src/utils/autoLayout/highlightSelectionUtils.ts @@ -1,3 +1,4 @@ +import { ReflowDirection } from "reflow/reflowTypes"; import { HighlightInfo } from "./highlightUtils"; export interface Point { @@ -9,12 +10,14 @@ export interface Point { * Select the closest highlight to the mouse position (in the direction of the). * @param highlights | HighlightInfo[] : all highlights for the current canvas. * @param e | any : mouse event. + * @param moveDirection | ReflowDirection : direction of the drag. * @param val | Point : mouse coordinates. * @returns HighlightInfo | undefined */ export const getHighlightPayload = ( highlights: HighlightInfo[], e: any, + moveDirection?: ReflowDirection, val?: Point, ): HighlightInfo | undefined => { if (!highlights || !highlights.length) return; @@ -29,7 +32,7 @@ export const getHighlightPayload = ( * Filter highlights that span the current mouse position. */ let filteredHighlights: HighlightInfo[] = []; - filteredHighlights = getViableDropPositions(highlights, pos); + filteredHighlights = getViableDropPositions(highlights, pos, moveDirection); if (!filteredHighlights || !filteredHighlights?.length) return; // Sort filtered highlights in ascending order of distance from mouse position. @@ -53,8 +56,9 @@ export const getHighlightPayload = ( function getViableDropPositions( arr: HighlightInfo[], pos: Point, + moveDirection?: ReflowDirection, ): HighlightInfo[] { - if (!arr) return arr || []; + if (!moveDirection || !arr) return arr || []; const DEFAULT_DROP_RANGE = 10; const verticalHighlights = arr.filter( (highlight: HighlightInfo) => highlight.isVertical, @@ -77,6 +81,8 @@ function getViableDropPositions( selection.push(highlight); }); const hasVerticalSelection = selection.length > 0; + const dropArea = localStorage.getItem("horizontalHighlightDropArea"); + const zoneSize = dropArea ? parseFloat(dropArea) : 0; horizontalHighlights.forEach((highlight: HighlightInfo) => { if (pos.x >= highlight.posX && pos.x <= highlight.posX + highlight.width) if ( @@ -84,13 +90,15 @@ function getViableDropPositions( pos.y <= highlight.posY + (highlight.dropZone?.bottom !== undefined - ? highlight.dropZone?.bottom * (hasVerticalSelection ? 0.2 : 1) + ? highlight.dropZone?.bottom * + (hasVerticalSelection ? zoneSize : 1) : DEFAULT_DROP_RANGE)) || (pos.y < highlight.posY && pos.y >= highlight.posY - (highlight.dropZone?.top !== undefined - ? highlight.dropZone?.top * (hasVerticalSelection ? 0.3 : 1) + ? highlight.dropZone?.top * + (hasVerticalSelection ? zoneSize + 0.1 : 1) : DEFAULT_DROP_RANGE)) ) selection.push(highlight); diff --git a/app/client/src/utils/autoLayout/highlightUtils.ts b/app/client/src/utils/autoLayout/highlightUtils.ts index 11a0d3cb821a..b9c6aec5f611 100644 --- a/app/client/src/utils/autoLayout/highlightUtils.ts +++ b/app/client/src/utils/autoLayout/highlightUtils.ts @@ -553,7 +553,8 @@ function updateVerticalHighlightDropZone( highlights: HighlightInfo[], canvasWidth: number, ): HighlightInfo[] { - const zoneSize = 0.35; + const dropArea = localStorage.getItem("verticalHighlightDropArea"); + const zoneSize = dropArea !== null ? parseFloat(dropArea) : 0.35; for (const [index, highlight] of highlights.entries()) { const nextHighlight: HighlightInfo | undefined = highlights[index + 1]; const previousHighlight: HighlightInfo | undefined = highlights[index - 1]; From 131c59f4bedbbac3aeb001d62df78922a82d103a Mon Sep 17 00:00:00 2001 From: Aswath K Date: Fri, 27 Jan 2023 21:45:08 +0530 Subject: [PATCH 443/708] expose autoLayoutConfig via WidgetFactory --- app/client/src/utils/WidgetFactory.tsx | 38 ++++++++++++++++++- .../src/utils/WidgetRegisterHelpers.tsx | 1 + .../src/widgets/CurrencyInputWidget/index.ts | 38 +++++++++---------- app/client/src/widgets/InputWidgetV2/index.ts | 38 +++++++++---------- .../src/widgets/PhoneInputWidget/index.ts | 38 +++++++++---------- app/client/src/widgets/constants.ts | 17 ++++++--- 6 files changed, 106 insertions(+), 64 deletions(-) diff --git a/app/client/src/utils/WidgetFactory.tsx b/app/client/src/utils/WidgetFactory.tsx index 64b41ac02d78..a2731e327a42 100644 --- a/app/client/src/utils/WidgetFactory.tsx +++ b/app/client/src/utils/WidgetFactory.tsx @@ -6,7 +6,7 @@ import { RenderMode } from "constants/WidgetConstants"; import { Stylesheet } from "entities/AppTheming"; import * as log from "loglevel"; import { WidgetConfigProps } from "reducers/entityReducers/widgetConfigReducer"; -import { CanvasWidgetStructure } from "widgets/constants"; +import { AutoLayoutConfig, CanvasWidgetStructure } from "widgets/constants"; import { addPropertyConfigIds, addSearchConfigToPanelConfig, @@ -16,6 +16,7 @@ import { PropertyPaneConfigTypes, } from "./WidgetFactoryHelpers"; import { WidgetFeatures } from "./WidgetFeatures"; +import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; type WidgetDerivedPropertyType = any; export type DerivedPropertiesMap = Record; @@ -73,6 +74,8 @@ class WidgetFactory { Record > = new Map(); + static autoLayoutConfigMap: Map = new Map(); + static registerWidgetBuilder( widgetType: string, widgetBuilder: WidgetBuilder, @@ -85,6 +88,7 @@ class WidgetFactory { features?: WidgetFeatures, loadingProperties?: Array, stylesheetConfig?: Stylesheet, + autoLayoutConfig?: AutoLayoutConfig, ) { if (!this.widgetTypes[widgetType]) { this.widgetTypes[widgetType] = widgetType; @@ -178,6 +182,26 @@ class WidgetFactory { WidgetFactory.getWidgetPropertyPaneStyleConfig(widgetType), ), ); + + autoLayoutConfig && + this.autoLayoutConfigMap.set(widgetType, { + ...autoLayoutConfig, + widgetSize: + autoLayoutConfig.widgetSize?.map((sizeConfig) => ({ + ...sizeConfig, + configuration: (props: WidgetProps) => { + if (!props) + return { + minWidth: + this.widgetConfigMap.get(widgetType)?.minWidth || + FILL_WIDGET_MIN_WIDTH, + minHeight: + this.widgetConfigMap.get(widgetType)?.minHeight || 80, + }; + return sizeConfig.configuration(props); + }, + })) || [], + }); } } @@ -308,6 +332,18 @@ class WidgetFactory { return map; } + static getWidgetAutoLayoutConfig(type: WidgetType): AutoLayoutConfig { + const map = this.autoLayoutConfigMap.get(type); + if (!map) { + return { + defaults: this.defaultPropertiesMap.get(type) || {}, + mobile: this.defaultPropertiesMap.get(type) || {}, + widgetSize: [], + }; + } + return map; + } + static getWidgetTypeConfigMap(): WidgetTypeConfigMap { const typeConfigMap: WidgetTypeConfigMap = {}; WidgetFactory.getWidgetTypes().forEach((type) => { diff --git a/app/client/src/utils/WidgetRegisterHelpers.tsx b/app/client/src/utils/WidgetRegisterHelpers.tsx index 72f262159c3d..89c15c815089 100644 --- a/app/client/src/utils/WidgetRegisterHelpers.tsx +++ b/app/client/src/utils/WidgetRegisterHelpers.tsx @@ -50,6 +50,7 @@ export const registerWidget = (Widget: any, config: WidgetConfiguration) => { config.features, config.properties.loadingProperties, config.properties.stylesheetConfig, + config.autoLayout, ); configureWidget(config); }; diff --git a/app/client/src/widgets/CurrencyInputWidget/index.ts b/app/client/src/widgets/CurrencyInputWidget/index.ts index 0124331f2a9e..68c91e4ec8f2 100644 --- a/app/client/src/widgets/CurrencyInputWidget/index.ts +++ b/app/client/src/widgets/CurrencyInputWidget/index.ts @@ -50,27 +50,27 @@ export const CONFIG = { mobile: { rows: 7, }, - }, - widgetSize: [ - { - viewportMinWidth: 0, - configuration: () => { - return { - minWidth: "60px", - minHeight: "70px", - }; + widgetSize: [ + { + viewportMinWidth: 0, + configuration: () => { + return { + minWidth: "60px", + minHeight: "70px", + }; + }, }, - }, - { - viewportMinWidth: layoutConfigurations.MOBILE.maxWidth, - configuration: () => { - return { - minWidth: "60px", - minHeight: "80px", - }; + { + viewportMinWidth: layoutConfigurations.MOBILE.maxWidth, + configuration: () => { + return { + minWidth: "60px", + minHeight: "80px", + }; + }, }, - }, - ], + ], + }, }; export default Widget; diff --git a/app/client/src/widgets/InputWidgetV2/index.ts b/app/client/src/widgets/InputWidgetV2/index.ts index 5bb80a859346..55bc074613cb 100644 --- a/app/client/src/widgets/InputWidgetV2/index.ts +++ b/app/client/src/widgets/InputWidgetV2/index.ts @@ -47,27 +47,27 @@ export const CONFIG = { mobile: { rows: 7, }, - }, - widgetSize: [ - { - viewportMinWidth: 0, - configuration: () => { - return { - minWidth: "60px", - minHeight: "70px", - }; + widgetSize: [ + { + viewportMinWidth: 0, + configuration: () => { + return { + minWidth: "60px", + minHeight: "70px", + }; + }, }, - }, - { - viewportMinWidth: layoutConfigurations.MOBILE.maxWidth, - configuration: () => { - return { - minWidth: "60px", - minHeight: "80px", - }; + { + viewportMinWidth: layoutConfigurations.MOBILE.maxWidth, + configuration: () => { + return { + minWidth: "60px", + minHeight: "80px", + }; + }, }, - }, - ], + ], + }, }; export default Widget; diff --git a/app/client/src/widgets/PhoneInputWidget/index.ts b/app/client/src/widgets/PhoneInputWidget/index.ts index 64b0cfbe733d..b52e9219bef3 100644 --- a/app/client/src/widgets/PhoneInputWidget/index.ts +++ b/app/client/src/widgets/PhoneInputWidget/index.ts @@ -49,27 +49,27 @@ export const CONFIG = { mobile: { rows: 7, }, - }, - widgetSize: [ - { - viewportMinWidth: 0, - configuration: () => { - return { - minWidth: "60px", - minHeight: "70px", - }; + widgetSize: [ + { + viewportMinWidth: 0, + configuration: () => { + return { + minWidth: "60px", + minHeight: "70px", + }; + }, }, - }, - { - viewportMinWidth: layoutConfigurations.MOBILE.maxWidth, - configuration: () => { - return { - minWidth: "60px", - minHeight: "80px", - }; + { + viewportMinWidth: layoutConfigurations.MOBILE.maxWidth, + configuration: () => { + return { + minWidth: "60px", + minHeight: "80px", + }; + }, }, - }, - ], + ], + }, }; export default Widget; diff --git a/app/client/src/widgets/constants.ts b/app/client/src/widgets/constants.ts index 7bceddd34385..c7f5a86eeedd 100644 --- a/app/client/src/widgets/constants.ts +++ b/app/client/src/widgets/constants.ts @@ -15,14 +15,19 @@ import { DerivedPropertiesMap } from "utils/WidgetFactory"; import { WidgetFeatures } from "utils/WidgetFeatures"; import { WidgetProps } from "./BaseWidget"; -interface AutoLayoutWidgetConfiguration { - defaults: { - rows?: number; - }; -} +type WidgetSizeConfig = { + viewportMinWidth: number; + configuration: (props: WidgetProps) => { [key: string]: string | number }; +}; + +export type AutoLayoutConfig = { + defaults: Record; + mobile: Record; + widgetSize: Array; +}; export interface WidgetConfiguration { - autoLayout?: AutoLayoutWidgetConfiguration; + autoLayout?: AutoLayoutConfig; type: string; name: string; iconSVG?: string; From da7fab17465ba3e4705cb554fa822a4831afab44 Mon Sep 17 00:00:00 2001 From: rahulramesha Date: Mon, 30 Jan 2023 19:18:26 +0530 Subject: [PATCH 444/708] Fixed to auto Conversion Algorithm --- app/client/src/actions/autoLayoutActions.ts | 10 + .../src/ce/constants/ReduxActionConstants.tsx | 1 + .../pages/Editor/AppPositionTypeControl.tsx | 23 +- .../src/sagas/AutoLayoutUpdateSagas.tsx | 96 ++- .../utils/DSLConversions/fixedToAutoLayout.ts | 678 ++++++++++++++++ .../tests/fixedToAutoLayout.test.ts | 724 ++++++++++++++++++ app/client/src/utils/WidgetFactory.tsx | 9 + app/client/src/utils/autoLayout/constants.ts | 1 + 8 files changed, 1520 insertions(+), 22 deletions(-) create mode 100644 app/client/src/utils/DSLConversions/fixedToAutoLayout.ts create mode 100644 app/client/src/utils/DSLConversions/tests/fixedToAutoLayout.test.ts diff --git a/app/client/src/actions/autoLayoutActions.ts b/app/client/src/actions/autoLayoutActions.ts index ebb39faecf0a..ba41e19dca92 100644 --- a/app/client/src/actions/autoLayoutActions.ts +++ b/app/client/src/actions/autoLayoutActions.ts @@ -1,4 +1,5 @@ import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants"; +import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; export const removeWrappersAction = (parentId: string) => ({ type: ReduxActionTypes.REMOVE_CHILD_WRAPPERS, @@ -24,3 +25,12 @@ export const updateLayoutForMobileBreakpointAction = ( canvasWidth, }, }); + +export const updateLayoutPositioning = ( + positioningType: AppPositioningTypes, +) => { + return { + type: ReduxActionTypes.UPDATE_LAYOUT_POSITIONING, + payload: positioningType, + }; +}; diff --git a/app/client/src/ce/constants/ReduxActionConstants.tsx b/app/client/src/ce/constants/ReduxActionConstants.tsx index 1aaf8b93ed0b..8458b9003e00 100644 --- a/app/client/src/ce/constants/ReduxActionConstants.tsx +++ b/app/client/src/ce/constants/ReduxActionConstants.tsx @@ -764,6 +764,7 @@ export const ReduxActionTypes = { ADD_CHILD_WRAPPERS: "ADD_CHILD_WRAPPERS", UPDATE_FILL_CHILD_LAYER: "UPDATE_FILL_CHILD_LAYER", RECALCULATE_COLUMNS: "RECALCULATE_COLUMNS", + UPDATE_LAYOUT_POSITIONING: "UPDATE_LAYOUT_POSITIONING", }; export type ReduxActionType = typeof ReduxActionTypes[keyof typeof ReduxActionTypes]; diff --git a/app/client/src/pages/Editor/AppPositionTypeControl.tsx b/app/client/src/pages/Editor/AppPositionTypeControl.tsx index 2851018f0228..d82b37bdf805 100644 --- a/app/client/src/pages/Editor/AppPositionTypeControl.tsx +++ b/app/client/src/pages/Editor/AppPositionTypeControl.tsx @@ -3,9 +3,7 @@ import React, { useEffect, useMemo } from "react"; import { useDispatch, useSelector } from "react-redux"; import styled from "styled-components"; -import { batchUpdateMultipleWidgetProperties } from "actions/controlActions"; import { Colors } from "constants/Colors"; -import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; import { Icon, IconName, IconSize, TooltipComponent } from "design-system-old"; import { AppPositioningTypeConfig, @@ -15,8 +13,8 @@ import { getCurrentAppPositioningType, isAutoLayoutEnabled, } from "selectors/editorSelectors"; -import { LayoutDirection, Positioning } from "utils/autoLayout/constants"; import { MainContainerLayoutControl } from "./MainContainerLayoutControl"; +import { updateLayoutPositioning } from "actions/autoLayoutActions"; interface ApplicationPositionTypeConfigOption { name: string; @@ -75,24 +73,7 @@ export function AppPositionTypeControl() { const updateAppPositioningLayout = ( layoutOption: ApplicationPositionTypeConfigOption, ) => { - const selectedType = - layoutOption.type !== AppPositioningTypes.AUTO - ? Positioning.Fixed - : Positioning.Vertical; - dispatch( - batchUpdateMultipleWidgetProperties([ - { - widgetId: MAIN_CONTAINER_WIDGET_ID, - updates: { - modify: { - positioning: selectedType, - useAutoLayout: selectedType !== Positioning.Fixed, - direction: LayoutDirection.Vertical, - }, - }, - }, - ]), - ); + dispatch(updateLayoutPositioning(layoutOption.type)); }; const handleKeyDown = (event: React.KeyboardEvent, index: number) => { diff --git a/app/client/src/sagas/AutoLayoutUpdateSagas.tsx b/app/client/src/sagas/AutoLayoutUpdateSagas.tsx index 42fa64c56c16..75515be5128c 100644 --- a/app/client/src/sagas/AutoLayoutUpdateSagas.tsx +++ b/app/client/src/sagas/AutoLayoutUpdateSagas.tsx @@ -4,7 +4,11 @@ import { ReduxActionErrorTypes, ReduxActionTypes, } from "ce/constants/ReduxActionConstants"; -import { ResponsiveBehavior } from "utils/autoLayout/constants"; +import { + LayoutDirection, + Positioning, + ResponsiveBehavior, +} from "utils/autoLayout/constants"; import log from "loglevel"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; import { all, call, put, select, takeLatest } from "redux-saga/effects"; @@ -17,6 +21,17 @@ import { wrapChildren, } from "../utils/autoLayout/AutoLayoutUtils"; import { getWidgets } from "./selectors"; +import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; +import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; +import { batchUpdateMultipleWidgetProperties } from "actions/controlActions"; +import { + getCurrentAppPositioningType, + getMainCanvasProps, +} from "selectors/editorSelectors"; +import { MainCanvasReduxState } from "reducers/uiReducers/mainCanvasReducer"; +import { updateLayoutForMobileBreakpointAction } from "actions/autoLayoutActions"; +import CanvasWidgetsNormalizer from "normalizers/CanvasWidgetsNormalizer"; +import convertDSLtoAuto from "utils/DSLConversions/fixedToAutoLayout"; type LayoutUpdatePayload = { parentId: string; @@ -130,6 +145,81 @@ export function* updateLayoutForMobileCheckpoint( } } +export function* updateLayoutPositioningSaga( + actionPayload: ReduxAction, +) { + try { + const payloadPositioningType = actionPayload.payload; + + if (payloadPositioningType === AppPositioningTypes.AUTO) { + const currPositioningType: AppPositioningTypes = yield select( + getCurrentAppPositioningType, + ); + + if (currPositioningType === AppPositioningTypes.AUTO) return; + + const allWidgets: CanvasWidgetsReduxState = yield select(getWidgets); + + const denormalizedDSL = CanvasWidgetsNormalizer.denormalize( + MAIN_CONTAINER_WIDGET_ID, + { canvasWidgets: allWidgets }, + ); + + const autoDSL = convertDSLtoAuto(denormalizedDSL); + log.debug("autoDSL", autoDSL); + + yield put( + updateAndSaveLayout( + CanvasWidgetsNormalizer.normalize(autoDSL).entities.canvasWidgets, + ), + ); + + yield call(recalculateOnPageLoad); + } else { + yield put( + batchUpdateMultipleWidgetProperties([ + { + widgetId: MAIN_CONTAINER_WIDGET_ID, + updates: { + modify: { + positioning: Positioning.Fixed, + useAutoLayout: false, + direction: LayoutDirection.Vertical, + }, + }, + }, + ]), + ); + } + } catch (error) { + yield put({ + type: ReduxActionErrorTypes.WIDGET_OPERATION_ERROR, + payload: { + action: ReduxActionTypes.RECALCULATE_COLUMNS, + error, + }, + }); + } +} + +export function* recalculateOnPageLoad() { + const appPositioningType: AppPositioningTypes = yield select( + getCurrentAppPositioningType, + ); + const mainCanvasProps: MainCanvasReduxState = yield select( + getMainCanvasProps, + ); + + yield put( + updateLayoutForMobileBreakpointAction( + MAIN_CONTAINER_WIDGET_ID, + appPositioningType === AppPositioningTypes.AUTO + ? mainCanvasProps?.isMobile + : false, + mainCanvasProps.width, + ), + ); +} export default function* layoutUpdateSagas() { yield all([ takeLatest(ReduxActionTypes.ADD_CHILD_WRAPPERS, addChildWrappers), @@ -139,5 +229,9 @@ export default function* layoutUpdateSagas() { ReduxActionTypes.RECALCULATE_COLUMNS, updateLayoutForMobileCheckpoint, ), + takeLatest( + ReduxActionTypes.UPDATE_LAYOUT_POSITIONING, + updateLayoutPositioningSaga, + ), ]); } diff --git a/app/client/src/utils/DSLConversions/fixedToAutoLayout.ts b/app/client/src/utils/DSLConversions/fixedToAutoLayout.ts new file mode 100644 index 000000000000..d5e1731101c2 --- /dev/null +++ b/app/client/src/utils/DSLConversions/fixedToAutoLayout.ts @@ -0,0 +1,678 @@ +import { FlexLayer } from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; +import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; +import { GridDefaults } from "constants/WidgetConstants"; +import { partition } from "lodash"; +import { + FlexLayerAlignment, + FlexVerticalAlignment, + Positioning, + ResponsiveBehavior, +} from "utils/autoLayout/constants"; +import WidgetFactory from "utils/WidgetFactory"; +import { DSLWidget } from "widgets/constants"; + +const unHandledWidgets = ["LIST_WIDGET", "FORM_WIDGET", "MODAL_WIDGET"]; +const nonFlexLayerWidgets = ["MODAL_WIDGET"]; + +/** + * + * @param dsl DSL to be Converted + * @returns dsl in an AutoLayout dsl format + */ +export default function convertDSLtoAuto(dsl: DSLWidget) { + if (!dsl || !dsl.children || dsl.children.length < 1) return dsl; + + if (dsl.type === "CANVAS_WIDGET") { + return { ...getAutoCanvasWidget(dsl) }; + } + + const currDSL: DSLWidget = { ...dsl, children: [] }; + + for (const child of dsl.children || []) { + if (child.type === "CANVAS_WIDGET") { + currDSL.children?.push(getAutoCanvasWidget(child)); + } else { + currDSL.children?.push(convertDSLtoAuto(child)); + } + } + + return currDSL; +} + +/** + * This is specifically for Auto widget + * @param dsl + * @returns auto layout converted Auto Widget + */ +export function getAutoCanvasWidget(dsl: DSLWidget): DSLWidget { + const { + calculatedBottomRow, + children, + flexLayers, + } = fitChildWidgetsIntoLayers(dsl.children); + + const bottomRow = calculatedBottomRow + ? calculatedBottomRow * GridDefaults.DEFAULT_GRID_ROW_HEIGHT + : dsl.bottomRow; + const minHeight = calculatedBottomRow + ? calculatedBottomRow * GridDefaults.DEFAULT_GRID_ROW_HEIGHT + : dsl.minHeight; + + // Add responsive propertied to the Canvas Widget props + return { + ...dsl, + minHeight, + bottomRow, + children, + flexLayers, + useAutoLayout: true, + responsiveBehavior: ResponsiveBehavior.Fill, + positioning: Positioning.Vertical, + }; +} + +/** + * This method fits Children widgets into respective cells and layers + * @param widgets + * @returns modified Children, FlexLayers and new bottom most row of the Canvas + */ +export function fitChildWidgetsIntoLayers( + widgets: DSLWidget[] | undefined, +): { + children: DSLWidget[]; + flexLayers: FlexLayer[]; + calculatedBottomRow?: number; +} { + const flexLayers: FlexLayer[] = []; + + if (!widgets || widgets.length < 1) { + return { children: [], flexLayers }; + } + + //separate ot widgets to be skipped + const [nonLayerWidgets, currWidgets] = partition( + widgets, + (widget) => nonFlexLayerWidgets.indexOf(widget.type) > -1, + ); + + //Sort Widgets from top to bottom + currWidgets.sort((a, b) => { + if (a.topRow === b.topRow) { + return a.leftColumn - b.leftColumn; + } + + return a.topRow - b.topRow; + }); + + let modifiedWidgets: DSLWidget[] = []; + let widgetsLeft = [...currWidgets]; + let childrenHeight = 0; + //Iterate till widgets are left in the Children array + while (widgetsLeft.length > 0) { + const { + flexLayer, + layerHeight, + leftOverWidgets, + widgetsInLayer, + } = getNextLayer(widgetsLeft); + widgetsLeft = [...leftOverWidgets]; + modifiedWidgets = modifiedWidgets.concat(widgetsInLayer); + flexLayers.push(flexLayer); + + childrenHeight += layerHeight; + } + + //Add unhandled widgets to children + for (const nonLayerWidget of nonLayerWidgets) { + modifiedWidgets.push( + unHandledWidgets.indexOf(nonLayerWidget.type) < 0 + ? convertDSLtoAuto(nonLayerWidget) + : { ...nonLayerWidget, positioning: Positioning.Fixed }, + ); + } + + return { + children: modifiedWidgets, + flexLayers, + calculatedBottomRow: childrenHeight + GridDefaults.CANVAS_EXTENSION_OFFSET, + }; +} + +/** + * get next layer of widgets of all the widgets supplied and return left Over Widgets + * @param currWidgets + * @returns + */ +function getNextLayer( + currWidgets: DSLWidget[], +): { + flexLayer: FlexLayer; + widgetsInLayer: DSLWidget[]; + leftOverWidgets: DSLWidget[]; + layerHeight: number; +} { + const currentLayerChildren = []; + + const { index, topLeftMostWidget } = getTopLeftMostWidget(currWidgets); + + const { + alignmentMap, + leftOverWidgets, + maxBottomRow, + minTopRow, + widgetsInLayer, + } = getWidgetsInLayer(topLeftMostWidget, index, currWidgets); + + const modifiedWidgetsInLayer = []; + let alignment = FlexLayerAlignment.None; + + //Recursively call convertDSLtoAuto to convert Children Widgets + for (const widget of widgetsInLayer) { + const currWidget = + unHandledWidgets.indexOf(widget.type) < 0 + ? convertDSLtoAuto(widget) + : { ...widget, positioning: Positioning.Fixed }; + const widgetConfig = WidgetFactory.getWidgetConfigMap(currWidget.type); + //get Responsive Behaviour + const responsiveBehavior = + (widgetConfig.responsiveBehavior as ResponsiveBehavior) || + ResponsiveBehavior.Hug; + + //get minWidth of the type + currWidget.minWidth = widgetConfig.minWidth || FILL_WIDGET_MIN_WIDTH; + + //Get Alignment of the Widget + alignment = alignmentMap[currWidget.widgetId] || FlexLayerAlignment.Start; + const flexVerticalAlignment = getWidgetVerticalAlignment( + currWidget, + minTopRow, + maxBottomRow, + ); + + modifiedWidgetsInLayer.push({ + ...currWidget, + responsiveBehavior, + alignment, + flexVerticalAlignment, + }); + + //If the widget type is not to be added in layer then add only to Children + if (nonFlexLayerWidgets.indexOf(currWidget.type) < 0) { + currentLayerChildren.push({ + id: currWidget.widgetId, + align: alignment, + }); + } + } + + const flexLayer = { children: currentLayerChildren }; + return { + flexLayer, + widgetsInLayer: modifiedWidgetsInLayer, + leftOverWidgets, + layerHeight: maxBottomRow - minTopRow, + }; +} + +/** + * This method returns the left most widget of the top layer among the left over widgets + * @param widgets + * @returns top left most widgets and index of it in the array + */ +export function getTopLeftMostWidget(widgets: DSLWidget[]) { + const topMostWidget = widgets[0]; + + let modifiedTopMostWidget: DSLWidget = { + ...topMostWidget, + leftColumn: 0, + }; + + let topLeftMostWidget: DSLWidget = { ...topMostWidget }; + let index = 0; + for (let i = 0; i < widgets.length; i++) { + const currWidget = widgets[i]; + if (currWidget.topRow >= modifiedTopMostWidget.bottomRow) break; + if ( + currWidget.widgetId === modifiedTopMostWidget.widgetId && + !areWidgetsOverlapping(currWidget, modifiedTopMostWidget) + ) + continue; + + if ( + currWidget.leftColumn <= topLeftMostWidget.leftColumn && + currWidget.topRow < topLeftMostWidget.bottomRow + ) { + topLeftMostWidget = { ...currWidget }; + modifiedTopMostWidget = { + ...currWidget, + leftColumn: 0, + }; + index = i; + } + } + return { topLeftMostWidget, index }; +} + +/** + * This method generates the widgets in the layer + * @param topLeftMostWidget + * @param index + * @param currWidgets + * @returns widgetsInLayer, leftOverWidgets, alignment of all widgets in the layer + */ +function getWidgetsInLayer( + topLeftMostWidget: DSLWidget, + index: number, + currWidgets: DSLWidget[], +): { + widgetsInLayer: DSLWidget[]; + leftOverWidgets: DSLWidget[]; + maxBottomRow: number; + minTopRow: number; + alignmentMap: { [key: string]: FlexLayerAlignment }; +} { + const widgetsInLayer = [topLeftMostWidget]; + const leftOverWidgets = [...currWidgets]; + + leftOverWidgets.splice(index, 1); + + //This is the widget against other widgets are checked against + let currCheckWidget = { + ...topLeftMostWidget, + leftColumn: topLeftMostWidget.rightColumn, + rightColumn: GridDefaults.DEFAULT_GRID_COLUMNS, + }; + + let maxBottomRow = currCheckWidget.bottomRow; + let minTopRow = currCheckWidget.topRow; + + let prevWidgetDistance = topLeftMostWidget.rightColumn; + // current Group to group widgets, if the distance between them is greater than + // 10% of the total width of canvas + let currentGroup = { + widgets: [topLeftMostWidget.widgetId], + leftColumn: topLeftMostWidget.leftColumn, + rightColumn: topLeftMostWidget.rightColumn, + }; + const groupedWidgets = []; + + if (leftOverWidgets.length === 0) { + groupedWidgets.push(currentGroup); + } + + while (leftOverWidgets.length > 0) { + const { currIndex, nextWidgetInLayer } = getNextWidgetInLayer( + leftOverWidgets, + maxBottomRow, + currCheckWidget, + ); + + //add current group to widget groups + if (!nextWidgetInLayer) { + groupedWidgets.push(currentGroup); + break; + } + + widgetsInLayer.push(nextWidgetInLayer); + + //If space between widgets is greater than 10% add current group to array of groups + // or add widget to the current Group + if ( + (nextWidgetInLayer.leftColumn - prevWidgetDistance) / + GridDefaults.DEFAULT_GRID_COLUMNS >= + 0.1 + ) { + groupedWidgets.push(currentGroup); + currentGroup = { + widgets: [nextWidgetInLayer.widgetId], + leftColumn: nextWidgetInLayer.leftColumn, + rightColumn: nextWidgetInLayer.rightColumn, + }; + } else { + currentGroup.widgets.push(nextWidgetInLayer.widgetId); + currentGroup.rightColumn = nextWidgetInLayer.rightColumn; + } + + prevWidgetDistance = nextWidgetInLayer.rightColumn; + + if (currIndex !== undefined) { + leftOverWidgets.splice(currIndex, 1); + } + maxBottomRow = Math.max(maxBottomRow, nextWidgetInLayer.bottomRow); + minTopRow = Math.min(minTopRow, nextWidgetInLayer.topRow); + + currCheckWidget = { + ...nextWidgetInLayer, + leftColumn: nextWidgetInLayer.rightColumn, + rightColumn: GridDefaults.DEFAULT_GRID_COLUMNS, + }; + + if (leftOverWidgets.length === 0) { + groupedWidgets.push(currentGroup); + } + } + + const alignmentMap = processGroupedWidgets(groupedWidgets); + return { + widgetsInLayer, + leftOverWidgets, + maxBottomRow, + minTopRow, + alignmentMap, + }; +} + +/** + * This method gets the next widget in layer and also the index of it in the Array + * @param leftOverWidgets + * @param maxBottomRow + * @param currCheckWidget + * @returns + */ +function getNextWidgetInLayer( + leftOverWidgets: DSLWidget[], + maxBottomRow: number, + currCheckWidget: DSLWidget, +) { + let nextWidgetInLayer: DSLWidget | undefined, currIndex; + for (let i = 0; i < leftOverWidgets.length; i++) { + const currWidget = leftOverWidgets[i]; + if (currWidget.topRow >= maxBottomRow) break; + if (!areWidgetsOverlapping(currWidget, currCheckWidget)) continue; + + if ( + !nextWidgetInLayer || + (currWidget.leftColumn < nextWidgetInLayer.leftColumn && + currWidget.topRow <= nextWidgetInLayer.bottomRow) + ) { + nextWidgetInLayer = { ...currWidget }; + currIndex = i; + } + } + + return { currIndex, nextWidgetInLayer }; +} + +/** + * This method scores the alignment of the widget with total width, + * This has a range of -1 to 1, -1 being Start, 0 being center, 1 being end. + * The value varies in a logarithmic curve in such a way that + * closer to the edges the value increases substantially rather than minutely around the center + * This also takes into account the width of the widget itself to give an accurate score for widgets of various sizes + * @param widgetMin + * @param widgetMax + * @param totalMin + * @param totalMax + * @returns number + */ +export function getAlignmentScore( + widgetMin: number, + widgetMax: number, + totalMin: number, + totalMax: number, +) { + const width = widgetMax - widgetMin; + const totalWidth = totalMax - totalMin; + + if (width === totalWidth) return -1; + + const orientationScore = + (totalMax - widgetMax - (widgetMin - totalMin)) / totalWidth; + + const tempWidgetMin = totalMin, + tempWidgetMax = totalMin + width; + const maxScore = + (totalMax - tempWidgetMax - (tempWidgetMin - totalMin)) / totalWidth; + + const directionalIndicator = orientationScore < 0 ? 1 : -1; + + const score = parseFloat( + ( + (directionalIndicator * orientationScore * orientationScore) / + (maxScore * maxScore) + ).toFixed(2), + ); + + return score === undefined || score === NaN ? -1 : score; +} + +/** + * This Method takes in groups of widgets and return alignments of all the widgets. + * @param groupedWidgets + * @returns alignments of individual widget + */ +export function processGroupedWidgets( + groupedWidgets: { + widgets: string[]; + leftColumn: number; + rightColumn: number; + }[], +) { + let condensedGroupedWidgets = [...groupedWidgets]; + if (groupedWidgets.length > 3) { + condensedGroupedWidgets = getCondensedGroupedWidgets(groupedWidgets); + } + + let widgetAlignments: { [key: string]: FlexLayerAlignment } = {}; + + switch (condensedGroupedWidgets.length) { + //Check the alignment of the group and assign the value to all the widgets + case 1: + const alignmentScore = getAlignmentScore( + condensedGroupedWidgets[0].leftColumn, + condensedGroupedWidgets[0].rightColumn, + 0, + GridDefaults.DEFAULT_GRID_COLUMNS, + ); + const alignment = getLayerAlignmentBasedOnScore(alignmentScore); + widgetAlignments = createAlignmentMapFromGroupedWidgets( + condensedGroupedWidgets[0], + alignment, + ); + break; + //same as previous case + case 2: + const alignmentScore1 = getAlignmentScore( + condensedGroupedWidgets[0].leftColumn, + condensedGroupedWidgets[0].rightColumn, + 0, + GridDefaults.DEFAULT_GRID_COLUMNS, + ); + const alignment1 = getLayerAlignmentBasedOnScore(alignmentScore1); + + const alignmentScore2 = getAlignmentScore( + condensedGroupedWidgets[1].leftColumn, + condensedGroupedWidgets[1].rightColumn, + 0, + GridDefaults.DEFAULT_GRID_COLUMNS, + ); + const alignment2 = getLayerAlignmentBasedOnScore(alignmentScore2); + widgetAlignments = createAlignmentMapFromGroupedWidgets( + condensedGroupedWidgets[0], + alignment2, + ); + + widgetAlignments = { + ...createAlignmentMapFromGroupedWidgets( + condensedGroupedWidgets[0], + alignment1, + ), + ...createAlignmentMapFromGroupedWidgets( + condensedGroupedWidgets[1], + alignment2, + ), + }; + break; + //If there are three distinct groups, they can be assigned to distinct alignments + case 3: + widgetAlignments = { + ...createAlignmentMapFromGroupedWidgets( + condensedGroupedWidgets[0], + FlexLayerAlignment.Start, + ), + ...createAlignmentMapFromGroupedWidgets( + condensedGroupedWidgets[1], + FlexLayerAlignment.Center, + ), + ...createAlignmentMapFromGroupedWidgets( + condensedGroupedWidgets[2], + FlexLayerAlignment.End, + ), + }; + break; + } + + return widgetAlignments; +} + +/** + * If there are more than 3 distinct groups, they will be condensed into 3 different groups + * @param groupedWidgets + * @returns + */ +export function getCondensedGroupedWidgets( + groupedWidgets: { + widgets: string[]; + leftColumn: number; + rightColumn: number; + }[], +) { + if (groupedWidgets.length <= 3) return groupedWidgets; + + let gapsArray = []; + for (let i = 1; i < groupedWidgets.length; i++) { + gapsArray.push({ + gap: groupedWidgets[i].leftColumn - groupedWidgets[i - 1].rightColumn, + index: i - 1, + }); + } + + gapsArray = gapsArray + .sort( + ( + a: { gap: number; index: number }, + b: { gap: number; index: number }, + ) => { + return b.gap - a.gap; + }, + ) + .slice(0, 2) + .sort( + ( + a: { gap: number; index: number }, + b: { gap: number; index: number }, + ) => { + return a.index - b.index; + }, + ); + + const condensedGroupedWidgets = []; + + let count = 0; + let gapIndex = gapsArray[count].index; + + let tempWidgetIds: any[] = [], + groupLeftColumn = groupedWidgets[0].leftColumn, + groupRightColumn = groupedWidgets[0].rightColumn; + + for (let i = 0; i < groupedWidgets.length; i++) { + if (i < gapIndex) { + tempWidgetIds = tempWidgetIds.concat(groupedWidgets[i].widgets); + groupLeftColumn = + tempWidgetIds.length > 0 + ? groupLeftColumn + : groupedWidgets[i].leftColumn; + groupRightColumn = groupedWidgets[i].rightColumn; + } + + if (i === gapIndex) { + tempWidgetIds = tempWidgetIds.concat(groupedWidgets[i].widgets); + groupLeftColumn = + tempWidgetIds.length > 0 + ? groupLeftColumn + : groupedWidgets[i].leftColumn; + groupRightColumn = groupedWidgets[i].rightColumn; + + condensedGroupedWidgets.push({ + widgets: tempWidgetIds, + leftColumn: groupLeftColumn, + rightColumn: groupRightColumn, + }); + + if (count === 0) { + count = 1; + gapIndex = gapsArray[count].index; + } else { + gapIndex = groupedWidgets.length - 1; + } + + tempWidgetIds = []; + groupRightColumn = groupedWidgets[i].rightColumn; + } + } + + return condensedGroupedWidgets; +} + +/** + * Method to get Alignment based on score + * @param alignmentScore + * @returns + */ +function getLayerAlignmentBasedOnScore(alignmentScore: number) { + if (alignmentScore > 0.4) return FlexLayerAlignment.End; + else if (alignmentScore < -0.4) return FlexLayerAlignment.Start; + else return FlexLayerAlignment.Center; +} + +/** + * Create Alignment map of widgets + * @param groupedWidget + * @param alignment + * @returns + */ +function createAlignmentMapFromGroupedWidgets( + groupedWidget: { widgets: string[]; leftColumn: number; rightColumn: number }, + alignment: FlexLayerAlignment, +) { + const alignmentMap: { [key: string]: FlexLayerAlignment } = {}; + + for (const widgetId of groupedWidget.widgets) { + alignmentMap[widgetId] = alignment; + } + + return alignmentMap; +} + +/** + * Method to get Vertical Alignment based on score + * @param widget + * @param minTopRow + * @param maxBottomRow + * @returns + */ +function getWidgetVerticalAlignment( + widget: DSLWidget, + minTopRow: number, + maxBottomRow: number, +): FlexVerticalAlignment { + const alignmentScore = getAlignmentScore( + widget.topRow, + widget.bottomRow, + minTopRow, + maxBottomRow, + ); + + if (alignmentScore < -0.3) return FlexVerticalAlignment.Top; + else if (alignmentScore > 0.3) return FlexVerticalAlignment.Bottom; + else return FlexVerticalAlignment.Center; +} + +function areWidgetsOverlapping(r1: DSLWidget, r2: DSLWidget) { + return !( + r2.leftColumn >= r1.rightColumn || + r2.rightColumn <= r1.leftColumn || + r2.topRow >= r1.bottomRow || + r2.bottomRow <= r1.topRow + ); +} diff --git a/app/client/src/utils/DSLConversions/tests/fixedToAutoLayout.test.ts b/app/client/src/utils/DSLConversions/tests/fixedToAutoLayout.test.ts new file mode 100644 index 000000000000..ca99378b9663 --- /dev/null +++ b/app/client/src/utils/DSLConversions/tests/fixedToAutoLayout.test.ts @@ -0,0 +1,724 @@ +import { Positioning, ResponsiveBehavior } from "utils/autoLayout/constants"; +import { DSLWidget } from "widgets/constants"; +import { + fitChildWidgetsIntoLayers, + getAutoCanvasWidget, + getCondensedGroupedWidgets, + getTopLeftMostWidget, + processGroupedWidgets, +} from "../fixedToAutoLayout"; + +describe("test fixed to Auto Conversion methods", () => { + const childWidgets = ([ + { + boxShadow: "none", + widgetName: "Button1", + topRow: 0.0, + bottomRow: 4.0, + type: "BUTTON_WIDGET", + leftColumn: 0.0, + rightColumn: 16.0, + widgetId: "oc0e52x3mq", + }, + { + widgetName: "Button3", + + topRow: 0.0, + bottomRow: 4.0, + type: "BUTTON_WIDGET", + leftColumn: 48.0, + rightColumn: 64.0, + widgetId: "em4ubqs787", + }, + { + widgetName: "Input1", + topRow: 2.0, + bottomRow: 9.0, + type: "INPUT_WIDGET_V2", + leftColumn: 23.0, + rightColumn: 43.0, + widgetId: "50rdpq2yow", + }, + { + widgetName: "Form1", + topRow: 11.0, + bottomRow: 50.0, + type: "FORM_WIDGET", + leftColumn: 2.0, + children: [ + { + widgetName: "Canvas1", + topRow: 0.0, + bottomRow: 390.0, + type: "CANVAS_WIDGET", + minHeight: 390.0, + leftColumn: 0.0, + children: [ + { + widgetName: "Text1", + topRow: 1.0, + bottomRow: 5.0, + type: "TEXT_WIDGET", + leftColumn: 1.5, + rightColumn: 25.5, + widgetId: "y1u22x7gj9", + parentId: "lwl0t7o358", + }, + { + widgetName: "Button4", + topRow: 33.0, + bottomRow: 37.0, + type: "BUTTON_WIDGET", + leftColumn: 46.0, + rightColumn: 62.0, + widgetId: "7dnsyyas3b", + parentId: "lwl0t7o358", + }, + { + widgetName: "Button5", + topRow: 33.0, + bottomRow: 37.0, + type: "BUTTON_WIDGET", + leftColumn: 30.0, + rightColumn: 46.0, + parentId: "lwl0t7o358", + }, + ], + widgetId: "lwl0t7o358", + parentId: "quvrkp960y", + }, + ], + rightColumn: 26.0, + widgetId: "quvrkp960y", + parentId: "0", + }, + { + widgetName: "Button6", + topRow: 26.0, + bottomRow: 30.0, + parentRowSpace: 10.0, + type: "BUTTON_WIDGET", + leftColumn: 30.0, + rightColumn: 46.0, + widgetId: "e9fhrq8uvf", + parentId: "0", + }, + { + widgetName: "Modal1", + topRow: 16.0, + bottomRow: 256.0, + parentRowSpace: 10.0, + type: "MODAL_WIDGET", + leftColumn: 9.0, + children: [], + rightColumn: 33.0, + widgetId: "tf847brtfd", + parentId: "0", + }, + { + widgetName: "Container1", + topRow: 36.0, + bottomRow: 61.0, + type: "CONTAINER_WIDGET", + leftColumn: 38.0, + children: [ + { + widgetName: "Canvas3", + topRow: 0.0, + bottomRow: 250.0, + type: "CANVAS_WIDGET", + minHeight: 250.0, + leftColumn: 0.0, + children: [ + { + widgetName: "Container2Copy", + topRow: 0.0, + bottomRow: 13.0, + type: "CONTAINER_WIDGET", + leftColumn: 36.0, + children: [ + { + widgetName: "Canvas4Copy", + topRow: 0.0, + bottomRow: 130.0, + type: "CANVAS_WIDGET", + minHeight: 130.0, + leftColumn: 0.0, + children: [ + { + widgetName: "Button9Copy", + topRow: 0.0, + bottomRow: 4.0, + type: "BUTTON_WIDGET", + leftColumn: 2.0, + rightColumn: 18.0, + widgetId: "rbgq3cl9j0", + parentId: "vap4aivehm", + }, + { + widgetName: "Button10Copy", + topRow: 7.0, + bottomRow: 11.0, + type: "BUTTON_WIDGET", + leftColumn: 33.0, + rightColumn: 49.0, + widgetId: "to5e5cr2ph", + parentId: "vap4aivehm", + }, + ], + rightColumn: 105.5625, + widgetId: "vap4aivehm", + parentId: "pd2zln825w", + }, + ], + rightColumn: 60.0, + widgetId: "pd2zln825w", + parentId: "b6wgydyko8", + renderMode: "CANVAS", + }, + { + widgetName: "Container2", + topRow: 10.0, + bottomRow: 23.0, + type: "CONTAINER_WIDGET", + leftColumn: 0.0, + children: [ + { + widgetName: "Canvas4", + topRow: 0.0, + bottomRow: 130.0, + type: "CANVAS_WIDGET", + minHeight: 130.0, + leftColumn: 0.0, + children: [ + { + widgetName: "Button9", + topRow: 0.0, + bottomRow: 4.0, + type: "BUTTON_WIDGET", + leftColumn: 2.0, + rightColumn: 18.0, + widgetId: "o529pnktws", + parentId: "se4m3djd2t", + }, + { + widgetName: "Button10", + topRow: 7.0, + bottomRow: 11.0, + type: "BUTTON_WIDGET", + leftColumn: 33.0, + rightColumn: 49.0, + widgetId: "vf1wsdypci", + parentId: "se4m3djd2t", + }, + ], + + rightColumn: 105.5625, + widgetId: "se4m3djd2t", + parentId: "x7ahy6olyf", + }, + ], + rightColumn: 24.0, + widgetId: "x7ahy6olyf", + parentId: "b6wgydyko8", + }, + ], + rightColumn: 301.5, + widgetId: "b6wgydyko8", + parentId: "vibz0hwj64", + }, + ], + rightColumn: 62.0, + parentId: "0", + }, + ] as unknown) as DSLWidget[]; + + const convertedChildren = [ + { + alignment: "start", + bottomRow: 4, + boxShadow: "none", + flexVerticalAlignment: "start", + leftColumn: 0, + minWidth: 450, + responsiveBehavior: "hug", + rightColumn: 16, + topRow: 0, + type: "BUTTON_WIDGET", + widgetId: "oc0e52x3mq", + widgetName: "Button1", + }, + { + alignment: "end", + bottomRow: 9, + flexVerticalAlignment: "end", + leftColumn: 23, + minWidth: 450, + responsiveBehavior: "hug", + rightColumn: 43, + topRow: 2, + type: "INPUT_WIDGET_V2", + widgetId: "50rdpq2yow", + widgetName: "Input1", + }, + { + alignment: "end", + bottomRow: 4, + flexVerticalAlignment: "start", + leftColumn: 48, + minWidth: 450, + responsiveBehavior: "hug", + rightColumn: 64, + topRow: 0, + type: "BUTTON_WIDGET", + widgetId: "em4ubqs787", + widgetName: "Button3", + }, + { + alignment: "start", + bottomRow: 50, + children: [ + { + bottomRow: 390, + children: [ + { + bottomRow: 5, + leftColumn: 1.5, + parentId: "lwl0t7o358", + rightColumn: 25.5, + topRow: 1, + type: "TEXT_WIDGET", + widgetId: "y1u22x7gj9", + widgetName: "Text1", + }, + { + bottomRow: 37, + leftColumn: 46, + parentId: "lwl0t7o358", + rightColumn: 62, + topRow: 33, + type: "BUTTON_WIDGET", + widgetId: "7dnsyyas3b", + widgetName: "Button4", + }, + { + bottomRow: 37, + leftColumn: 30, + parentId: "lwl0t7o358", + rightColumn: 46, + topRow: 33, + type: "BUTTON_WIDGET", + widgetName: "Button5", + }, + ], + leftColumn: 0, + minHeight: 390, + parentId: "quvrkp960y", + topRow: 0, + type: "CANVAS_WIDGET", + widgetId: "lwl0t7o358", + widgetName: "Canvas1", + }, + ], + flexVerticalAlignment: "start", + leftColumn: 2, + minWidth: 450, + parentId: "0", + positioning: "fixed", + responsiveBehavior: "hug", + rightColumn: 26, + topRow: 11, + type: "FORM_WIDGET", + widgetId: "quvrkp960y", + widgetName: "Form1", + }, + { + alignment: "start", + bottomRow: 30, + flexVerticalAlignment: "center", + leftColumn: 30, + minWidth: 450, + parentId: "0", + parentRowSpace: 10, + responsiveBehavior: "hug", + rightColumn: 46, + topRow: 26, + type: "BUTTON_WIDGET", + widgetId: "e9fhrq8uvf", + widgetName: "Button6", + }, + { + alignment: "end", + bottomRow: 61, + children: [ + { + bottomRow: 250, + children: [ + { + alignment: "start", + bottomRow: 23, + children: [ + { + bottomRow: 100, + children: [ + { + alignment: "start", + bottomRow: 4, + flexVerticalAlignment: "start", + leftColumn: 2, + minWidth: 450, + parentId: "se4m3djd2t", + responsiveBehavior: "hug", + rightColumn: 18, + topRow: 0, + type: "BUTTON_WIDGET", + widgetId: "o529pnktws", + widgetName: "Button9", + }, + { + alignment: "center", + bottomRow: 11, + flexVerticalAlignment: "start", + leftColumn: 33, + minWidth: 450, + parentId: "se4m3djd2t", + responsiveBehavior: "hug", + rightColumn: 49, + topRow: 7, + type: "BUTTON_WIDGET", + widgetId: "vf1wsdypci", + widgetName: "Button10", + }, + ], + flexLayers: [ + { + children: [ + { + align: "start", + id: "o529pnktws", + }, + ], + }, + { + children: [ + { + align: "center", + id: "vf1wsdypci", + }, + ], + }, + ], + leftColumn: 0, + minHeight: 100, + parentId: "x7ahy6olyf", + positioning: "vertical", + responsiveBehavior: "fill", + rightColumn: 105.5625, + topRow: 0, + type: "CANVAS_WIDGET", + useAutoLayout: true, + widgetId: "se4m3djd2t", + widgetName: "Canvas4", + }, + ], + flexVerticalAlignment: "end", + leftColumn: 0, + minWidth: 450, + parentId: "b6wgydyko8", + responsiveBehavior: "hug", + rightColumn: 24, + topRow: 10, + type: "CONTAINER_WIDGET", + widgetId: "x7ahy6olyf", + widgetName: "Container2", + }, + { + alignment: "end", + bottomRow: 13, + children: [ + { + bottomRow: 100, + children: [ + { + alignment: "start", + bottomRow: 4, + flexVerticalAlignment: "start", + leftColumn: 2, + minWidth: 450, + parentId: "vap4aivehm", + responsiveBehavior: "hug", + rightColumn: 18, + topRow: 0, + type: "BUTTON_WIDGET", + widgetId: "rbgq3cl9j0", + widgetName: "Button9Copy", + }, + { + alignment: "center", + bottomRow: 11, + flexVerticalAlignment: "start", + leftColumn: 33, + minWidth: 450, + parentId: "vap4aivehm", + responsiveBehavior: "hug", + rightColumn: 49, + topRow: 7, + type: "BUTTON_WIDGET", + widgetId: "to5e5cr2ph", + widgetName: "Button10Copy", + }, + ], + flexLayers: [ + { + children: [ + { + align: "start", + id: "rbgq3cl9j0", + }, + ], + }, + { + children: [ + { + align: "center", + id: "to5e5cr2ph", + }, + ], + }, + ], + leftColumn: 0, + minHeight: 100, + parentId: "pd2zln825w", + positioning: "vertical", + responsiveBehavior: "fill", + rightColumn: 105.5625, + topRow: 0, + type: "CANVAS_WIDGET", + useAutoLayout: true, + widgetId: "vap4aivehm", + widgetName: "Canvas4Copy", + }, + ], + flexVerticalAlignment: "start", + leftColumn: 36, + minWidth: 450, + parentId: "b6wgydyko8", + renderMode: "CANVAS", + responsiveBehavior: "hug", + rightColumn: 60, + topRow: 0, + type: "CONTAINER_WIDGET", + widgetId: "pd2zln825w", + widgetName: "Container2Copy", + }, + ], + flexLayers: [ + { + children: [ + { + align: "start", + id: "x7ahy6olyf", + }, + { + align: "end", + id: "pd2zln825w", + }, + ], + }, + ], + leftColumn: 0, + minHeight: 250, + parentId: "vibz0hwj64", + positioning: "vertical", + responsiveBehavior: "fill", + rightColumn: 301.5, + topRow: 0, + type: "CANVAS_WIDGET", + useAutoLayout: true, + widgetId: "b6wgydyko8", + widgetName: "Canvas3", + }, + ], + flexVerticalAlignment: "start", + leftColumn: 38, + minWidth: 450, + parentId: "0", + responsiveBehavior: "hug", + rightColumn: 62, + topRow: 36, + type: "CONTAINER_WIDGET", + widgetName: "Container1", + }, + { + bottomRow: 256, + children: [], + leftColumn: 9, + parentId: "0", + parentRowSpace: 10, + positioning: "fixed", + rightColumn: 33, + topRow: 16, + type: "MODAL_WIDGET", + widgetId: "tf847brtfd", + widgetName: "Modal1", + }, + ]; + + it("test getAutoCanvasWidget method to add responsive props to dsl", () => { + const canvasDsl = { + type: "CANVAS_WIDGET", + widgetId: "0", + leftColumn: 40, + rightColumn: 340, + topRow: 20, + bottomRow: 400, + minHeight: 400, + children: [], + }; + + const responsiveCanvasDsl = { + type: "CANVAS_WIDGET", + widgetId: "0", + leftColumn: 40, + rightColumn: 340, + topRow: 20, + bottomRow: 400, + minHeight: 400, + children: [], + useAutoLayout: true, + responsiveBehavior: ResponsiveBehavior.Fill, + positioning: Positioning.Vertical, + flexLayers: [], + }; + + expect(getAutoCanvasWidget((canvasDsl as unknown) as DSLWidget)).toEqual( + responsiveCanvasDsl, + ); + }); + + it("test fitChildWidgetsIntoLayers method", () => { + const flexLayers = [ + { + children: [ + { + align: "start", + id: "oc0e52x3mq", + }, + { + align: "end", + id: "50rdpq2yow", + }, + { + align: "end", + id: "em4ubqs787", + }, + ], + }, + { + children: [ + { + align: "start", + id: "quvrkp960y", + }, + { + align: "start", + id: "e9fhrq8uvf", + }, + ], + }, + { + children: [ + { + align: "end", + id: undefined, + }, + ], + }, + ]; + + const calculatedBottomRow = 75; + expect( + fitChildWidgetsIntoLayers((childWidgets as unknown) as DSLWidget[]), + ).toEqual({ children: convertedChildren, flexLayers, calculatedBottomRow }); + }); + + it("test getTopLeftMostWidget method should return the left most widget in the layer", () => { + const topLeftMostWidget = { + bottomRow: 4, + boxShadow: "none", + leftColumn: 0, + rightColumn: 16, + topRow: 0, + type: "BUTTON_WIDGET", + widgetId: "oc0e52x3mq", + widgetName: "Button1", + }; + + expect(getTopLeftMostWidget(childWidgets)).toEqual({ + topLeftMostWidget, + index: 0, + }); + }); + + it("test processGroupedWidgets and condensedGroupedWidgets to get Alignments based on groups", () => { + const groups = [ + { + widgets: ["1", "2"], + leftColumn: 3, + rightColumn: 11, //7 + }, + { + widgets: ["3", "4"], + leftColumn: 18, + rightColumn: 27, //9 + }, + { + widgets: ["5", "6"], + leftColumn: 36, + rightColumn: 43, //7 + }, + { + widgets: ["7", "8"], + leftColumn: 50, + rightColumn: 54, //8 + }, + { + widgets: ["9", "10"], + leftColumn: 62, + rightColumn: 64, + }, + ]; + const condensedGroups = [ + { + leftColumn: 3, + rightColumn: 27, + widgets: ["1", "2", "3", "4"], + }, + { + leftColumn: 3, + rightColumn: 54, + widgets: ["5", "6", "7", "8"], + }, + { + leftColumn: 3, + rightColumn: 64, + widgets: ["9", "10"], + }, + ]; + const widgetAlignments = { + "1": "start", + "2": "start", + "3": "start", + "4": "start", + "5": "center", + "6": "center", + "7": "center", + "8": "center", + "9": "end", + "10": "end", + }; + + expect(getCondensedGroupedWidgets(groups)).toEqual(condensedGroups); + expect(processGroupedWidgets(groups)).toEqual(widgetAlignments); + }); +}); diff --git a/app/client/src/utils/WidgetFactory.tsx b/app/client/src/utils/WidgetFactory.tsx index 64b41ac02d78..1c05c56e5fd2 100644 --- a/app/client/src/utils/WidgetFactory.tsx +++ b/app/client/src/utils/WidgetFactory.tsx @@ -223,6 +223,15 @@ class WidgetFactory { return Array.from(this.widgetMap.keys()); } + static getWidgetConfigMap(widgetType: WidgetType): Partial { + const map = this.widgetConfigMap.get(widgetType); + if (!map) { + log.error("Widget type validation is not defined"); + return {}; + } + return map; + } + static getWidgetDerivedPropertiesMap( widgetType: WidgetType, ): DerivedPropertiesMap { diff --git a/app/client/src/utils/autoLayout/constants.ts b/app/client/src/utils/autoLayout/constants.ts index 2b1cf2663b57..f620daa3f55e 100644 --- a/app/client/src/utils/autoLayout/constants.ts +++ b/app/client/src/utils/autoLayout/constants.ts @@ -59,6 +59,7 @@ export enum Overflow { } export enum FlexLayerAlignment { + None = "none", Start = "start", Center = "center", End = "end", From b267f34c91362531067974a29098a03827ae621f Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 30 Jan 2023 10:40:40 -0500 Subject: [PATCH 445/708] wip --- .../src/sagas/AutoLayoutUpdateSagas.tsx | 2 +- .../src/utils/autoLayout/AutoLayoutUtils.ts | 28 +++++++--- .../src/utils/autoLayout/flexWidgetUtils.ts | 54 +++++++++++++++++++ app/client/src/widgets/ButtonWidget/index.ts | 19 +++++++ app/client/src/widgets/constants.ts | 6 +-- 5 files changed, 97 insertions(+), 12 deletions(-) diff --git a/app/client/src/sagas/AutoLayoutUpdateSagas.tsx b/app/client/src/sagas/AutoLayoutUpdateSagas.tsx index 42fa64c56c16..1f1e761d63a3 100644 --- a/app/client/src/sagas/AutoLayoutUpdateSagas.tsx +++ b/app/client/src/sagas/AutoLayoutUpdateSagas.tsx @@ -111,7 +111,7 @@ export function* updateLayoutForMobileCheckpoint( const { canvasWidth, isMobile, parentId } = actionPayload.payload; const allWidgets: CanvasWidgetsReduxState = yield select(getWidgets); const updatedWidgets: CanvasWidgetsReduxState = isMobile - ? alterLayoutForMobile(allWidgets, parentId, canvasWidth) + ? alterLayoutForMobile(allWidgets, parentId, canvasWidth, canvasWidth) : alterLayoutForDesktop(allWidgets, parentId); yield put(updateAndSaveLayout(updatedWidgets)); log.debug( diff --git a/app/client/src/utils/autoLayout/AutoLayoutUtils.ts b/app/client/src/utils/autoLayout/AutoLayoutUtils.ts index 2e556d0d83a8..d5fe01a91adc 100644 --- a/app/client/src/utils/autoLayout/AutoLayoutUtils.ts +++ b/app/client/src/utils/autoLayout/AutoLayoutUtils.ts @@ -16,6 +16,7 @@ import { import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; import { updateWidgetPositions } from "utils/autoLayout/positionUtils"; +import { getMinPixelWidth } from "./flexWidgetUtils"; function getCanvas(widgets: CanvasWidgetsReduxState, containerId: string) { const container = widgets[containerId]; @@ -165,6 +166,7 @@ export function alterLayoutForMobile( allWidgets: CanvasWidgetsReduxState, parentId: string, canvasWidth: number, + mainCanvasWidth: number, ): CanvasWidgetsReduxState { let widgets = { ...allWidgets }; const parent = widgets[parentId]; @@ -177,16 +179,24 @@ export function alterLayoutForMobile( for (const child of children) { const widget = { ...widgets[child] }; + const minWidth: number | undefined = getMinPixelWidth( + widget, + mainCanvasWidth, + ); if (widget.responsiveBehavior === ResponsiveBehavior.Fill) { widget.mobileRightColumn = 64; widget.mobileLeftColumn = 0; } else if ( widget.responsiveBehavior === ResponsiveBehavior.Hug && - widget.minWidth + minWidth ) { - const { minWidth, rightColumn } = widget; + const { leftColumn, rightColumn } = widget; const columnSpace = (canvasWidth - FLEXBOX_PADDING * 2) / 64; - if (columnSpace * rightColumn < minWidth) { + if (columnSpace * (rightColumn - leftColumn) < minWidth) { + /** + * Set a proper width for the widget => left column = 0; + * Actual positioning of the widget will be updated by updateWidgetPositions function. + */ widget.mobileLeftColumn = 0; widget.mobileRightColumn = Math.min(minWidth / columnSpace, 64); } @@ -197,11 +207,13 @@ export function alterLayoutForMobile( widget.mobileBottomRow = widget.mobileTopRow + widget.autoLayout.mobile.rows; } - widgets = alterLayoutForMobile( - widgets, - child, - (canvasWidth * (widget.mobileRightColumn || 1)) / 64, - ); + if (widget.mobileRightColumn !== undefined) + widgets = alterLayoutForMobile( + widgets, + child, + canvasWidth * (widget.mobileRightColumn / 64), + mainCanvasWidth, + ); widgets[child] = widget; widgets = updateWidgetPositions(widgets, child, true); } diff --git a/app/client/src/utils/autoLayout/flexWidgetUtils.ts b/app/client/src/utils/autoLayout/flexWidgetUtils.ts index 27e246d1b4bc..979e6373dd4a 100644 --- a/app/client/src/utils/autoLayout/flexWidgetUtils.ts +++ b/app/client/src/utils/autoLayout/flexWidgetUtils.ts @@ -1,3 +1,11 @@ +import WidgetFactory from "utils/WidgetFactory"; +import { WidgetSizeConfig } from "widgets/constants"; + +export interface MinSize { + minHeight: number | string; + minWidth: number | string; +} + export function getRightColumn(widget: any, isMobile: boolean): number { return isMobile && widget.mobileRightColumn !== undefined ? widget.mobileRightColumn @@ -111,3 +119,49 @@ export function getWidgetRows(widget: any, isMobile: boolean): number { const divisor = widget.parentRowSpace === 1 ? 10 : 1; return getBottomRow(widget, isMobile) / divisor - getTopRow(widget, isMobile); } + +/** + * Calculates the minimum size of a widget based on the widget type and the canvas width. + * @param widget | Widget props + * @param canvasWidth | number : main canvas width. + * @returns MinSize | undefined + */ +export function getMinSize( + widget: any, + canvasWidth: number, +): MinSize | undefined { + // Get the widget size configuration. + const sizeConfig = WidgetFactory.getWidgetAutoLayoutConfig(widget.type); + if (!sizeConfig || !sizeConfig?.widgetSize?.length) return; + + // Find the most suitable breakpoint for the canvas width. + const sizes: WidgetSizeConfig[] = sizeConfig?.widgetSize; + let index = 0; + while (index < sizes?.length && canvasWidth > sizes[index].viewportMinWidth) { + index += 1; + } + + // Get the minimum size for the widget at this breakpoint. + const { minHeight, minWidth } = sizes[index - 1].configuration(widget); + + return { minHeight, minWidth }; +} + +/** + * Return the minimum pixel width of a widget based on the widget type and the canvas width. + * minSize can be configured in columns (number) or pixels (string). + * Return an appropriate pixel width based on the minSize type. + */ +export function getMinPixelWidth( + widget: any, + canvasWidth: number, +): number | undefined { + if (!widget) return; + const minSize = getMinSize(widget, canvasWidth); + if (!minSize) return; + const arr: string[] = + typeof minSize.minWidth === "string" ? minSize.minWidth.split("px") : []; + if (arr.length) return parseInt(arr[0]); + if (typeof minSize.minWidth === "number") + return minSize.minWidth * widget.parentColumnSpace; +} diff --git a/app/client/src/widgets/ButtonWidget/index.ts b/app/client/src/widgets/ButtonWidget/index.ts index 084bb982d583..79e87550fe39 100644 --- a/app/client/src/widgets/ButtonWidget/index.ts +++ b/app/client/src/widgets/ButtonWidget/index.ts @@ -41,6 +41,25 @@ export const CONFIG = { styleConfig: Widget.getPropertyPaneStyleConfig(), stylesheetConfig: Widget.getStylesheetConfig(), }, + autoLayout: { + defaults: { + rows: 4, + }, + mobile: { + rows: 4, + }, + widgetSize: [ + { + viewportMinWidth: 0, + configuration: () => { + return { + minWidth: "120px", + minHeight: "40px", + }; + }, + }, + ], + }, }; export default Widget; diff --git a/app/client/src/widgets/constants.ts b/app/client/src/widgets/constants.ts index c7f5a86eeedd..8f658ca2e221 100644 --- a/app/client/src/widgets/constants.ts +++ b/app/client/src/widgets/constants.ts @@ -15,14 +15,14 @@ import { DerivedPropertiesMap } from "utils/WidgetFactory"; import { WidgetFeatures } from "utils/WidgetFeatures"; import { WidgetProps } from "./BaseWidget"; -type WidgetSizeConfig = { +export type WidgetSizeConfig = { viewportMinWidth: number; configuration: (props: WidgetProps) => { [key: string]: string | number }; }; export type AutoLayoutConfig = { - defaults: Record; - mobile: Record; + defaults: Record; + mobile: Record; widgetSize: Array; }; From 839293730a8bf571691eebf582d1af52d0d41c82 Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 30 Jan 2023 15:21:03 -0500 Subject: [PATCH 446/708] remove mouse direction usage --- .../WidgetNameComponent/index.tsx | 2 +- .../Editor/WidgetsEditor/CanvasContainer.tsx | 4 -- .../CanvasArenas/hooks/canvasDraggingUtils.ts | 57 ------------------- .../hooks/useAutoLayoutHighlights.ts | 9 +-- .../CanvasArenas/hooks/useCanvasDragging.ts | 20 +------ .../autoLayout/highlightSelectionUtils.ts | 19 ++----- .../src/utils/autoLayout/highlightUtils.ts | 3 +- 7 files changed, 10 insertions(+), 104 deletions(-) diff --git a/app/client/src/components/editorComponents/WidgetNameComponent/index.tsx b/app/client/src/components/editorComponents/WidgetNameComponent/index.tsx index bb398075c857..f01c1827cb17 100644 --- a/app/client/src/components/editorComponents/WidgetNameComponent/index.tsx +++ b/app/client/src/components/editorComponents/WidgetNameComponent/index.tsx @@ -205,7 +205,7 @@ export function WidgetNameComponent(props: WidgetNameComponentProps) { if (!isFocused) return; if (!isSelected) { - selectWidget(props.widgetId); + selectWidget(SelectionRequestType.One, [props.widgetId]); } const widgetHeight = props.widgetProps.bottomRow - props.widgetProps.topRow; diff --git a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx index 7a88932bda55..5e18ca8a9ff2 100644 --- a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx +++ b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx @@ -76,10 +76,6 @@ function CanvasContainer() { const isPageInitializing = isFetchingPage || !isLayoutingInitialized; useEffect(() => { - if (!localStorage.getItem("verticalHighlightDropArea")) - localStorage.setItem("verticalHighlightDropArea", "0.35"); - if (!localStorage.getItem("horizontalHighlightDropArea")) - localStorage.setItem("horizontalHighlightDropArea", "0.2"); return () => { dispatch(forceOpenWidgetPanel(false)); }; diff --git a/app/client/src/pages/common/CanvasArenas/hooks/canvasDraggingUtils.ts b/app/client/src/pages/common/CanvasArenas/hooks/canvasDraggingUtils.ts index 749fa8a603fe..590669b388ab 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/canvasDraggingUtils.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/canvasDraggingUtils.ts @@ -316,60 +316,3 @@ export const updateRectanglesPostReflow = ( return rectanglesToDraw; }; - -/** - * Get mouse move direction using an average of last five mouse positions. - * @param lastMousePositions | { x: number; y: number }[] : array of last five mouse positions. - * @param currentPosition | { x: number; y: number } : current mouse position. - * @param direction | ReflowDirection : current direction. - * @param updateMousePosition | ({ x, y }: { x: number; y: number }) => void : function to update mouse position. - * @returns ReflowDirection - */ -export function getInterpolatedMoveDirection( - lastMousePositions: { x: number; y: number }[], - currentPosition: { x: number; y: number }, - direction: ReflowDirection, - updateMousePosition: ({ x, y }: { x: number; y: number }) => void, -): ReflowDirection { - if (!lastMousePositions.length) { - updateMousePosition(currentPosition); - return direction; - } - - const averagePosition = getAverageMousePosition(lastMousePositions); - updateMousePosition(currentPosition); - const deltaX = currentPosition.x - averagePosition.x; - const deltaY = currentPosition.y - averagePosition.y; - - if (Math.abs(deltaY) > Math.abs(deltaX)) - return deltaY > 0 ? ReflowDirection.BOTTOM : ReflowDirection.TOP; - if (Math.abs(deltaX) > Math.abs(deltaY)) - return deltaX > 0 ? ReflowDirection.RIGHT : ReflowDirection.LEFT; - if ( - Math.abs(deltaX) === Math.abs(deltaY) && - direction === ReflowDirection.UNSET - ) { - /** - * If the direction is unset and mouse position is perfectly diagonal. - * Then set the direction vertically (random choice). - */ - return deltaY > 0 ? ReflowDirection.BOTTOM : ReflowDirection.TOP; - } - return direction; -} - -function getAverageMousePosition( - lastMousePositions: { x: number; y: number }[], -) { - const accumulatedPositions = lastMousePositions.reduce( - (acc, curr) => { - return { x: acc.x + curr.x, y: acc.y + curr.y }; - }, - { x: 0, y: 0 }, - ); - const averagePosition = { - x: accumulatedPositions.x / lastMousePositions.length, - y: accumulatedPositions.y / lastMousePositions.length, - }; - return averagePosition; -} diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index ecdd87b1d0c1..67a2471df9bb 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -1,6 +1,5 @@ import { ResponsiveBehavior } from "utils/autoLayout/constants"; import { useSelector } from "react-redux"; -import { ReflowDirection } from "reflow/reflowTypes"; import { getWidgets } from "sagas/selectors"; import { getCanvasWidth } from "selectors/editorSelectors"; import { getIsMobile } from "selectors/mainCanvasSelectors"; @@ -101,13 +100,9 @@ export const useAutoLayoutHighlights = ({ /** * Highlight a drop position based on mouse position and move direction. * @param e | MouseMoveEvent - * @param moveDirection | ReflowDirection * @returns HighlightInfo | undefined */ - const highlightDropPosition = ( - e: any, - moveDirection: ReflowDirection, - ): HighlightInfo | undefined => { + const highlightDropPosition = (e: any): HighlightInfo | undefined => { if (!highlights || !highlights.length) highlights = deriveHighlightsFromLayers( allWidgets, @@ -121,7 +116,6 @@ export const useAutoLayoutHighlights = ({ const highlight: HighlightInfo | undefined = getHighlightPayload( highlights, e, - moveDirection, ); if (!highlight) return; // console.log("#### selection", highlight); @@ -145,7 +139,6 @@ export const useAutoLayoutHighlights = ({ const payload: HighlightInfo | undefined = getHighlightPayload( highlights, null, - undefined, val, ); if (!payload) return; diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index d1f14f47522b..6c9413cf135c 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -26,7 +26,6 @@ import { } from "utils/WidgetPropsUtils"; import { getEdgeDirection, - getInterpolatedMoveDirection, getMoveDirection, getReflowedSpaces, modifyBlockDimension, @@ -194,8 +193,6 @@ export const useCanvasDragging = ( id: "", }; - let lastMousePositions: { x: number; y: number }[] = []; - const resetCanvasState = () => { throttledStopReflowing(); reflow.current?.resetReflow(); @@ -383,9 +380,6 @@ export const useCanvasDragging = ( ); rowRef.current = newRows ? newRows : rowRef.current; }; - const updateMousePosition = ({ x, y }: { x: number; y: number }) => { - lastMousePositions = [{ x, y }, ...lastMousePositions.slice(0, 4)]; - }; const onMouseMove = (e: any, firstMove = false) => { if (isDragging && canvasIsDragging && slidingArenaRef.current) { @@ -438,19 +432,9 @@ export const useCanvasDragging = ( triggerReflow(e, firstMove); if (useAutoLayout && isCurrentDraggedCanvas) { - currentDirection.current = getInterpolatedMoveDirection( - lastMousePositions, - { x: e.clientX, y: e.clientY }, - currentDirection.current, - updateMousePosition, - ); setTimeout(() => { - if (currentDirection.current !== ReflowDirection.UNSET) - selectedHighlight = highlightDropPosition( - e, - currentDirection.current, - ); - }, 100); + selectedHighlight = highlightDropPosition(e); + }, 50); } } isUpdatingRows = renderBlocks( diff --git a/app/client/src/utils/autoLayout/highlightSelectionUtils.ts b/app/client/src/utils/autoLayout/highlightSelectionUtils.ts index dcfe4cf7ecd2..2b802545fb94 100644 --- a/app/client/src/utils/autoLayout/highlightSelectionUtils.ts +++ b/app/client/src/utils/autoLayout/highlightSelectionUtils.ts @@ -1,4 +1,3 @@ -import { ReflowDirection } from "reflow/reflowTypes"; import { HighlightInfo } from "./highlightUtils"; export interface Point { @@ -10,14 +9,12 @@ export interface Point { * Select the closest highlight to the mouse position (in the direction of the). * @param highlights | HighlightInfo[] : all highlights for the current canvas. * @param e | any : mouse event. - * @param moveDirection | ReflowDirection : direction of the drag. * @param val | Point : mouse coordinates. * @returns HighlightInfo | undefined */ export const getHighlightPayload = ( highlights: HighlightInfo[], e: any, - moveDirection?: ReflowDirection, val?: Point, ): HighlightInfo | undefined => { if (!highlights || !highlights.length) return; @@ -32,7 +29,7 @@ export const getHighlightPayload = ( * Filter highlights that span the current mouse position. */ let filteredHighlights: HighlightInfo[] = []; - filteredHighlights = getViableDropPositions(highlights, pos, moveDirection); + filteredHighlights = getViableDropPositions(highlights, pos); if (!filteredHighlights || !filteredHighlights?.length) return; // Sort filtered highlights in ascending order of distance from mouse position. @@ -40,7 +37,7 @@ export const getHighlightPayload = ( return calculateDistance(a, pos) - calculateDistance(b, pos); }); - // console.log("#### arr", arr, highlights, moveDirection); + // console.log("#### arr", arr, highlights); // Return the closest highlight. return arr[0]; @@ -50,15 +47,13 @@ export const getHighlightPayload = ( * Filter highlights based on direction of drag. * @param arr | HighlightInfo[] : all highlights for the current canvas. * @param pos | Point : current mouse coordinates. - * @param moveDirection | ReflowDirection : direction of the drag. * @returns HighlightInfo | undefined */ function getViableDropPositions( arr: HighlightInfo[], pos: Point, - moveDirection?: ReflowDirection, ): HighlightInfo[] { - if (!moveDirection || !arr) return arr || []; + if (!arr) return arr || []; const DEFAULT_DROP_RANGE = 10; const verticalHighlights = arr.filter( (highlight: HighlightInfo) => highlight.isVertical, @@ -81,8 +76,6 @@ function getViableDropPositions( selection.push(highlight); }); const hasVerticalSelection = selection.length > 0; - const dropArea = localStorage.getItem("horizontalHighlightDropArea"); - const zoneSize = dropArea ? parseFloat(dropArea) : 0; horizontalHighlights.forEach((highlight: HighlightInfo) => { if (pos.x >= highlight.posX && pos.x <= highlight.posX + highlight.width) if ( @@ -90,15 +83,13 @@ function getViableDropPositions( pos.y <= highlight.posY + (highlight.dropZone?.bottom !== undefined - ? highlight.dropZone?.bottom * - (hasVerticalSelection ? zoneSize : 1) + ? highlight.dropZone?.bottom * (hasVerticalSelection ? 0.2 : 1) : DEFAULT_DROP_RANGE)) || (pos.y < highlight.posY && pos.y >= highlight.posY - (highlight.dropZone?.top !== undefined - ? highlight.dropZone?.top * - (hasVerticalSelection ? zoneSize + 0.1 : 1) + ? highlight.dropZone?.top * (hasVerticalSelection ? 0.3 : 1) : DEFAULT_DROP_RANGE)) ) selection.push(highlight); diff --git a/app/client/src/utils/autoLayout/highlightUtils.ts b/app/client/src/utils/autoLayout/highlightUtils.ts index b9c6aec5f611..11a0d3cb821a 100644 --- a/app/client/src/utils/autoLayout/highlightUtils.ts +++ b/app/client/src/utils/autoLayout/highlightUtils.ts @@ -553,8 +553,7 @@ function updateVerticalHighlightDropZone( highlights: HighlightInfo[], canvasWidth: number, ): HighlightInfo[] { - const dropArea = localStorage.getItem("verticalHighlightDropArea"); - const zoneSize = dropArea !== null ? parseFloat(dropArea) : 0.35; + const zoneSize = 0.35; for (const [index, highlight] of highlights.entries()) { const nextHighlight: HighlightInfo | undefined = highlights[index + 1]; const previousHighlight: HighlightInfo | undefined = highlights[index - 1]; From de52d8f77f953a8998c965c09f832b3d5e67477f Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 30 Jan 2023 16:11:39 -0500 Subject: [PATCH 447/708] fix highlight rendering in firefox --- .../hooks/useAutoLayoutHighlights.ts | 2 +- .../hooks/useRenderBlocksOnCanvas.ts | 5 ++++- .../autoLayout/highlightSelectionUtils.ts | 5 ++--- app/client/src/utils/layoutPropertiesUtils.ts | 18 +++++++++--------- 4 files changed, 16 insertions(+), 14 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index 67a2471df9bb..7271ca9a9bd0 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -118,7 +118,7 @@ export const useAutoLayoutHighlights = ({ e, ); if (!highlight) return; - // console.log("#### selection", highlight); + lastActiveHighlight = highlight; return highlight; }; diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useRenderBlocksOnCanvas.ts b/app/client/src/pages/common/CanvasArenas/hooks/useRenderBlocksOnCanvas.ts index 94e2aead83f0..09fb22b86461 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useRenderBlocksOnCanvas.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useRenderBlocksOnCanvas.ts @@ -172,7 +172,10 @@ export const useRenderBlocksOnCanvas = ( : parentOffsetTop && scrollParent.scrollTop > parentOffsetTop ? scrollParent.scrollTop - parentOffsetTop : 0; - canvasCtx.roundRect(posX, posY - val, width, height, 4); + // roundRect is not currently supported in firefox. + if (canvasCtx.roundRect) + canvasCtx.roundRect(posX, posY - val, width, height, 4); + else canvasCtx.rect(posX, posY - val, width, height); canvasCtx.fill(); canvasCtx.stroke(); canvasCtx.save(); diff --git a/app/client/src/utils/autoLayout/highlightSelectionUtils.ts b/app/client/src/utils/autoLayout/highlightSelectionUtils.ts index 2b802545fb94..d984e3fda494 100644 --- a/app/client/src/utils/autoLayout/highlightSelectionUtils.ts +++ b/app/client/src/utils/autoLayout/highlightSelectionUtils.ts @@ -21,10 +21,9 @@ export const getHighlightPayload = ( // Current mouse coordinates. const pos: Point = { - x: e?.offsetX || val?.x, - y: e?.offsetY || val?.y, + x: e ? e.offsetX || e.layerX : val?.x, + y: e ? e.offsetY || e.layerY : val?.y, }; - /** * Filter highlights that span the current mouse position. */ diff --git a/app/client/src/utils/layoutPropertiesUtils.ts b/app/client/src/utils/layoutPropertiesUtils.ts index 0dc84f799757..b48348ba6e56 100644 --- a/app/client/src/utils/layoutPropertiesUtils.ts +++ b/app/client/src/utils/layoutPropertiesUtils.ts @@ -291,15 +291,15 @@ export function getDefaultResponsiveBehavior(widgetType: string) { export function getResponsiveLayoutConfig(widgetType: string) { // ToDO(Ashok): disabling for now, will be revisited at a later point - - // const defaultBehavior = getDefaultResponsiveBehavior(widgetType); + return []; + const defaultBehavior = getDefaultResponsiveBehavior(widgetType); return [ - // { - // sectionName: "Responsive Layout", - // children: [ - // generateResponsiveBehaviorConfig(defaultBehavior), - // generateVerticalAlignmentConfig(), - // ], - // }, + { + sectionName: "Responsive Layout", + children: [ + generateResponsiveBehaviorConfig(defaultBehavior), + generateVerticalAlignmentConfig(), + ], + }, ]; } From 5d7f1ae062cf6b4a4995ba30a98e518bd8e1ca33 Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 30 Jan 2023 16:43:39 -0500 Subject: [PATCH 448/708] add util functions for getting size config --- .../src/utils/autoLayout/AutoLayoutUtils.ts | 6 ++++++ .../src/utils/autoLayout/flexWidgetUtils.ts | 20 ++++++++++++++----- app/client/src/widgets/ButtonWidget/index.ts | 2 +- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/app/client/src/utils/autoLayout/AutoLayoutUtils.ts b/app/client/src/utils/autoLayout/AutoLayoutUtils.ts index d5fe01a91adc..26680a05c82e 100644 --- a/app/client/src/utils/autoLayout/AutoLayoutUtils.ts +++ b/app/client/src/utils/autoLayout/AutoLayoutUtils.ts @@ -183,6 +183,7 @@ export function alterLayoutForMobile( widget, mainCanvasWidth, ); + if (widget.responsiveBehavior === ResponsiveBehavior.Fill) { widget.mobileRightColumn = 64; widget.mobileLeftColumn = 0; @@ -200,13 +201,18 @@ export function alterLayoutForMobile( widget.mobileLeftColumn = 0; widget.mobileRightColumn = Math.min(minWidth / columnSpace, 64); } + } else { + widget.mobileLeftColumn = widget.leftColumn; + widget.mobileRightColumn = widget.rightColumn; } + widget.mobileTopRow = widget.topRow; widget.mobileBottomRow = widget.bottomRow; if (widget.autoLayout?.mobile?.rows) { widget.mobileBottomRow = widget.mobileTopRow + widget.autoLayout.mobile.rows; } + if (widget.mobileRightColumn !== undefined) widgets = alterLayoutForMobile( widgets, diff --git a/app/client/src/utils/autoLayout/flexWidgetUtils.ts b/app/client/src/utils/autoLayout/flexWidgetUtils.ts index 979e6373dd4a..5a32108fd53e 100644 --- a/app/client/src/utils/autoLayout/flexWidgetUtils.ts +++ b/app/client/src/utils/autoLayout/flexWidgetUtils.ts @@ -130,6 +130,20 @@ export function getMinSize( widget: any, canvasWidth: number, ): MinSize | undefined { + // Get the widget size configuration. + const sizeConfig = getCurrentSizeConfig(widget, canvasWidth); + if (!sizeConfig) return; + + // Get the minimum size for the widget at this breakpoint. + const { minHeight, minWidth } = sizeConfig.configuration(widget); + + return { minHeight, minWidth }; +} + +export function getCurrentSizeConfig( + widget: any, + canvasWidth: number, +): WidgetSizeConfig | undefined { // Get the widget size configuration. const sizeConfig = WidgetFactory.getWidgetAutoLayoutConfig(widget.type); if (!sizeConfig || !sizeConfig?.widgetSize?.length) return; @@ -140,11 +154,7 @@ export function getMinSize( while (index < sizes?.length && canvasWidth > sizes[index].viewportMinWidth) { index += 1; } - - // Get the minimum size for the widget at this breakpoint. - const { minHeight, minWidth } = sizes[index - 1].configuration(widget); - - return { minHeight, minWidth }; + return sizes[index - 1]; } /** diff --git a/app/client/src/widgets/ButtonWidget/index.ts b/app/client/src/widgets/ButtonWidget/index.ts index 79e87550fe39..adcb5b1e697f 100644 --- a/app/client/src/widgets/ButtonWidget/index.ts +++ b/app/client/src/widgets/ButtonWidget/index.ts @@ -53,7 +53,7 @@ export const CONFIG = { viewportMinWidth: 0, configuration: () => { return { - minWidth: "120px", + minWidth: "100px", minHeight: "40px", }; }, From b1332c24e3693a6a6b2a2126670bb10c54bf981f Mon Sep 17 00:00:00 2001 From: Aswath K Date: Wed, 1 Feb 2023 19:26:52 +0530 Subject: [PATCH 449/708] widget triggers updateWidgetPosition for its parent if it violates minWidth requirement --- app/client/src/actions/autoLayoutActions.ts | 7 +++++ .../src/ce/constants/ReduxActionConstants.tsx | 2 ++ .../appsmith/autoLayout/FlexComponent.tsx | 30 +++++++++++++++++-- .../src/sagas/AutoLayoutUpdateSagas.tsx | 21 +++++++++++++ .../src/utils/WidgetRegisterHelpers.tsx | 5 +++- app/client/src/widgets/BaseWidget.tsx | 4 +++ app/client/src/widgets/withWidgetProps.tsx | 3 ++ 7 files changed, 69 insertions(+), 3 deletions(-) diff --git a/app/client/src/actions/autoLayoutActions.ts b/app/client/src/actions/autoLayoutActions.ts index ebb39faecf0a..5308d803454e 100644 --- a/app/client/src/actions/autoLayoutActions.ts +++ b/app/client/src/actions/autoLayoutActions.ts @@ -24,3 +24,10 @@ export const updateLayoutForMobileBreakpointAction = ( canvasWidth, }, }); + +export const widgetViolatedMinDimentionsAction = (parentId: string) => ({ + type: ReduxActionTypes.WIDGET_VIOLATED_MIN_DIMENSIONS, + payload: { + parentId, + }, +}); diff --git a/app/client/src/ce/constants/ReduxActionConstants.tsx b/app/client/src/ce/constants/ReduxActionConstants.tsx index 1aaf8b93ed0b..7b167f627118 100644 --- a/app/client/src/ce/constants/ReduxActionConstants.tsx +++ b/app/client/src/ce/constants/ReduxActionConstants.tsx @@ -764,6 +764,7 @@ export const ReduxActionTypes = { ADD_CHILD_WRAPPERS: "ADD_CHILD_WRAPPERS", UPDATE_FILL_CHILD_LAYER: "UPDATE_FILL_CHILD_LAYER", RECALCULATE_COLUMNS: "RECALCULATE_COLUMNS", + WIDGET_VIOLATED_MIN_DIMENSIONS: "WIDGET_VIOLATED_MIN_DIMENSIONS", }; export type ReduxActionType = typeof ReduxActionTypes[keyof typeof ReduxActionTypes]; @@ -966,6 +967,7 @@ export const WidgetReduxActionTypes: { [key: string]: string } = { WIDGET_BULK_DELETE: "WIDGET_BULK_DELETE", WIDGET_SINGLE_DELETE: "WIDGET_SINGLE_DELETE", WIDGET_UPDATE_PROPERTY: "WIDGET_UPDATE_PROPERTY", + WIDGET_REACHED_MIN_WIDTH: "WIDGET_REACHED_MIN_WIDTH", }; export type ReduxActionErrorType = typeof ReduxActionErrorTypes[keyof typeof ReduxActionErrorTypes]; diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx index 57ea86704d6d..1383638b9bc6 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx @@ -1,4 +1,10 @@ -import React, { CSSProperties, ReactNode, useCallback, useMemo } from "react"; +import React, { + CSSProperties, + ReactNode, + useCallback, + useEffect, + useMemo, +} from "react"; import styled from "styled-components"; import { @@ -6,7 +12,7 @@ import { widgetTypeClassname, WIDGET_PADDING, } from "constants/WidgetConstants"; -import { useSelector } from "react-redux"; +import { useDispatch, useSelector } from "react-redux"; import { snipingModeSelector } from "selectors/editorSelectors"; import { getIsResizing } from "selectors/widgetSelectors"; import { @@ -17,6 +23,8 @@ import { import { useClickToSelectWidget } from "utils/hooks/useClickToSelectWidget"; import { usePositionedContainerZIndex } from "utils/hooks/usePositionedContainerZIndex"; import { checkIsDropTarget } from "../PositionedContainer"; +import { widgetViolatedMinDimentionsAction } from "actions/autoLayoutActions"; +import { debounce } from "lodash"; export type AutoLayoutProps = { children: ReactNode; @@ -41,6 +49,7 @@ const FlexWidget = styled.div` export function FlexComponent(props: AutoLayoutProps) { const isSnipingMode = useSelector(snipingModeSelector); + const dispatch = useDispatch(); const clickToSelectWidget = useClickToSelectWidget(props.widgetId); const onClickFn = useCallback( @@ -68,6 +77,23 @@ export function FlexComponent(props: AutoLayoutProps) { const isResizing = useSelector(getIsResizing); + const widgetReachedMinWidth = useCallback( + debounce((parentId) => { + dispatch(widgetViolatedMinDimentionsAction(parentId)); + }, 50), + [], + ); + + useEffect(() => { + if ( + props.minWidth && + props.componentWidth < props.minWidth && + props.parentId + ) { + widgetReachedMinWidth(props.parentId); + } + }, [props.componentWidth]); + const flexComponentStyle: CSSProperties = useMemo(() => { return { display: "flex", diff --git a/app/client/src/sagas/AutoLayoutUpdateSagas.tsx b/app/client/src/sagas/AutoLayoutUpdateSagas.tsx index 1f1e761d63a3..79cac61cf14e 100644 --- a/app/client/src/sagas/AutoLayoutUpdateSagas.tsx +++ b/app/client/src/sagas/AutoLayoutUpdateSagas.tsx @@ -17,6 +17,7 @@ import { wrapChildren, } from "../utils/autoLayout/AutoLayoutUtils"; import { getWidgets } from "./selectors"; +import { updateWidgetPositions } from "utils/autoLayout/positionUtils"; type LayoutUpdatePayload = { parentId: string; @@ -130,6 +131,22 @@ export function* updateLayoutForMobileCheckpoint( } } +const processedParentIds = new Map(); + +function* widgetViolatedMinDimensionsSaga( + action: ReduxAction<{ parentId: string }>, +) { + const isMobile: boolean = yield select(getIsMobile); + const allWidgets: CanvasWidgetsReduxState = yield select(getWidgets); + + const parentId = action.payload.parentId; + if (processedParentIds.has(parentId)) return; + processedParentIds.set(parentId, true); + setTimeout(() => processedParentIds.delete(parentId), 1000); + const updatedWidgets = updateWidgetPositions(allWidgets, parentId, isMobile); + yield put(updateAndSaveLayout(updatedWidgets)); +} + export default function* layoutUpdateSagas() { yield all([ takeLatest(ReduxActionTypes.ADD_CHILD_WRAPPERS, addChildWrappers), @@ -139,5 +156,9 @@ export default function* layoutUpdateSagas() { ReduxActionTypes.RECALCULATE_COLUMNS, updateLayoutForMobileCheckpoint, ), + takeLatest( + ReduxActionTypes.WIDGET_VIOLATED_MIN_DIMENSIONS, + widgetViolatedMinDimensionsSaga, + ), ]); } diff --git a/app/client/src/utils/WidgetRegisterHelpers.tsx b/app/client/src/utils/WidgetRegisterHelpers.tsx index 89c15c815089..60ccc1e243ac 100644 --- a/app/client/src/utils/WidgetRegisterHelpers.tsx +++ b/app/client/src/utils/WidgetRegisterHelpers.tsx @@ -72,7 +72,10 @@ export const configureWidget = (config: WidgetConfiguration) => { const _config = { ...config.defaults, ...features, - autoLayout: config.autoLayout, + autoLayout: { + defaults: config.autoLayout?.defaults, + mobile: config.autoLayout?.mobile, + }, searchTags: config.searchTags, type: config.type, hideCard: !!config.hideCard || !config.iconSVG, diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index 924df17504f3..65d69e8bb105 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -56,6 +56,7 @@ import { isAutoHeightEnabledForWidget, shouldUpdateWidgetHeightAutomatically, } from "./WidgetUtils"; +import { getMinPixelWidth } from "utils/autoLayout/flexWidgetUtils"; /*** * BaseWidget @@ -459,6 +460,7 @@ abstract class BaseWidget< makeFlex(content: ReactNode) { const { componentHeight, componentWidth } = this.getComponentDimensions(); + const minWidth = getMinPixelWidth(this.props, this.props.mainCanvasWidth); return (
); } diff --git a/app/client/src/pages/Editor/WidgetsEditor/index.tsx b/app/client/src/pages/Editor/WidgetsEditor/index.tsx index 12b33d452b57..cd25b55c0495 100644 --- a/app/client/src/pages/Editor/WidgetsEditor/index.tsx +++ b/app/client/src/pages/Editor/WidgetsEditor/index.tsx @@ -6,11 +6,14 @@ import { closePropertyPane, closeTableFilterPane } from "actions/widgetActions"; import Debugger from "components/editorComponents/Debugger"; import EditorContextProvider from "components/editorComponents/EditorContextProvider"; import { getCurrentApplication } from "selectors/applicationSelectors"; + import { getCurrentPageId, getCurrentPageName, getIsFetchingPage, } from "selectors/editorSelectors"; +import { getCanvasWidgets } from "selectors/entitiesSelector"; +import { isMultiPaneActive } from "selectors/multiPaneSelectors"; import { getIsOnboardingTasksView, inGuidedTour, @@ -30,8 +33,6 @@ import CanvasContainer from "./CanvasContainer"; import CanvasTopSection from "./EmptyCanvasSection"; import PageTabs from "./PageTabs"; import PropertyPaneContainer from "./PropertyPaneContainer"; -import { isMultiPaneActive } from "selectors/multiPaneSelectors"; -import { getCanvasWidgets } from "selectors/entitiesSelector"; import WidgetTopBar from "./WidgetTopBar"; /* eslint-disable react/display-name */ @@ -114,7 +115,6 @@ function WidgetsEditor() { ); PerformanceTracker.stopTracking(); - return ( {showOnboardingTasks ? ( @@ -130,6 +130,7 @@ function WidgetsEditor() { className="relative flex flex-row w-full overflow-hidden justify-center" data-testid="widgets-editor" draggable + id="widgets-editor" onClick={handleWrapperClick} onDragStart={onDragStart} > diff --git a/app/client/src/pages/common/CanvasArenas/hooks/canvasDraggingUtils.test.ts b/app/client/src/pages/common/CanvasArenas/hooks/canvasDraggingUtils.test.ts index c651e6414fdf..077962742b18 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/canvasDraggingUtils.test.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/canvasDraggingUtils.test.ts @@ -98,6 +98,7 @@ describe("test canvasDraggingUtils Methods", () => { rowHeight: 50, widgetId: "id", isNotColliding: true, + type: "CANVAS_WIDGET", }; expect( @@ -186,6 +187,7 @@ describe("test canvasDraggingUtils Methods", () => { rowHeight: 20, widgetId: "id", isNotColliding: true, + type: "CANVAS_WIDGET", }; expect( modifyBlockDimension(draggingBlock, 10, 10, 100, true, false), @@ -213,6 +215,7 @@ describe("test canvasDraggingUtils Methods", () => { rowHeight: VERTICAL_RESIZE_MIN_LIMIT, widgetId: "id", isNotColliding: true, + type: "CANVAS_WIDGET", }; expect( modifyBlockDimension(draggingBlock, 10, 10, 100, true, false), @@ -240,6 +243,7 @@ describe("test canvasDraggingUtils Methods", () => { rowHeight: 40, widgetId: "id", isNotColliding: true, + type: "CANVAS_WIDGET", }; expect( modifyBlockDimension(draggingBlock, 10, 10, 100, false, false), @@ -269,6 +273,7 @@ describe("test canvasDraggingUtils Methods", () => { widgetId: "id", isNotColliding: true, fixedHeight: 90, + type: "CANVAS_WIDGET", }; expect( modifyBlockDimension(draggingBlock, 10, 10, 100, false, false), @@ -335,6 +340,7 @@ describe("test canvasDraggingUtils Methods", () => { rowHeight: 90, widgetId: "1", isNotColliding: false, + type: "CANVAS_WIDGET", }, { left: 100, @@ -345,6 +351,7 @@ describe("test canvasDraggingUtils Methods", () => { rowHeight: 95, widgetId: "2", isNotColliding: false, + type: "CANVAS_WIDGET", }, { left: 300, @@ -355,6 +362,7 @@ describe("test canvasDraggingUtils Methods", () => { rowHeight: 34, widgetId: "3", isNotColliding: false, + type: "CANVAS_WIDGET", }, { left: 400, @@ -365,6 +373,7 @@ describe("test canvasDraggingUtils Methods", () => { rowHeight: 12, widgetId: "4", isNotColliding: true, + type: "CANVAS_WIDGET", }, ]; diff --git a/app/client/src/resizable/resizenreflow/index.tsx b/app/client/src/resizable/resizenreflow/index.tsx index b20c11ced5ae..190a979b6f46 100644 --- a/app/client/src/resizable/resizenreflow/index.tsx +++ b/app/client/src/resizable/resizenreflow/index.tsx @@ -1,19 +1,31 @@ +import { reflowMoveAction, stopReflowAction } from "actions/reflowActions"; import { isHandleResizeAllowed } from "components/editorComponents/ResizableUtils"; import { OccupiedSpace } from "constants/CanvasEditorConstants"; -import { WIDGET_PADDING } from "constants/WidgetConstants"; +import { Colors } from "constants/Colors"; +import { GridDefaults, WIDGET_PADDING } from "constants/WidgetConstants"; import React, { ReactNode, useEffect, useRef, useState } from "react"; -import { useSelector } from "react-redux"; +import { useDispatch, useSelector } from "react-redux"; import { animated, Spring } from "react-spring"; import { useDrag } from "react-use-gesture"; +import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; import { GridProps, MovementLimitMap, ReflowDirection, ReflowedSpace, + ReflowedSpaceMap, } from "reflow/reflowTypes"; -import { getContainerOccupiedSpacesSelectorWhileResizing } from "selectors/editorSelectors"; +import { getWidgets } from "sagas/selectors"; +import { + getContainerOccupiedSpacesSelectorWhileResizing, + getCurrentAppPositioningType, +} from "selectors/editorSelectors"; import { getReflowSelector } from "selectors/widgetReflowSelectors"; import styled, { StyledComponent } from "styled-components"; +import { + getFillWidgetLengthForLayer, + getLayerIndexOfWidget, +} from "utils/autoLayout/AutoLayoutUtils"; import { LayoutDirection, ResponsiveBehavior, @@ -25,13 +37,33 @@ import PerformanceTracker, { } from "utils/PerformanceTracker"; import { isDropZoneOccupied } from "utils/WidgetPropsUtils"; -const ResizeWrapper = styled(animated.div)<{ $prevents: boolean }>` +const ResizeWrapper = styled(animated.div)<{ + $prevents: boolean; + isHovered: boolean; + showBoundaries: boolean; +}>` display: block; & { * { pointer-events: ${(props) => !props.$prevents && "none"}; } } + ${(props) => { + if (props.showBoundaries) { + return ` + box-shadow: 0px 0px 0px 1px ${ + props.isHovered ? Colors.WATUSI : "#f86a2b" + }; + border-radius: 0px 4px 4px 4px; + border: 1px solid ${Colors.GREY_1}; + outline: 1px solid ${Colors.GREY_1} !important; + outline-offset: 1px;`; + } else { + return ` + border: 1px solid transparent; + `; + } + }}} `; const getSnappedValues = ( @@ -177,12 +209,14 @@ type ResizableProps = { direction?: LayoutDirection; paddingOffset: number; isMobile: boolean; + showResizeBoundary: boolean; }; export function ReflowResizable(props: ResizableProps) { const resizableRef = useRef(null); const [isResizing, setResizing] = useState(false); - + const isAutoHeight = + useSelector(getCurrentAppPositioningType) === AppPositioningTypes.AUTO; const occupiedSpacesBySiblingWidgets = useSelector( getContainerOccupiedSpacesSelectorWhileResizing(props.parentId), ); @@ -246,6 +280,45 @@ export function ReflowResizable(props: ResizableProps) { reset: false, direction: ReflowDirection.UNSET, }); + const allWidgets = useSelector(getWidgets); + const dispatch = useDispatch(); + const triggerAutoLayoutBasedReflow = (resizedPositions: OccupiedSpace) => { + const { widgetId } = props; + const widget = allWidgets[widgetId]; + if (!widget || !widget.parentId) return; + const parent = allWidgets[widget.parentId]; + if (!parent) return; + const flexLayers = parent.flexLayers; + const layerIndex = getLayerIndexOfWidget(flexLayers, widgetId); + if (layerIndex === -1) return; + const layer = flexLayers[layerIndex]; + const widgets = { + ...allWidgets, + [props.widgetId]: { + ...allWidgets[props.widgetId], + leftColumn: resizedPositions.left, + rightColumn: resizedPositions.right, + topRow: resizedPositions.top, + bottomRow: resizedPositions.bottom, + }, + }; + const fillWidgetsLength = getFillWidgetLengthForLayer(layer, widgets); + if (fillWidgetsLength) { + let correctedMovementMap: ReflowedSpaceMap = {}; + for (const child of layer.children) { + const childWidget = allWidgets[child.id]; + if (childWidget.responsiveBehavior === ResponsiveBehavior.Fill) { + correctedMovementMap = { + ...correctedMovementMap, + [child.id]: { + width: fillWidgetsLength * widget.parentColumnSpace, + }, + }; + } + } + dispatch(reflowMoveAction(correctedMovementMap)); + } + }; const setNewDimensions = (rect: DimensionProps) => { const { direction, height, width, x, y } = rect; @@ -309,6 +382,9 @@ export function ReflowResizable(props: ResizableProps) { if (bottomMostRow) { props.updateBottomRow(bottomMostRow); } + if (isAutoHeight && resizedPositions) { + triggerAutoLayoutBasedReflow(resizedPositions); + } return newRect; }); @@ -329,8 +405,8 @@ export function ReflowResizable(props: ResizableProps) { }, [props.componentHeight, props.componentWidth, isResizing]); const handles = []; - - if (props.handles.left) { + const widget = allWidgets[props.widgetId]; + if (!(isAutoHeight && widget.leftColumn === 0) && props.handles.left) { handles.push({ dragCallback: (x: number) => { setNewDimensions({ @@ -347,7 +423,7 @@ export function ReflowResizable(props: ResizableProps) { }); } - if (props.handles.top) { + if (!isAutoHeight && props.handles.top) { handles.push({ dragCallback: (x: number, y: number) => { setNewDimensions({ @@ -364,7 +440,12 @@ export function ReflowResizable(props: ResizableProps) { }); } - if (props.handles.right) { + if ( + !( + isAutoHeight && widget.rightColumn === GridDefaults.DEFAULT_GRID_COLUMNS + ) && + props.handles.right + ) { handles.push({ dragCallback: (x: number) => { setNewDimensions({ @@ -471,6 +552,9 @@ export function ReflowResizable(props: ResizableProps) { } const onResizeStop = () => { togglePointerEvents(true); + if (isAutoHeight) { + dispatch(stopReflowAction()); + } props.onStop( { width: newDimensions.width, @@ -518,15 +602,17 @@ export function ReflowResizable(props: ResizableProps) { /> ); }); - + const bufferForBoundary = 3; const widgetWidth = - reflowedPosition?.width === undefined + (reflowedPosition?.width === undefined ? newDimensions.width - : reflowedPosition.width - 2 * WIDGET_PADDING; + : reflowedPosition.width - 2 * WIDGET_PADDING) + + 2 * bufferForBoundary; const widgetHeight = - reflowedPosition?.height === undefined + (reflowedPosition?.height === undefined ? newDimensions.height - : reflowedPosition.height - 2 * WIDGET_PADDING; + : reflowedPosition.height - 2 * WIDGET_PADDING) + + 2 * bufferForBoundary; return ( {(_props) => ( @@ -550,7 +637,9 @@ export function ReflowResizable(props: ResizableProps) { $prevents={pointerEvents} className={props.className} id={`resize-${props.widgetId}`} + isHovered={props.isHovered} ref={resizableRef} + showBoundaries={props.showResizeBoundary} style={_props} > {props.children} diff --git a/app/client/src/utils/autoLayout/AutoLayoutUtils.ts b/app/client/src/utils/autoLayout/AutoLayoutUtils.ts index f2e41998707e..d27f634b0c6a 100644 --- a/app/client/src/utils/autoLayout/AutoLayoutUtils.ts +++ b/app/client/src/utils/autoLayout/AutoLayoutUtils.ts @@ -1,9 +1,3 @@ -import { - defaultAutoLayoutWidgets, - FlexLayerAlignment, - Positioning, - ResponsiveBehavior, -} from "utils/autoLayout/constants"; import { FlexLayer, LayerChild, @@ -15,6 +9,12 @@ import { } from "constants/WidgetConstants"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; +import { + defaultAutoLayoutWidgets, + FlexLayerAlignment, + Positioning, + ResponsiveBehavior, +} from "utils/autoLayout/constants"; import { updateWidgetPositions } from "utils/autoLayout/positionUtils"; function getCanvas(widgets: CanvasWidgetsReduxState, containerId: string) { @@ -379,17 +379,9 @@ export function getLayerIndexOfWidget( }); } export function getFillWidgetLengthForLayer( - allWidgets: CanvasWidgetsReduxState, - widgetId: string, + layer: any, + allWidgets: any, ): number | undefined { - const widget = allWidgets[widgetId]; - if (!widget || !widget.parentId) return; - const parent = allWidgets[widget.parentId]; - if (!parent) return; - const flexLayers = parent.flexLayers; - const layerIndex = getLayerIndexOfWidget(flexLayers, widgetId); - if (layerIndex === -1) return; - const layer = flexLayers[layerIndex]; let fillLength = GridDefaults.DEFAULT_GRID_COLUMNS; let hugLength = 0, fillCount = 0; diff --git a/app/client/src/utils/autoLayout/positionUtils.ts b/app/client/src/utils/autoLayout/positionUtils.ts index 2cbecdecf8ba..e23b550bd33e 100644 --- a/app/client/src/utils/autoLayout/positionUtils.ts +++ b/app/client/src/utils/autoLayout/positionUtils.ts @@ -1,14 +1,14 @@ -import { - FlexLayerAlignment, - Positioning, - ResponsiveBehavior, -} from "utils/autoLayout/constants"; import { FlexLayer } from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; import { GridDefaults, MAIN_CONTAINER_WIDGET_ID, } from "constants/WidgetConstants"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; +import { + FlexLayerAlignment, + Positioning, + ResponsiveBehavior, +} from "utils/autoLayout/constants"; import { WidgetProps } from "widgets/BaseWidget"; import { getBottomRow, diff --git a/app/client/src/utils/autocomplete/TernWorkerService.ts b/app/client/src/utils/autocomplete/TernWorkerService.ts index e308da7f23da..85dc2179b579 100644 --- a/app/client/src/utils/autocomplete/TernWorkerService.ts +++ b/app/client/src/utils/autocomplete/TernWorkerService.ts @@ -41,20 +41,22 @@ function TernWorkerServer(this: any, ts: any) { } worker.onmessage = function(e) { const data = e.data; - if (data.type == TernWorkerAction.GET_FILE) { - getFile(ts, data.name, function(err, text) { - send({ - type: TernWorkerAction.GET_FILE, - err: String(err), - text: text, - id: data.id, + if (data) { + if (data.type == TernWorkerAction.GET_FILE) { + getFile(ts, data.name, function(err, text) { + send({ + type: TernWorkerAction.GET_FILE, + err: String(err), + text: text, + id: data.id, + }); }); - }); - } else if (data.type == TernWorkerAction.DEBUG) { - window.console.log(data.message); - } else if (data.id && pending[data.id]) { - pending[data.id](data.err, data.body); - delete pending[data.id]; + } else if (data.type == TernWorkerAction.DEBUG) { + window.console.log(data.message); + } else if (data.id && pending[data.id]) { + pending[data.id](data.err, data.body); + delete pending[data.id]; + } } }; worker.onerror = function(e) { diff --git a/app/client/src/utils/hooks/useDynamicAppLayout.tsx b/app/client/src/utils/hooks/useDynamicAppLayout.tsx index 9fac7f3240aa..1f682a5f75c0 100644 --- a/app/client/src/utils/hooks/useDynamicAppLayout.tsx +++ b/app/client/src/utils/hooks/useDynamicAppLayout.tsx @@ -11,6 +11,7 @@ import { MAIN_CONTAINER_WIDGET_ID, } from "constants/WidgetConstants"; import { APP_MODE } from "entities/App"; +import { SIDE_NAV_WIDTH } from "pages/common/SideNav"; import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; import { getIsAppSettingsPaneOpen } from "selectors/appSettingsPaneSelectors"; import { @@ -26,18 +27,18 @@ import { getExplorerWidth, } from "selectors/explorerSelector"; import { getIsCanvasInitialized } from "selectors/mainCanvasSelectors"; -import { getPropertyPaneWidth } from "selectors/propertyPaneSelectors"; -import { scrollbarWidth } from "utils/helpers"; -import { useWindowSizeHooks } from "./dragResizeHooks"; import { getPaneCount, getTabsPaneWidth, isMultiPaneActive, } from "selectors/multiPaneSelectors"; -import { SIDE_NAV_WIDTH } from "pages/common/SideNav"; +import { getPropertyPaneWidth } from "selectors/propertyPaneSelectors"; +import { scrollbarWidth } from "utils/helpers"; +import { useWindowSizeHooks } from "./dragResizeHooks"; const BORDERS_WIDTH = 2; const GUTTER_WIDTH = 72; +export const AUTOLAYOUT_RESIZER_WIDTH_BUFFER = 40; export const useDynamicAppLayout = () => { const dispatch = useDispatch(); @@ -55,6 +56,7 @@ export const useDynamicAppLayout = () => { const tabsPaneWidth = useSelector(getTabsPaneWidth); const isMultiPane = useSelector(isMultiPaneActive); const paneCount = useSelector(getPaneCount); + const appPositioningType = useSelector(getCurrentAppPositioningType); // /** // * calculates min height @@ -139,6 +141,11 @@ export const useDynamicAppLayout = () => { ) { calculatedWidth = ele.clientWidth; } + + if (appPositioningType === AppPositioningTypes.AUTO && isPreviewMode) { + calculatedWidth -= AUTOLAYOUT_RESIZER_WIDTH_BUFFER; + } + switch (true) { case maxWidth < 0: case appLayout?.type === "FLUID": @@ -202,6 +209,7 @@ export const useDynamicAppLayout = () => { currentPageId, appMode, appLayout, + isPreviewMode, ]); const resizeObserver = new ResizeObserver(immediateDebouncedResize); @@ -217,7 +225,7 @@ export const useDynamicAppLayout = () => { return () => { ele && resizeObserver.unobserve(ele); }; - }, [appLayout, currentPageId]); + }, [appLayout, currentPageId, isPreviewMode]); /** * when screen height is changed, update canvas layout @@ -254,7 +262,6 @@ export const useDynamicAppLayout = () => { propertyPaneWidth, isAppSettingsPaneOpen, ]); - const appPositioningType = useSelector(getCurrentAppPositioningType); useEffect(() => { function relayoutAtBreakpoint() { diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index 40a890645fba..fcb0f70ba70b 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -348,25 +348,22 @@ abstract class BaseWidget< * @param showControls */ showWidgetName(content: ReactNode, showControls = false) { - return ( - <> - {!this.props.disablePropertyPane && ( - - )} + return !this.props.disablePropertyPane ? ( + {content} - + + ) : ( + { content } ); } diff --git a/app/client/src/widgets/DividerWidget/widget/index.test.tsx b/app/client/src/widgets/DividerWidget/widget/index.test.tsx index d4051bbe75fe..4e4908025738 100644 --- a/app/client/src/widgets/DividerWidget/widget/index.test.tsx +++ b/app/client/src/widgets/DividerWidget/widget/index.test.tsx @@ -1,10 +1,10 @@ +import { render } from "@testing-library/react"; +import { dark, theme } from "constants/DefaultTheme"; import React from "react"; +import { Provider } from "react-redux"; +import configureStore from "redux-mock-store"; import { ThemeProvider } from "styled-components"; import DividerWidget, { DividerWidgetProps } from "./"; -import configureStore from "redux-mock-store"; -import { render } from "@testing-library/react"; -import { Provider } from "react-redux"; -import { theme, dark } from "constants/DefaultTheme"; jest.mock("react-dnd", () => ({ useDrag: jest.fn().mockReturnValue([{ isDragging: false }, jest.fn()]), @@ -13,6 +13,13 @@ jest.mock("react-dnd", () => ({ describe("", () => { const initialState = { ui: { + users: { + featureFlag: { + data: { + AUTO_LAYOUT: false, + }, + }, + }, widgetDragResize: { lastSelectedWidget: "Widget1", selectedWidgets: ["Widget1"], diff --git a/app/client/src/widgets/DropdownWidget/widget/index.test.tsx b/app/client/src/widgets/DropdownWidget/widget/index.test.tsx index e7d5f34eae9e..86ac16eaed4e 100644 --- a/app/client/src/widgets/DropdownWidget/widget/index.test.tsx +++ b/app/client/src/widgets/DropdownWidget/widget/index.test.tsx @@ -1,10 +1,10 @@ +import { fireEvent, render, screen } from "@testing-library/react"; +import { dark, theme } from "constants/DefaultTheme"; import React from "react"; +import { Provider } from "react-redux"; +import configureStore from "redux-mock-store"; import { ThemeProvider } from "styled-components"; import DropdownWidget, { DropdownWidgetProps } from "./"; -import configureStore from "redux-mock-store"; -import { fireEvent, render, screen } from "@testing-library/react"; -import { Provider } from "react-redux"; -import { theme, dark } from "constants/DefaultTheme"; import "@testing-library/jest-dom"; @@ -21,6 +21,13 @@ describe("", () => { lastSelectedWidget: "Widget1", selectedWidgets: ["Widget1"], }, + users: { + featureFlag: { + data: { + AUTO_LAYOUT: false, + }, + }, + }, propertyPane: { isVisible: true, widgetId: "Widget1", diff --git a/app/client/test/testCommon.ts b/app/client/test/testCommon.ts index 87b055356eb0..7fe75c054d51 100644 --- a/app/client/test/testCommon.ts +++ b/app/client/test/testCommon.ts @@ -1,4 +1,3 @@ -import { editorInitializer } from "utils/editor/EditorUtils"; import { Page, ReduxActionTypes, @@ -11,6 +10,7 @@ import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsRe import { createSelector } from "reselect"; import { getCanvasWidgetsPayload } from "sagas/PageSagas"; import { getCanvasWidgets } from "selectors/entitiesSelector"; +import { editorInitializer } from "utils/editor/EditorUtils"; import { extractCurrentDSL } from "utils/WidgetPropsUtils"; import { AppState } from "@appsmith/reducers"; From 0d1ae2641986d27bda4051641f4224cd3ff2f63c Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Fri, 3 Feb 2023 19:53:21 +0530 Subject: [PATCH 454/708] polishing based on design feedback --- .../assets/icons/ads/app-icons/laptop-alt.svg | 4 +++ .../icons/ads/app-icons/monitor-alt.svg | 6 ++++ .../ads/app-icons/monitor-smartphone-alt.svg | 7 ++++ .../icons/ads/app-icons/smartphone-alt.svg | 4 +++ .../assets/icons/ads/app-icons/tablet-alt.svg | 4 +++ .../icons/ads/app-icons/tabletr-alt.svg | 4 +++ .../editorComponents/ResizableComponent.tsx | 2 +- .../WidgetNameComponent/SettingsControl.tsx | 8 ++--- app/client/src/constants/Colors.tsx | 2 +- .../pages/Editor/AppPositionTypeControl.tsx | 18 +++++----- .../Editor/MainContainerLayoutControl.tsx | 35 ++++++++++++------- .../Editor/WidgetsEditor/CanvasContainer.tsx | 27 ++++++++------ 12 files changed, 83 insertions(+), 38 deletions(-) create mode 100644 app/client/src/assets/icons/ads/app-icons/laptop-alt.svg create mode 100644 app/client/src/assets/icons/ads/app-icons/monitor-alt.svg create mode 100644 app/client/src/assets/icons/ads/app-icons/monitor-smartphone-alt.svg create mode 100644 app/client/src/assets/icons/ads/app-icons/smartphone-alt.svg create mode 100644 app/client/src/assets/icons/ads/app-icons/tablet-alt.svg create mode 100644 app/client/src/assets/icons/ads/app-icons/tabletr-alt.svg diff --git a/app/client/src/assets/icons/ads/app-icons/laptop-alt.svg b/app/client/src/assets/icons/ads/app-icons/laptop-alt.svg new file mode 100644 index 000000000000..6f5bc9b2bb06 --- /dev/null +++ b/app/client/src/assets/icons/ads/app-icons/laptop-alt.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/client/src/assets/icons/ads/app-icons/monitor-alt.svg b/app/client/src/assets/icons/ads/app-icons/monitor-alt.svg new file mode 100644 index 000000000000..cfe89ca5b891 --- /dev/null +++ b/app/client/src/assets/icons/ads/app-icons/monitor-alt.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/app/client/src/assets/icons/ads/app-icons/monitor-smartphone-alt.svg b/app/client/src/assets/icons/ads/app-icons/monitor-smartphone-alt.svg new file mode 100644 index 000000000000..37af17e6f9db --- /dev/null +++ b/app/client/src/assets/icons/ads/app-icons/monitor-smartphone-alt.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/app/client/src/assets/icons/ads/app-icons/smartphone-alt.svg b/app/client/src/assets/icons/ads/app-icons/smartphone-alt.svg new file mode 100644 index 000000000000..fdc158c121ad --- /dev/null +++ b/app/client/src/assets/icons/ads/app-icons/smartphone-alt.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/client/src/assets/icons/ads/app-icons/tablet-alt.svg b/app/client/src/assets/icons/ads/app-icons/tablet-alt.svg new file mode 100644 index 000000000000..967c5b6ff970 --- /dev/null +++ b/app/client/src/assets/icons/ads/app-icons/tablet-alt.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/client/src/assets/icons/ads/app-icons/tabletr-alt.svg b/app/client/src/assets/icons/ads/app-icons/tabletr-alt.svg new file mode 100644 index 000000000000..f5ba2f5c43db --- /dev/null +++ b/app/client/src/assets/icons/ads/app-icons/tabletr-alt.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/client/src/components/editorComponents/ResizableComponent.tsx b/app/client/src/components/editorComponents/ResizableComponent.tsx index dfe4e38ea338..879f0cf905c6 100644 --- a/app/client/src/components/editorComponents/ResizableComponent.tsx +++ b/app/client/src/components/editorComponents/ResizableComponent.tsx @@ -330,7 +330,7 @@ export const ResizableComponent = memo(function ResizableComponent( !(NonResizableWidgets.includes(props.type) || isMultiSelected) || !props.isFlexChild; const isHovered = isFocused && !isSelected; - const showResizeBoundary = isHovered || isSelected; + const showResizeBoundary = !isPreviewMode && (isHovered || isSelected); return ( ` justify-self: flex-end; height: 100%; @@ -30,7 +30,7 @@ const SettingsWrapper = styled.div<{ widgetWidth: number }>` display: flex; justify-content: space-between; align-items: center; - max-width: ${(props) => props.widgetWidth + 1}px; + max-width: ${(props) => props.widgetWidth - BORDER_RADIUS / 2}px; & { pre { margin: 0 5px 0 0; @@ -39,8 +39,8 @@ const SettingsWrapper = styled.div<{ widgetWidth: number }>` line-height: ${(props) => props.theme.fontSizes[3] - 1}px; } } - border-top-left-radius: 4px; - border-top-right-radius: 4px; + border-top-left-radius: ${BORDER_RADIUS}px; + border-top-right-radius: ${BORDER_RADIUS}px; border: ${WidgetNameBoundary}px solid ${Colors.GREY_1}; border-bottom: none; `; diff --git a/app/client/src/constants/Colors.tsx b/app/client/src/constants/Colors.tsx index 3cbd0e9a366b..38bfe8964564 100644 --- a/app/client/src/constants/Colors.tsx +++ b/app/client/src/constants/Colors.tsx @@ -75,7 +75,7 @@ export const Colors = { SOLID_MERCURY: "#E5E5E5", TROUT_DARK: "#535B62", ALABASTER: "#F9F8F8", - WATUSI: "#FFA67E", + WATUSI: "#FF9B4E", GRAY: "#858282", GRAY2: "#939090", DOVE_GRAY2: "#716e6e", diff --git a/app/client/src/pages/Editor/AppPositionTypeControl.tsx b/app/client/src/pages/Editor/AppPositionTypeControl.tsx index 2851018f0228..8aa07b5f006b 100644 --- a/app/client/src/pages/Editor/AppPositionTypeControl.tsx +++ b/app/client/src/pages/Editor/AppPositionTypeControl.tsx @@ -4,9 +4,11 @@ import { useDispatch, useSelector } from "react-redux"; import styled from "styled-components"; import { batchUpdateMultipleWidgetProperties } from "actions/controlActions"; +import { ReactComponent as DesktopIcon } from "assets/icons/ads/app-icons/monitor-alt.svg"; +import { ReactComponent as MultiDeviceIcon } from "assets/icons/ads/app-icons/monitor-smartphone-alt.svg"; import { Colors } from "constants/Colors"; import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; -import { Icon, IconName, IconSize, TooltipComponent } from "design-system-old"; +import { IconName, TooltipComponent } from "design-system-old"; import { AppPositioningTypeConfig, AppPositioningTypes, @@ -17,13 +19,15 @@ import { } from "selectors/editorSelectors"; import { LayoutDirection, Positioning } from "utils/autoLayout/constants"; import { MainContainerLayoutControl } from "./MainContainerLayoutControl"; - interface ApplicationPositionTypeConfigOption { name: string; type: AppPositioningTypes; icon?: IconName; } - +const IconObj: any = { + fluid: , + desktop: , +}; export const AppsmithDefaultPositionType: AppPositioningTypeConfig = { type: AppPositioningTypes.FIXED, }; @@ -151,11 +155,9 @@ export function AppPositionTypeControl() { ref={(input) => buttonRefs.push(input)} tabIndex={index === focusedIndex ? 0 : -1} > - +
+ {IconObj[layoutOption.icon]} +
); diff --git a/app/client/src/pages/Editor/MainContainerLayoutControl.tsx b/app/client/src/pages/Editor/MainContainerLayoutControl.tsx index 2a5c71e88266..11121ea23fe7 100644 --- a/app/client/src/pages/Editor/MainContainerLayoutControl.tsx +++ b/app/client/src/pages/Editor/MainContainerLayoutControl.tsx @@ -1,19 +1,30 @@ +import { ReactComponent as DesktopIcon } from "assets/icons/ads/app-icons/monitor-alt.svg"; +import { ReactComponent as MultiDeviceIcon } from "assets/icons/ads/app-icons/monitor-smartphone-alt.svg"; +import { ReactComponent as MobileIcon } from "assets/icons/ads/app-icons/smartphone-alt.svg"; +import { ReactComponent as TabletIcon } from "assets/icons/ads/app-icons/tablet-alt.svg"; +import { ReactComponent as TabletLandscapeIcon } from "assets/icons/ads/app-icons/tabletr-alt.svg"; import classNames from "classnames"; +import React, { useCallback, useMemo } from "react"; import { useDispatch, useSelector } from "react-redux"; -import React, { useMemo, useCallback } from "react"; -import { - getCurrentApplicationId, - getCurrentApplicationLayout, -} from "selectors/editorSelectors"; -import { Colors } from "constants/Colors"; +import { updateApplicationLayout } from "actions/applicationActions"; +import { IconName, TooltipComponent } from "design-system-old"; import { AppLayoutConfig, SupportedLayouts, } from "reducers/entityReducers/pageListReducer"; -import { TooltipComponent, Icon, IconName, IconSize } from "design-system-old"; -import { updateApplicationLayout } from "actions/applicationActions"; +import { + getCurrentApplicationId, + getCurrentApplicationLayout, +} from "selectors/editorSelectors"; +const IconObj: any = { + FLUID: , + DESKTOP: , + TABLET: , + TABLET_LARGE: , + MOBILE: , +}; interface AppsmithLayoutConfigOption { name: string; type: SupportedLayouts; @@ -140,11 +151,9 @@ export function MainContainerLayoutControl() { ref={(input) => buttonRefs.push(input)} tabIndex={index === focusedIndex ? 0 : -1} > - +
+ {IconObj[layoutOption.type]} +
); diff --git a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx index 72cb842b204b..6b73d57e52f2 100644 --- a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx +++ b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx @@ -44,36 +44,40 @@ import Canvas from "../Canvas"; const AutoLayoutCanvasResizer = styled.div` position: sticky; cursor: col-resize; - width: 4px; + width: 2px; height: 100%; display: flex; background: #d9d9d9; align-items: center; justify-content: flex-start; z-index: 2; + transition: width 300ms ease; + transition: background 300ms ease; .canvas-resizer-icon { border-left: 4px solid; border-color: #d7d7d7; - margin-left: 4px; + transition: border 300ms ease; + margin-left: 2px; & > svg { fill: #d7d7d7; + transition: fill 300ms ease; } } - &:hover { + &:hover, + &:active { + width: 3px; + transition: width 300ms ease; background: #ff9b4e; - transition: background 250ms ease; + transition: background 300ms ease; .canvas-resizer-icon { border-color: #ff9b4e; - transition: border 250ms ease; + transition: border 300ms ease; & > svg { fill: #ff9b4e; - transition: fill 250ms ease; + transition: fill 300ms ease; } } } - &::after { - height: 100%; - } `; const Container = styled.section<{ background: string; @@ -207,16 +211,17 @@ function CanvasContainer() { // e.stopPropagation(); }; - const mouseUpHandler = function() { + const mouseUpHandler = function(e: any) { // Remove the handlers of `mousemove` and `mouseup` + mouseMoveHandler(e); document.removeEventListener("mousemove", events[0] as any); document.removeEventListener("mouseup", mouseUpHandler); events = []; }; const rightResizer: any = ele.querySelectorAll(".resizer-right")[0]; const rightMove = (e: any) => mouseDownHandler(e); - rightResizer.addEventListener("mousedown", rightMove); + return () => { rightResizer.removeEventListener("mousedown", rightMove); }; From 1ff271aaf590bd771c0267d70dcf53bb84ebf258 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Fri, 3 Feb 2023 19:55:05 +0530 Subject: [PATCH 455/708] border css update. --- app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx index 6b73d57e52f2..a7772afb1ca3 100644 --- a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx +++ b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx @@ -54,7 +54,7 @@ const AutoLayoutCanvasResizer = styled.div` transition: width 300ms ease; transition: background 300ms ease; .canvas-resizer-icon { - border-left: 4px solid; + border-left: 2px solid; border-color: #d7d7d7; transition: border 300ms ease; margin-left: 2px; From 929a2241176d11a591fe78279f0cf98265d68604 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Fri, 3 Feb 2023 22:27:44 +0530 Subject: [PATCH 456/708] readjusting lost dimension buffers. --- .../WidgetNameComponent/index.tsx | 50 +++++++++++-------- .../src/resizable/resizenreflow/index.tsx | 27 ++++++---- 2 files changed, 47 insertions(+), 30 deletions(-) diff --git a/app/client/src/components/editorComponents/WidgetNameComponent/index.tsx b/app/client/src/components/editorComponents/WidgetNameComponent/index.tsx index 3af87023589c..a20a2096640c 100644 --- a/app/client/src/components/editorComponents/WidgetNameComponent/index.tsx +++ b/app/client/src/components/editorComponents/WidgetNameComponent/index.tsx @@ -5,6 +5,7 @@ import { Layers } from "constants/Layers"; import { WidgetType } from "constants/WidgetConstants"; import React from "react"; import { useDispatch, useSelector } from "react-redux"; +import { RESIZE_BORDER_BUFFER } from "resizable/resizenreflow"; import { SelectionRequestType } from "sagas/WidgetSelectUtils"; import { hideErrors } from "selectors/debuggerSelectors"; import { @@ -144,6 +145,7 @@ export function WidgetNameComponent(props: WidgetNameComponentProps) { selectedWidgets.includes(props.widgetId); const shouldShowWidgetName = () => { return ( + !isResizingOrDragging && !isPreviewMode && !isMultiSelectedWidget && (isSnipingMode @@ -238,7 +240,8 @@ export function WidgetNameComponent(props: WidgetNameComponentProps) { }); } }; - const popperOffset: any = [-5, 4]; + // bottom offset is RESIZE_BORDER_BUFFER - 1 because bottom border is none for the widget name + const popperOffset: any = [-RESIZE_BORDER_BUFFER, RESIZE_BORDER_BUFFER - 1]; const widgetWidth = (props.widgetProps.rightColumn - props.widgetProps.leftColumn) * props.widgetProps.parentColumnSpace; @@ -246,27 +249,34 @@ export function WidgetNameComponent(props: WidgetNameComponentProps) { - - - - + // adding this here as well to instantly remove popper content. popper seems to be adding a transition state before hiding itself. + // I could not find a way to turn it off. + showWidgetName ? ( + + + + + + ) : ( +
+ ) } enforceFocus={false} - isOpen={showWidgetName && !isResizingOrDragging} + hoverCloseDelay={0} + isOpen={showWidgetName} minimal modifiers={{ offset: { diff --git a/app/client/src/resizable/resizenreflow/index.tsx b/app/client/src/resizable/resizenreflow/index.tsx index 190a979b6f46..c3985226b166 100644 --- a/app/client/src/resizable/resizenreflow/index.tsx +++ b/app/client/src/resizable/resizenreflow/index.tsx @@ -36,6 +36,13 @@ import PerformanceTracker, { PerformanceTransactionName, } from "utils/PerformanceTracker"; import { isDropZoneOccupied } from "utils/WidgetPropsUtils"; +const resizeBorderPadding = 1; +const resizeBorder = 1; +const resizeBoxShadow = 1; +const resizeOutline = 1; + +export const RESIZE_BORDER_BUFFER = + resizeBorderPadding + resizeBorder + resizeBoxShadow + resizeOutline; const ResizeWrapper = styled(animated.div)<{ $prevents: boolean; @@ -51,16 +58,17 @@ const ResizeWrapper = styled(animated.div)<{ ${(props) => { if (props.showBoundaries) { return ` - box-shadow: 0px 0px 0px 1px ${ + box-shadow: 0px 0px 0px ${resizeBoxShadow}px ${ props.isHovered ? Colors.WATUSI : "#f86a2b" }; border-radius: 0px 4px 4px 4px; - border: 1px solid ${Colors.GREY_1}; - outline: 1px solid ${Colors.GREY_1} !important; + border: ${resizeBorder}px solid ${Colors.GREY_1}; + padding: ${resizeBorderPadding}px; + outline: ${resizeOutline}px solid ${Colors.GREY_1} !important; outline-offset: 1px;`; } else { return ` - border: 1px solid transparent; + border: 0px solid transparent; `; } }}} @@ -602,17 +610,15 @@ export function ReflowResizable(props: ResizableProps) { /> ); }); - const bufferForBoundary = 3; + const bufferForBoundary = props.showResizeBoundary ? RESIZE_BORDER_BUFFER : 0; const widgetWidth = (reflowedPosition?.width === undefined ? newDimensions.width - : reflowedPosition.width - 2 * WIDGET_PADDING) + - 2 * bufferForBoundary; + : reflowedPosition.width - 2 * WIDGET_PADDING) + bufferForBoundary; const widgetHeight = (reflowedPosition?.height === undefined ? newDimensions.height - : reflowedPosition.height - 2 * WIDGET_PADDING) + - 2 * bufferForBoundary; + : reflowedPosition.height - 2 * WIDGET_PADDING) + bufferForBoundary; return ( {(_props) => ( From 00f3289ddc153e6814a49aa690f9f59c0f34cbca Mon Sep 17 00:00:00 2001 From: Preet Date: Fri, 3 Feb 2023 12:28:04 -0500 Subject: [PATCH 457/708] fix dnd issues post merge --- .../designSystems/appsmith/autoLayout/FlexComponent.tsx | 1 - app/client/src/utils/autoLayout/highlightUtils.ts | 8 ++++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx index 57ea86704d6d..02341d72442c 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx @@ -98,7 +98,6 @@ export function FlexComponent(props: AutoLayoutProps) { return ( Date: Fri, 3 Feb 2023 13:07:09 -0500 Subject: [PATCH 458/708] hide resize boundary when a widget is being dragged --- .../src/components/editorComponents/ResizableComponent.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/client/src/components/editorComponents/ResizableComponent.tsx b/app/client/src/components/editorComponents/ResizableComponent.tsx index 468bff0d335a..eaae309b7119 100644 --- a/app/client/src/components/editorComponents/ResizableComponent.tsx +++ b/app/client/src/components/editorComponents/ResizableComponent.tsx @@ -336,7 +336,8 @@ export const ResizableComponent = memo(function ResizableComponent( !(NonResizableWidgets.includes(props.type) || isMultiSelected) || !props.isFlexChild; const isHovered = isFocused && !isSelected; - const showResizeBoundary = !isPreviewMode && (isHovered || isSelected); + const showResizeBoundary = + !isPreviewMode && !isDragging && (isHovered || isSelected); return ( Date: Sat, 4 Feb 2023 00:08:28 +0530 Subject: [PATCH 459/708] fixing merge bugs. --- .../src/resizable/resizenreflow/index.tsx | 25 +++++++++++-------- .../src/utils/autoLayout/AutoLayoutUtils.ts | 9 +++++-- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/app/client/src/resizable/resizenreflow/index.tsx b/app/client/src/resizable/resizenreflow/index.tsx index 4fa1f2fd777d..6e19b51a5c01 100644 --- a/app/client/src/resizable/resizenreflow/index.tsx +++ b/app/client/src/resizable/resizenreflow/index.tsx @@ -1,10 +1,15 @@ -import React, { ReactNode, useState, useEffect, useRef } from "react"; -import styled, { StyledComponent } from "styled-components"; +import { reflowMoveAction, stopReflowAction } from "actions/reflowActions"; +import { isHandleResizeAllowed } from "components/editorComponents/ResizableUtils"; +import { OccupiedSpace } from "constants/CanvasEditorConstants"; +import { Colors } from "constants/Colors"; import { GridDefaults, WidgetHeightLimits, WIDGET_PADDING, } from "constants/WidgetConstants"; +import React, { ReactNode, useEffect, useRef, useState } from "react"; +import { useDispatch, useSelector } from "react-redux"; +import { animated, Spring } from "react-spring"; import { useDrag } from "react-use-gesture"; import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; import { @@ -20,6 +25,7 @@ import { getCurrentAppPositioningType, } from "selectors/editorSelectors"; import { getReflowSelector } from "selectors/widgetReflowSelectors"; +import styled, { StyledComponent } from "styled-components"; import { getFillWidgetLengthForLayer, getLayerIndexOfWidget, @@ -34,12 +40,6 @@ import PerformanceTracker, { PerformanceTransactionName, } from "utils/PerformanceTracker"; import { isDropZoneOccupied } from "utils/WidgetPropsUtils"; -import { useDispatch, useSelector } from "react-redux"; -import { OccupiedSpace } from "constants/CanvasEditorConstants"; -import { reflowMoveAction, stopReflowAction } from "actions/reflowActions"; -import { animated, Spring } from "react-spring"; -import { Colors } from "constants/Colors"; -import { isHandleResizeAllowed } from "components/editorComponents/ResizableUtils"; const resizeBorderPadding = 1; const resizeBorder = 1; const resizeBoxShadow = 1; @@ -321,7 +321,10 @@ export function ReflowResizable(props: ResizableProps) { let correctedMovementMap: ReflowedSpaceMap = {}; for (const child of layer.children) { const childWidget = allWidgets[child.id]; - if (childWidget.responsiveBehavior === ResponsiveBehavior.Fill) { + if ( + childWidget && + childWidget.responsiveBehavior === ResponsiveBehavior.Fill + ) { correctedMovementMap = { ...correctedMovementMap, [child.id]: { @@ -661,7 +664,9 @@ export function ReflowResizable(props: ResizableProps) { maxHeight: (props.maxDynamicHeight || WidgetHeightLimits.MAX_HEIGHT_IN_ROWS) * GridDefaults.DEFAULT_GRID_ROW_HEIGHT, - transform: `translate3d(${newDimensions.x}px,${newDimensions.y}px,0)`, + transform: `translate3d(${newDimensions.x - + bufferForBoundary / 2}px,${newDimensions.y - + bufferForBoundary / 2}px,0)`, }} > {(_props) => ( diff --git a/app/client/src/utils/autoLayout/AutoLayoutUtils.ts b/app/client/src/utils/autoLayout/AutoLayoutUtils.ts index d27f634b0c6a..b107342abf7e 100644 --- a/app/client/src/utils/autoLayout/AutoLayoutUtils.ts +++ b/app/client/src/utils/autoLayout/AutoLayoutUtils.ts @@ -387,9 +387,14 @@ export function getFillWidgetLengthForLayer( fillCount = 0; for (const child of layer.children) { const childWidget = allWidgets[child.id]; - if (childWidget.responsiveBehavior !== ResponsiveBehavior.Fill) + if (!childWidget) { + continue; + } + if (childWidget.responsiveBehavior !== ResponsiveBehavior.Fill) { hugLength += childWidget.rightColumn - childWidget.leftColumn; - else fillCount += 1; + } else { + fillCount += 1; + } } fillLength = (fillLength - hugLength) / (fillCount || 1); return fillLength; From 2b7ae435c1f4b033eb2a333a4f13e0f1b20e9f1b Mon Sep 17 00:00:00 2001 From: rahulramesha Date: Mon, 6 Feb 2023 18:40:36 +0530 Subject: [PATCH 460/708] auto to fixed Conversion algorithm commit --- .../appsmith/autoLayout/FlexBoxComponent.tsx | 13 +- .../src/sagas/AutoLayoutUpdateSagas.tsx | 34 +- .../utils/DSLConversions/autoToFixedLayout.ts | 498 ++++++++++++++++++ .../utils/DSLConversions/fixedToAutoLayout.ts | 2 +- .../src/utils/autoLayout/flexWidgetUtils.ts | 24 +- 5 files changed, 528 insertions(+), 43 deletions(-) create mode 100644 app/client/src/utils/DSLConversions/autoToFixedLayout.ts diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx index 9d338420bdf3..bd9371e0a78b 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx @@ -12,7 +12,7 @@ import { useSelector } from "react-redux"; import { getWidgets } from "sagas/selectors"; import { getAppMode } from "selectors/entitiesSelector"; import AutoLayoutLayer from "./AutoLayoutLayer"; -import { FLEXBOX_PADDING } from "constants/WidgetConstants"; +import { FLEXBOX_PADDING, GridDefaults } from "constants/WidgetConstants"; import { getWidgetWidth } from "utils/autoLayout/flexWidgetUtils"; export interface FlexBoxProps { @@ -149,10 +149,13 @@ function FlexBoxComponent(props: FlexBoxProps) { key={index} start={start} widgetId={props.widgetId} - wrapCenter={centerColumns > 64} - wrapEnd={endColumns > 64} - wrapLayer={startColumns + centerColumns + endColumns > 64} - wrapStart={startColumns > 64} + wrapCenter={centerColumns > GridDefaults.DEFAULT_GRID_COLUMNS} + wrapEnd={endColumns > GridDefaults.DEFAULT_GRID_COLUMNS} + wrapLayer={ + startColumns + centerColumns + endColumns > + GridDefaults.DEFAULT_GRID_COLUMNS + } + wrapStart={startColumns > GridDefaults.DEFAULT_GRID_COLUMNS} /> ); } diff --git a/app/client/src/sagas/AutoLayoutUpdateSagas.tsx b/app/client/src/sagas/AutoLayoutUpdateSagas.tsx index 75515be5128c..de1094c2a083 100644 --- a/app/client/src/sagas/AutoLayoutUpdateSagas.tsx +++ b/app/client/src/sagas/AutoLayoutUpdateSagas.tsx @@ -4,11 +4,7 @@ import { ReduxActionErrorTypes, ReduxActionTypes, } from "ce/constants/ReduxActionConstants"; -import { - LayoutDirection, - Positioning, - ResponsiveBehavior, -} from "utils/autoLayout/constants"; +import { ResponsiveBehavior } from "utils/autoLayout/constants"; import log from "loglevel"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; import { all, call, put, select, takeLatest } from "redux-saga/effects"; @@ -23,7 +19,6 @@ import { import { getWidgets } from "./selectors"; import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; -import { batchUpdateMultipleWidgetProperties } from "actions/controlActions"; import { getCurrentAppPositioningType, getMainCanvasProps, @@ -32,6 +27,7 @@ import { MainCanvasReduxState } from "reducers/uiReducers/mainCanvasReducer"; import { updateLayoutForMobileBreakpointAction } from "actions/autoLayoutActions"; import CanvasWidgetsNormalizer from "normalizers/CanvasWidgetsNormalizer"; import convertDSLtoAuto from "utils/DSLConversions/fixedToAutoLayout"; +import { convertNormalizedDSLToFixed } from "utils/DSLConversions/autoToFixedLayout"; type LayoutUpdatePayload = { parentId: string; @@ -149,17 +145,16 @@ export function* updateLayoutPositioningSaga( actionPayload: ReduxAction, ) { try { + const currPositioningType: AppPositioningTypes = yield select( + getCurrentAppPositioningType, + ); const payloadPositioningType = actionPayload.payload; - if (payloadPositioningType === AppPositioningTypes.AUTO) { - const currPositioningType: AppPositioningTypes = yield select( - getCurrentAppPositioningType, - ); + if (currPositioningType === payloadPositioningType) return; - if (currPositioningType === AppPositioningTypes.AUTO) return; - - const allWidgets: CanvasWidgetsReduxState = yield select(getWidgets); + const allWidgets: CanvasWidgetsReduxState = yield select(getWidgets); + if (payloadPositioningType === AppPositioningTypes.AUTO) { const denormalizedDSL = CanvasWidgetsNormalizer.denormalize( MAIN_CONTAINER_WIDGET_ID, { canvasWidgets: allWidgets }, @@ -177,18 +172,7 @@ export function* updateLayoutPositioningSaga( yield call(recalculateOnPageLoad); } else { yield put( - batchUpdateMultipleWidgetProperties([ - { - widgetId: MAIN_CONTAINER_WIDGET_ID, - updates: { - modify: { - positioning: Positioning.Fixed, - useAutoLayout: false, - direction: LayoutDirection.Vertical, - }, - }, - }, - ]), + updateAndSaveLayout(convertNormalizedDSLToFixed(allWidgets, "DESKTOP")), ); } } catch (error) { diff --git a/app/client/src/utils/DSLConversions/autoToFixedLayout.ts b/app/client/src/utils/DSLConversions/autoToFixedLayout.ts new file mode 100644 index 000000000000..b22cdb4cf818 --- /dev/null +++ b/app/client/src/utils/DSLConversions/autoToFixedLayout.ts @@ -0,0 +1,498 @@ +import { FlexLayer } from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; +import { + GridDefaults, + layoutConfigurations, + MAIN_CONTAINER_WIDGET_ID, +} from "constants/WidgetConstants"; +import { partition } from "lodash"; +import CanvasWidgetsNormalizer from "normalizers/CanvasWidgetsNormalizer"; +import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; +import { SupportedLayouts } from "reducers/entityReducers/pageListReducer"; +import { HORIZONTAL_RESIZE_MIN_LIMIT } from "reflow/reflowTypes"; +import { + alterLayoutForDesktop, + alterLayoutForMobile, +} from "utils/autoLayout/AutoLayoutUtils"; +import { FlexLayerAlignment, Positioning } from "utils/autoLayout/constants"; +import { + getWidgetWidth, + getWidgetHeight, + getTopRow, + getBottomRow, + getLeftColumn, + getRightColumn, +} from "utils/autoLayout/flexWidgetUtils"; +import { DSLWidget, FlattenedWidgetProps } from "widgets/constants"; + +type LayerAlignmentData = { + widgets: FlattenedWidgetProps[]; + width: number; + type: FlexLayerAlignment; +}; + +type AlignmentLayerMap = { + start: LayerAlignmentData; + center: LayerAlignmentData; + end: LayerAlignmentData; +}; + +const nonFlexLayerWidgets = ["MODAL_WIDGET"]; + +const deletedResponsiveProperties = [ + "mobileLeftColumn", + "mobileRightColumn", + "mobileTopRow", + "mobileBottomRow", + "responsiveBehavior", + "alignment", + "flexVerticalAlignment", +]; + +/** + * Main Method to convert Auto DSL to Fixed DSL + * @param dsl DSL to be Converted to Fixed Layout + * @param destinationLayout Destination Layout Size + * @returns Converted Fixed DSL + */ +export default function convertDSLtoFixed( + dsl: DSLWidget, + destinationLayout: SupportedLayouts, +) { + const allWidgets = CanvasWidgetsNormalizer.normalize(dsl).entities + .canvasWidgets; + + const convertedWidgets = convertNormalizedDSLToFixed( + allWidgets, + destinationLayout, + ); + + const convertedDSL = CanvasWidgetsNormalizer.denormalize( + MAIN_CONTAINER_WIDGET_ID, + { + canvasWidgets: convertedWidgets, + }, + ); + + return convertedDSL; +} + +/** + * Convert Normalized Auto DSL to Fixed Layout DSL + * @param widgets Normalized Auto DSL to be converted + * @param destinationLayout Destination Layout Size + * @returns Converted Normalized Fixed Layout DSL + */ +export function convertNormalizedDSLToFixed( + widgets: CanvasWidgetsReduxState, + destinationLayout: SupportedLayouts, +) { + const isMobile = getIsMobile(destinationLayout); + + const alteredWidgets = isMobile + ? alterLayoutForMobile( + widgets, + MAIN_CONTAINER_WIDGET_ID, + layoutConfigurations[destinationLayout].maxWidth || + layoutConfigurations.MOBILE.maxWidth, + ) + : alterLayoutForDesktop(widgets, MAIN_CONTAINER_WIDGET_ID); + + const convertedWidgets = getFixedCanvasWidget( + alteredWidgets, + MAIN_CONTAINER_WIDGET_ID, + isMobile, + ); + + return convertedWidgets; +} + +/** + * Converts Widget with widgetId and it's children to Fixed layout recursively + * @param widgets Normalized Auto DSL + * @param widgetId + * @param isMobile + * @returns + */ +function convertAutoWidgetToFixed( + widgets: CanvasWidgetsReduxState, + widgetId: string, + isMobile: boolean, +): CanvasWidgetsReduxState { + const currWidget = { ...widgets[widgetId] }; + if (!currWidget) return widgets; + + let currWidgets = { ...widgets }; + + if (currWidget.type === "CANVAS_WIDGET") { + return getFixedCanvasWidget(currWidgets, widgetId, isMobile); + } else { + if (currWidget.children && currWidget.children.length > 0) { + for (const childId of currWidget.children) { + currWidgets = convertAutoWidgetToFixed(currWidgets, childId, isMobile); + } + } + + // Delete responsive properties of widgets + for (const responsiveProperty of deletedResponsiveProperties) { + delete currWidget[responsiveProperty]; + } + + currWidgets[widgetId] = { ...currWidget }; + } + + return currWidgets; +} + +/** + * Convert the Canvas Widget of canvasId to Fixed + * @param widgets + * @param canvasId + * @param isMobile + * @returns + */ +function getFixedCanvasWidget( + widgets: CanvasWidgetsReduxState, + canvasId: string, + isMobile: boolean, +): CanvasWidgetsReduxState { + const canvasWidget = { ...widgets[canvasId] }; + if ( + !canvasWidget || + !canvasWidget.children || + !canvasWidget.useAutoLayout || + !canvasWidget.flexLayers + ) { + return widgets; + } + + //if Mobile, use the existing already calculated positions in `alterLayoutForMobile` + if (isMobile) { + return processMobileCanvasChildren(widgets, canvasId); + } + + //If not mobile/wrapped use the flexLayer alignments to updated the positions of the widgets + const { children, updatedWidgets } = processLayers( + widgets, + canvasWidget.flexLayers, + ); + + //separate the widgets to be skipped + const [nonLayerChildren, layerChildren] = partition( + canvasWidget.children, + (widgetId) => + nonFlexLayerWidgets.indexOf(updatedWidgets[widgetId].type) > -1, + ); + + // delete widgets that are in Layer Children but not in children + const deletedWidgets = layerChildren.filter((f) => !children.includes(f)); + + for (const deletedWidgetId of deletedWidgets) { + delete updatedWidgets[deletedWidgetId]; + } + + // Delete Canvas widget responsive properties + delete canvasWidget.flexLayers; + delete canvasWidget.responsiveBehavior; + + updatedWidgets[canvasId] = { + ...canvasWidget, + children: [...children, ...nonLayerChildren], + useAutoLayout: false, + positioning: Positioning.Fixed, + }; + + return updatedWidgets; +} + +/** + * Process the mobile canvas Widgets with already existing positions/dimensions + * @param widgets + * @param canvasId + * @returns + */ +function processMobileCanvasChildren( + widgets: CanvasWidgetsReduxState, + canvasId: string, +) { + const canvasWidget = { ...widgets[canvasId] }; + + let currWidgets = { ...widgets }; + + for (const childId of canvasWidget.children || []) { + currWidgets = convertAutoWidgetToFixed(currWidgets, childId, true); + const currWidget = currWidgets[childId]; + + currWidgets[childId] = { + ...currWidget, + topRow: getTopRow(currWidget, true), + bottomRow: getBottomRow(currWidget, true), + leftColumn: getLeftColumn(currWidget, true), + rightColumn: getRightColumn(currWidget, true), + }; + } + + // Delete Canvas widget responsive properties + delete canvasWidget.flexLayers; + delete canvasWidget.responsiveBehavior; + + currWidgets[canvasId] = { + ...canvasWidget, + useAutoLayout: false, + positioning: Positioning.Fixed, + }; + + return currWidgets; +} + +/** + * Process Layers iteratively to calculate positions based on layer and alignment + * @param widgets + * @param flexLayers + * @returns + */ +function processLayers( + widgets: CanvasWidgetsReduxState, + flexLayers: FlexLayer[], +) { + let bottomRow = 0; + let updatedWidgets = { ...widgets }; + let children: string[] = []; + + for (const flexLayer of flexLayers) { + let currChildren; + ({ bottomRow, currChildren, updatedWidgets } = processIndividualLayer( + updatedWidgets, + flexLayer, + bottomRow, + )); + + children = [...children, ...currChildren]; + } + + return { updatedWidgets, children }; +} + +/** + * Process Individual layer to calculate positions based on layer and alignment + * @param widgets + * @param flexLayer + * @param currentBottomRow + * @returns + */ +function processIndividualLayer( + widgets: CanvasWidgetsReduxState, + flexLayer: FlexLayer, + currentBottomRow: number, +) { + const { children: flexChildren } = flexLayer; + + let currChildren: string[] = []; + + const alignmentLayerMap: AlignmentLayerMap = { + start: { widgets: [], width: 0, type: FlexLayerAlignment.Start }, + center: { widgets: [], width: 0, type: FlexLayerAlignment.Center }, + end: { widgets: [], width: 0, type: FlexLayerAlignment.End }, + }; + + //update alignment layer map by iterating each children within layer + for (const child of flexChildren) { + const widget = widgets[child.id]; + currChildren.push(child.id); + + if (child.align === "end") { + alignmentLayerMap.end.widgets.push(widget); + alignmentLayerMap.end.width += getWidgetWidth(widget, false); + } else if (child.align === "center") { + alignmentLayerMap.center.widgets.push(widget); + alignmentLayerMap.center.width += getWidgetWidth(widget, false); + } else { + alignmentLayerMap.start.widgets.push(widget); + alignmentLayerMap.start.width += getWidgetWidth(widget, false); + } + } + + //using alignmentLayerMap calculate and update positions of each children within layer + const { children, currWidgets, nextBottomRow } = placeWidgetsWithoutWrap( + widgets, + alignmentLayerMap, + currentBottomRow, + ); + + currChildren = [...children]; + + return { + updatedWidgets: currWidgets, + bottomRow: nextBottomRow, + currChildren, + }; +} + +/** + * returns if the destinationLayout isMobile ("Logic can be updated later based on updated logic") + * @param destinationLayout + * @returns + */ +function getIsMobile(destinationLayout: SupportedLayouts) { + return destinationLayout === "MOBILE"; +} + +/** + * using alignmentLayerMap calculate and update positions of each children within layer without wrapping + * @param widgets + * @param alignmentLayerMap + * @param currentBottomRow + * @returns + */ +function placeWidgetsWithoutWrap( + widgets: CanvasWidgetsReduxState, + alignmentLayerMap: AlignmentLayerMap, + currentBottomRow: number, +): { + currWidgets: CanvasWidgetsReduxState; + nextBottomRow: number; + children: string[]; +} { + let children: string[] = []; + let availableLeftColumn = 0; + + let maxBottomRow = currentBottomRow; + + let currWidgets = { ...widgets }; + + const layerAlignmentDataList = Object.values(alignmentLayerMap); + + for (const layerAlignmentData of layerAlignmentDataList) { + const { type, widgets, width } = layerAlignmentData; + + if (widgets.length > 0) { + const { + children: currChildren, + convertedWidgets, + currentAvailableLeftColumn, + maxBottomRow: currMaxBottomRow, + } = calculateWidgetDimensionForAlignment( + currWidgets, + availableLeftColumn, + widgets, + width, + type, + currentBottomRow, + ); + currWidgets = { ...convertedWidgets }; + children = [...children, ...currChildren]; + availableLeftColumn = currentAvailableLeftColumn; + maxBottomRow = Math.max(currMaxBottomRow, maxBottomRow); + } + } + + return { currWidgets, children, nextBottomRow: maxBottomRow }; +} + +/** + * Calculate widget positions of each widget in alignedWidgets + * @param widgets + * @param availableLeftColumn currently available left column + * @param alignedWidgets widgets to be updated in the current Alignment + * @param alignedWidth width of all widgets in the alignment + * @param flexLayerAlignment + * @param currentBottomRow Current bottom Row. + * @returns + */ +function calculateWidgetDimensionForAlignment( + widgets: CanvasWidgetsReduxState, + availableLeftColumn: number, + alignedWidgets: FlattenedWidgetProps[], + alignedWidth: number, + flexLayerAlignment: FlexLayerAlignment, + currentBottomRow: number, +): { + convertedWidgets: CanvasWidgetsReduxState; + children: string[]; + currentAvailableLeftColumn: number; + maxBottomRow: number; +} { + //Compare calculated leftColumn based on width to availableLeftColumn + let currentAvailableLeftColumn = Math.max( + getCalculatedLeftColumn( + alignedWidth, + GridDefaults.DEFAULT_GRID_COLUMNS, + flexLayerAlignment, + ), + availableLeftColumn, + ); + let maxBottomRow = currentBottomRow; + + let currWidgets: CanvasWidgetsReduxState = { ...widgets }; + const children = []; + + for (const widget of alignedWidgets) { + const width = getWidgetWidth(widget, false); + const height = getWidgetHeight(widget, false); + + //if the currentAvailableLeftColumn cannot possibly fit another widget within, + //return with not adding the ids in the children, which can be further used to delete those widgets + if ( + currentAvailableLeftColumn >= + GridDefaults.DEFAULT_GRID_COLUMNS - HORIZONTAL_RESIZE_MIN_LIMIT + ) + return { + convertedWidgets: currWidgets, + children, + currentAvailableLeftColumn, + maxBottomRow, + }; + + const leftColumn = currentAvailableLeftColumn; + const rightColumn = Math.min( + currentAvailableLeftColumn + width, + GridDefaults.DEFAULT_GRID_COLUMNS, + ); + + //update positions + currWidgets[widget.widgetId] = { + ...widget, + topRow: currentBottomRow, + bottomRow: currentBottomRow + height, + leftColumn: leftColumn, + rightColumn: rightColumn, + }; + + //call convertAutoWidgetToFixed to recursively calculate positions + currWidgets = convertAutoWidgetToFixed(currWidgets, widget.widgetId, false); + children.push(widget.widgetId); + currentAvailableLeftColumn = rightColumn; + maxBottomRow = Math.max(maxBottomRow, currentBottomRow + height); + } + + return { + convertedWidgets: currWidgets, + children, + currentAvailableLeftColumn, + maxBottomRow, + }; +} + +/** + * return the leftColumn required to add the widgets based + * on totalWidth of all widgets and alignment + * @param width + * @param totalWidth + * @param flexLayerAlignment + * @returns + */ +function getCalculatedLeftColumn( + width: number, + totalWidth: number, + flexLayerAlignment: FlexLayerAlignment, +): number { + if ( + flexLayerAlignment === FlexLayerAlignment.Start || + flexLayerAlignment === FlexLayerAlignment.None + ) { + return 0; + } else if (flexLayerAlignment === FlexLayerAlignment.Center) { + return Math.ceil((totalWidth - width) / 2); + } else { + return totalWidth - width; + } +} diff --git a/app/client/src/utils/DSLConversions/fixedToAutoLayout.ts b/app/client/src/utils/DSLConversions/fixedToAutoLayout.ts index bd7ff0c52258..aedc857a5d17 100644 --- a/app/client/src/utils/DSLConversions/fixedToAutoLayout.ts +++ b/app/client/src/utils/DSLConversions/fixedToAutoLayout.ts @@ -89,7 +89,7 @@ export function fitChildWidgetsIntoLayers( return { children: [], flexLayers }; } - //separate ot widgets to be skipped + //separate the widgets to be skipped const [nonLayerWidgets, currWidgets] = partition( widgets, (widget) => nonFlexLayerWidgets.indexOf(widget.type) > -1, diff --git a/app/client/src/utils/autoLayout/flexWidgetUtils.ts b/app/client/src/utils/autoLayout/flexWidgetUtils.ts index 27e246d1b4bc..966781d6071f 100644 --- a/app/client/src/utils/autoLayout/flexWidgetUtils.ts +++ b/app/client/src/utils/autoLayout/flexWidgetUtils.ts @@ -1,7 +1,7 @@ export function getRightColumn(widget: any, isMobile: boolean): number { - return isMobile && widget.mobileRightColumn !== undefined - ? widget.mobileRightColumn - : widget.rightColumn; + return isMobile && widget?.mobileRightColumn !== undefined + ? widget?.mobileRightColumn + : widget?.rightColumn; } export function setRightColumn( @@ -16,9 +16,9 @@ export function setRightColumn( } export function getLeftColumn(widget: any, isMobile: boolean): number { - return isMobile && widget.mobileLeftColumn !== undefined - ? widget.mobileLeftColumn - : widget.leftColumn; + return isMobile && widget?.mobileLeftColumn !== undefined + ? widget?.mobileLeftColumn + : widget?.leftColumn; } export function setLeftColumn( @@ -33,9 +33,9 @@ export function setLeftColumn( } export function getTopRow(widget: any, isMobile: boolean): number { - return isMobile && widget.mobileTopRow !== undefined - ? widget.mobileTopRow - : widget.topRow; + return isMobile && widget?.mobileTopRow !== undefined + ? widget?.mobileTopRow + : widget?.topRow; } export function setTopRow( @@ -50,9 +50,9 @@ export function setTopRow( } export function getBottomRow(widget: any, isMobile: boolean): number { - return isMobile && widget.mobileBottomRow !== undefined - ? widget.mobileBottomRow - : widget.bottomRow; + return isMobile && widget?.mobileBottomRow !== undefined + ? widget?.mobileBottomRow + : widget?.bottomRow; } export function setBottomRow( From f91aea23ba3e463ec7f674b2b79c9d4cfa6004a7 Mon Sep 17 00:00:00 2001 From: Aswath K Date: Mon, 6 Feb 2023 17:08:45 +0530 Subject: [PATCH 461/708] removes viewport specific height from widgets --- .../src/ce/constants/ReduxActionConstants.tsx | 1 - app/client/src/pages/Editor/WidgetCard.tsx | 6 ------ app/client/src/selectors/editorSelectors.tsx | 2 -- .../src/utils/WidgetRegisterHelpers.tsx | 4 ---- .../src/utils/autoLayout/AutoLayoutUtils.ts | 5 ----- .../BaseInputWidget/component/index.tsx | 14 -------------- app/client/src/widgets/ButtonWidget/index.ts | 19 ------------------- .../src/widgets/CurrencyInputWidget/index.ts | 6 ------ app/client/src/widgets/InputWidgetV2/index.ts | 6 ------ .../src/widgets/PhoneInputWidget/index.ts | 6 ------ .../widgets/components/LabelWithTooltip.tsx | 10 ---------- 11 files changed, 79 deletions(-) diff --git a/app/client/src/ce/constants/ReduxActionConstants.tsx b/app/client/src/ce/constants/ReduxActionConstants.tsx index b1b33ae4c544..b69d0f9d8847 100644 --- a/app/client/src/ce/constants/ReduxActionConstants.tsx +++ b/app/client/src/ce/constants/ReduxActionConstants.tsx @@ -962,7 +962,6 @@ export const WidgetReduxActionTypes: { [key: string]: string } = { WIDGET_BULK_DELETE: "WIDGET_BULK_DELETE", WIDGET_SINGLE_DELETE: "WIDGET_SINGLE_DELETE", WIDGET_UPDATE_PROPERTY: "WIDGET_UPDATE_PROPERTY", - WIDGET_REACHED_MIN_WIDTH: "WIDGET_REACHED_MIN_WIDTH", }; export type ReduxActionErrorType = typeof ReduxActionErrorTypes[keyof typeof ReduxActionErrorTypes]; diff --git a/app/client/src/pages/Editor/WidgetCard.tsx b/app/client/src/pages/Editor/WidgetCard.tsx index 851364636b3d..635ce813f1c2 100644 --- a/app/client/src/pages/Editor/WidgetCard.tsx +++ b/app/client/src/pages/Editor/WidgetCard.tsx @@ -7,7 +7,6 @@ import { generateReactKey } from "utils/generators"; import { Colors } from "constants/Colors"; import { useWidgetSelection } from "utils/hooks/useWidgetSelection"; import { IconWrapper } from "constants/IconConstants"; -import { isAutoLayout } from "selectors/mainCanvasSelectors"; type CardProps = { details: WidgetCardProps; @@ -73,10 +72,6 @@ function WidgetCard(props: CardProps) { const { deselectAll } = useWidgetSelection(); const onDragStart = (e: any) => { - let rows = (props.details as any).rows; - if (isAutoLayout()) { - rows = (props.details as any).autoLayout?.defaults?.rows ?? rows; - } e.preventDefault(); e.stopPropagation(); AnalyticsUtil.logEvent("WIDGET_CARD_DRAG", { @@ -86,7 +81,6 @@ function WidgetCard(props: CardProps) { setDraggingNewWidget && setDraggingNewWidget(true, { ...props.details, - rows: rows, widgetId: generateReactKey(), }); deselectAll(); diff --git a/app/client/src/selectors/editorSelectors.tsx b/app/client/src/selectors/editorSelectors.tsx index 9e26cfb936b2..f12b2b8d7ebc 100644 --- a/app/client/src/selectors/editorSelectors.tsx +++ b/app/client/src/selectors/editorSelectors.tsx @@ -288,7 +288,6 @@ export const getWidgetCards = createSelector( const _cards: WidgetCardProps[] = cards.map((config) => { const { - autoLayout, columns, detachFromLayout = false, displayName, @@ -299,7 +298,6 @@ export const getWidgetCards = createSelector( type, } = config; return { - autoLayout, key, type, rows, diff --git a/app/client/src/utils/WidgetRegisterHelpers.tsx b/app/client/src/utils/WidgetRegisterHelpers.tsx index 60ccc1e243ac..bcc1796af003 100644 --- a/app/client/src/utils/WidgetRegisterHelpers.tsx +++ b/app/client/src/utils/WidgetRegisterHelpers.tsx @@ -72,10 +72,6 @@ export const configureWidget = (config: WidgetConfiguration) => { const _config = { ...config.defaults, ...features, - autoLayout: { - defaults: config.autoLayout?.defaults, - mobile: config.autoLayout?.mobile, - }, searchTags: config.searchTags, type: config.type, hideCard: !!config.hideCard || !config.iconSVG, diff --git a/app/client/src/utils/autoLayout/AutoLayoutUtils.ts b/app/client/src/utils/autoLayout/AutoLayoutUtils.ts index f618876ddbb0..83ce4e155b6b 100644 --- a/app/client/src/utils/autoLayout/AutoLayoutUtils.ts +++ b/app/client/src/utils/autoLayout/AutoLayoutUtils.ts @@ -209,11 +209,6 @@ export function alterLayoutForMobile( widget.mobileTopRow = widget.topRow; widget.mobileBottomRow = widget.bottomRow; - if (widget.autoLayout?.mobile?.rows) { - widget.mobileBottomRow = - widget.mobileTopRow + widget.autoLayout.mobile.rows; - } - if (widget.mobileRightColumn !== undefined) widgets = alterLayoutForMobile( widgets, diff --git a/app/client/src/widgets/BaseInputWidget/component/index.tsx b/app/client/src/widgets/BaseInputWidget/component/index.tsx index aab521aebffd..ee5ec1c8d56d 100644 --- a/app/client/src/widgets/BaseInputWidget/component/index.tsx +++ b/app/client/src/widgets/BaseInputWidget/component/index.tsx @@ -80,10 +80,6 @@ const InputComponentWrapper = styled((props) => ( }>` ${labelLayoutStyles} - .auto-layout & { - min-width: 60px; - } - cursor: ${({ disabled }) => (disabled ? "not-allowed" : "auto")}; .${Classes.INPUT_GROUP} { display: flex; @@ -372,16 +368,6 @@ const TextInputWrapper = styled.div<{ box-shadow: ${({ boxShadow }) => `${boxShadow}`} !important; min-height: 32px; - .auto-layout && { - min-height: 40px; - flex: 0 40px; - } - - .mobile-view .auto-layout && { - min-height: 36px; - flex: 0 36px; - } - &:hover { border-color: ${({ disabled, hasError }) => { if (disabled) { diff --git a/app/client/src/widgets/ButtonWidget/index.ts b/app/client/src/widgets/ButtonWidget/index.ts index adcb5b1e697f..084bb982d583 100644 --- a/app/client/src/widgets/ButtonWidget/index.ts +++ b/app/client/src/widgets/ButtonWidget/index.ts @@ -41,25 +41,6 @@ export const CONFIG = { styleConfig: Widget.getPropertyPaneStyleConfig(), stylesheetConfig: Widget.getStylesheetConfig(), }, - autoLayout: { - defaults: { - rows: 4, - }, - mobile: { - rows: 4, - }, - widgetSize: [ - { - viewportMinWidth: 0, - configuration: () => { - return { - minWidth: "100px", - minHeight: "40px", - }; - }, - }, - ], - }, }; export default Widget; diff --git a/app/client/src/widgets/CurrencyInputWidget/index.ts b/app/client/src/widgets/CurrencyInputWidget/index.ts index 68c91e4ec8f2..852a2488fbe0 100644 --- a/app/client/src/widgets/CurrencyInputWidget/index.ts +++ b/app/client/src/widgets/CurrencyInputWidget/index.ts @@ -44,12 +44,6 @@ export const CONFIG = { stylesheetConfig: Widget.getStylesheetConfig(), }, autoLayout: { - defaults: { - rows: 8, - }, - mobile: { - rows: 7, - }, widgetSize: [ { viewportMinWidth: 0, diff --git a/app/client/src/widgets/InputWidgetV2/index.ts b/app/client/src/widgets/InputWidgetV2/index.ts index 55bc074613cb..b2305d0be570 100644 --- a/app/client/src/widgets/InputWidgetV2/index.ts +++ b/app/client/src/widgets/InputWidgetV2/index.ts @@ -41,12 +41,6 @@ export const CONFIG = { stylesheetConfig: Widget.getStylesheetConfig(), }, autoLayout: { - defaults: { - rows: 8, - }, - mobile: { - rows: 7, - }, widgetSize: [ { viewportMinWidth: 0, diff --git a/app/client/src/widgets/PhoneInputWidget/index.ts b/app/client/src/widgets/PhoneInputWidget/index.ts index b52e9219bef3..577065610bc4 100644 --- a/app/client/src/widgets/PhoneInputWidget/index.ts +++ b/app/client/src/widgets/PhoneInputWidget/index.ts @@ -43,12 +43,6 @@ export const CONFIG = { stylesheetConfig: Widget.getStylesheetConfig(), }, autoLayout: { - defaults: { - rows: 8, - }, - mobile: { - rows: 7, - }, widgetSize: [ { viewportMinWidth: 0, diff --git a/app/client/src/widgets/components/LabelWithTooltip.tsx b/app/client/src/widgets/components/LabelWithTooltip.tsx index 9b912ed1241f..5667c5fbae32 100644 --- a/app/client/src/widgets/components/LabelWithTooltip.tsx +++ b/app/client/src/widgets/components/LabelWithTooltip.tsx @@ -187,16 +187,6 @@ export const StyledLabel = styled(Label)` `} } - .auto-layout && { - margin-bottom: 10px; - font-size: 15px; - } - - .mobile-view .auto-layout && { - margin-bottom: 4px; - font-size: 14px; - } - ${({ $isDynamicHeightEnabled }) => $isDynamicHeightEnabled ? "&& { text-overflow: initial; white-space: initial; }" From e6671d1d3e550b043a2b99cfb37075926d9aa910 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Tue, 7 Feb 2023 11:24:55 +0530 Subject: [PATCH 462/708] Fixing bugs in resizing and widget name component. --- .../appsmith/autoLayout/FlexComponent.tsx | 1 + .../WidgetNameComponent/index.tsx | 8 +++++++- .../src/resizable/resizenreflow/index.tsx | 4 +++- app/client/src/sagas/WidgetDeletionSagas.ts | 18 +++++++----------- 4 files changed, 18 insertions(+), 13 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx index 02341d72442c..610206587d8d 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx @@ -98,6 +98,7 @@ export function FlexComponent(props: AutoLayoutProps) { return ( state.ui.widgetDragResize.lastSelectedWidget, ); + const isAutoLayout = + useSelector(getCurrentAppPositioningType) === AppPositioningTypes.AUTO; const selectedWidgets = useSelector( (state: AppState) => state.ui.widgetDragResize.selectedWidgets, ); @@ -174,7 +178,9 @@ export function WidgetNameComponent(props: WidgetNameComponentProps) { propertyPaneWidgetId === props.widgetId ) currentActivity = Activities.ACTIVE; - const targetNode: any = document.getElementById(`${props.widgetId}`); + const targetNode: any = document.getElementById( + `${isAutoLayout ? "auto_" : ""}${props.widgetId}`, + ); // This state tells us to disable dragging, // This is usually true when widgets themselves implement drag/drop diff --git a/app/client/src/resizable/resizenreflow/index.tsx b/app/client/src/resizable/resizenreflow/index.tsx index 6e19b51a5c01..b5e3a69b5c53 100644 --- a/app/client/src/resizable/resizenreflow/index.tsx +++ b/app/client/src/resizable/resizenreflow/index.tsx @@ -459,7 +459,9 @@ export function ReflowResizable(props: ResizableProps) { if ( !( - isAutoHeight && widget.rightColumn === GridDefaults.DEFAULT_GRID_COLUMNS + isAutoHeight && + widget.leftColumn !== 0 && + widget.rightColumn === GridDefaults.DEFAULT_GRID_COLUMNS ) && props.handles.right ) { diff --git a/app/client/src/sagas/WidgetDeletionSagas.ts b/app/client/src/sagas/WidgetDeletionSagas.ts index 0394fd183932..c9ebc3d65759 100644 --- a/app/client/src/sagas/WidgetDeletionSagas.ts +++ b/app/client/src/sagas/WidgetDeletionSagas.ts @@ -13,7 +13,6 @@ import { } from "actions/pageActions"; import { closePropertyPane, closeTableFilterPane } from "actions/widgetActions"; import { selectWidgetInitAction } from "actions/widgetSelectionActions"; -import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; import { ENTITY_TYPE } from "entities/AppsmithConsole"; import LOG_TYPE from "entities/AppsmithConsole/logtype"; import { flattenDeep, omit, orderBy } from "lodash"; @@ -21,29 +20,26 @@ import { CanvasWidgetsReduxState, FlattenedWidgetProps, } from "reducers/entityReducers/canvasWidgetsReducer"; -import { MainCanvasReduxState } from "reducers/uiReducers/mainCanvasReducer"; import { all, call, put, select, takeEvery } from "redux-saga/effects"; -import { getMainCanvasProps } from "selectors/editorSelectors"; +import { SelectionRequestType } from "sagas/WidgetSelectUtils"; import { getIsMobile } from "selectors/mainCanvasSelectors"; +import { + inGuidedTour, + isExploringSelector, +} from "selectors/onboardingSelectors"; import { getSelectedWidgets } from "selectors/ui"; import AnalyticsUtil from "utils/AnalyticsUtil"; import AppsmithConsole from "utils/AppsmithConsole"; +import { showUndoRedoToast } from "utils/replayHelpers"; +import WidgetFactory from "utils/WidgetFactory"; import { WidgetProps } from "widgets/BaseWidget"; import { updateFlexLayersOnDelete } from "../utils/autoLayout/AutoLayoutUtils"; import { getSelectedWidget, getWidget, getWidgets } from "./selectors"; import { getAllWidgetsInTree, - resizePublishedMainCanvasToLowestWidget, updateListWidgetPropertiesOnChildDelete, WidgetsInTree, } from "./WidgetOperationUtils"; -import { showUndoRedoToast } from "utils/replayHelpers"; -import WidgetFactory from "utils/WidgetFactory"; -import { - inGuidedTour, - isExploringSelector, -} from "selectors/onboardingSelectors"; -import { SelectionRequestType } from "sagas/WidgetSelectUtils"; const WidgetTypes = WidgetFactory.widgetTypes; From f1453776734e2f09343a40a556d46565cd7a6337 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Tue, 7 Feb 2023 16:15:28 +0530 Subject: [PATCH 463/708] fixing jest test cases except positionUtils.test.ts --- .../WidgetNameComponent/index.tsx | 4 +- .../GlobalHotKeys/GlobalHotKeys.test.tsx | 2 +- .../src/pages/Editor/MainContainer.test.tsx | 214 +++++++++--------- app/client/src/sagas/WidgetOperationSagas.tsx | 10 +- app/client/src/sagas/WidgetSelectUtils.ts | 40 ++-- app/client/src/utils/AppsmithUtils.tsx | 16 +- .../utils/autoLayout/highlightUtils.test.ts | 9 +- 7 files changed, 153 insertions(+), 142 deletions(-) diff --git a/app/client/src/components/editorComponents/WidgetNameComponent/index.tsx b/app/client/src/components/editorComponents/WidgetNameComponent/index.tsx index 551a859542cf..1d291efc37f3 100644 --- a/app/client/src/components/editorComponents/WidgetNameComponent/index.tsx +++ b/app/client/src/components/editorComponents/WidgetNameComponent/index.tsx @@ -147,6 +147,8 @@ export function WidgetNameComponent(props: WidgetNameComponentProps) { selectedWidgets && selectedWidgets.length > 1 && selectedWidgets.includes(props.widgetId); + // True when any widget is dragging or resizing, including this one + const isResizingOrDragging = !!isResizing || !!isDragging; const shouldShowWidgetName = () => { return ( !isResizingOrDragging && @@ -189,8 +191,6 @@ export function WidgetNameComponent(props: WidgetNameComponentProps) { (state: AppState) => state.ui.widgetDragResize.isDraggingDisabled, ); - // True when any widget is dragging or resizing, including this one - const isResizingOrDragging = !!isResizing || !!isDragging; const allowDrag = canDrag( isResizingOrDragging, isDraggingDisabled, diff --git a/app/client/src/pages/Editor/GlobalHotKeys/GlobalHotKeys.test.tsx b/app/client/src/pages/Editor/GlobalHotKeys/GlobalHotKeys.test.tsx index 97dca0a9ac85..1d9df9a14e22 100644 --- a/app/client/src/pages/Editor/GlobalHotKeys/GlobalHotKeys.test.tsx +++ b/app/client/src/pages/Editor/GlobalHotKeys/GlobalHotKeys.test.tsx @@ -442,6 +442,7 @@ describe("Cut/Copy/Paste hotkey", () => { , + { initialState: store.getState(), sagasToRun: sagasToRunForTests }, ); const artBoard: any = await component.queryByTestId("t--canvas-artboard"); // deselect all other widgets @@ -481,7 +482,6 @@ describe("Cut/Copy/Paste hotkey", () => { ); }); await component.findByTestId("t--selection-box"); - act(() => { dispatchTestKeyboardEventWithCode( component.container, diff --git a/app/client/src/pages/Editor/MainContainer.test.tsx b/app/client/src/pages/Editor/MainContainer.test.tsx index 14fb165ed299..338afe3f5877 100644 --- a/app/client/src/pages/Editor/MainContainer.test.tsx +++ b/app/client/src/pages/Editor/MainContainer.test.tsx @@ -338,112 +338,114 @@ describe("Drag and Drop widgets into Main container", () => { expect(finalPositions.top).toEqual(initPositions.top); }); - it("When widgets are colliding with other widgets move them back to previous position", () => { - const children: any = buildChildren([ - { - type: "TABS_WIDGET", - topRow: 5, - bottomRow: 15, - leftColumn: 5, - rightColumn: 15, - }, - { - type: "TABLE_WIDGET", - topRow: 15, - bottomRow: 25, - leftColumn: 5, - rightColumn: 15, - }, - ]); - const dsl: any = widgetCanvasFactory.build({ - children, - }); - spyGetCanvasWidgetDsl.mockImplementation(mockGetCanvasWidgetDsl); - mockGetIsFetchingPage.mockImplementation(() => false); - - const component = render( - - - - - - - , - { initialState: store.getState(), sagasToRun: sagasToRunForTests }, - ); - const propPane = component.queryByTestId("t--propertypane"); - expect(propPane).toBeNull(); - const canvasWidgets = component.queryAllByTestId("test-widget"); - expect(canvasWidgets.length).toBe(2); - const tabsWidget: any = component.container.querySelector( - ".t--draggable-tabswidget", - ); - const tab: any = component.container.querySelector(".t--widget-tabswidget"); - const initPositions = { - left: tab.style.left, - top: tab.style.top, - }; - - act(() => { - fireEvent.mouseOver(tabsWidget); - }); - - act(() => { - fireEvent.dragStart(tabsWidget); - }); - - const mainCanvas: any = component.queryByTestId("div-dragarena-0"); - act(() => { - fireEvent( - mainCanvas, - syntheticTestMouseEvent( - new MouseEvent("mousemove", { - bubbles: true, - cancelable: true, - }), - { - offsetX: 0, - offsetY: 0, - }, - ), - ); - }); - act(() => { - fireEvent( - mainCanvas, - syntheticTestMouseEvent( - new MouseEvent("mousemove", { - bubbles: true, - cancelable: true, - }), - { - offsetX: 0, - offsetY: 50, - }, - ), - ); - fireEvent( - mainCanvas, - syntheticTestMouseEvent( - new MouseEvent("mouseup", { - bubbles: true, - cancelable: true, - }), - ), - ); - }); - const movedTab: any = component.container.querySelector( - ".t--widget-tabswidget", - ); - const finalPositions = { - left: movedTab.style.left, - top: movedTab.style.top, - }; - expect(finalPositions.left).toEqual(initPositions.left); - expect(finalPositions.top).toEqual(initPositions.top); - }); + // ToDO(Ashok): Check with Rahul if this test case is still relevant post reflow. + + // it("When widgets are colliding with other widgets move them back to previous position", () => { + // const children: any = buildChildren([ + // { + // type: "TABS_WIDGET", + // topRow: 5, + // bottomRow: 15, + // leftColumn: 5, + // rightColumn: 15, + // }, + // { + // type: "TABLE_WIDGET", + // topRow: 15, + // bottomRow: 25, + // leftColumn: 5, + // rightColumn: 15, + // }, + // ]); + // const dsl: any = widgetCanvasFactory.build({ + // children, + // }); + // spyGetCanvasWidgetDsl.mockImplementation(mockGetCanvasWidgetDsl); + // mockGetIsFetchingPage.mockImplementation(() => false); + + // const component = render( + // + // + // + // + // + // + // , + // { initialState: store.getState(), sagasToRun: sagasToRunForTests }, + // ); + // const propPane = component.queryByTestId("t--propertypane"); + // expect(propPane).toBeNull(); + // const canvasWidgets = component.queryAllByTestId("test-widget"); + // expect(canvasWidgets.length).toBe(2); + // const tabsWidget: any = component.container.querySelector( + // ".t--draggable-tabswidget", + // ); + // const tab: any = component.container.querySelector(".t--widget-tabswidget"); + // const initPositions = { + // left: tab.style.left, + // top: tab.style.top, + // }; + + // act(() => { + // fireEvent.mouseOver(tabsWidget); + // }); + + // act(() => { + // fireEvent.dragStart(tabsWidget); + // }); + + // const mainCanvas: any = component.queryByTestId("div-dragarena-0"); + // act(() => { + // fireEvent( + // mainCanvas, + // syntheticTestMouseEvent( + // new MouseEvent("mousemove", { + // bubbles: true, + // cancelable: true, + // }), + // { + // offsetX: 0, + // offsetY: 0, + // }, + // ), + // ); + // }); + // act(() => { + // fireEvent( + // mainCanvas, + // syntheticTestMouseEvent( + // new MouseEvent("mousemove", { + // bubbles: true, + // cancelable: true, + // }), + // { + // offsetX: 0, + // offsetY: 50, + // }, + // ), + // ); + // fireEvent( + // mainCanvas, + // syntheticTestMouseEvent( + // new MouseEvent("mouseup", { + // bubbles: true, + // cancelable: true, + // }), + // ), + // ); + // }); + // const movedTab: any = component.container.querySelector( + // ".t--widget-tabswidget", + // ); + // const finalPositions = { + // left: movedTab.style.left, + // top: movedTab.style.top, + // }; + // expect(finalPositions.left).toEqual(initPositions.left); + // expect(finalPositions.top).toEqual(initPositions.top); + // }); it("When widgets are out of bottom most bounds of parent canvas, canvas has to expand", () => { const children: any = buildChildren([ diff --git a/app/client/src/sagas/WidgetOperationSagas.tsx b/app/client/src/sagas/WidgetOperationSagas.tsx index befcb44b798d..0972f14805e6 100644 --- a/app/client/src/sagas/WidgetOperationSagas.tsx +++ b/app/client/src/sagas/WidgetOperationSagas.tsx @@ -80,6 +80,7 @@ import { validateProperty } from "./EvaluationsSaga"; import { generateAutoHeightLayoutTreeAction } from "actions/autoHeightActions"; import { stopReflowAction } from "actions/reflowActions"; import { updateMultipleWidgetProperties } from "actions/widgetActions"; +import { selectWidgetInitAction } from "actions/widgetSelectionActions"; import { WidgetSpace } from "constants/CanvasEditorConstants"; import { getSlidingArenaName } from "constants/componentClassNameConstants"; import { DataTree } from "entities/DataTree/dataTreeFactory"; @@ -105,12 +106,14 @@ import { collisionCheckPostReflow, getBottomRowAfterReflow, } from "utils/reflowHookUtils"; +import WidgetFactory from "utils/WidgetFactory"; import { addChildToPastedFlexLayers, isStack, pasteWidgetInFlexLayers, } from "../utils/autoLayout/AutoLayoutUtils"; import { getCanvasSizeAfterWidgetMove } from "./CanvasSagas/DraggingCanvasSagas"; +import { getWidget, getWidgets, getWidgetsMeta } from "./selectors"; import widgetAdditionSagas from "./WidgetAdditionSagas"; import { traverseTreeAndExecuteBlueprintChildOperations } from "./WidgetBlueprintSagas"; import widgetDeletionSagas from "./WidgetDeletionSagas"; @@ -149,11 +152,8 @@ import { purgeOrphanedDynamicPaths, WIDGET_PASTE_PADDING, } from "./WidgetOperationUtils"; -import { SelectionRequestType } from "./WidgetSelectUtils"; -import { selectWidgetInitAction } from "actions/widgetSelectionActions"; import { widgetSelectionSagas } from "./WidgetSelectionSagas"; -import { getWidget, getWidgets, getWidgetsMeta } from "./selectors"; -import WidgetFactory from "utils/WidgetFactory"; +import { SelectionRequestType } from "./WidgetSelectUtils"; export function* resizeSaga(resizeAction: ReduxAction) { try { @@ -1648,7 +1648,7 @@ function* pasteWidgetSaga( */ if (widget.parentId) { const pastingIntoWidget = widgets[widget.parentId]; - if (isStack(widgets, pastingIntoWidget)) { + if (pastingIntoWidget && isStack(widgets, pastingIntoWidget)) { if (widget.widgetId === widgetIdMap[copiedWidget.widgetId]) widgets = pasteWidgetInFlexLayers( widgets, diff --git a/app/client/src/sagas/WidgetSelectUtils.ts b/app/client/src/sagas/WidgetSelectUtils.ts index cf3726931b6f..624e4af492dc 100644 --- a/app/client/src/sagas/WidgetSelectUtils.ts +++ b/app/client/src/sagas/WidgetSelectUtils.ts @@ -1,26 +1,26 @@ +import { setSelectedWidgetAncestry } from "actions/widgetSelectionActions"; +import { createMessage, SELECT_ALL_WIDGETS_MSG } from "ce/constants/messages"; +import { + ReduxActionErrorTypes, + ReduxActionTypes, +} from "ce/constants/ReduxActionConstants"; +import { checkIsDropTarget } from "components/designSystems/appsmith/PositionedContainer"; +import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; +import { Toaster, Variant } from "design-system-old"; +import { uniq } from "lodash"; import { CanvasWidgetsReduxState, FlattenedWidgetProps, } from "reducers/entityReducers/canvasWidgetsReducer"; -import { uniq } from "lodash"; import { call, put, select } from "redux-saga/effects"; -import { getLastSelectedWidget } from "selectors/ui"; import { getWidgetImmediateChildren, getWidgetMetaProps, getWidgets, } from "sagas/selectors"; import { getWidgetChildrenIds } from "sagas/WidgetOperationUtils"; -import { checkIsDropTarget } from "components/designSystems/appsmith/PositionedContainer"; -import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; +import { getLastSelectedWidget, getSelectedWidgets } from "selectors/ui"; import WidgetFactory from "utils/WidgetFactory"; -import { setSelectedWidgetAncestry } from "actions/widgetSelectionActions"; -import { Toaster, Variant } from "design-system-old"; -import { createMessage, SELECT_ALL_WIDGETS_MSG } from "ce/constants/messages"; -import { - ReduxActionErrorTypes, - ReduxActionTypes, -} from "ce/constants/ReduxActionConstants"; /** * Selection types that are possible for widget select @@ -216,16 +216,24 @@ function* getDroppingCanvasOfWidget(widgetLastSelected: FlattenedWidgetProps) { function* getLastSelectedCanvas() { const lastSelectedWidget: string = yield select(getLastSelectedWidget); + const selectedWidgets: string[] = yield select(getSelectedWidgets); + const areMultipleWidgetsSelected: boolean = selectedWidgets.length > 1; const canvasWidgets: CanvasWidgetsReduxState = yield select(getWidgets); const widgetLastSelected = lastSelectedWidget && canvasWidgets[lastSelectedWidget]; if (widgetLastSelected) { - const canvasToSelect: string = yield call( - getDroppingCanvasOfWidget, - widgetLastSelected, - ); - return canvasToSelect ? canvasToSelect : MAIN_CONTAINER_WIDGET_ID; + if (areMultipleWidgetsSelected) { + return widgetLastSelected.parentId || MAIN_CONTAINER_WIDGET_ID; + } + if (!areMultipleWidgetsSelected) { + const canvasToSelect: string = yield call( + getDroppingCanvasOfWidget, + widgetLastSelected, + ); + return canvasToSelect ? canvasToSelect : MAIN_CONTAINER_WIDGET_ID; + } } + return MAIN_CONTAINER_WIDGET_ID; } diff --git a/app/client/src/utils/AppsmithUtils.tsx b/app/client/src/utils/AppsmithUtils.tsx index ae5d6534622d..2e95c74c7432 100644 --- a/app/client/src/utils/AppsmithUtils.tsx +++ b/app/client/src/utils/AppsmithUtils.tsx @@ -1,15 +1,15 @@ import { getAppsmithConfigs } from "@appsmith/configs"; +import { ERROR_CODES } from "@appsmith/constants/ApiConstants"; +import { createMessage, ERROR_500 } from "@appsmith/constants/messages"; import * as Sentry from "@sentry/react"; -import AnalyticsUtil from "./AnalyticsUtil"; import { Property } from "api/ActionAPI"; +import { AppIconCollection, AppIconName } from "design-system-old"; import _ from "lodash"; -import { ActionDataState } from "reducers/entityReducers/actionsReducer"; import * as log from "loglevel"; -import { AppIconCollection, AppIconName } from "design-system-old"; -import { ERROR_CODES } from "@appsmith/constants/ApiConstants"; -import { createMessage, ERROR_500 } from "@appsmith/constants/messages"; -import { JSCollectionData } from "reducers/entityReducers/jsActionsReducer"; import { osName } from "react-device-detect"; +import { ActionDataState } from "reducers/entityReducers/actionsReducer"; +import { JSCollectionData } from "reducers/entityReducers/jsActionsReducer"; +import AnalyticsUtil from "./AnalyticsUtil"; export const initializeAnalyticsAndTrackers = () => { const appsmithConfigs = getAppsmithConfigs(); @@ -442,8 +442,8 @@ export const isMacOs = () => { */ export function areArraysEqual(arr1: string[], arr2: string[]) { if (arr1.length !== arr2.length) return false; - - if (arr1.sort().join(",") === arr2.sort().join(",")) return true; + // Because the array is frozen in strict mode, you'll need to copy the array before sorting it + if ([...arr1].sort().join(",") === [...arr2].sort().join(",")) return true; return false; } diff --git a/app/client/src/utils/autoLayout/highlightUtils.test.ts b/app/client/src/utils/autoLayout/highlightUtils.test.ts index 3a622a6c073a..f3f3b74f5437 100644 --- a/app/client/src/utils/autoLayout/highlightUtils.test.ts +++ b/app/client/src/utils/autoLayout/highlightUtils.test.ts @@ -1,8 +1,8 @@ +import { FLEXBOX_PADDING, RenderModes } from "constants/WidgetConstants"; import { FlexLayerAlignment, ResponsiveBehavior, } from "utils/autoLayout/constants"; -import { FLEXBOX_PADDING, RenderModes } from "constants/WidgetConstants"; import { getWidgetHeight } from "./flexWidgetUtils"; import { deriveHighlightsFromLayers, @@ -47,12 +47,13 @@ describe("test HighlightUtils methods", () => { "1", 99, ); + // ToDo(Preet): These numbers have to be more of a config to make more sense of them. expect(highlights.length).toEqual(3); expect(highlights[0].isVertical).toBeFalsy; expect(highlights[0].width).toEqual(33); expect(highlights[0].height).toEqual(4); - expect(highlights[1].posX).toEqual(33); - expect(highlights[2].posX).toEqual(66); + expect(highlights[1].posX).toEqual(35); + expect(highlights[2].posX).toEqual(68); }); it("should add horizontal heights before every layer and below the last layer", () => { const widgets = { @@ -457,7 +458,7 @@ describe("test HighlightUtils methods", () => { FLEXBOX_PADDING, ); expect(result.highlights[1].height).toEqual(60); - expect(result.highlights[2].posX).toEqual(636); + expect(result.highlights[2].posX).toEqual(634); expect(result.highlights[2].posY).toEqual( widgets["3"].mobileTopRow * widgets["3"].parentRowSpace + FLEXBOX_PADDING, From 48b2c0628508cdc7f4bb015354118a6b20a09d36 Mon Sep 17 00:00:00 2001 From: rahulramesha Date: Tue, 7 Feb 2023 17:45:22 +0530 Subject: [PATCH 464/708] fix mobile wrapped layout conversion --- app/client/src/utils/DSLConversions/autoToFixedLayout.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/client/src/utils/DSLConversions/autoToFixedLayout.ts b/app/client/src/utils/DSLConversions/autoToFixedLayout.ts index b22cdb4cf818..b90136e5a065 100644 --- a/app/client/src/utils/DSLConversions/autoToFixedLayout.ts +++ b/app/client/src/utils/DSLConversions/autoToFixedLayout.ts @@ -219,7 +219,6 @@ function processMobileCanvasChildren( let currWidgets = { ...widgets }; for (const childId of canvasWidget.children || []) { - currWidgets = convertAutoWidgetToFixed(currWidgets, childId, true); const currWidget = currWidgets[childId]; currWidgets[childId] = { @@ -229,6 +228,8 @@ function processMobileCanvasChildren( leftColumn: getLeftColumn(currWidget, true), rightColumn: getRightColumn(currWidget, true), }; + + currWidgets = convertAutoWidgetToFixed(currWidgets, childId, true); } // Delete Canvas widget responsive properties From e36c2024c359797508097117f9fbbd58874bd43a Mon Sep 17 00:00:00 2001 From: rahulramesha Date: Tue, 7 Feb 2023 17:46:13 +0530 Subject: [PATCH 465/708] add jest test for auto to fixed layout algorithm --- .../tests/autoToFixedLayout.test.ts | 1135 +++++++++++++++++ .../tests/fixedToAutoLayout.test.ts | 1 - 2 files changed, 1135 insertions(+), 1 deletion(-) create mode 100644 app/client/src/utils/DSLConversions/tests/autoToFixedLayout.test.ts diff --git a/app/client/src/utils/DSLConversions/tests/autoToFixedLayout.test.ts b/app/client/src/utils/DSLConversions/tests/autoToFixedLayout.test.ts new file mode 100644 index 000000000000..ce90a6f22d2e --- /dev/null +++ b/app/client/src/utils/DSLConversions/tests/autoToFixedLayout.test.ts @@ -0,0 +1,1135 @@ +import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; +import { convertNormalizedDSLToFixed } from "../autoToFixedLayout"; + +describe("test Auto to Fixed Conversion methods", () => { + const autoLayoutWidgets = ({ + "0": { + widgetName: "MainContainer", + topRow: 0, + bottomRow: 870, + type: "CANVAS_WIDGET", + canExtend: true, + minHeight: 870, + useAutoLayout: true, + leftColumn: 0, + children: [ + "3rp273r2sw", + "jej39vfb46", + "qwdu5rldu2", + "hrduq6qeyy", + "y8kkqs25a5", + "p6em4n29z7", + "jxob7zdb75", + "n123229jea", + "fhh2k44omh", + "m9mrywyvpj", + "8o21a70kqn", + ], + positioning: "vertical", + direction: "Vertical", + rightColumn: 4896, + detachFromLayout: true, + widgetId: "0", + flexLayers: [ + { + children: [ + { + id: "3rp273r2sw", + align: "center", + }, + ], + }, + { + children: [ + { + id: "jej39vfb46", + align: "start", + }, + { + id: "qwdu5rldu2", + align: "start", + }, + { + id: "hrduq6qeyy", + align: "start", + }, + { + id: "y8kkqs25a5", + align: "start", + }, + ], + }, + { + children: [ + { + id: "p6em4n29z7", + align: "start", + }, + { + id: "jxob7zdb75", + align: "center", + }, + ], + }, + { + children: [ + { + id: "n123229jea", + align: "start", + }, + { + id: "fhh2k44omh", + align: "end", + }, + ], + }, + { + children: [ + { + id: "m9mrywyvpj", + align: "start", + }, + ], + }, + { + children: [ + { + id: "8o21a70kqn", + align: "start", + }, + ], + }, + ], + responsiveBehavior: "fill", + }, + "3rp273r2sw": { + widgetName: "Button3", + topRow: 0, + bottomRow: 4, + parentRowSpace: 10, + type: "BUTTON_WIDGET", + leftColumn: 24, + rightColumn: 40, + widgetId: "3rp273r2sw", + minWidth: 120, + parentId: "0", + responsiveBehavior: "hug", + alignment: "center", + flexVerticalAlignment: "start", + }, + jej39vfb46: { + widgetName: "Button15", + topRow: 4, + bottomRow: 8, + type: "BUTTON_WIDGET", + leftColumn: 0, + rightColumn: 16, + widgetId: "jej39vfb46", + minWidth: 120, + responsiveBehavior: "hug", + alignment: "start", + flexVerticalAlignment: "start", + }, + qwdu5rldu2: { + widgetName: "Button5", + topRow: 4, + bottomRow: 8, + parentRowSpace: 10, + type: "BUTTON_WIDGET", + leftColumn: 16, + rightColumn: 32, + widgetId: "qwdu5rldu2", + minWidth: 120, + parentId: "0", + responsiveBehavior: "hug", + alignment: "start", + flexVerticalAlignment: "start", + }, + hrduq6qeyy: { + widgetName: "Input2", + topRow: 4, + bottomRow: 11, + type: "INPUT_WIDGET_V2", + leftColumn: 32, + rightColumn: 51, + widgetId: "hrduq6qeyy", + minWidth: 450, + parentId: "0", + responsiveBehavior: "fill", + alignment: "start", + flexVerticalAlignment: "start", + }, + y8kkqs25a5: { + widgetName: "Button4", + topRow: 4, + bottomRow: 8, + type: "BUTTON_WIDGET", + leftColumn: 51, + rightColumn: 64, + isDefaultClickDisabled: true, + widgetId: "y8kkqs25a5", + minWidth: 120, + parentId: "0", + responsiveBehavior: "hug", + alignment: "start", + flexVerticalAlignment: "start", + }, + p6em4n29z7: { + widgetName: "Button6", + topRow: 11, + bottomRow: 15, + type: "BUTTON_WIDGET", + leftColumn: 0, + rightColumn: 17.375565610859727, + widgetId: "p6em4n29z7", + minWidth: 120, + parentId: "0", + responsiveBehavior: "hug", + alignment: "start", + flexVerticalAlignment: "start", + }, + jxob7zdb75: { + widgetName: "Button7", + topRow: 11, + bottomRow: 15, + type: "BUTTON_WIDGET", + leftColumn: 24, + rightColumn: 40, + widgetId: "jxob7zdb75", + minWidth: 120, + responsiveBehavior: "hug", + alignment: "center", + flexVerticalAlignment: "start", + }, + n123229jea: { + widgetName: "Button9", + topRow: 15, + bottomRow: 19, + parentRowSpace: 10, + type: "BUTTON_WIDGET", + leftColumn: 0, + rightColumn: 17.375565610859727, + widgetId: "n123229jea", + minWidth: 120, + parentId: "0", + responsiveBehavior: "hug", + alignment: "start", + flexVerticalAlignment: "start", + }, + fhh2k44omh: { + widgetName: "Button8", + topRow: 15, + bottomRow: 19, + type: "BUTTON_WIDGET", + leftColumn: 48, + rightColumn: 64, + widgetId: "fhh2k44omh", + minWidth: 120, + parentId: "0", + responsiveBehavior: "hug", + alignment: "end", + flexVerticalAlignment: "start", + }, + "5c6gd8ynfa": { + widgetName: "Button10", + topRow: 0, + bottomRow: 4, + type: "BUTTON_WIDGET", + leftColumn: 0, + rightColumn: 17.375565610859727, + widgetId: "5c6gd8ynfa", + minWidth: 120, + parentId: "vv54unn046", + responsiveBehavior: "hug", + alignment: "start", + flexVerticalAlignment: "start", + }, + wixla6nh38: { + widgetName: "Input1", + topRow: 0, + bottomRow: 7, + type: "INPUT_WIDGET_V2", + leftColumn: 17.375565610859727, + rightColumn: 64, + widgetId: "wixla6nh38", + minWidth: 450, + parentId: "vv54unn046", + responsiveBehavior: "fill", + alignment: "start", + flexVerticalAlignment: "start", + }, + krrqtqc1o3: { + widgetName: "Image1", + topRow: 7, + bottomRow: 19, + type: "IMAGE_WIDGET", + leftColumn: 0, + rightColumn: 15, + widgetId: "krrqtqc1o3", + minWidth: 450, + parentId: "vv54unn046", + responsiveBehavior: "hug", + alignment: "start", + flexVerticalAlignment: "start", + }, + "0cb5t22zd2": { + widgetName: "Button11", + topRow: 0, + bottomRow: 4, + type: "BUTTON_WIDGET", + leftColumn: 24, + rightColumn: 40, + widgetId: "0cb5t22zd2", + minWidth: 120, + parentId: "mw6t1nvt67", + responsiveBehavior: "hug", + alignment: "center", + flexVerticalAlignment: "start", + }, + u0cd188upj: { + widgetName: "Button12", + topRow: 4, + bottomRow: 8, + type: "BUTTON_WIDGET", + leftColumn: 0, + rightColumn: 17.375565610859727, + widgetId: "u0cd188upj", + minWidth: 120, + parentId: "mw6t1nvt67", + responsiveBehavior: "hug", + alignment: "start", + flexVerticalAlignment: "start", + }, + mw6t1nvt67: { + mobileBottomRow: 100, + widgetName: "Canvas2", + topRow: 0, + bottomRow: 100, + type: "CANVAS_WIDGET", + minHeight: 100, + mobileRightColumn: 64, + useAutoLayout: true, + leftColumn: 0, + children: ["0cb5t22zd2", "u0cd188upj"], + positioning: "vertical", + rightColumn: 64, + widgetId: "mw6t1nvt67", + minWidth: 450, + parentId: "eh5p39ko9z", + mobileTopRow: 0, + mobileLeftColumn: 0, + flexLayers: [ + { + children: [ + { + id: "0cb5t22zd2", + align: "center", + }, + ], + }, + { + children: [ + { + id: "u0cd188upj", + align: "start", + }, + ], + }, + ], + responsiveBehavior: "fill", + }, + eh5p39ko9z: { + widgetName: "Container2", + topRow: 7, + bottomRow: 17, + type: "CONTAINER_WIDGET", + leftColumn: 15, + children: ["mw6t1nvt67"], + rightColumn: 64, + widgetId: "eh5p39ko9z", + minWidth: 450, + parentId: "vv54unn046", + responsiveBehavior: "fill", + alignment: "start", + flexVerticalAlignment: "start", + }, + vv54unn046: { + mobileBottomRow: 350, + widgetName: "Canvas1", + topRow: 0, + bottomRow: 210, + type: "CANVAS_WIDGET", + minHeight: 210, + mobileRightColumn: 64, + useAutoLayout: true, + leftColumn: 0, + children: ["5c6gd8ynfa", "wixla6nh38", "krrqtqc1o3", "eh5p39ko9z"], + positioning: "vertical", + rightColumn: 64, + widgetId: "vv54unn046", + minWidth: 450, + parentId: "m9mrywyvpj", + mobileTopRow: 0, + mobileLeftColumn: 0, + flexLayers: [ + { + children: [ + { + id: "5c6gd8ynfa", + align: "start", + }, + { + id: "wixla6nh38", + align: "start", + }, + ], + }, + { + children: [ + { + id: "krrqtqc1o3", + align: "start", + }, + { + id: "eh5p39ko9z", + align: "start", + }, + ], + }, + ], + responsiveBehavior: "fill", + }, + m9mrywyvpj: { + widgetName: "Container1", + topRow: 19, + bottomRow: 40, + type: "CONTAINER_WIDGET", + leftColumn: 0, + children: ["vv54unn046"], + rightColumn: 64, + widgetId: "m9mrywyvpj", + minWidth: 450, + parentId: "0", + responsiveBehavior: "fill", + alignment: "start", + flexVerticalAlignment: "start", + }, + fjt4m0ern5: { + widgetName: "Text1", + topRow: 1, + bottomRow: 5, + type: "TEXT_WIDGET", + leftColumn: 1.5, + rightColumn: 25.5, + widgetId: "fjt4m0ern5", + minWidth: 450, + parentId: "007w6lokqp", + responsiveBehavior: "fill", + }, + nepom5s2di: { + widgetName: "Button13", + topRow: 33, + bottomRow: 37, + type: "BUTTON_WIDGET", + leftColumn: 46, + rightColumn: 62, + widgetId: "nepom5s2di", + minWidth: 120, + parentId: "007w6lokqp", + responsiveBehavior: "hug", + }, + zencnl8sel: { + widgetName: "Button14", + topRow: 33, + bottomRow: 37, + type: "BUTTON_WIDGET", + leftColumn: 30, + rightColumn: 46, + widgetId: "zencnl8sel", + minWidth: 120, + parentId: "007w6lokqp", + responsiveBehavior: "hug", + }, + "007w6lokqp": { + mobileBottomRow: 390, + widgetName: "Canvas3", + topRow: 0, + bottomRow: 390, + type: "CANVAS_WIDGET", + minHeight: 390, + mobileRightColumn: 64, + leftColumn: 0, + children: ["fjt4m0ern5", "nepom5s2di", "zencnl8sel"], + rightColumn: 300.75, + widgetId: "007w6lokqp", + minWidth: 450, + parentId: "8o21a70kqn", + mobileTopRow: 0, + responsiveBehavior: "fill", + mobileLeftColumn: 0, + flexLayers: [], + }, + "8o21a70kqn": { + widgetName: "Form1", + topRow: 40, + bottomRow: 79, + type: "FORM_WIDGET", + leftColumn: 0, + children: ["007w6lokqp"], + positioning: "fixed", + rightColumn: 64, + widgetId: "8o21a70kqn", + minWidth: 450, + parentId: "0", + responsiveBehavior: "fill", + alignment: "start", + flexVerticalAlignment: "start", + }, + } as unknown) as CanvasWidgetsReduxState; + + const fixedLayoutWidgets = { + "0": { + bottomRow: 870, + canExtend: true, + children: [ + "3rp273r2sw", + "jej39vfb46", + "qwdu5rldu2", + "hrduq6qeyy", + "y8kkqs25a5", + "p6em4n29z7", + "jxob7zdb75", + "n123229jea", + "fhh2k44omh", + "m9mrywyvpj", + "8o21a70kqn", + ], + detachFromLayout: true, + direction: "Vertical", + leftColumn: 0, + minHeight: 870, + positioning: "fixed", + rightColumn: 4896, + topRow: 0, + type: "CANVAS_WIDGET", + useAutoLayout: false, + widgetId: "0", + widgetName: "MainContainer", + }, + "007w6lokqp": { + bottomRow: 390, + children: ["fjt4m0ern5", "nepom5s2di", "zencnl8sel"], + flexLayers: [], + leftColumn: 0, + minHeight: 390, + minWidth: 450, + mobileBottomRow: 390, + mobileLeftColumn: 0, + mobileRightColumn: 64, + mobileTopRow: 0, + parentId: "8o21a70kqn", + responsiveBehavior: "fill", + rightColumn: 300.75, + topRow: 0, + type: "CANVAS_WIDGET", + widgetId: "007w6lokqp", + widgetName: "Canvas3", + }, + "0cb5t22zd2": { + bottomRow: 4, + leftColumn: 24, + minWidth: 120, + parentId: "mw6t1nvt67", + rightColumn: 40, + topRow: 0, + type: "BUTTON_WIDGET", + widgetId: "0cb5t22zd2", + widgetName: "Button11", + }, + "3rp273r2sw": { + bottomRow: 4, + leftColumn: 24, + minWidth: 120, + parentId: "0", + parentRowSpace: 10, + rightColumn: 40, + topRow: 0, + type: "BUTTON_WIDGET", + widgetId: "3rp273r2sw", + widgetName: "Button3", + }, + "5c6gd8ynfa": { + bottomRow: 4, + leftColumn: 0, + minWidth: 120, + parentId: "vv54unn046", + rightColumn: 17.375565610859727, + topRow: 0, + type: "BUTTON_WIDGET", + widgetId: "5c6gd8ynfa", + widgetName: "Button10", + }, + "8o21a70kqn": { + bottomRow: 621, + children: ["007w6lokqp"], + leftColumn: 0, + minWidth: 450, + parentId: "0", + positioning: "fixed", + rightColumn: 64, + topRow: 230, + type: "FORM_WIDGET", + widgetId: "8o21a70kqn", + widgetName: "Form1", + }, + eh5p39ko9z: { + bottomRow: 108, + children: ["mw6t1nvt67"], + leftColumn: 15, + minWidth: 450, + parentId: "vv54unn046", + rightColumn: 64, + topRow: 7, + type: "CONTAINER_WIDGET", + widgetId: "eh5p39ko9z", + widgetName: "Container2", + }, + fhh2k44omh: { + bottomRow: 19, + leftColumn: 48, + minWidth: 120, + parentId: "0", + rightColumn: 64, + topRow: 15, + type: "BUTTON_WIDGET", + widgetId: "fhh2k44omh", + widgetName: "Button8", + }, + fjt4m0ern5: { + bottomRow: 5, + leftColumn: 1.5, + minWidth: 450, + parentId: "007w6lokqp", + responsiveBehavior: "fill", + rightColumn: 25.5, + topRow: 1, + type: "TEXT_WIDGET", + widgetId: "fjt4m0ern5", + widgetName: "Text1", + }, + hrduq6qeyy: { + bottomRow: 11, + leftColumn: 32, + minWidth: 450, + parentId: "0", + rightColumn: 51, + topRow: 4, + type: "INPUT_WIDGET_V2", + widgetId: "hrduq6qeyy", + widgetName: "Input2", + }, + jej39vfb46: { + bottomRow: 8, + leftColumn: 0, + minWidth: 120, + rightColumn: 16, + topRow: 4, + type: "BUTTON_WIDGET", + widgetId: "jej39vfb46", + widgetName: "Button15", + }, + jxob7zdb75: { + bottomRow: 15, + leftColumn: 24, + minWidth: 120, + rightColumn: 40, + topRow: 11, + type: "BUTTON_WIDGET", + widgetId: "jxob7zdb75", + widgetName: "Button7", + }, + krrqtqc1o3: { + bottomRow: 19, + leftColumn: 0, + minWidth: 450, + parentId: "vv54unn046", + rightColumn: 15, + topRow: 7, + type: "IMAGE_WIDGET", + widgetId: "krrqtqc1o3", + widgetName: "Image1", + }, + m9mrywyvpj: { + bottomRow: 230, + children: ["vv54unn046"], + leftColumn: 0, + minWidth: 450, + parentId: "0", + rightColumn: 64, + topRow: 19, + type: "CONTAINER_WIDGET", + widgetId: "m9mrywyvpj", + widgetName: "Container1", + }, + mw6t1nvt67: { + bottomRow: 100, + children: ["0cb5t22zd2", "u0cd188upj"], + leftColumn: 0, + minHeight: 100, + minWidth: 450, + mobileBottomRow: 100, + mobileLeftColumn: 0, + mobileRightColumn: 64, + mobileTopRow: 0, + parentId: "eh5p39ko9z", + positioning: "fixed", + rightColumn: 64, + topRow: 0, + type: "CANVAS_WIDGET", + useAutoLayout: false, + widgetId: "mw6t1nvt67", + widgetName: "Canvas2", + }, + n123229jea: { + bottomRow: 19, + leftColumn: 0, + minWidth: 120, + parentId: "0", + parentRowSpace: 10, + rightColumn: 17.375565610859727, + topRow: 15, + type: "BUTTON_WIDGET", + widgetId: "n123229jea", + widgetName: "Button9", + }, + nepom5s2di: { + bottomRow: 37, + leftColumn: 46, + minWidth: 120, + parentId: "007w6lokqp", + responsiveBehavior: "hug", + rightColumn: 62, + topRow: 33, + type: "BUTTON_WIDGET", + widgetId: "nepom5s2di", + widgetName: "Button13", + }, + p6em4n29z7: { + bottomRow: 15, + leftColumn: 0, + minWidth: 120, + parentId: "0", + rightColumn: 17.375565610859727, + topRow: 11, + type: "BUTTON_WIDGET", + widgetId: "p6em4n29z7", + widgetName: "Button6", + }, + qwdu5rldu2: { + bottomRow: 8, + leftColumn: 16, + minWidth: 120, + parentId: "0", + parentRowSpace: 10, + rightColumn: 32, + topRow: 4, + type: "BUTTON_WIDGET", + widgetId: "qwdu5rldu2", + widgetName: "Button5", + }, + u0cd188upj: { + bottomRow: 8, + leftColumn: 0, + minWidth: 120, + parentId: "mw6t1nvt67", + rightColumn: 17.375565610859727, + topRow: 4, + type: "BUTTON_WIDGET", + widgetId: "u0cd188upj", + widgetName: "Button12", + }, + vv54unn046: { + bottomRow: 210, + children: ["5c6gd8ynfa", "wixla6nh38", "krrqtqc1o3", "eh5p39ko9z"], + leftColumn: 0, + minHeight: 210, + minWidth: 450, + mobileBottomRow: 350, + mobileLeftColumn: 0, + mobileRightColumn: 64, + mobileTopRow: 0, + parentId: "m9mrywyvpj", + positioning: "fixed", + rightColumn: 64, + topRow: 0, + type: "CANVAS_WIDGET", + useAutoLayout: false, + widgetId: "vv54unn046", + widgetName: "Canvas1", + }, + wixla6nh38: { + bottomRow: 7, + leftColumn: 17.375565610859727, + minWidth: 450, + parentId: "vv54unn046", + rightColumn: 64, + topRow: 0, + type: "INPUT_WIDGET_V2", + widgetId: "wixla6nh38", + widgetName: "Input1", + }, + y8kkqs25a5: { + bottomRow: 8, + isDefaultClickDisabled: true, + leftColumn: 51, + minWidth: 120, + parentId: "0", + rightColumn: 64, + topRow: 4, + type: "BUTTON_WIDGET", + widgetId: "y8kkqs25a5", + widgetName: "Button4", + }, + zencnl8sel: { + bottomRow: 37, + leftColumn: 30, + minWidth: 120, + parentId: "007w6lokqp", + responsiveBehavior: "hug", + rightColumn: 46, + topRow: 33, + type: "BUTTON_WIDGET", + widgetId: "zencnl8sel", + widgetName: "Button14", + }, + }; + + const fixedLayoutMobileWidgets = { + "0": { + bottomRow: 870, + canExtend: true, + children: [ + "3rp273r2sw", + "jej39vfb46", + "qwdu5rldu2", + "hrduq6qeyy", + "y8kkqs25a5", + "p6em4n29z7", + "jxob7zdb75", + "n123229jea", + "fhh2k44omh", + "m9mrywyvpj", + "8o21a70kqn", + ], + detachFromLayout: true, + direction: "Vertical", + leftColumn: 0, + minHeight: 870, + positioning: "fixed", + rightColumn: 4896, + topRow: 0, + type: "CANVAS_WIDGET", + useAutoLayout: false, + widgetId: "0", + widgetName: "MainContainer", + }, + "007w6lokqp": { + bottomRow: 390, + children: ["fjt4m0ern5", "nepom5s2di", "zencnl8sel"], + flexLayers: [], + leftColumn: 0, + minHeight: 390, + minWidth: 450, + mobileBottomRow: 390, + mobileLeftColumn: 0, + mobileRightColumn: 64, + mobileTopRow: 0, + parentId: "8o21a70kqn", + responsiveBehavior: "fill", + rightColumn: 300.75, + topRow: 0, + type: "CANVAS_WIDGET", + widgetId: "007w6lokqp", + widgetName: "Canvas3", + }, + "0cb5t22zd2": { + bottomRow: 4, + leftColumn: 24, + minWidth: 120, + parentId: "mw6t1nvt67", + rightColumn: 40, + topRow: 0, + type: "BUTTON_WIDGET", + widgetId: "0cb5t22zd2", + widgetName: "Button11", + }, + "3rp273r2sw": { + bottomRow: 4, + leftColumn: 24, + minWidth: 120, + parentId: "0", + parentRowSpace: 10, + rightColumn: 40, + topRow: 0, + type: "BUTTON_WIDGET", + widgetId: "3rp273r2sw", + widgetName: "Button3", + }, + "5c6gd8ynfa": { + bottomRow: 4, + leftColumn: 0, + minWidth: 120, + parentId: "vv54unn046", + rightColumn: 17.375565610859727, + topRow: 0, + type: "BUTTON_WIDGET", + widgetId: "5c6gd8ynfa", + widgetName: "Button10", + }, + "8o21a70kqn": { + bottomRow: 629, + children: ["007w6lokqp"], + leftColumn: 0, + minWidth: 450, + parentId: "0", + positioning: "fixed", + rightColumn: 64, + topRow: 238, + type: "FORM_WIDGET", + widgetId: "8o21a70kqn", + widgetName: "Form1", + }, + eh5p39ko9z: { + bottomRow: 124, + children: ["mw6t1nvt67"], + leftColumn: 0, + minWidth: 450, + parentId: "vv54unn046", + rightColumn: 64, + topRow: 23, + type: "CONTAINER_WIDGET", + widgetId: "eh5p39ko9z", + widgetName: "Container2", + }, + fhh2k44omh: { + bottomRow: 27, + leftColumn: 48, + minWidth: 120, + parentId: "0", + rightColumn: 64, + topRow: 23, + type: "BUTTON_WIDGET", + widgetId: "fhh2k44omh", + widgetName: "Button8", + }, + fjt4m0ern5: { + bottomRow: 5, + leftColumn: 1.5, + minWidth: 450, + parentId: "007w6lokqp", + responsiveBehavior: "fill", + rightColumn: 25.5, + topRow: 1, + type: "TEXT_WIDGET", + widgetId: "fjt4m0ern5", + widgetName: "Text1", + }, + hrduq6qeyy: { + bottomRow: 15, + leftColumn: 0, + minWidth: 450, + parentId: "0", + rightColumn: 64, + topRow: 8, + type: "INPUT_WIDGET_V2", + widgetId: "hrduq6qeyy", + widgetName: "Input2", + }, + jej39vfb46: { + bottomRow: 8, + leftColumn: 0, + minWidth: 120, + rightColumn: 17.375565610859727, + topRow: 4, + type: "BUTTON_WIDGET", + widgetId: "jej39vfb46", + widgetName: "Button15", + }, + jxob7zdb75: { + bottomRow: 23, + leftColumn: 24, + minWidth: 120, + rightColumn: 40, + topRow: 19, + type: "BUTTON_WIDGET", + widgetId: "jxob7zdb75", + widgetName: "Button7", + }, + krrqtqc1o3: { + bottomRow: 23, + leftColumn: 0, + minWidth: 450, + parentId: "vv54unn046", + rightColumn: 64, + topRow: 11, + type: "IMAGE_WIDGET", + widgetId: "krrqtqc1o3", + widgetName: "Image1", + }, + m9mrywyvpj: { + bottomRow: 238, + children: ["vv54unn046"], + leftColumn: 0, + minWidth: 450, + parentId: "0", + rightColumn: 64, + topRow: 27, + type: "CONTAINER_WIDGET", + widgetId: "m9mrywyvpj", + widgetName: "Container1", + }, + mw6t1nvt67: { + bottomRow: 100, + children: ["0cb5t22zd2", "u0cd188upj"], + leftColumn: 0, + minHeight: 100, + minWidth: 450, + mobileBottomRow: 100, + mobileLeftColumn: 0, + mobileRightColumn: 64, + mobileTopRow: 0, + parentId: "eh5p39ko9z", + positioning: "fixed", + rightColumn: 64, + topRow: 0, + type: "CANVAS_WIDGET", + useAutoLayout: false, + widgetId: "mw6t1nvt67", + widgetName: "Canvas2", + }, + n123229jea: { + bottomRow: 27, + leftColumn: 0, + minWidth: 120, + parentId: "0", + parentRowSpace: 10, + rightColumn: 17.375565610859727, + topRow: 23, + type: "BUTTON_WIDGET", + widgetId: "n123229jea", + widgetName: "Button9", + }, + nepom5s2di: { + bottomRow: 37, + leftColumn: 46, + minWidth: 120, + parentId: "007w6lokqp", + responsiveBehavior: "hug", + rightColumn: 62, + topRow: 33, + type: "BUTTON_WIDGET", + widgetId: "nepom5s2di", + widgetName: "Button13", + }, + p6em4n29z7: { + bottomRow: 23, + leftColumn: 0, + minWidth: 120, + parentId: "0", + rightColumn: 17.375565610859727, + topRow: 19, + type: "BUTTON_WIDGET", + widgetId: "p6em4n29z7", + widgetName: "Button6", + }, + qwdu5rldu2: { + bottomRow: 8, + leftColumn: 17.375565610859727, + minWidth: 120, + parentId: "0", + parentRowSpace: 10, + rightColumn: 33.375565610859724, + topRow: 4, + type: "BUTTON_WIDGET", + widgetId: "qwdu5rldu2", + widgetName: "Button5", + }, + u0cd188upj: { + bottomRow: 8, + leftColumn: 0, + minWidth: 120, + parentId: "mw6t1nvt67", + rightColumn: 17.375565610859727, + topRow: 4, + type: "BUTTON_WIDGET", + widgetId: "u0cd188upj", + widgetName: "Button12", + }, + vv54unn046: { + bottomRow: 210, + children: ["5c6gd8ynfa", "wixla6nh38", "krrqtqc1o3", "eh5p39ko9z"], + leftColumn: 0, + minHeight: 210, + minWidth: 450, + mobileBottomRow: 210, + mobileLeftColumn: 0, + mobileRightColumn: 64, + mobileTopRow: 0, + parentId: "m9mrywyvpj", + positioning: "fixed", + rightColumn: 64, + topRow: 0, + type: "CANVAS_WIDGET", + useAutoLayout: false, + widgetId: "vv54unn046", + widgetName: "Canvas1", + }, + wixla6nh38: { + bottomRow: 11, + leftColumn: 0, + minWidth: 450, + parentId: "vv54unn046", + rightColumn: 64, + topRow: 4, + type: "INPUT_WIDGET_V2", + widgetId: "wixla6nh38", + widgetName: "Input1", + }, + y8kkqs25a5: { + bottomRow: 19, + isDefaultClickDisabled: true, + leftColumn: 0, + minWidth: 120, + parentId: "0", + rightColumn: 13, + topRow: 15, + type: "BUTTON_WIDGET", + widgetId: "y8kkqs25a5", + widgetName: "Button4", + }, + zencnl8sel: { + bottomRow: 37, + leftColumn: 30, + minWidth: 120, + parentId: "007w6lokqp", + responsiveBehavior: "hug", + rightColumn: 46, + topRow: 33, + type: "BUTTON_WIDGET", + widgetId: "zencnl8sel", + widgetName: "Button14", + }, + }; + + it("Convert Normalized auto DSL to fixed Normalized DSl without wrap", () => { + expect(convertNormalizedDSLToFixed(autoLayoutWidgets, "DESKTOP")).toEqual( + fixedLayoutWidgets, + ); + }); + + it("Convert Normalized auto DSL to fixed Normalized DSl in mobile layout", () => { + expect(convertNormalizedDSLToFixed(autoLayoutWidgets, "MOBILE")).toEqual( + fixedLayoutMobileWidgets, + ); + }); +}); diff --git a/app/client/src/utils/DSLConversions/tests/fixedToAutoLayout.test.ts b/app/client/src/utils/DSLConversions/tests/fixedToAutoLayout.test.ts index ca99378b9663..13301b62d713 100644 --- a/app/client/src/utils/DSLConversions/tests/fixedToAutoLayout.test.ts +++ b/app/client/src/utils/DSLConversions/tests/fixedToAutoLayout.test.ts @@ -22,7 +22,6 @@ describe("test fixed to Auto Conversion methods", () => { }, { widgetName: "Button3", - topRow: 0.0, bottomRow: 4.0, type: "BUTTON_WIDGET", From fc6d76c0875ed16bcca972c352f6a1a3be5408e3 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Tue, 7 Feb 2023 18:21:24 +0530 Subject: [PATCH 466/708] fixing modal first set of bugs. --- .../WidgetNameComponent/index.tsx | 1 + app/client/src/resizable/resize/index.tsx | 53 ++++++++----------- .../src/resizable/resizenreflow/index.tsx | 2 +- .../widgets/ModalWidget/component/index.tsx | 48 ++++++++++------- .../src/widgets/ModalWidget/widget/index.tsx | 24 ++------- 5 files changed, 58 insertions(+), 70 deletions(-) diff --git a/app/client/src/components/editorComponents/WidgetNameComponent/index.tsx b/app/client/src/components/editorComponents/WidgetNameComponent/index.tsx index 1d291efc37f3..33807e5f9dac 100644 --- a/app/client/src/components/editorComponents/WidgetNameComponent/index.tsx +++ b/app/client/src/components/editorComponents/WidgetNameComponent/index.tsx @@ -303,6 +303,7 @@ export function WidgetNameComponent(props: WidgetNameComponentProps) { placement="top-start" popoverClassName="widget-name-popper" portalContainer={document.getElementById("widgets-editor") || undefined} + targetTagName="div" usePortal > {props.children} diff --git a/app/client/src/resizable/resize/index.tsx b/app/client/src/resizable/resize/index.tsx index 6e3f5476618a..0937906fc98c 100644 --- a/app/client/src/resizable/resize/index.tsx +++ b/app/client/src/resizable/resize/index.tsx @@ -1,21 +1,13 @@ -import React, { ReactNode, useState, useEffect, forwardRef, Ref } from "react"; -import styled, { StyledComponent } from "styled-components"; +import { isHandleResizeAllowed } from "components/editorComponents/ResizableUtils"; +import React, { ReactNode, useEffect, useState } from "react"; +import { Spring } from "react-spring"; import { useDrag } from "react-use-gesture"; -import { Spring, animated } from "react-spring"; +import { ReflowDirection } from "reflow/reflowTypes"; +import { ResizeWrapper } from "resizable/resizenreflow"; +import { StyledComponent } from "styled-components"; import PerformanceTracker, { PerformanceTransactionName, } from "utils/PerformanceTracker"; -import { ReflowDirection } from "reflow/reflowTypes"; -import { isHandleResizeAllowed } from "components/editorComponents/ResizableUtils"; - -const ResizeWrapper = styled(animated.div)<{ $prevents: boolean }>` - display: block; - & { - * { - pointer-events: ${(props) => !props.$prevents && "none"}; - } - } -`; const getSnappedValues = ( x: number, @@ -31,6 +23,7 @@ const getSnappedValues = ( type ResizableHandleProps = { allowResize: boolean; showLightBorder?: boolean; + isHovered: boolean; disableDot: boolean; dragCallback: (x: number, y: number) => void; component: StyledComponent<"div", Record>; @@ -64,8 +57,8 @@ function ResizableHandle(props: ResizableHandleProps) { const propsToPass = { ...bind(), showAsBorder: !props.allowResize, - showLightBorder: props.showLightBorder, disableDot: props.disableDot, + isHovered: props.isHovered, }; return ; @@ -100,22 +93,19 @@ type ResizableProps = { ) => boolean; className?: string; resizeDualSides?: boolean; + widgetId: string; showLightBorder?: boolean; zWidgetType?: string; - zWidgetId?: string; }; -export const Resizable = forwardRef(function Resizable( - props: ResizableProps, - ref: Ref, -) { +export const Resizable = function Resizable(props: ResizableProps) { // Performance tracking start const sentryPerfTags = props.zWidgetType ? [{ name: "widget_type", value: props.zWidgetType }] : []; PerformanceTracker.startTracking( PerformanceTransactionName.SHOW_RESIZE_HANDLES, - { widgetId: props.zWidgetId }, + { widgetId: props.widgetId }, true, sentryPerfTags, ); @@ -299,25 +289,27 @@ export const Resizable = forwardRef(function Resizable( }, ); }; - + const showResizeBoundary = props.enableHorizontalResize; const renderHandles = handles.map((handle, index) => { - const disableDot = !isHandleResizeAllowed( - props.enableHorizontalResize, - props.enableVerticalResize, - handle.handleDirection, - ); + const disableDot = + !showResizeBoundary || + !isHandleResizeAllowed( + props.enableHorizontalResize, + props.enableVerticalResize, + handle.handleDirection, + ); return ( { togglePointerEvents(false); props.onStart(); }} onStop={onResizeStop} - showLightBorder={props.showLightBorder} snapGrid={props.snapGrid} /> ); @@ -345,7 +337,8 @@ export const Resizable = forwardRef(function Resizable( {props.children} @@ -354,6 +347,6 @@ export const Resizable = forwardRef(function Resizable( )} ); -}); +}; export default Resizable; diff --git a/app/client/src/resizable/resizenreflow/index.tsx b/app/client/src/resizable/resizenreflow/index.tsx index b5e3a69b5c53..3ea3c48a4f8c 100644 --- a/app/client/src/resizable/resizenreflow/index.tsx +++ b/app/client/src/resizable/resizenreflow/index.tsx @@ -48,7 +48,7 @@ const resizeOutline = 1; export const RESIZE_BORDER_BUFFER = resizeBorderPadding + resizeBorder + resizeBoxShadow + resizeOutline; -const ResizeWrapper = styled(animated.div)<{ +export const ResizeWrapper = styled(animated.div)<{ $prevents: boolean; isHovered: boolean; showBoundaries: boolean; diff --git a/app/client/src/widgets/ModalWidget/component/index.tsx b/app/client/src/widgets/ModalWidget/component/index.tsx index a3dbd0727a0a..14f0a0342729 100644 --- a/app/client/src/widgets/ModalWidget/component/index.tsx +++ b/app/client/src/widgets/ModalWidget/component/index.tsx @@ -1,31 +1,31 @@ import React, { ReactNode, RefObject, - useRef, useEffect, useMemo, + useRef, useState, } from "react"; -import { Overlay, Classes } from "@blueprintjs/core"; +import { Classes, Overlay } from "@blueprintjs/core"; import { get, omit } from "lodash"; -import styled from "styled-components"; import { useDispatch, useSelector } from "react-redux"; +import styled from "styled-components"; +import { AppState } from "@appsmith/reducers"; +import { closeTableFilterPane } from "actions/widgetActions"; import { UIElementSize } from "components/editorComponents/ResizableUtils"; import { + BottomHandleStyles, LeftHandleStyles, RightHandleStyles, TopHandleStyles, - BottomHandleStyles, } from "components/editorComponents/ResizeStyledComponents"; import { Layers } from "constants/Layers"; import Resizable from "resizable/resize"; +import AnalyticsUtil from "utils/AnalyticsUtil"; import { getCanvasClassName } from "utils/generators"; -import { AppState } from "@appsmith/reducers"; import { useWidgetDragResize } from "utils/hooks/dragResizeHooks"; -import AnalyticsUtil from "utils/AnalyticsUtil"; -import { closeTableFilterPane } from "actions/widgetActions"; const Container = styled.div<{ width?: number; @@ -75,6 +75,9 @@ const Container = styled.div<{ left: ${(props) => props.left}px; bottom: ${(props) => props.bottom}px; right: ${(props) => props.right}px; + .bp3-popover2-target.bp3-popover2-open { + height: 100%; + } } } } @@ -133,7 +136,6 @@ export default function ModalComponent(props: ModalComponentProps) { null, ); const { enableResize = false } = props; - const resizeRef = React.useRef(null); const [modalPosition, setModalPosition] = useState("fixed"); @@ -219,9 +221,23 @@ export default function ModalComponent(props: ModalComponentProps) { return !props.isDynamicHeightEnabled && enableResize; }, [props.isDynamicHeightEnabled, enableResize]); + const getModalContent = () => { + return ( + + {props.children} + + ); + }; + const getResizableContent = () => { //id for Content is required for Copy Paste inside the modal - return ( + return enableResize ? ( false} onStart={onResizeStart} onStop={onResizeStop} - ref={resizeRef} resizeDualSides showLightBorder snapGrid={{ x: 1, y: 1 }} + widgetId={props.widgetId} > - - {props.children} - + {getModalContent()} + ) : ( + getModalContent() ); }; diff --git a/app/client/src/widgets/ModalWidget/widget/index.tsx b/app/client/src/widgets/ModalWidget/widget/index.tsx index 6dbb50b528ed..f3a41016c46b 100644 --- a/app/client/src/widgets/ModalWidget/widget/index.tsx +++ b/app/client/src/widgets/ModalWidget/widget/index.tsx @@ -220,22 +220,9 @@ export class ModalWidget extends BaseWidget { makeModalComponent(content: ReactNode, isEditMode: boolean) { const artBoard = document.getElementById("art-board"); const portalContainer = isEditMode && artBoard ? artBoard : undefined; - const { - focusedWidget, - isDragging, - isSnipingMode, - selectedWidget, - selectedWidgets, - widgetId, - } = this.props; - - const isWidgetFocused = - focusedWidget === widgetId || - selectedWidget === widgetId || - selectedWidgets.includes(widgetId); - - const isResizeEnabled = - !isDragging && isWidgetFocused && isEditMode && !isSnipingMode; + const { isPreviewMode, isSnipingMode } = this.props; + + const isResizeEnabled = isEditMode && !isSnipingMode && !isPreviewMode; return ( { const props = { mainCanvasWidth: getCanvasWidth(state), isSnipingMode: snipingModeSelector(state), - selectedWidget: state.ui.widgetDragResize.lastSelectedWidget, - selectedWidgets: state.ui.widgetDragResize.selectedWidgets, - focusedWidget: state.ui.widgetDragResize.focusedWidget, - isDragging: state.ui.widgetDragResize.isDragging, isResizing: state.ui.widgetDragResize.isResizing, + isPreviewMode: state.ui.editor.isPreviewMode, }; return props; }; From f6df013e4aa41ad51430d6543813bb6ce8ef291e Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 7 Feb 2023 08:56:59 -0500 Subject: [PATCH 467/708] fix position utils test --- .../utils/autoLayout/positionUtils.test.ts | 44 +++++++++++++++++-- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/app/client/src/utils/autoLayout/positionUtils.test.ts b/app/client/src/utils/autoLayout/positionUtils.test.ts index 5155a6863944..9f044f931696 100644 --- a/app/client/src/utils/autoLayout/positionUtils.test.ts +++ b/app/client/src/utils/autoLayout/positionUtils.test.ts @@ -1,5 +1,6 @@ import { FlexLayerAlignment, + Positioning, ResponsiveBehavior, } from "utils/autoLayout/constants"; import { FlexLayer } from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; @@ -17,6 +18,7 @@ import { Row, updateWidgetPositions, } from "./positionUtils"; +import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; describe("test PositionUtils methods", () => { describe("test extractAlignmentInfo method", () => { @@ -722,7 +724,7 @@ describe("test PositionUtils methods", () => { mobileLeftColumn: 0, mobileRightColumn: 640, responsiveBehavior: ResponsiveBehavior.Fill, - parentId: "4", + parentId: "0", flexLayers: [ { children: [ @@ -732,8 +734,8 @@ describe("test PositionUtils methods", () => { }, ], }, - "4": { - widgetId: "3", + "0": { + widgetId: "0", leftColumn: 0, rightColumn: 64, alignment: FlexLayerAlignment.Start, @@ -752,6 +754,12 @@ describe("test PositionUtils methods", () => { mobileRightColumn: 64, responsiveBehavior: ResponsiveBehavior.Fill, parentId: "", + positioning: Positioning.Vertical, + appPositioningType: AppPositioningTypes.AUTO, + useAutoLayout: true, + flexLayers: [ + { children: [{ id: "3", align: FlexLayerAlignment.Start }] }, + ], }, }; const result = updateWidgetPositions(widgets, "3", false); @@ -856,6 +864,36 @@ describe("test PositionUtils methods", () => { parentId: "0", children: ["3"], }, + "0": { + widgetId: "0", + leftColumn: 0, + rightColumn: 64, + alignment: FlexLayerAlignment.Start, + topRow: 0, + bottomRow: 700, + type: "CANVAS_WIDGET", + widgetName: "MainContainer", + renderMode: RenderModes.CANVAS, + version: 1, + parentColumnSpace: 1, + parentRowSpace: 1, + isLoading: false, + mobileTopRow: 0, + mobileBottomRow: 700, + mobileLeftColumn: 0, + mobileRightColumn: 640, + responsiveBehavior: ResponsiveBehavior.Fill, + parentId: "4", + flexLayers: [ + { + children: [{ id: "4", align: FlexLayerAlignment.Start }], + }, + ], + children: ["4"], + positioning: Positioning.Vertical, + appPositioningType: AppPositioningTypes.AUTO, + useAutoLayout: true, + }, }; const result = updateWidgetPositions(widgets, "3", true); expect(result["3"].mobileBottomRow).toEqual(120); From e2e8000ebd3f121f5006028fbce0c4bc16e56495 Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 7 Feb 2023 09:57:15 -0500 Subject: [PATCH 468/708] fix server test --- .../test/java/com/appsmith/server/services/PageServiceTest.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/PageServiceTest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/PageServiceTest.java index 7f376bfdcba1..c7e94bf784fb 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/PageServiceTest.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/PageServiceTest.java @@ -758,8 +758,6 @@ public void clonePage_whenPageCloned_defaultIdsRetained() { final LayoutDTO layoutDTO = layoutActionService.updateLayout(page.getId(), page.getApplicationId(), layout.getId(), layout).block(); - final LayoutDTO layoutDTO = layoutActionService.updateLayout(page.getId(), page.getApplicationId(), layout.getId(), layout).block(); - // Save actionCollection ActionCollectionDTO actionCollectionDTO = new ActionCollectionDTO(); actionCollectionDTO.setName("testCollection1"); From 0e048d42483e4c8550c8f8a6e050bd12f599470c Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 7 Feb 2023 10:14:03 -0500 Subject: [PATCH 469/708] create selector to calculate column requirement --- .../appsmith/autoLayout/FlexBoxComponent.tsx | 40 ++++++------------- .../hooks/useAutoLayoutHighlights.ts | 6 +-- .../hooks/useBlocksToBeDraggedOnCanvas.ts | 2 +- .../CanvasArenas/hooks/useCanvasDragging.ts | 2 +- .../hooks/useRenderBlocksOnCanvas.ts | 2 +- .../src/selectors/autoLayoutSelectors.tsx | 38 ++++++++++++++++-- .../src/utils/autoLayout/AutoLayoutUtils.ts | 37 +++++++++++++++-- .../autoLayoutDraggingUtils.test.ts | 5 +-- .../autoLayout/autoLayoutDraggingUtils.ts | 5 +-- .../src/utils/autoLayout/autoLayoutTypes.ts | 40 +++++++++++++++++++ .../utils/autoLayout/autoLayoutUtils.test.ts | 5 +-- .../autoLayout/highlightSelectionUtils.ts | 2 +- .../utils/autoLayout/highlightUtils.test.ts | 2 +- .../src/utils/autoLayout/highlightUtils.ts | 34 ++++------------ .../utils/autoLayout/positionUtils.test.ts | 2 +- .../src/utils/autoLayout/positionUtils.ts | 2 +- 16 files changed, 138 insertions(+), 86 deletions(-) create mode 100644 app/client/src/utils/autoLayout/autoLayoutTypes.ts diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx index 9d338420bdf3..b6a7afe94179 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx @@ -9,11 +9,14 @@ import { } from "utils/autoLayout/constants"; import { APP_MODE } from "entities/App"; import { useSelector } from "react-redux"; -import { getWidgets } from "sagas/selectors"; import { getAppMode } from "selectors/entitiesSelector"; import AutoLayoutLayer from "./AutoLayoutLayer"; import { FLEXBOX_PADDING } from "constants/WidgetConstants"; -import { getWidgetWidth } from "utils/autoLayout/flexWidgetUtils"; +import { + FlexBoxAlignmentColumnInfo, + FlexLayer, +} from "utils/autoLayout/autoLayoutTypes"; +import { getColumnsForAllLayers } from "selectors/autoLayoutSelectors"; export interface FlexBoxProps { direction?: LayoutDirection; @@ -26,15 +29,6 @@ export interface FlexBoxProps { isMobile?: boolean; } -export interface LayerChild { - id: string; - align: FlexLayerAlignment; -} - -export interface FlexLayer { - children: LayerChild[]; -} - export const DEFAULT_HIGHLIGHT_SIZE = 4; export const FlexContainer = styled.div<{ @@ -65,12 +59,14 @@ export const FlexContainer = styled.div<{ `; function FlexBoxComponent(props: FlexBoxProps) { - const allWidgets = useSelector(getWidgets); const direction: LayoutDirection = props.direction || LayoutDirection.Horizontal; const appMode = useSelector(getAppMode); const leaveSpaceForWidgetName = appMode === APP_MODE.EDIT; const isMobile: boolean = props.isMobile || false; + const alignmentColumnInfo: FlexBoxAlignmentColumnInfo = useSelector( + getColumnsForAllLayers(props.widgetId), + ); const renderChildren = () => { if (!props.children) return null; @@ -96,20 +92,12 @@ function FlexBoxComponent(props: FlexBoxProps) { function processLayers(map: { [key: string]: any }) { const layers = []; - let index = 0; - for (const layer of props.flexLayers) { + for (const [index, layer] of props.flexLayers.entries()) { layers.push(processIndividualLayer(layer, map, index)); - index += 1; } return layers; } - function getColumns(id: string, isMobile: boolean): number { - const widget = allWidgets[id]; - if (!widget) return 0; - return getWidgetWidth(widget, isMobile); - } - function processIndividualLayer( layer: FlexLayer, map: { [key: string]: any }, @@ -120,22 +108,18 @@ function FlexBoxComponent(props: FlexBoxProps) { const start = [], center = [], end = []; - let startColumns = 0, - centerColumns = 0, - endColumns = 0; + const startColumns = alignmentColumnInfo[index][FlexLayerAlignment.Start], + centerColumns = alignmentColumnInfo[index][FlexLayerAlignment.Center], + endColumns = alignmentColumnInfo[index][FlexLayerAlignment.End]; for (const child of children) { const widget = map[child.id]; - if (child.align === "end") { end.push(widget); - endColumns += getColumns(child.id, isMobile); } else if (child.align === "center") { center.push(widget); - centerColumns += getColumns(child.id, isMobile); } else { start.push(widget); - startColumns += getColumns(child.id, isMobile); } } diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index 7271ca9a9bd0..24063b4d9d80 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -3,16 +3,14 @@ import { useSelector } from "react-redux"; import { getWidgets } from "sagas/selectors"; import { getCanvasWidth } from "selectors/editorSelectors"; import { getIsMobile } from "selectors/mainCanvasSelectors"; -import { - deriveHighlightsFromLayers, - HighlightInfo, -} from "utils/autoLayout/highlightUtils"; +import { deriveHighlightsFromLayers } from "utils/autoLayout/highlightUtils"; import WidgetFactory from "utils/WidgetFactory"; import { WidgetDraggingBlock } from "./useBlocksToBeDraggedOnCanvas"; import { getHighlightPayload, Point, } from "utils/autoLayout/highlightSelectionUtils"; +import { HighlightInfo } from "utils/autoLayout/autoLayoutTypes"; export interface AutoLayoutHighlightProps { blocksToDraw: WidgetDraggingBlock[]; diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts b/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts index 156862252542..31b5b158d329 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts @@ -30,8 +30,8 @@ import { widgetOperationParams, } from "utils/WidgetPropsUtils"; import { XYCord } from "./useRenderBlocksOnCanvas"; -import { HighlightInfo } from "utils/autoLayout/highlightUtils"; import { SelectionRequestType } from "sagas/WidgetSelectUtils"; +import { HighlightInfo } from "utils/autoLayout/autoLayoutTypes"; export interface WidgetDraggingUpdateParams extends WidgetDraggingBlock { updateWidgetParams: WidgetOperationParams; diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index 6c9413cf135c..9d49b2a04715 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -15,7 +15,7 @@ import { } from "reflow/reflowTypes"; import { getParentOffsetTop } from "selectors/autoLayoutSelectors"; import { getCanvasScale } from "selectors/editorSelectors"; -import { HighlightInfo } from "utils/autoLayout/highlightUtils"; +import { HighlightInfo } from "utils/autoLayout/autoLayoutTypes"; import { getNearestParentCanvas } from "utils/generators"; import { useWidgetDragResize } from "utils/hooks/dragResizeHooks"; import { ReflowInterface, useReflow } from "utils/hooks/useReflow"; diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useRenderBlocksOnCanvas.ts b/app/client/src/pages/common/CanvasArenas/hooks/useRenderBlocksOnCanvas.ts index 09fb22b86461..a48d90679a80 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useRenderBlocksOnCanvas.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useRenderBlocksOnCanvas.ts @@ -3,7 +3,7 @@ import { CONTAINER_GRID_PADDING } from "constants/WidgetConstants"; import { useSelector } from "react-redux"; import { SpaceMap } from "reflow/reflowTypes"; import { getZoomLevel } from "selectors/editorSelectors"; -import { HighlightInfo } from "utils/autoLayout/highlightUtils"; +import { HighlightInfo } from "utils/autoLayout/autoLayoutTypes"; import { getAbsolutePixels } from "utils/helpers"; import { modifyDrawingRectangles } from "./canvasDraggingUtils"; import { WidgetDraggingBlock } from "./useBlocksToBeDraggedOnCanvas"; diff --git a/app/client/src/selectors/autoLayoutSelectors.tsx b/app/client/src/selectors/autoLayoutSelectors.tsx index 46b4f7b1e1ca..1fb668fe9299 100644 --- a/app/client/src/selectors/autoLayoutSelectors.tsx +++ b/app/client/src/selectors/autoLayoutSelectors.tsx @@ -1,11 +1,14 @@ import { AppState } from "ce/reducers"; -import { - FlexLayer, - LayerChild, -} from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; import { FLEXBOX_PADDING, GridDefaults } from "constants/WidgetConstants"; import { createSelector } from "reselect"; import { getWidgets } from "sagas/selectors"; +import { + AlignmentColumnInfo, + FlexBoxAlignmentColumnInfo, + FlexLayer, + LayerChild, +} from "utils/autoLayout/autoLayoutTypes"; +import { getAlignmentColumnInfo } from "utils/autoLayout/AutoLayoutUtils"; import { getIsMobile } from "./mainCanvasSelectors"; export const getFlexLayers = (parentId: string) => { @@ -63,3 +66,30 @@ export const getParentOffsetTop = (widgetId: string) => : parent.topRow; return top * GridDefaults.DEFAULT_GRID_ROW_HEIGHT + FLEXBOX_PADDING; }); + +export const getAlignmentColumns = (widgetId: string, layerIndex: number) => + createSelector( + getWidgets, + getIsMobile, + getFlexLayers(widgetId), + (widgets, isMobile, flexLayers): AlignmentColumnInfo => { + const layer: FlexLayer = flexLayers[layerIndex]; + return getAlignmentColumnInfo(widgets, layer, isMobile); + }, + ); + +export const getColumnsForAllLayers = (widgetId: string) => + createSelector( + getWidgets, + getIsMobile, + getFlexLayers(widgetId), + (widgets, isMobile, flexLayers): FlexBoxAlignmentColumnInfo => { + const res: { [key: number]: AlignmentColumnInfo } = {}; + if (!flexLayers || !flexLayers.length) return res; + for (const [index, layer] of flexLayers.entries()) { + const info = getAlignmentColumnInfo(widgets, layer, isMobile); + res[index] = info; + } + return res; + }, + ); diff --git a/app/client/src/utils/autoLayout/AutoLayoutUtils.ts b/app/client/src/utils/autoLayout/AutoLayoutUtils.ts index b107342abf7e..057f2a949ab9 100644 --- a/app/client/src/utils/autoLayout/AutoLayoutUtils.ts +++ b/app/client/src/utils/autoLayout/AutoLayoutUtils.ts @@ -1,7 +1,4 @@ -import { - FlexLayer, - LayerChild, -} from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; +import { FlexLayer, LayerChild } from "./autoLayoutTypes"; import { FLEXBOX_PADDING, GridDefaults, @@ -16,6 +13,8 @@ import { ResponsiveBehavior, } from "utils/autoLayout/constants"; import { updateWidgetPositions } from "utils/autoLayout/positionUtils"; +import { AlignmentColumnInfo } from "./autoLayoutTypes"; +import { getWidgetWidth } from "./flexWidgetUtils"; function getCanvas(widgets: CanvasWidgetsReduxState, containerId: string) { const container = widgets[containerId]; @@ -399,3 +398,33 @@ export function getFillWidgetLengthForLayer( fillLength = (fillLength - hugLength) / (fillCount || 1); return fillLength; } + +export function getAlignmentColumnInfo( + widgets: CanvasWidgetsReduxState, + layer: FlexLayer, + isMobile: boolean, +): AlignmentColumnInfo { + if (!layer) + return { + [FlexLayerAlignment.Start]: 0, + [FlexLayerAlignment.Center]: 0, + [FlexLayerAlignment.End]: 0, + }; + let start = 0, + end = 0, + center = 0; + for (const child of layer.children) { + const widget = widgets[child.id]; + if (!widget) continue; + if (child.align === FlexLayerAlignment.End) + end += getWidgetWidth(widget, isMobile); + else if (child.align === FlexLayerAlignment.Center) + center += getWidgetWidth(widget, isMobile); + else start += getWidgetWidth(widget, isMobile); + } + return { + [FlexLayerAlignment.Start]: start, + [FlexLayerAlignment.Center]: center, + [FlexLayerAlignment.End]: end, + }; +} diff --git a/app/client/src/utils/autoLayout/autoLayoutDraggingUtils.test.ts b/app/client/src/utils/autoLayout/autoLayoutDraggingUtils.test.ts index dfde6ed92ff5..cceb15afc6d8 100644 --- a/app/client/src/utils/autoLayout/autoLayoutDraggingUtils.test.ts +++ b/app/client/src/utils/autoLayout/autoLayoutDraggingUtils.test.ts @@ -1,7 +1,4 @@ -import { - FlexLayer, - LayerChild, -} from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; +import { FlexLayer } from "./autoLayoutTypes"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; import { addNewLayer, diff --git a/app/client/src/utils/autoLayout/autoLayoutDraggingUtils.ts b/app/client/src/utils/autoLayout/autoLayoutDraggingUtils.ts index 70e63a239ec9..7b87680fc5ff 100644 --- a/app/client/src/utils/autoLayout/autoLayoutDraggingUtils.ts +++ b/app/client/src/utils/autoLayout/autoLayoutDraggingUtils.ts @@ -1,8 +1,5 @@ import { FlexLayerAlignment } from "utils/autoLayout/constants"; -import { - FlexLayer, - LayerChild, -} from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; +import { FlexLayer, LayerChild } from "./autoLayoutTypes"; import { isArray } from "lodash"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; import { updateWidgetPositions } from "./positionUtils"; diff --git a/app/client/src/utils/autoLayout/autoLayoutTypes.ts b/app/client/src/utils/autoLayout/autoLayoutTypes.ts new file mode 100644 index 000000000000..5ad87212487f --- /dev/null +++ b/app/client/src/utils/autoLayout/autoLayoutTypes.ts @@ -0,0 +1,40 @@ +import { FlexLayerAlignment } from "./constants"; + +export type AlignmentColumnInfo = { + [key in FlexLayerAlignment]: number; +}; + +export type FlexBoxAlignmentColumnInfo = { + [key: number]: AlignmentColumnInfo; +}; + +export interface LayerChild { + id: string; + align: FlexLayerAlignment; +} + +export interface FlexLayer { + children: LayerChild[]; +} + +export interface DropZone { + top?: number; + bottom?: number; + left?: number; + right?: number; +} + +export interface HighlightInfo { + isNewLayer: boolean; // determines if a new layer / child has been added directly to the container. + index: number; // index of the child in props.children. + layerIndex?: number; // index of layer in props.flexLayers. + rowIndex: number; // index of highlight within a horizontal layer. + alignment: FlexLayerAlignment; // alignment of the child in the layer. + posX: number; // x position of the highlight. + posY: number; // y position of the highlight. + width: number; // width of the highlight. + height: number; // height of the highlight. + isVertical: boolean; // determines if the highlight is vertical or horizontal. + canvasId: string; // widgetId of the canvas to which the highlight belongs. + dropZone: DropZone; // size of the drop zone of this highlight. +} diff --git a/app/client/src/utils/autoLayout/autoLayoutUtils.test.ts b/app/client/src/utils/autoLayout/autoLayoutUtils.test.ts index 15525c6413ff..e2e05c85f10e 100644 --- a/app/client/src/utils/autoLayout/autoLayoutUtils.test.ts +++ b/app/client/src/utils/autoLayout/autoLayoutUtils.test.ts @@ -1,7 +1,4 @@ -import { - FlexLayer, - LayerChild, -} from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; +import { FlexLayer, LayerChild } from "./autoLayoutTypes"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; import { getLayerIndexOfWidget, diff --git a/app/client/src/utils/autoLayout/highlightSelectionUtils.ts b/app/client/src/utils/autoLayout/highlightSelectionUtils.ts index d984e3fda494..126bf551248b 100644 --- a/app/client/src/utils/autoLayout/highlightSelectionUtils.ts +++ b/app/client/src/utils/autoLayout/highlightSelectionUtils.ts @@ -1,4 +1,4 @@ -import { HighlightInfo } from "./highlightUtils"; +import { HighlightInfo } from "./autoLayoutTypes"; export interface Point { x: number; diff --git a/app/client/src/utils/autoLayout/highlightUtils.test.ts b/app/client/src/utils/autoLayout/highlightUtils.test.ts index f3f3b74f5437..296e39372f15 100644 --- a/app/client/src/utils/autoLayout/highlightUtils.test.ts +++ b/app/client/src/utils/autoLayout/highlightUtils.test.ts @@ -3,13 +3,13 @@ import { FlexLayerAlignment, ResponsiveBehavior, } from "utils/autoLayout/constants"; +import { HighlightInfo } from "./autoLayoutTypes"; import { getWidgetHeight } from "./flexWidgetUtils"; import { deriveHighlightsFromLayers, generateHighlightsForAlignment, generateVerticalHighlights, getCanvasWidth, - HighlightInfo, VerticalHighlightsPayload, } from "./highlightUtils"; import { data } from "./testData"; diff --git a/app/client/src/utils/autoLayout/highlightUtils.ts b/app/client/src/utils/autoLayout/highlightUtils.ts index 53b25bbb39d6..709dfce843a6 100644 --- a/app/client/src/utils/autoLayout/highlightUtils.ts +++ b/app/client/src/utils/autoLayout/highlightUtils.ts @@ -1,9 +1,5 @@ import { FlexLayerAlignment } from "utils/autoLayout/constants"; -import { - DEFAULT_HIGHLIGHT_SIZE, - FlexLayer, - LayerChild, -} from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; +import { DEFAULT_HIGHLIGHT_SIZE } from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; import { FLEXBOX_PADDING, GridDefaults, @@ -25,28 +21,12 @@ import { getWrappedAlignmentInfo, Widget, } from "./positionUtils"; - -export interface DropZone { - top?: number; - bottom?: number; - left?: number; - right?: number; -} - -export interface HighlightInfo { - isNewLayer: boolean; // determines if a new layer / child has been added directly to the container. - index: number; // index of the child in props.children. - layerIndex?: number; // index of layer in props.flexLayers. - rowIndex: number; // index of highlight within a horizontal layer. - alignment: FlexLayerAlignment; // alignment of the child in the layer. - posX: number; // x position of the highlight. - posY: number; // y position of the highlight. - width: number; // width of the highlight. - height: number; // height of the highlight. - isVertical: boolean; // determines if the highlight is vertical or horizontal. - canvasId: string; // widgetId of the canvas to which the highlight belongs. - dropZone: DropZone; // size of the drop zone of this highlight. -} +import { + DropZone, + FlexLayer, + HighlightInfo, + LayerChild, +} from "./autoLayoutTypes"; /** * @param allWidgets : CanvasWidgetsReduxState diff --git a/app/client/src/utils/autoLayout/positionUtils.test.ts b/app/client/src/utils/autoLayout/positionUtils.test.ts index 9f044f931696..2c95d03302f4 100644 --- a/app/client/src/utils/autoLayout/positionUtils.test.ts +++ b/app/client/src/utils/autoLayout/positionUtils.test.ts @@ -3,7 +3,7 @@ import { Positioning, ResponsiveBehavior, } from "utils/autoLayout/constants"; -import { FlexLayer } from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; +import { FlexLayer } from "./autoLayoutTypes"; import { RenderModes } from "constants/WidgetConstants"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; import { diff --git a/app/client/src/utils/autoLayout/positionUtils.ts b/app/client/src/utils/autoLayout/positionUtils.ts index e23b550bd33e..b4ce3c30df04 100644 --- a/app/client/src/utils/autoLayout/positionUtils.ts +++ b/app/client/src/utils/autoLayout/positionUtils.ts @@ -1,4 +1,4 @@ -import { FlexLayer } from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; +import { FlexLayer } from "./autoLayoutTypes"; import { GridDefaults, MAIN_CONTAINER_WIDGET_ID, From 006e486b9b5d128cff1c88f5617ccdce7ee4f779 Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 7 Feb 2023 10:27:37 -0500 Subject: [PATCH 470/708] fix import --- app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts b/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts index 9394e3480b42..7f7c28e9d816 100644 --- a/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts +++ b/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts @@ -7,7 +7,6 @@ import { FlexLayerAlignment, LayoutDirection, } from "utils/autoLayout/constants"; -import { FlexLayer } from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; import { GridDefaults } from "constants/WidgetConstants"; import log from "loglevel"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; @@ -23,7 +22,7 @@ import { updateRelationships, } from "utils/autoLayout/autoLayoutDraggingUtils"; import { updateWidgetPositions } from "utils/autoLayout/positionUtils"; -import { HighlightInfo } from "utils/autoLayout/highlightUtils"; +import { HighlightInfo, FlexLayer } from "utils/autoLayout/autoLayoutTypes"; function* addWidgetAndReorderSaga( actionPayload: ReduxAction<{ From 720c0b43bdaada641364c0ce6ab279b8b2aeb65c Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 7 Feb 2023 11:23:36 -0500 Subject: [PATCH 471/708] bug fix --- .../appsmith/autoLayout/FlexBoxComponent.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx index b6a7afe94179..406a589d19f5 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx @@ -13,6 +13,7 @@ import { getAppMode } from "selectors/entitiesSelector"; import AutoLayoutLayer from "./AutoLayoutLayer"; import { FLEXBOX_PADDING } from "constants/WidgetConstants"; import { + AlignmentColumnInfo, FlexBoxAlignmentColumnInfo, FlexLayer, } from "utils/autoLayout/autoLayoutTypes"; @@ -108,9 +109,10 @@ function FlexBoxComponent(props: FlexBoxProps) { const start = [], center = [], end = []; - const startColumns = alignmentColumnInfo[index][FlexLayerAlignment.Start], - centerColumns = alignmentColumnInfo[index][FlexLayerAlignment.Center], - endColumns = alignmentColumnInfo[index][FlexLayerAlignment.End]; + const columnInfo: AlignmentColumnInfo = alignmentColumnInfo[index]; + const startColumns = columnInfo ? columnInfo[FlexLayerAlignment.Start] : 0, + centerColumns = columnInfo ? columnInfo[FlexLayerAlignment.Center] : 0, + endColumns = columnInfo ? columnInfo[FlexLayerAlignment.End] : 0; for (const child of children) { const widget = map[child.id]; From 1db5c358a36d17a6202d437b21c15626ae068ff0 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Wed, 8 Feb 2023 15:01:39 +0530 Subject: [PATCH 472/708] making changes to modal after the fixes for auto height instant updates. --- .../src/widgets/ModalWidget/widget/index.tsx | 23 +++---------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/app/client/src/widgets/ModalWidget/widget/index.tsx b/app/client/src/widgets/ModalWidget/widget/index.tsx index a17b64613df5..bf5ef595b60b 100644 --- a/app/client/src/widgets/ModalWidget/widget/index.tsx +++ b/app/client/src/widgets/ModalWidget/widget/index.tsx @@ -8,6 +8,8 @@ import { UIElementSize } from "components/editorComponents/ResizableUtils"; import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; import { RenderMode, WIDGET_PADDING } from "constants/WidgetConstants"; import { ValidationTypes } from "constants/WidgetValidation"; +import { Stylesheet } from "entities/AppTheming"; +import { SelectionRequestType } from "sagas/WidgetSelectUtils"; import { getCanvasWidth, snipingModeSelector } from "selectors/editorSelectors"; import { Alignment, Positioning, Spacing } from "utils/autoLayout/constants"; import { generateClassName } from "utils/generators"; @@ -16,12 +18,6 @@ import WidgetFactory from "utils/WidgetFactory"; import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; import { isAutoHeightEnabledForWidget } from "widgets/WidgetUtils"; import ModalComponent from "../component"; -// import { generatePositioningConfig } from "utils/layoutPropertiesUtils"; -import { Stylesheet } from "entities/AppTheming"; -import { SelectionRequestType } from "sagas/WidgetSelectUtils"; -import WidgetNameComponent from "components/editorComponents/WidgetNameComponent"; -import { EVAL_ERROR_PATH } from "utils/DynamicBindingUtils"; -import { get } from "lodash"; const minSize = 100; @@ -223,18 +219,6 @@ export class ModalWidget extends BaseWidget { const isResizeEnabled = isEditMode && !isSnipingMode && !isPreviewMode; - const settingsComponent = isEditMode ? ( - - ) : null; - return ( { portalContainer={portalContainer} resizeModal={this.onModalResize} scrollContents={!!this.props.shouldScrollContents} - settingsComponent={settingsComponent} widgetId={this.props.widgetId} widgetName={this.props.widgetName} width={this.getModalWidth(this.props.width)} @@ -267,7 +250,7 @@ export class ModalWidget extends BaseWidget { getCanvasView() { let children = this.getChildren(); children = this.makeModalSelectable(children); - // children = this.showWidgetName(children, true); + children = this.showWidgetName(children, true); return this.makeModalComponent(children, true); } From 3aa46adcd68d7a2d3397dd769b2eba27b46013e3 Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 8 Feb 2023 11:12:51 -0500 Subject: [PATCH 473/708] fix drop target height update on mobile viewport --- .../editorComponents/DropTargetComponent.tsx | 32 +++++++++++++++---- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/app/client/src/components/editorComponents/DropTargetComponent.tsx b/app/client/src/components/editorComponents/DropTargetComponent.tsx index 3e4ac197ea59..b3ff3c9ccc00 100644 --- a/app/client/src/components/editorComponents/DropTargetComponent.tsx +++ b/app/client/src/components/editorComponents/DropTargetComponent.tsx @@ -97,9 +97,21 @@ const updateHeight = ( } }; -function useUpdateRows(bottomRow: number, widgetId: string, parentId?: string) { +function useUpdateRows( + bottomRow: number, + widgetId: string, + parentId?: string, + mobileBottomRow?: number, + isMobile?: boolean, + isAutoLayoutActive?: boolean, +) { // This gives us the number of rows - const snapRows = getCanvasSnapRows(bottomRow); + const snapRows = getCanvasSnapRows( + bottomRow, + mobileBottomRow, + isMobile, + isAutoLayoutActive, + ); // Put the existing snap rows in a ref. const rowRef = useRef(snapRows); @@ -139,7 +151,6 @@ function useUpdateRows(bottomRow: number, widgetId: string, parentId?: string) { occupiedSpacesByChildren, widgetId, ); - // If the current number of rows in the drop target is less // than the expected number of rows in the drop target if (rowRef.current < newRows) { @@ -185,11 +196,14 @@ function useUpdateRows(bottomRow: number, widgetId: string, parentId?: string) { export function DropTargetComponent(props: DropTargetComponentProps) { // Get if this is in preview mode. const isPreviewMode = useSelector(previewModeSelector); - + const isAutoLayoutActive = useSelector(isAutoLayoutEnabled); const { contextValue, dropTargetRef, rowRef } = useUpdateRows( props.bottomRow, props.widgetId, props.parentId, + props.mobileBottomRow, + props.isMobile, + isAutoLayoutActive, ); // Are we currently resizing? @@ -202,7 +216,6 @@ export function DropTargetComponent(props: DropTargetComponentProps) { ); // Are we changing the auto height limits by dragging the signifiers? const { isAutoHeightWithLimitsChanging } = useAutoHeightUIState(); - const isAutoLayoutActive = useSelector(isAutoLayoutEnabled); // dragDetails contains of info needed for a container jump: // which parent the dragging widget belongs, // which canvas is active(being dragged on), @@ -240,7 +253,14 @@ export function DropTargetComponent(props: DropTargetComponentProps) { props.widgetId === MAIN_CONTAINER_WIDGET_ID, ); } - }, [props.widgetId, props.bottomRow, isDragging, isResizing]); + }, [ + props.widgetId, + props.bottomRow, + props.mobileBottomRow, + props.isMobile, + isDragging, + isResizing, + ]); const handleFocus = (e: any) => { // Making sure that we don't deselect the widget From cb7b09c950a13da2aef6d8a8b5a74f0ec2e9c7d7 Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 8 Feb 2023 15:36:49 -0500 Subject: [PATCH 474/708] update selector for widgetNameComponent --- .../ClientSideTests/Widgets/Switch/Switch_spec.js | 2 +- app/client/cypress/locators/Widgets.json | 3 ++- app/client/cypress/support/widgetCommands.js | 10 ++++++---- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Switch/Switch_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Switch/Switch_spec.js index 726ca6a5ca30..42e54b5e7731 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Switch/Switch_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Switch/Switch_spec.js @@ -18,7 +18,7 @@ describe("Switch Widget Functionality", function() { cy.widgetText( "Toggler", formWidgetsPage.switchWidget, - widgetsPage.switchInput, + widgetsPage.widgetNameSpan, ); /** * @param{Text} Random Value diff --git a/app/client/cypress/locators/Widgets.json b/app/client/cypress/locators/Widgets.json index b06f59d1acec..131f64069c67 100644 --- a/app/client/cypress/locators/Widgets.json +++ b/app/client/cypress/locators/Widgets.json @@ -201,5 +201,6 @@ "filepickerwidgetv2": ".t--widget-filepickerwidgetv2", "filepickerwidgetv2CancelBtn": "button.uppy-DashboardContent-back", "filepickerwidgetv2CloseModalBtn": "button.uppy-Dashboard-close", - "codescannerwidget": ".t--widget-codescannerwidget" + "codescannerwidget": ".t--widget-codescannerwidget", + "widgetNameSpan": ".t--widget-propertypane-toggle > .t--widget-name" } diff --git a/app/client/cypress/support/widgetCommands.js b/app/client/cypress/support/widgetCommands.js index d1cee2dcfae4..b8be1893002b 100644 --- a/app/client/cypress/support/widgetCommands.js +++ b/app/client/cypress/support/widgetCommands.js @@ -280,7 +280,7 @@ Cypress.Commands.add("widgetText", (text, inputcss, innercss) => { .type("{enter}"); cy.get(inputcss) .first() - .trigger("mouseover", { force: true }); + .click({ force: true }); cy.contains(innercss, text); }); @@ -1261,9 +1261,11 @@ Cypress.Commands.add("openPropertyPane", (widgetType) => { .first() .trigger("mouseover", { force: true }) .wait(500); - cy.get( - `${selector}:first-of-type .t--widget-propertypane-toggle > .t--widget-name`, - ) + cy.get(`${selector}:first-of-type`) + .first() + .click({ force: true }) + .wait(500); + cy.get(".t--widget-propertypane-toggle > .t--widget-name") .first() .click({ force: true }); // eslint-disable-next-line cypress/no-unnecessary-waiting From 8875071a06c3833c798cc3580caea2c3a436d2bf Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 8 Feb 2023 16:11:17 -0500 Subject: [PATCH 475/708] fix selectors for widgetText command --- app/client/cypress.json | 6 +++++- .../Binding/Bind_Button_with_API_spec.js | 2 +- .../Binding/NavigateToFeatureValidation_spec.js | 6 +++++- .../TableV2Widgets_NavigateTo_Validation_spec.js | 2 +- .../TableWidgets_NavigateTo_Validation_spec.js | 6 +++++- .../Entity_Explorer_DragAndDropWidget_spec.js | 2 +- .../Entity_Explorer_Multiple_Widgets_spec.js | 6 +++++- ...ity_Explorer_Widgets_Copy_Delete_Undo_spec.js | 2 +- ...Copy_Paste_Delete_Undo_Keyboard_Event_spec.js | 2 +- .../ClientSideTests/Git/GitSync/Git_spec.js | 12 ++++++------ .../ThemingTests/Theme_Default_spec.js | 2 +- .../ThemingTests/Theme_FormWidget_spec.js | 6 +++--- .../ClientSideTests/Widgets/Audio/audio_spec.js | 8 ++++++-- .../Widgets/Button/Button_spec.js | 2 +- .../ClientSideTests/Widgets/Chart/Chart_spec.js | 2 +- .../Widgets/Chart/Custom_Chart_spec.js | 2 +- .../Widgets/Checkbox/CheckBox_spec.js | 2 +- .../Widgets/Checkbox/CheckboxGroup2_spec.js | 2 +- .../ClientSideTests/Widgets/Container_spec.js | 2 +- .../Widgets/Datepicker/DatePicker2_spec.js | 2 +- .../Datepicker/DatePicker_With_Switch_spec.js | 2 +- .../Form/FormWidget_With_RichTextEditor_spec.js | 2 +- .../Widgets/Form/FormWithSwitch_spec.js | 2 +- .../ClientSideTests/Widgets/Image/Image_spec.js | 2 +- .../ClientSideTests/Widgets/Input/Input_spec.js | 2 +- .../ClientSideTests/Widgets/List/List4_spec.js | 2 +- .../ClientSideTests/Widgets/Others/Video_spec.js | 8 ++++++-- .../Widgets/RTE/RichTextEditor_spec.js | 2 +- .../ClientSideTests/Widgets/Radio/Radio_spec.js | 2 +- .../Widgets/Select/select_Widget_Bug_spec.js | 2 +- .../Select/select_Widget_validation_spec.js | 2 +- .../Widgets/Tab/Tab_new_scenario_spec.js | 2 +- .../ClientSideTests/Widgets/Tab/Tab_spec.js | 2 +- .../TableV1/Table_Button_Icon_validation_spec.js | 2 +- .../TableV1/Table_Widget_Copy_Paste_spec.js | 6 +++++- .../Widgets/TableV1/Table_spec.js | 6 +++++- .../TableV2_Button_Icon_validation_spec.js | 2 +- .../TableV2/TableV2_Widget_Copy_Paste_spec.js | 2 +- .../Widgets/TableV2/TableV2_spec.js | 2 +- .../Widgets/Text/Text_new_feature_spec.js | 2 +- .../ClientSideTests/Widgets/Text/Text_spec.js | 2 +- .../ApiTests/API_Mustache_spec.js | 2 +- .../JSOnLoad_cyclic_dependency_errors_spec.js | 6 +++++- app/client/cypress/support/widgetCommands.js | 16 ++++++++++------ 44 files changed, 98 insertions(+), 58 deletions(-) diff --git a/app/client/cypress.json b/app/client/cypress.json index dacb775096be..0001b91bc2e2 100644 --- a/app/client/cypress.json +++ b/app/client/cypress.json @@ -19,5 +19,9 @@ "retries": { "runMode": 1, "openMode": 0 - } + }, + "env": { + "USERNAME": "preet.sidhu@appsmith.com", + "PASSWORD": "appsmith@123" +} } diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Binding/Bind_Button_with_API_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Binding/Bind_Button_with_API_spec.js index fcb6c4882270..d740a650b14e 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Binding/Bind_Button_with_API_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Binding/Bind_Button_with_API_spec.js @@ -44,7 +44,7 @@ describe("Bind a button and Api usecase", function() { cy.widgetText( testdata.buttonName, widgetsPage.buttonWidget, - widgetsPage.buttonWidget + " " + commonlocators.widgetNameTag, + widgetsPage.widgetNameSpan, ); }); diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Binding/NavigateToFeatureValidation_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Binding/NavigateToFeatureValidation_spec.js index 82aba0f7ffd6..f3df118e7543 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Binding/NavigateToFeatureValidation_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Binding/NavigateToFeatureValidation_spec.js @@ -23,7 +23,11 @@ describe("Table Widget with Input Widget and Navigate to functionality validatio it("Table Widget Functionality with multiple page", function() { cy.openPropertyPane("tablewidget"); - cy.widgetText("Table1", widgetsPage.tableWidget, commonlocators.tableInner); + cy.widgetText( + "Table1", + widgetsPage.tableWidget, + widgetsPage.widgetNameSpan, + ); cy.testJsontext("tabledata", JSON.stringify(testdata.TablePagination)); }); diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Binding/TableV2Widgets_NavigateTo_Validation_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Binding/TableV2Widgets_NavigateTo_Validation_spec.js index 20fab4791f89..e07cd71c22b5 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Binding/TableV2Widgets_NavigateTo_Validation_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Binding/TableV2Widgets_NavigateTo_Validation_spec.js @@ -39,7 +39,7 @@ describe("Table Widget V2 and Navigate to functionality validation", function() cy.widgetText( "Table1", widgetsPage.tableWidgetV2, - commonlocators.tableV2Inner, + widgetsPage.widgetNameSpan, ); cy.testJsontext("tabledata", JSON.stringify(testdata.TablePagination)); cy.focused().blur(); diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Binding/TableWidgets_NavigateTo_Validation_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Binding/TableWidgets_NavigateTo_Validation_spec.js index b1ad0948098f..d50e17dcfc9a 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Binding/TableWidgets_NavigateTo_Validation_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Binding/TableWidgets_NavigateTo_Validation_spec.js @@ -35,7 +35,11 @@ describe("Table Widget and Navigate to functionality validation", function() { .should("be.visible") .click({ force: true }); cy.openPropertyPane("tablewidget"); - cy.widgetText("Table1", widgetsPage.tableWidget, commonlocators.tableInner); + cy.widgetText( + "Table1", + widgetsPage.tableWidget, + widgetsPage.widgetNameSpan, + ); cy.testJsontext("tabledata", JSON.stringify(testdata.TablePagination)); cy.focused().blur(); cy.get(widgetsPage.tableOnRowSelect) diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_DragAndDropWidget_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_DragAndDropWidget_spec.js index 795fb821e61d..5a574a41318e 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_DragAndDropWidget_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_DragAndDropWidget_spec.js @@ -23,7 +23,7 @@ describe("Entity explorer Drag and Drop widgets testcases", function() { cy.widgetText( "FormTest", formWidgetsPage.formWidget, - formWidgetsPage.formInner, + widgetsPage.widgetNameSpan, ); /** * @param{Text} Random Colour diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_Multiple_Widgets_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_Multiple_Widgets_spec.js index 0daf7adb82d2..3cb739fea6ad 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_Multiple_Widgets_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_Multiple_Widgets_spec.js @@ -35,7 +35,11 @@ describe("Entity explorer tests related to widgets and validation", function() { cy.Createpage(pageid); cy.addDsl(tdsl); cy.openPropertyPane("tablewidget"); - cy.widgetText("Table1", widgetsPage.tableWidget, commonlocators.tableInner); + cy.widgetText( + "Table1", + widgetsPage.tableWidget, + widgetsPage.widgetNameSpan, + ); cy.GlobalSearchEntity("Table1"); cy.OpenBindings("Table1"); cy.get(explorer.property) diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_Widgets_Copy_Delete_Undo_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_Widgets_Copy_Delete_Undo_spec.js index 6999fbe56745..c5d2cf52eaa5 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_Widgets_Copy_Delete_Undo_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_Widgets_Copy_Delete_Undo_spec.js @@ -15,7 +15,7 @@ describe("Test Suite to validate copy/delete/undo functionalites", function() { cy.widgetText( "FormTest", formWidgetsPage.formWidget, - formWidgetsPage.formInner, + widgetsPage.widgetNameSpan, ); cy.get(commonlocators.copyWidget).click(); // eslint-disable-next-line cypress/no-unnecessary-waiting diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_Widgets_Copy_Paste_Delete_Undo_Keyboard_Event_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_Widgets_Copy_Paste_Delete_Undo_Keyboard_Event_spec.js index fe23293c3527..484899b504e1 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_Widgets_Copy_Paste_Delete_Undo_Keyboard_Event_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_Widgets_Copy_Paste_Delete_Undo_Keyboard_Event_spec.js @@ -17,7 +17,7 @@ describe("Test Suite to validate copy/delete/undo functionalites", function() { cy.widgetText( "FormTest", formWidgetsPage.formWidget, - formWidgetsPage.formInner, + widgetsPage.widgetNameSpan, ); cy.get("body").click(); cy.get("body").type(`{${modifierKey}}c`); diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Git/GitSync/Git_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Git/GitSync/Git_spec.js index 5f823b3e419a..73983eba5c5b 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Git/GitSync/Git_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Git/GitSync/Git_spec.js @@ -70,7 +70,7 @@ describe.skip("Git sync:", function() { cy.widgetText( buttonNameTemp0Branch, widgetsPage.buttonWidget, - commonlocators.buttonInner, + widgetsPage.widgetNameSpan, ); cy.commitAndPush(); cy.switchGitBranch(tempBranch); @@ -87,7 +87,7 @@ describe.skip("Git sync:", function() { cy.widgetText( buttonNameMainBranch, widgetsPage.buttonWidget, - commonlocators.buttonInner, + widgetsPage.widgetNameSpan, ); cy.get(homePage.publishButton).click(); cy.get(gitSyncLocators.commitCommentInput).type("Initial Commit"); @@ -113,7 +113,7 @@ describe.skip("Git sync:", function() { cy.widgetText( buttonNameTempBranch1, widgetsPage.buttonWidget, - commonlocators.buttonInner, + widgetsPage.widgetNameSpan, ); cy.commitAndPush(); @@ -121,7 +121,7 @@ describe.skip("Git sync:", function() { cy.widgetText( buttonNameMainBranchEdited, widgetsPage.buttonWidget, - commonlocators.buttonInner, + widgetsPage.widgetNameSpan, ); cy.commitAndPush(); @@ -185,7 +185,7 @@ describe.skip("Git sync:", function() { cy.widgetText( inputNameTempBranch3, widgetsPage.inputWidget, - commonlocators.inputWidgetInner, + widgetsPage.widgetNameSpan, ); cy.commitAndPush(); @@ -201,7 +201,7 @@ describe.skip("Git sync:", function() { cy.widgetText( inputNameTempBranch31, widgetsPage.inputWidget, - commonlocators.inputWidgetInner, + widgetsPage.widgetNameSpan, ); cy.commitAndPush(true); diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/ThemingTests/Theme_Default_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/ThemingTests/Theme_Default_spec.js index 763b1c5adee8..0e3fb0bb03e4 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/ThemingTests/Theme_Default_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/ThemingTests/Theme_Default_spec.js @@ -75,7 +75,7 @@ describe("Theme validation for default data", function() { cy.widgetText( "FormTest", formWidgetsPage.formWidget, - formWidgetsPage.formInner, + widgetsPage.widgetNameSpan, ); cy.moveToStyleTab(); cy.get(widgetsPage.backgroundcolorPickerNew) diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/ThemingTests/Theme_FormWidget_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/ThemingTests/Theme_FormWidget_spec.js index ec5650490476..4fe1b5452d0c 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/ThemingTests/Theme_FormWidget_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/ThemingTests/Theme_FormWidget_spec.js @@ -170,7 +170,7 @@ describe("Theme validation usecases", function() { cy.widgetText( "FormTest", formWidgetsPage.formWidget, - formWidgetsPage.formInner, + widgetsPage.widgetNameSpan, ); cy.moveToStyleTab(); cy.get(widgetsPage.backgroundcolorPickerNew) @@ -231,7 +231,7 @@ describe("Theme validation usecases", function() { cy.widgetText( "FormTest", formWidgetsPage.formWidget, - formWidgetsPage.formInner, + widgetsPage.widgetNameSpan, ); cy.moveToStyleTab(); cy.get(widgetsPage.backgroundcolorPickerNew) @@ -303,7 +303,7 @@ describe("Theme validation usecases", function() { cy.widgetText( "FormTest", formWidgetsPage.formWidget, - formWidgetsPage.formInner, + widgetsPage.widgetNameSpan, ); cy.moveToStyleTab(); cy.get(widgetsPage.backgroundcolorPickerNew) diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Audio/audio_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Audio/audio_spec.js index d0c466a86620..124b19b1ba30 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Audio/audio_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Audio/audio_spec.js @@ -10,7 +10,11 @@ describe("Audio Widget Functionality", function() { it("Audio Widget play functionality validation", function() { cy.openPropertyPane("audiowidget"); - cy.widgetText("Audio1", widgetsPage.audioWidget, commonlocators.audioInner); + cy.widgetText( + "Audio1", + widgetsPage.audioWidget, + widgetsPage.widgetNameSpan, + ); cy.get(commonlocators.onPlay).click(); cy.selectShowMsg(); cy.addSuccessMessage("Play success"); @@ -59,7 +63,7 @@ describe("Audio Widget Functionality", function() { cy.widgetText( "Button1", widgetsPage.buttonWidget, - commonlocators.buttonInner, + widgetsPage.widgetNameSpan, ); cy.get(commonlocators.onClick).click(); cy.selectResetWidget(); diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Button/Button_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Button/Button_spec.js index 5a135c3e0b1c..8c99b2cbc0c7 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Button/Button_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Button/Button_spec.js @@ -81,7 +81,7 @@ describe("Button Widget Functionality", function() { cy.widgetText( this.data.ButtonName, widgetsPage.buttonWidget, - widgetsPage.buttonWidget + " " + commonlocators.widgetNameTag, + widgetsPage.widgetNameSpan, ); //Changing the text on the Button diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Chart/Chart_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Chart/Chart_spec.js index 97c21e55b1ac..6bcb22e588d9 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Chart/Chart_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Chart/Chart_spec.js @@ -24,7 +24,7 @@ describe("Chart Widget Functionality", function() { cy.widgetText( "Test", viewWidgetsPage.chartWidget, - commonlocators.containerInnerText, + widgetsPage.widgetNameSpan, ); cy.EnableAllCodeEditors(); //changing the Chart Title diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Chart/Custom_Chart_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Chart/Custom_Chart_spec.js index c4fc1d5d7eb6..bfdb3b4d35c5 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Chart/Custom_Chart_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Chart/Custom_Chart_spec.js @@ -23,7 +23,7 @@ describe("Chart Widget Functionality around custom chart feature", function() { cy.widgetText( "Test", viewWidgetsPage.chartWidget, - commonlocators.containerInnerText, + widgetsPage.widgetNameSpan, ); //changing the Chart Title /** diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Checkbox/CheckBox_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Checkbox/CheckBox_spec.js index b2ed3d3a86e8..4f3136a9af7a 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Checkbox/CheckBox_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Checkbox/CheckBox_spec.js @@ -18,7 +18,7 @@ describe("Checkbox Widget Functionality", function() { cy.widgetText( "checker", formWidgetsPage.checkboxWidget, - widgetsPage.checkboxInput, + widgetsPage.widgetNameSpan, ); /** * @param{Text} Random Value diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Checkbox/CheckboxGroup2_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Checkbox/CheckboxGroup2_spec.js index 571ad23809db..e0bcc57e5ecc 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Checkbox/CheckboxGroup2_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Checkbox/CheckboxGroup2_spec.js @@ -19,7 +19,7 @@ describe("Checkbox Group Widget Functionality", function() { cy.widgetText( "checkboxgrouptest", formWidgetsPage.checkboxGroupWidget, - formWidgetsPage.checkboxGroupInput, + widgetsPage.widgetNameSpan, ); /** * @param{IndexValue} Provide Input Index Value diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Container_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Container_spec.js index 577d501af214..b0012d4380e3 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Container_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Container_spec.js @@ -18,7 +18,7 @@ describe("Container Widget Functionality", function() { cy.widgetText( "job", widgetsPage.containerWidget, - commonlocators.containerInnerText, + widgetsPage.widgetNameSpan, ); cy.moveToStyleTab(); /** diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Datepicker/DatePicker2_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Datepicker/DatePicker2_spec.js index 9d679fa63c4e..1d07fadd1dcb 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Datepicker/DatePicker2_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Datepicker/DatePicker2_spec.js @@ -22,7 +22,7 @@ describe("DatePicker Widget Functionality", function() { cy.widgetText( this.data.Datepickername, formWidgetsPage.datepickerWidget, - formWidgetsPage.datepickerWidget + " " + commonlocators.widgetNameTag, + widgetsPage.widgetNameSpan, ); // change the date to next day diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Datepicker/DatePicker_With_Switch_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Datepicker/DatePicker_With_Switch_spec.js index 323b80c27840..9d510578b17c 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Datepicker/DatePicker_With_Switch_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Datepicker/DatePicker_With_Switch_spec.js @@ -13,7 +13,7 @@ describe("Switch Widget within Form widget Functionality", function() { cy.widgetText( "Toggler", formWidgetsPage.switchWidget, - widgetsPage.switchInput, + widgetsPage.widgetNameSpan, ); cy.testCodeMirror(this.data.switchInputName); cy.get(widgetsPage.switchLabel).should("have.text", "Switch1"); diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Form/FormWidget_With_RichTextEditor_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Form/FormWidget_With_RichTextEditor_spec.js index 825a0cdc1df8..9920e43d481b 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Form/FormWidget_With_RichTextEditor_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Form/FormWidget_With_RichTextEditor_spec.js @@ -17,7 +17,7 @@ describe("RichTextEditor Widget Functionality in Form", function() { cy.widgetText( this.data.RichTextEditorName, formWidgetsPage.richTextEditorWidget, - formWidgetsPage.richTextEditorWidget + " " + commonlocators.widgetNameTag, + widgetsPage.widgetNameSpan, ); //Validate Html diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Form/FormWithSwitch_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Form/FormWithSwitch_spec.js index 1441c8b020cf..bb8022aab454 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Form/FormWithSwitch_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Form/FormWithSwitch_spec.js @@ -14,7 +14,7 @@ describe("Switch Widget within Form widget Functionality", function() { cy.widgetText( "Toggler", formWidgetsPage.switchWidget, - widgetsPage.switchInput, + widgetsPage.widgetNameSpan, ); // Change the widget label name cy.testCodeMirror(this.data.switchInputName); diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Image/Image_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Image/Image_spec.js index b6d447637a2e..d40da28ad7fb 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Image/Image_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Image/Image_spec.js @@ -18,7 +18,7 @@ describe("Image Widget Functionality", function() { cy.widgetText( "img", viewWidgetsPage.imageWidget, - viewWidgetsPage.imagecontainer, + widgetsPage.widgetNameSpan, ); cy.testJsontext("defaultimage", this.data.defaultimage); cy.wait(1000); diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Input/Input_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Input/Input_spec.js index 1238897cdb7d..96b0a7cfa4c9 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Input/Input_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Input/Input_spec.js @@ -35,7 +35,7 @@ describe("Input Widget Functionality", function() { * @param{InputWidget}Mouseover * @param{InputPre Css} Assertion */ - cy.widgetText("day", widgetsPage.inputWidget, widgetsPage.inputval); + cy.widgetText("day", widgetsPage.inputWidget, widgetsPage.widgetNameSpan); cy.get(widgetsPage.datatype) .last() .click({ force: true }) diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/List/List4_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/List/List4_spec.js index 669be9a529ce..86416739171f 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/List/List4_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/List/List4_spec.js @@ -269,7 +269,7 @@ describe("Container Widget Functionality", function() { cy.widgetText( "List2", widgetsPage.listWidgetName, - widgetsPage.listWidgetName + " " + commonlocators.listWidgetNameTag, + widgetsPage.widgetNameSpan, ); // Change the list widget name from Entity Explorer cy.renameEntity("List2", "List1"); diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Others/Video_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Others/Video_spec.js index 6f72ae64365a..83af41a09113 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Others/Video_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Others/Video_spec.js @@ -10,7 +10,11 @@ describe("Video Widget Functionality", function() { it("Video Widget play functionality validation", function() { cy.openPropertyPane("videowidget"); - cy.widgetText("Video1", widgetsPage.videoWidget, commonlocators.videoInner); + cy.widgetText( + "Video1", + widgetsPage.videoWidget, + widgetsPage.widgetNameSpan, + ); cy.get(commonlocators.onPlay).click(); cy.selectShowMsg(); cy.addSuccessMessage("Play success"); @@ -80,7 +84,7 @@ describe("Video Widget Functionality", function() { cy.widgetText( "Button1", widgetsPage.buttonWidget, - commonlocators.buttonInner, + widgetsPage.widgetNameSpan, ); cy.get(commonlocators.onClick).click(); cy.selectResetWidget(); diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/RTE/RichTextEditor_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/RTE/RichTextEditor_spec.js index 35064579c512..565992c12bc9 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/RTE/RichTextEditor_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/RTE/RichTextEditor_spec.js @@ -46,7 +46,7 @@ describe("RichTextEditor Widget Functionality", function() { cy.widgetText( this.data.RichTextEditorName, formWidgetsPage.richTextEditorWidget, - formWidgetsPage.richTextEditorWidget + " " + commonlocators.widgetNameTag, + widgetsPage.widgetNameSpan, ); //Edit the text area with Html diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Radio/Radio_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Radio/Radio_spec.js index 82dea6560807..f9e7ce98c5bb 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Radio/Radio_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Radio/Radio_spec.js @@ -17,7 +17,7 @@ describe("Radio Widget Functionality", function() { cy.widgetText( "radiotest", formWidgetsPage.radioWidget, - formWidgetsPage.radioInput, + widgetsPage.widgetNameSpan, ); /** * @param{IndexValue} Provide Input Index Value diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Select/select_Widget_Bug_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Select/select_Widget_Bug_spec.js index 1f1e614d97ed..cac6b9230263 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Select/select_Widget_Bug_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Select/select_Widget_Bug_spec.js @@ -14,7 +14,7 @@ describe("Select Widget Functionality", function() { cy.widgetText( "Select1", widgetsPage.selectwidget, - commonlocators.selectInner, + widgetsPage.widgetNameSpan, ); }); diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Select/select_Widget_validation_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Select/select_Widget_validation_spec.js index 742ebc6aac74..0a4e601263d6 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Select/select_Widget_validation_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Select/select_Widget_validation_spec.js @@ -13,7 +13,7 @@ describe("Select Widget Functionality", function() { cy.widgetText( "Select1", widgetsPage.selectwidget, - commonlocators.selectInner, + widgetsPage.widgetNameSpan, ); }); diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Tab/Tab_new_scenario_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Tab/Tab_new_scenario_spec.js index 7c3ce23b7c9c..94c5d7afb64a 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Tab/Tab_new_scenario_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Tab/Tab_new_scenario_spec.js @@ -8,7 +8,7 @@ describe("Tab widget test", function() { }); it("Tab Widget Functionality Test with Modal on change of selected tab", function() { cy.openPropertyPane("tabswidget"); - cy.widgetText("tab", Layoutpage.tabWidget, Layoutpage.tabInput); + cy.widgetText("tab", Layoutpage.tabWidget, widgetsPage.widgetNameSpan); cy.AddActionWithModal(); cy.get(".t--widget-buttonwidget:contains('Confirm')").click({ force: true, diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Tab/Tab_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Tab/Tab_spec.js index c82eab11d9f7..15b72569c7ce 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Tab/Tab_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Tab/Tab_spec.js @@ -15,7 +15,7 @@ describe("Tab widget test", function() { * @param{TabWidget}Mouseover * @param{TabPre Css} Assertion */ - cy.widgetText("tab", Layoutpage.tabWidget, Layoutpage.tabInput); + cy.widgetText("tab", Layoutpage.tabWidget, widgetsPage.widgetNameSpan); /** * @param{IndexValue} Provide input Index Value * @param{Text} Provide Index Text Value diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/TableV1/Table_Button_Icon_validation_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/TableV1/Table_Button_Icon_validation_spec.js index 3551b37a737f..5d06fdbb2e47 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/TableV1/Table_Button_Icon_validation_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/TableV1/Table_Button_Icon_validation_spec.js @@ -15,7 +15,7 @@ describe("Table Widget property pane feature validation", function() { cy.widgetText( "Table_1", widgetsPage.tableWidget, - commonlocators.tableInner, + widgetsPage.widgetNameSpan, ); cy.createModal("Modal", this.data.ModalName); cy.isSelectRow(1); diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/TableV1/Table_Widget_Copy_Paste_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/TableV1/Table_Widget_Copy_Paste_spec.js index 19c5bb52932f..57ebab152d6d 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/TableV1/Table_Widget_Copy_Paste_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/TableV1/Table_Widget_Copy_Paste_spec.js @@ -11,7 +11,11 @@ describe("Test Suite to validate copy/paste table Widget", function() { it("Copy paste table widget and valdiate application status", function() { const modifierKey = Cypress.platform === "darwin" ? "meta" : "ctrl"; cy.openPropertyPane("tablewidget"); - cy.widgetText("Table1", widgetsPage.tableWidget, commonlocators.tableInner); + cy.widgetText( + "Table1", + widgetsPage.tableWidget, + widgetsPage.widgetNameSpan, + ); cy.get("body").type(`{${modifierKey}}c`); // eslint-disable-next-line cypress/no-unnecessary-waiting cy.wait(500); diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/TableV1/Table_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/TableV1/Table_spec.js index 8a81e4094360..d08fa7bf4301 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/TableV1/Table_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/TableV1/Table_spec.js @@ -17,7 +17,11 @@ describe("Table Widget Functionality", function() { * @param{ChartWidget}Mouseover * @param{ChartPre Css} Assertion */ - cy.widgetText("Table1", widgetsPage.tableWidget, commonlocators.tableInner); + cy.widgetText( + "Table1", + widgetsPage.tableWidget, + widgetsPage.widgetNameSpan, + ); cy.testJsontext("tabledata", JSON.stringify(this.data.TableInput)); cy.wait("@updateLayout"); //cy.get(widgetsPage.ColumnAction).click({ force: true }); diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/TableV2/TableV2_Button_Icon_validation_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/TableV2/TableV2_Button_Icon_validation_spec.js index 61044640ec86..67bac7539921 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/TableV2/TableV2_Button_Icon_validation_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/TableV2/TableV2_Button_Icon_validation_spec.js @@ -15,7 +15,7 @@ describe("Table Widget V2 property pane feature validation", function() { cy.widgetText( "Table_1", widgetsPage.tableWidgetV2, - commonlocators.tableV2Inner, + widgetsPage.widgetNameSpan, ); //cy.createModal("Modal", this.data.ModalName); cy.createModalWithIndex("Modal", 1); diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/TableV2/TableV2_Widget_Copy_Paste_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/TableV2/TableV2_Widget_Copy_Paste_spec.js index c3e0d8fb193d..7f678ead7f77 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/TableV2/TableV2_Widget_Copy_Paste_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/TableV2/TableV2_Widget_Copy_Paste_spec.js @@ -13,7 +13,7 @@ describe("Test Suite to validate copy/paste table Widget V2", function() { cy.widgetText( "Table1", widgetsPage.tableWidgetV2, - commonlocators.tableV2Inner, + widgetsPage.widgetNameSpan, ); cy.get("body").type(`{${modifierKey}}c`); // eslint-disable-next-line cypress/no-unnecessary-waiting diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/TableV2/TableV2_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/TableV2/TableV2_spec.js index 11cddb29fa4a..2fcef65e500c 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/TableV2/TableV2_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/TableV2/TableV2_spec.js @@ -24,7 +24,7 @@ describe("Table Widget V2 Functionality", function() { cy.widgetText( "Table1", widgetsPage.tableWidgetV2, - commonlocators.tableV2Inner, + widgetsPage.widgetNameSpan, ); cy.testJsontext("tabledata", JSON.stringify(this.data.TableInput)); cy.wait("@updateLayout"); diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Text/Text_new_feature_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Text/Text_new_feature_spec.js index 07057540fb19..8227daae81af 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Text/Text_new_feature_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Text/Text_new_feature_spec.js @@ -45,7 +45,7 @@ describe("Text Widget color/font/alignment Functionality", function() { cy.widgetText( this.data.TextName, widgetsPage.textWidget, - widgetsPage.textWidget + " " + commonlocators.widgetNameTag, + widgetsPage.widgetNameSpan, ); //Changing the text label diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Text/Text_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Text/Text_spec.js index 229dc17eab6d..716973eed32f 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Text/Text_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Text/Text_spec.js @@ -17,7 +17,7 @@ describe("Text Widget Functionality", function() { cy.widgetText( this.data.TextName, widgetsPage.textWidget, - widgetsPage.textWidget + " " + commonlocators.widgetNameTag, + widgetsPage.widgetNameSpan, ); //Changing the text label cy.testCodeMirror(this.data.TextLabelValue); diff --git a/app/client/cypress/integration/Regression_TestSuite/ServerSideTests/ApiTests/API_Mustache_spec.js b/app/client/cypress/integration/Regression_TestSuite/ServerSideTests/ApiTests/API_Mustache_spec.js index 2aa66867cc55..7db1312a4db6 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ServerSideTests/ApiTests/API_Mustache_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ServerSideTests/ApiTests/API_Mustache_spec.js @@ -10,7 +10,7 @@ describe("Moustache test Functionality", function() { }); it("Moustache test Functionality", function() { cy.openPropertyPane("textwidget"); - cy.widgetText("Api", widgetsPage.textWidget, widgetsPage.textInputval); + cy.widgetText("Api", widgetsPage.textWidget, widgetsPage.widgetNameSpan); cy.testCodeMirror("users"); cy.NavigateToAPI_Panel(); cy.log("Navigation to API Panel screen successful"); diff --git a/app/client/cypress/integration/Regression_TestSuite/ServerSideTests/OnLoadTests/JSOnLoad_cyclic_dependency_errors_spec.js b/app/client/cypress/integration/Regression_TestSuite/ServerSideTests/OnLoadTests/JSOnLoad_cyclic_dependency_errors_spec.js index 4a3ebf19ca1d..586c286cac51 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ServerSideTests/OnLoadTests/JSOnLoad_cyclic_dependency_errors_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ServerSideTests/OnLoadTests/JSOnLoad_cyclic_dependency_errors_spec.js @@ -75,7 +75,11 @@ describe("Cyclic Dependency Informational Error Messages", function() { cy.get(widgetsPage.widgetSwitchId).click(); cy.openPropertyPane("inputwidgetv2"); cy.get(widgetsPage.defaultInput).type("{{" + queryName + ".data[0].gender"); - cy.widgetText("gender", widgetsPage.inputWidget, widgetsPage.inputval); + cy.widgetText( + "gender", + widgetsPage.inputWidget, + widgetsPage.widgetNameSpan, + ); cy.assertPageSave(); }); diff --git a/app/client/cypress/support/widgetCommands.js b/app/client/cypress/support/widgetCommands.js index b8be1893002b..9ebfe117dcd5 100644 --- a/app/client/cypress/support/widgetCommands.js +++ b/app/client/cypress/support/widgetCommands.js @@ -1281,9 +1281,11 @@ Cypress.Commands.add("openPropertyPaneCopy", (widgetType) => { .last() .trigger("mouseover", { force: true }) .wait(500); - cy.get( - `${selector}:first-of-type .t--widget-propertypane-toggle > .t--widget-name`, - ) + cy.get(`${selector}:first-of-type`) + .first() + .click({ force: true }) + .wait(500); + cy.get(".t--widget-propertypane-toggle > .t--widget-name") .first() .click({ force: true }); // eslint-disable-next-line cypress/no-unnecessary-waiting @@ -1675,11 +1677,13 @@ Cypress.Commands.add("openPropertyPaneWithIndex", (widgetType, index) => { .scrollIntoView() .trigger("mouseover", { force: true }) .wait(500); - cy.get( - `${selector}:first-of-type .t--widget-propertypane-toggle > .t--widget-name`, - ) + cy.get(`${selector}:first-of-type`) .eq(index) .scrollIntoView() + .click({ force: true }) + .wait(500); + cy.get(".t--widget-propertypane-toggle > .t--widget-name") + .first() .click({ force: true }); // eslint-disable-next-line cypress/no-unnecessary-waiting cy.wait(1000); From f4bdf20f87d331d2c0177706ab470998bc697c97 Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 8 Feb 2023 16:33:46 -0500 Subject: [PATCH 476/708] fix widgetsPage import --- app/client/cypress.json | 6 +----- .../ExplorerTests/Entity_Explorer_DragAndDropWidget_spec.js | 1 + .../Entity_Explorer_Widgets_Copy_Delete_Undo_spec.js | 1 + ...er_Widgets_Copy_Paste_Delete_Undo_Keyboard_Event_spec.js | 1 + .../ClientSideTests/Widgets/Checkbox/CheckboxGroup2_spec.js | 1 + .../ClientSideTests/Widgets/Datepicker/DatePicker2_spec.js | 1 + .../Widgets/Form/FormWidget_With_RichTextEditor_spec.js | 1 + .../ClientSideTests/Widgets/Image/Image_spec.js | 1 + .../ClientSideTests/Widgets/RTE/RichTextEditor_spec.js | 1 + .../ClientSideTests/Widgets/Radio/Radio_spec.js | 1 + .../ClientSideTests/Widgets/Tab/Tab_new_scenario_spec.js | 1 + 11 files changed, 11 insertions(+), 5 deletions(-) diff --git a/app/client/cypress.json b/app/client/cypress.json index 0001b91bc2e2..dacb775096be 100644 --- a/app/client/cypress.json +++ b/app/client/cypress.json @@ -19,9 +19,5 @@ "retries": { "runMode": 1, "openMode": 0 - }, - "env": { - "USERNAME": "preet.sidhu@appsmith.com", - "PASSWORD": "appsmith@123" -} + } } diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_DragAndDropWidget_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_DragAndDropWidget_spec.js index 5a574a41318e..3d203aa68272 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_DragAndDropWidget_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_DragAndDropWidget_spec.js @@ -3,6 +3,7 @@ const explorer = require("../../../../locators/explorerlocators.json"); const commonlocators = require("../../../../locators/commonlocators.json"); const formWidgetsPage = require("../../../../locators/FormWidgets.json"); const publish = require("../../../../locators/publishWidgetspage.json"); +const widgetsPage = require("../../../../locators/Widgets.json"); describe("Entity explorer Drag and Drop widgets testcases", function() { it("Drag and drop form widget and validate", function() { diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_Widgets_Copy_Delete_Undo_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_Widgets_Copy_Delete_Undo_spec.js index c5d2cf52eaa5..404c1862ca1e 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_Widgets_Copy_Delete_Undo_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_Widgets_Copy_Delete_Undo_spec.js @@ -2,6 +2,7 @@ const apiwidget = require("../../../../locators/apiWidgetslocator.json"); const commonlocators = require("../../../../locators/commonlocators.json"); const formWidgetsPage = require("../../../../locators/FormWidgets.json"); const dsl = require("../../../../fixtures/formWidgetdsl.json"); +const widgetsPage = require("../../../../locators/Widgets.json"); before(() => { cy.addDsl(dsl); diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_Widgets_Copy_Paste_Delete_Undo_Keyboard_Event_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_Widgets_Copy_Paste_Delete_Undo_Keyboard_Event_spec.js index 484899b504e1..767f747bca87 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_Widgets_Copy_Paste_Delete_Undo_Keyboard_Event_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_Widgets_Copy_Paste_Delete_Undo_Keyboard_Event_spec.js @@ -2,6 +2,7 @@ const apiwidget = require("../../../../locators/apiWidgetslocator.json"); const commonlocators = require("../../../../locators/commonlocators.json"); const formWidgetsPage = require("../../../../locators/FormWidgets.json"); const dsl = require("../../../../fixtures/formWithInputdsl.json"); +const widgetsPage = require("../../../../locators/Widgets.json"); import { ObjectsRegistry } from "../../../../support/Objects/Registry"; let ee = ObjectsRegistry.EntityExplorer; diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Checkbox/CheckboxGroup2_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Checkbox/CheckboxGroup2_spec.js index e0bcc57e5ecc..f9c525f12899 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Checkbox/CheckboxGroup2_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Checkbox/CheckboxGroup2_spec.js @@ -3,6 +3,7 @@ const formWidgetsPage = require("../../../../../locators/FormWidgets.json"); const publish = require("../../../../../locators/publishWidgetspage.json"); const explorer = require("../../../../../locators/explorerlocators.json"); const dsl = require("../../../../../fixtures/checkboxgroupDsl.json"); +const widgetsPage = require("../../../../../locators/Widgets.json"); describe("Checkbox Group Widget Functionality", function() { before(() => { diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Datepicker/DatePicker2_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Datepicker/DatePicker2_spec.js index 1d07fadd1dcb..628a8c57e8a5 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Datepicker/DatePicker2_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Datepicker/DatePicker2_spec.js @@ -2,6 +2,7 @@ const commonlocators = require("../../../../../locators/commonlocators.json"); const formWidgetsPage = require("../../../../../locators/FormWidgets.json"); const dsl = require("../../../../../fixtures/newFormDsl.json"); const publishPage = require("../../../../../locators/publishWidgetspage.json"); +const widgetsPage = require("../../../../../locators/Widgets.json"); const dayjs = require("dayjs"); describe("DatePicker Widget Functionality", function() { diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Form/FormWidget_With_RichTextEditor_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Form/FormWidget_With_RichTextEditor_spec.js index 9920e43d481b..44d1b88ca184 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Form/FormWidget_With_RichTextEditor_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Form/FormWidget_With_RichTextEditor_spec.js @@ -1,6 +1,7 @@ const commonlocators = require("../../../../../locators/commonlocators.json"); const formWidgetsPage = require("../../../../../locators/FormWidgets.json"); const dsl = require("../../../../../fixtures/formWithRTEDsl.json"); +const widgetsPage = require("../../../../../locators/Widgets.json"); describe("RichTextEditor Widget Functionality in Form", function() { before(() => { diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Image/Image_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Image/Image_spec.js index d40da28ad7fb..33adf0eefb9b 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Image/Image_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Image/Image_spec.js @@ -1,6 +1,7 @@ const commonlocators = require("../../../../../locators/commonlocators.json"); const viewWidgetsPage = require("../../../../../locators/ViewWidgets.json"); const publish = require("../../../../../locators/publishWidgetspage.json"); +const widgetsPage = require("../../../../../locators/Widgets.json"); const dsl = require("../../../../../fixtures/displayWidgetDsl.json"); describe("Image Widget Functionality", function() { diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/RTE/RichTextEditor_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/RTE/RichTextEditor_spec.js index 565992c12bc9..c7c0a9346177 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/RTE/RichTextEditor_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/RTE/RichTextEditor_spec.js @@ -2,6 +2,7 @@ const commonlocators = require("../../../../../locators/commonlocators.json"); const formWidgetsPage = require("../../../../../locators/FormWidgets.json"); const dsl = require("../../../../../fixtures/formdsl1.json"); const publishPage = require("../../../../../locators/publishWidgetspage.json"); +const widgetsPage = require("../../../../../locators/Widgets.json"); /** * A function to set the content inside an RTE widget diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Radio/Radio_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Radio/Radio_spec.js index f9e7ce98c5bb..cf86b424d82f 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Radio/Radio_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Radio/Radio_spec.js @@ -2,6 +2,7 @@ const commonlocators = require("../../../../../locators/commonlocators.json"); const formWidgetsPage = require("../../../../../locators/FormWidgets.json"); const publish = require("../../../../../locators/publishWidgetspage.json"); const dsl = require("../../../../../fixtures/newFormDsl.json"); +const widgetsPage = require("../../../../../locators/Widgets.json"); describe("Radio Widget Functionality", function() { before(() => { diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Tab/Tab_new_scenario_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Tab/Tab_new_scenario_spec.js index 94c5d7afb64a..b1a2aa7b0fa4 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Tab/Tab_new_scenario_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Tab/Tab_new_scenario_spec.js @@ -1,6 +1,7 @@ const Layoutpage = require("../../../../../locators/Layout.json"); const publish = require("../../../../../locators/publishWidgetspage.json"); const dsl = require("../../../../../fixtures/tabsWithWidgetDsl.json"); +const widgetsPage = require("../../../../../locators/Widgets.json"); describe("Tab widget test", function() { before(() => { From 1cab3bfa9a8028e26db0c048ee6eb025b70befe3 Mon Sep 17 00:00:00 2001 From: Aswath K Date: Thu, 9 Feb 2023 10:51:03 +0530 Subject: [PATCH 477/708] make the widget not to shrink below the minWidth --- .../src/sagas/AutoLayoutUpdateSagas.tsx | 16 ++++- .../CanvasSagas/AutoLayoutDraggingSagas.ts | 6 +- .../sagas/CanvasSagas/DraggingCanvasSagas.ts | 3 + app/client/src/sagas/WidgetDeletionSagas.ts | 7 ++ app/client/src/sagas/WidgetOperationSagas.tsx | 6 ++ app/client/src/utils/WidgetFactory.tsx | 2 - .../src/utils/autoLayout/AutoLayoutUtils.ts | 37 +++++++--- .../autoLayout/autoLayoutDraggingUtils.ts | 8 ++- .../src/utils/autoLayout/positionUtils.ts | 70 +++++++++++++++++-- app/client/src/widgets/BaseWidget.tsx | 7 +- app/client/src/widgets/InputWidgetV2/index.ts | 2 +- app/client/src/widgets/constants.ts | 2 - 12 files changed, 141 insertions(+), 25 deletions(-) diff --git a/app/client/src/sagas/AutoLayoutUpdateSagas.tsx b/app/client/src/sagas/AutoLayoutUpdateSagas.tsx index 79cac61cf14e..403f1a66567f 100644 --- a/app/client/src/sagas/AutoLayoutUpdateSagas.tsx +++ b/app/client/src/sagas/AutoLayoutUpdateSagas.tsx @@ -18,6 +18,7 @@ import { } from "../utils/autoLayout/AutoLayoutUtils"; import { getWidgets } from "./selectors"; import { updateWidgetPositions } from "utils/autoLayout/positionUtils"; +import { getCanvasWidth } from "selectors/editorSelectors"; type LayoutUpdatePayload = { parentId: string; @@ -51,11 +52,13 @@ function* addChildWrappers(actionPayload: ReduxAction) { const { parentId } = actionPayload.payload; const allWidgets: CanvasWidgetsReduxState = yield select(getWidgets); const isMobile: boolean = yield select(getIsMobile); + const mainCanvasWidth: number = yield select(getCanvasWidth); const updatedWidgets: CanvasWidgetsReduxState = yield call( wrapChildren, allWidgets, parentId, isMobile, + mainCanvasWidth, ); yield put(updateAndSaveLayout(updatedWidgets)); log.debug("empty wrapper removal took", performance.now() - start, "ms"); @@ -81,11 +84,13 @@ export function* updateFillChildInfo( const { responsiveBehavior, widgetId } = actionPayload.payload; const allWidgets: CanvasWidgetsReduxState = yield select(getWidgets); const isMobile: boolean = yield select(getIsMobile); + const mainCanvasWidth: number = yield select(getCanvasWidth); const updatedWidgets: CanvasWidgetsReduxState = updateFillChildStatus( allWidgets, widgetId, responsiveBehavior === ResponsiveBehavior.Fill, isMobile, + mainCanvasWidth, ); yield put(updateAndSaveLayout(updatedWidgets)); log.debug("updating fill child info took", performance.now() - start, "ms"); @@ -111,9 +116,10 @@ export function* updateLayoutForMobileCheckpoint( const start = performance.now(); const { canvasWidth, isMobile, parentId } = actionPayload.payload; const allWidgets: CanvasWidgetsReduxState = yield select(getWidgets); + const mainCanvasWidth: number = yield select(getCanvasWidth); const updatedWidgets: CanvasWidgetsReduxState = isMobile ? alterLayoutForMobile(allWidgets, parentId, canvasWidth, canvasWidth) - : alterLayoutForDesktop(allWidgets, parentId); + : alterLayoutForDesktop(allWidgets, parentId, mainCanvasWidth); yield put(updateAndSaveLayout(updatedWidgets)); log.debug( "updating layout for mobile viewport took", @@ -138,12 +144,18 @@ function* widgetViolatedMinDimensionsSaga( ) { const isMobile: boolean = yield select(getIsMobile); const allWidgets: CanvasWidgetsReduxState = yield select(getWidgets); + const mainCanvasWidth: number = yield select(getCanvasWidth); const parentId = action.payload.parentId; if (processedParentIds.has(parentId)) return; processedParentIds.set(parentId, true); setTimeout(() => processedParentIds.delete(parentId), 1000); - const updatedWidgets = updateWidgetPositions(allWidgets, parentId, isMobile); + const updatedWidgets = updateWidgetPositions( + allWidgets, + parentId, + isMobile, + mainCanvasWidth, + ); yield put(updateAndSaveLayout(updatedWidgets)); } diff --git a/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts b/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts index 7f7c28e9d816..7d7eabc7e89e 100644 --- a/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts +++ b/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts @@ -21,8 +21,9 @@ import { updateExistingLayer, updateRelationships, } from "utils/autoLayout/autoLayoutDraggingUtils"; -import { updateWidgetPositions } from "utils/autoLayout/positionUtils"; import { HighlightInfo, FlexLayer } from "utils/autoLayout/autoLayoutTypes"; +import { updateWidgetPositions } from "utils/autoLayout/positionUtils"; +import { getCanvasWidth } from "selectors/editorSelectors"; function* addWidgetAndReorderSaga( actionPayload: ReduxAction<{ @@ -140,6 +141,7 @@ function* reorderAutolayoutChildren(params: { } = params; const widgets = Object.assign({}, allWidgets); if (!movedWidgets) return widgets; + const mainCanvasWidth: number = yield select(getCanvasWidth); const selectedWidgets = [...movedWidgets]; let updatedWidgets: CanvasWidgetsReduxState = updateRelationships( @@ -148,6 +150,7 @@ function* reorderAutolayoutChildren(params: { parentId, false, isMobile, + mainCanvasWidth, ); // Update flexLayers for a vertical stack. @@ -217,6 +220,7 @@ function* reorderAutolayoutChildren(params: { updatedWidgets, parentId, isMobile, + mainCanvasWidth, ); return widgetsAfterPositionUpdate; diff --git a/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts b/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts index 240f820c421e..e9e5b9085782 100644 --- a/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts +++ b/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts @@ -27,6 +27,7 @@ import { getWidget, getWidgets } from "sagas/selectors"; import { getUpdateDslAfterCreatingChild } from "sagas/WidgetAdditionSagas"; import { traverseTreeAndExecuteBlueprintChildOperations } from "sagas/WidgetBlueprintSagas"; import { + getCanvasWidth, getCurrentAppPositioningType, getMainCanvasProps, getOccupiedSpacesSelectorForContainer, @@ -325,12 +326,14 @@ function* moveWidgetsSaga( * then update the flex layers. */ const isMobile: boolean = yield select(getIsMobile); + const mainCanvasWidth: number = yield select(getCanvasWidth); updatedWidgets = updateRelationships( draggedBlocksToUpdate.map((block) => block.widgetId), updatedWidgets, canvasId, true, isMobile, + mainCanvasWidth, ); } const updatedWidgetsOnMove: CanvasWidgetsReduxState = yield call( diff --git a/app/client/src/sagas/WidgetDeletionSagas.ts b/app/client/src/sagas/WidgetDeletionSagas.ts index c9ebc3d65759..c5121d1c3bcb 100644 --- a/app/client/src/sagas/WidgetDeletionSagas.ts +++ b/app/client/src/sagas/WidgetDeletionSagas.ts @@ -22,6 +22,7 @@ import { } from "reducers/entityReducers/canvasWidgetsReducer"; import { all, call, put, select, takeEvery } from "redux-saga/effects"; import { SelectionRequestType } from "sagas/WidgetSelectUtils"; +import { getCanvasWidth } from "selectors/editorSelectors"; import { getIsMobile } from "selectors/mainCanvasSelectors"; import { inGuidedTour, @@ -94,12 +95,14 @@ function* deleteTabChildSaga( }; // Update flex layers of a canvas upon deletion of a widget. const isMobile: boolean = yield select(getIsMobile); + const mainCanvasWidth: number = yield select(getCanvasWidth); const widgetsAfterUpdatingFlexLayers: CanvasWidgetsReduxState = yield call( updateFlexLayersOnDelete, parentUpdatedWidgets, widgetId, tabWidget.parentId, isMobile, + mainCanvasWidth, ); yield put(updateAndSaveLayout(widgetsAfterUpdatingFlexLayers)); yield call(postDelete, widgetId, label, otherWidgetsToDelete); @@ -224,12 +227,14 @@ function* deleteSaga(deleteAction: ReduxAction) { if (updatedObj) { const { finalWidgets, otherWidgetsToDelete, widgetName } = updatedObj; const isMobile: boolean = yield select(getIsMobile); + const mainCanvasWidth: number = yield select(getCanvasWidth); // Update flex layers of a canvas upon deletion of a widget. const widgetsAfterUpdatingFlexLayers: CanvasWidgetsReduxState = updateFlexLayersOnDelete( finalWidgets, widgetId, parentId, isMobile, + mainCanvasWidth, ); yield put(updateAndSaveLayout(widgetsAfterUpdatingFlexLayers)); yield put(generateAutoHeightLayoutTreeAction(true, true)); @@ -302,6 +307,7 @@ function* deleteAllSelectedWidgetsSaga( let widgetsAfterUpdatingFlexLayers: CanvasWidgetsReduxState = finalWidgets; if (parentId) { const isMobile: boolean = yield select(getIsMobile); + const mainCanvasWidth: number = yield select(getCanvasWidth); for (const widgetId of selectedWidgets) { widgetsAfterUpdatingFlexLayers = yield call( updateFlexLayersOnDelete, @@ -309,6 +315,7 @@ function* deleteAllSelectedWidgetsSaga( widgetId, parentId, isMobile, + mainCanvasWidth, ); } } diff --git a/app/client/src/sagas/WidgetOperationSagas.tsx b/app/client/src/sagas/WidgetOperationSagas.tsx index 0972f14805e6..175ad060e69a 100644 --- a/app/client/src/sagas/WidgetOperationSagas.tsx +++ b/app/client/src/sagas/WidgetOperationSagas.tsx @@ -39,6 +39,7 @@ import { takeLeading, } from "redux-saga/effects"; import { + getCanvasWidth, getContainerWidgetSpacesSelector, getCurrentAppPositioningType, getCurrentPageId, @@ -177,6 +178,7 @@ export function* resizeSaga(resizeAction: ReduxAction) { const appPositioningType: AppPositioningTypes = yield select( getCurrentAppPositioningType, ); + const mainCanvasWidth: number = yield select(getCanvasWidth); widget = { ...widget, leftColumn, rightColumn, topRow, bottomRow }; const movedWidgets: { [widgetId: string]: FlattenedWidgetProps; @@ -208,6 +210,7 @@ export function* resizeSaga(resizeAction: ReduxAction) { movedWidgets, parentId, isMobile, + mainCanvasWidth, ); } log.debug("resize computations took", performance.now() - start, "ms"); @@ -1320,6 +1323,7 @@ function* pasteWidgetSaga( let widgets: CanvasWidgetsReduxState = canvasWidgets; const selectedWidget: FlattenedWidgetProps = yield getSelectedWidgetWhenPasting(); const isMobile: boolean = yield select(getIsMobile); + const mainCanvasWidth: number = yield select(getCanvasWidth); let reflowedMovementMap, gridProps: GridProps | undefined, newPastingPositionMap: SpaceMap | undefined, @@ -1656,6 +1660,7 @@ function* pasteWidgetSaga( widget, reverseWidgetIdMap[widget.widgetId], isMobile, + mainCanvasWidth, ); else if (widget.type !== "CANVAS_WIDGET") widgets = addChildToPastedFlexLayers( @@ -1663,6 +1668,7 @@ function* pasteWidgetSaga( widget, widgetIdMap, isMobile, + mainCanvasWidth, ); } } diff --git a/app/client/src/utils/WidgetFactory.tsx b/app/client/src/utils/WidgetFactory.tsx index a2731e327a42..84987f7e49ed 100644 --- a/app/client/src/utils/WidgetFactory.tsx +++ b/app/client/src/utils/WidgetFactory.tsx @@ -336,8 +336,6 @@ class WidgetFactory { const map = this.autoLayoutConfigMap.get(type); if (!map) { return { - defaults: this.defaultPropertiesMap.get(type) || {}, - mobile: this.defaultPropertiesMap.get(type) || {}, widgetSize: [], }; } diff --git a/app/client/src/utils/autoLayout/AutoLayoutUtils.ts b/app/client/src/utils/autoLayout/AutoLayoutUtils.ts index 25cec50b1353..39ee17ca69e1 100644 --- a/app/client/src/utils/autoLayout/AutoLayoutUtils.ts +++ b/app/client/src/utils/autoLayout/AutoLayoutUtils.ts @@ -46,7 +46,8 @@ export function removeChildLayers( export function* wrapChildren( allWidgets: CanvasWidgetsReduxState, containerId: string, - isMobile?: boolean, + isMobile: boolean, + mainCanvasWidth: number, ) { const widgets = { ...allWidgets }; let canvas = getCanvas(widgets, containerId); @@ -71,6 +72,7 @@ export function* wrapChildren( widgets, canvas.widgetId, isMobile, + mainCanvasWidth, ); return updatedWidgets; } @@ -79,7 +81,8 @@ export function updateFlexLayersOnDelete( allWidgets: CanvasWidgetsReduxState, widgetId: string, parentId: string, - isMobile?: boolean, + isMobile: boolean, + mainCanvasWidth: number, ): CanvasWidgetsReduxState { const widgets = { ...allWidgets }; if ( @@ -131,7 +134,7 @@ export function updateFlexLayersOnDelete( }; widgets[parentId] = parent; - return updateWidgetPositions(widgets, parentId, isMobile); + return updateWidgetPositions(widgets, parentId, isMobile, mainCanvasWidth); } export function updateFillChildStatus( @@ -139,6 +142,7 @@ export function updateFillChildStatus( widgetId: string, fill: boolean, isMobile: boolean, + mainCanvasWidth: number, ) { let widgets = { ...allWidgets }; const widget = widgets[widgetId]; @@ -158,7 +162,12 @@ export function updateFillChildStatus( }, }; - return updateWidgetPositions(widgets, canvas.widgetId, isMobile); + return updateWidgetPositions( + widgets, + canvas.widgetId, + isMobile, + mainCanvasWidth, + ); } export function alterLayoutForMobile( @@ -215,15 +224,16 @@ export function alterLayoutForMobile( mainCanvasWidth, ); widgets[child] = widget; - widgets = updateWidgetPositions(widgets, child, true); + widgets = updateWidgetPositions(widgets, child, true, mainCanvasWidth); } - widgets = updateWidgetPositions(widgets, parentId, true); + widgets = updateWidgetPositions(widgets, parentId, true, mainCanvasWidth); return widgets; } export function alterLayoutForDesktop( allWidgets: CanvasWidgetsReduxState, parentId: string, + mainCanvasWidth: number, ): CanvasWidgetsReduxState { let widgets = { ...allWidgets }; const parent = widgets[parentId]; @@ -232,9 +242,9 @@ export function alterLayoutForDesktop( if (!isStack(allWidgets, parent)) return widgets; if (!children || !children.length) return widgets; - widgets = updateWidgetPositions(widgets, parentId, false); + widgets = updateWidgetPositions(widgets, parentId, false, mainCanvasWidth); for (const child of children) { - widgets = alterLayoutForDesktop(widgets, child); + widgets = alterLayoutForDesktop(widgets, child, mainCanvasWidth); } return widgets; } @@ -249,6 +259,7 @@ export function pasteWidgetInFlexLayers( widget: any, originalWidgetId: string, isMobile: boolean, + mainCanvasWidth: number, ): CanvasWidgetsReduxState { let widgets = { ...allWidgets }; const parent = widgets[parentId]; @@ -312,7 +323,7 @@ export function pasteWidgetInFlexLayers( flexLayers, }, }; - return updateWidgetPositions(widgets, parentId, isMobile); + return updateWidgetPositions(widgets, parentId, isMobile, mainCanvasWidth); } /** @@ -326,6 +337,7 @@ export function addChildToPastedFlexLayers( widget: any, widgetIdMap: Record, isMobile: boolean, + mainCanvasWidth: number, ): CanvasWidgetsReduxState { let widgets = { ...allWidgets }; const parent = widgets[widget.parentId]; @@ -358,7 +370,12 @@ export function addChildToPastedFlexLayers( flexLayers, }, }; - return updateWidgetPositions(widgets, parent.widgetId, isMobile); + return updateWidgetPositions( + widgets, + parent.widgetId, + isMobile, + mainCanvasWidth, + ); } export function isStack( diff --git a/app/client/src/utils/autoLayout/autoLayoutDraggingUtils.ts b/app/client/src/utils/autoLayout/autoLayoutDraggingUtils.ts index 7b87680fc5ff..8ca9c99c2c9a 100644 --- a/app/client/src/utils/autoLayout/autoLayoutDraggingUtils.ts +++ b/app/client/src/utils/autoLayout/autoLayoutDraggingUtils.ts @@ -70,6 +70,7 @@ export function updateRelationships( parentId: string, onlyUpdateFlexLayers = false, isMobile = false, + mainCanvasWidth: number, ): CanvasWidgetsReduxState { const widgets = { ...allWidgets }; // Check if parent has changed @@ -113,7 +114,12 @@ export function updateRelationships( } if (prevParents.length) { for (const id of prevParents) { - const updatedWidgets = updateWidgetPositions(widgets, id, isMobile); + const updatedWidgets = updateWidgetPositions( + widgets, + id, + isMobile, + mainCanvasWidth, + ); return updatedWidgets; } } diff --git a/app/client/src/utils/autoLayout/positionUtils.ts b/app/client/src/utils/autoLayout/positionUtils.ts index b4ce3c30df04..6ba0fabe18a1 100644 --- a/app/client/src/utils/autoLayout/positionUtils.ts +++ b/app/client/src/utils/autoLayout/positionUtils.ts @@ -12,12 +12,14 @@ import { import { WidgetProps } from "widgets/BaseWidget"; import { getBottomRow, + getMinPixelWidth, getTopRow, getWidgetHeight, getWidgetRows, getWidgetWidth, setDimensions, } from "./flexWidgetUtils"; +import { getCanvasWidth } from "./highlightUtils"; export type Widget = WidgetProps & { children?: string[] | undefined; @@ -45,6 +47,7 @@ export function updateWidgetPositions( allWidgets: CanvasWidgetsReduxState, parentId: string, isMobile = false, + mainCanvasWidth: number, ): CanvasWidgetsReduxState { let widgets = { ...allWidgets }; try { @@ -56,6 +59,14 @@ export function updateWidgetPositions( const parent = widgets[parentId]; if (!parent) return widgets; + const canvasWidth = getCanvasWidth( + parent, + widgets, + mainCanvasWidth, + isMobile, + ); + const columnSpace = canvasWidth / GridDefaults.DEFAULT_GRID_COLUMNS; + let height = 0; if (parent.flexLayers && parent.flexLayers?.length) { /** @@ -66,7 +77,14 @@ export function updateWidgetPositions( const payload: { height: number; widgets: CanvasWidgetsReduxState; - } = calculateWidgetPositions(widgets, layer, height, isMobile); + } = calculateWidgetPositions( + widgets, + layer, + height, + isMobile, + mainCanvasWidth, + columnSpace, + ); widgets = payload.widgets; height += payload.height; } @@ -110,7 +128,12 @@ export function updateWidgetPositions( // parent.parentId ? widgets[parent.parentId].widgetName : "no parent", // ); if (shouldUpdateHeight && parent.parentId) - return updateWidgetPositions(widgets, parent.parentId, isMobile); + return updateWidgetPositions( + widgets, + parent.parentId, + isMobile, + mainCanvasWidth, + ); } return widgets; } catch (e) { @@ -124,6 +147,8 @@ function calculateWidgetPositions( layer: FlexLayer, topRow: number, isMobile = false, + mainCanvasWidth: number, + columnSpace: number, ): { height: number; widgets: CanvasWidgetsReduxState } { /** * Get information break down on each alignment within the layer. @@ -152,6 +177,8 @@ function calculateWidgetPositions( topRow, fillWidgetLength, isMobile, + mainCanvasWidth, + columnSpace, ); return placeWidgetsWithoutWrap( @@ -160,6 +187,8 @@ function calculateWidgetPositions( topRow, fillWidgetLength, isMobile, + mainCanvasWidth, + columnSpace, ); } @@ -180,6 +209,8 @@ export function placeWidgetsWithoutWrap( topRow: number, fillWidgetLength: number, isMobile = false, + mainCanvasWidth: number, + columnSpace: number, totalHeight = 0, ): { height: number; widgets: CanvasWidgetsReduxState } { let widgets = { ...allWidgets }; @@ -198,11 +229,15 @@ export function placeWidgetsWithoutWrap( each.columns, ); for (const widget of each.children) { + const minWidth = getMinPixelWidth(widget, mainCanvasWidth); const height = getWidgetHeight(widget, isMobile); - const width = + let width = widget.responsiveBehavior === ResponsiveBehavior.Fill ? fillWidgetLength : getWidgetWidth(widget, isMobile); + if (minWidth && width * columnSpace < minWidth) { + width = minWidth / columnSpace; + } if (!totalHeight) maxHeight = Math.max(maxHeight, height); const updatedWidget = setDimensions( widget, @@ -222,6 +257,20 @@ export function placeWidgetsWithoutWrap( } } + // Trigger a position update for the widgets inside container widgets + for (const each of arr) { + for (const widget of each.children) { + if (widget.type === "CONTAINER_WIDGET" && widget.children?.length) { + widgets = updateWidgetPositions( + widgets, + widget.children[0], + isMobile, + mainCanvasWidth, + ); + } + } + } + return { height: maxHeight, widgets }; } @@ -425,6 +474,8 @@ function updatePositionsForFlexWrap( topRow: number, fillWidgetLength: number, isMobile: boolean, + mainCanvasWidth: number, + columnSpace: number, ): { height: number; widgets: CanvasWidgetsReduxState } { let widgets = { ...allWidgets }; @@ -436,13 +487,22 @@ function updatePositionsForFlexWrap( // if there is only one alignment in this row, this implies that it may be wrapped. const payload = each.length === 1 - ? placeWrappedWidgets(widgets, each[0], top, fillWidgetLength, isMobile) + ? placeWrappedWidgets( + widgets, + each[0], + top, + fillWidgetLength, + isMobile, + mainCanvasWidth, + ) : placeWidgetsWithoutWrap( widgets, each, top, fillWidgetLength, isMobile, + mainCanvasWidth, + columnSpace, ); widgets = payload.widgets; top += payload.height; @@ -487,6 +547,7 @@ export function placeWrappedWidgets( topRow: number, fillWidgetLength: number, isMobile = false, + mainCanvasWidth: number, ): { height: number; widgets: CanvasWidgetsReduxState } { let widgets = { ...allWidgets }; @@ -503,6 +564,7 @@ export function placeWrappedWidgets( startRow, fillWidgetLength, isMobile, + mainCanvasWidth, height, ); widgets = result.widgets; diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index 73fc699268f8..26ad271ec5da 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -472,7 +472,10 @@ abstract class BaseWidget< makeFlex(content: ReactNode) { const { componentHeight, componentWidth } = this.getComponentDimensions(); - const minWidth = getMinPixelWidth(this.props, this.props.mainCanvasWidth); + const minWidth = getMinPixelWidth( + this.props, + this.props?.mainCanvasWidth || 0, + ); return ( { return { - minWidth: "60px", + minWidth: "200px", minHeight: "80px", }; }, diff --git a/app/client/src/widgets/constants.ts b/app/client/src/widgets/constants.ts index 8f658ca2e221..9dca395db03a 100644 --- a/app/client/src/widgets/constants.ts +++ b/app/client/src/widgets/constants.ts @@ -21,8 +21,6 @@ export type WidgetSizeConfig = { }; export type AutoLayoutConfig = { - defaults: Record; - mobile: Record; widgetSize: Array; }; From 7524107aaabc947c376ff23447e9165ef3cf43cc Mon Sep 17 00:00:00 2001 From: Aswath K Date: Thu, 9 Feb 2023 12:19:53 +0530 Subject: [PATCH 478/708] fix: unable to resize widget on mobile view --- .../src/utils/autoLayout/AutoLayoutUtils.ts | 94 ++++++++++++++----- app/client/src/widgets/InputWidgetV2/index.ts | 2 +- 2 files changed, 71 insertions(+), 25 deletions(-) diff --git a/app/client/src/utils/autoLayout/AutoLayoutUtils.ts b/app/client/src/utils/autoLayout/AutoLayoutUtils.ts index 39ee17ca69e1..547edebd6a3b 100644 --- a/app/client/src/utils/autoLayout/AutoLayoutUtils.ts +++ b/app/client/src/utils/autoLayout/AutoLayoutUtils.ts @@ -14,7 +14,7 @@ import { ResponsiveBehavior, } from "utils/autoLayout/constants"; import { updateWidgetPositions } from "utils/autoLayout/positionUtils"; -import { getMinPixelWidth, getWidgetWidth } from "./flexWidgetUtils"; +import { getWidgetWidth } from "./flexWidgetUtils"; import { AlignmentColumnInfo } from "./autoLayoutTypes"; function getCanvas(widgets: CanvasWidgetsReduxState, containerId: string) { @@ -170,6 +170,66 @@ export function updateFillChildStatus( ); } +// export function alterLayoutForMobile( +// allWidgets: CanvasWidgetsReduxState, +// parentId: string, +// canvasWidth: number, +// mainCanvasWidth: number, +// ): CanvasWidgetsReduxState { +// let widgets = { ...allWidgets }; +// const parent = widgets[parentId]; +// const children = parent.children; + +// if (!isStack(allWidgets, parent)) { +// return widgets; +// } +// if (!children || !children.length) return widgets; + +// for (const child of children) { +// const widget = { ...widgets[child] }; +// const minWidth: number | undefined = getMinPixelWidth( +// widget, +// mainCanvasWidth, +// ); + +// if (widget.responsiveBehavior === ResponsiveBehavior.Fill) { +// widget.mobileRightColumn = 64; +// widget.mobileLeftColumn = 0; +// } else if ( +// widget.responsiveBehavior === ResponsiveBehavior.Hug && +// minWidth +// ) { +// const { leftColumn, rightColumn } = widget; +// const columnSpace = (canvasWidth - FLEXBOX_PADDING * 2) / 64; +// if (columnSpace * (rightColumn - leftColumn) < minWidth) { +// /** +// * Set a proper width for the widget => left column = 0; +// * Actual positioning of the widget will be updated by updateWidgetPositions function. +// */ +// widget.mobileLeftColumn = 0; +// widget.mobileRightColumn = Math.min(minWidth / columnSpace, 64); +// } +// } else { +// widget.mobileLeftColumn = widget.leftColumn; +// widget.mobileRightColumn = widget.rightColumn; +// } + +// widget.mobileTopRow = widget.topRow; +// widget.mobileBottomRow = widget.bottomRow; +// if (widget.mobileRightColumn !== undefined) +// widgets = alterLayoutForMobile( +// widgets, +// child, +// (canvasWidth * widget.mobileRightColumn) / 64, +// mainCanvasWidth, +// ); +// widgets[child] = widget; +// widgets = updateWidgetPositions(widgets, child, true, mainCanvasWidth); +// } +// widgets = updateWidgetPositions(widgets, parentId, true, mainCanvasWidth); +// return widgets; +// } + export function alterLayoutForMobile( allWidgets: CanvasWidgetsReduxState, parentId: string, @@ -187,42 +247,28 @@ export function alterLayoutForMobile( for (const child of children) { const widget = { ...widgets[child] }; - const minWidth: number | undefined = getMinPixelWidth( - widget, - mainCanvasWidth, - ); - if (widget.responsiveBehavior === ResponsiveBehavior.Fill) { widget.mobileRightColumn = 64; widget.mobileLeftColumn = 0; } else if ( widget.responsiveBehavior === ResponsiveBehavior.Hug && - minWidth + widget.minWidth ) { - const { leftColumn, rightColumn } = widget; + const { minWidth, rightColumn } = widget; const columnSpace = (canvasWidth - FLEXBOX_PADDING * 2) / 64; - if (columnSpace * (rightColumn - leftColumn) < minWidth) { - /** - * Set a proper width for the widget => left column = 0; - * Actual positioning of the widget will be updated by updateWidgetPositions function. - */ + if (columnSpace * rightColumn < minWidth) { widget.mobileLeftColumn = 0; widget.mobileRightColumn = Math.min(minWidth / columnSpace, 64); } - } else { - widget.mobileLeftColumn = widget.leftColumn; - widget.mobileRightColumn = widget.rightColumn; } - widget.mobileTopRow = widget.topRow; widget.mobileBottomRow = widget.bottomRow; - if (widget.mobileRightColumn !== undefined) - widgets = alterLayoutForMobile( - widgets, - child, - canvasWidth * (widget.mobileRightColumn / 64), - mainCanvasWidth, - ); + widgets = alterLayoutForMobile( + widgets, + child, + (canvasWidth * (widget.mobileRightColumn || 1)) / 64, + mainCanvasWidth, + ); widgets[child] = widget; widgets = updateWidgetPositions(widgets, child, true, mainCanvasWidth); } diff --git a/app/client/src/widgets/InputWidgetV2/index.ts b/app/client/src/widgets/InputWidgetV2/index.ts index 47a25f9698a9..b2305d0be570 100644 --- a/app/client/src/widgets/InputWidgetV2/index.ts +++ b/app/client/src/widgets/InputWidgetV2/index.ts @@ -55,7 +55,7 @@ export const CONFIG = { viewportMinWidth: layoutConfigurations.MOBILE.maxWidth, configuration: () => { return { - minWidth: "200px", + minWidth: "60px", minHeight: "80px", }; }, From 2be1f1ec14c4b1d2aa2f9687bcae29d0e003e4c9 Mon Sep 17 00:00:00 2001 From: Aswath K Date: Thu, 9 Feb 2023 13:18:36 +0530 Subject: [PATCH 479/708] fix: test files --- .../autoLayoutDraggingUtils.test.ts | 6 +++ .../utils/autoLayout/autoLayoutUtils.test.ts | 7 +++ .../utils/autoLayout/positionUtils.test.ts | 45 ++++++++++++++++--- 3 files changed, 53 insertions(+), 5 deletions(-) diff --git a/app/client/src/utils/autoLayout/autoLayoutDraggingUtils.test.ts b/app/client/src/utils/autoLayout/autoLayoutDraggingUtils.test.ts index cceb15afc6d8..2e86f757e214 100644 --- a/app/client/src/utils/autoLayout/autoLayoutDraggingUtils.test.ts +++ b/app/client/src/utils/autoLayout/autoLayoutDraggingUtils.test.ts @@ -64,6 +64,7 @@ describe("test AutoLayoutDraggingUtils methods", () => { }); describe("test updateRelationships method", () => { + const mainCanvasWidth = 960; it("should remove moved widgets from old parent's layers and assign new parent to the widgets", () => { const widgets = { ...data }; const movedWidget = "pt32jvs72k"; @@ -72,6 +73,9 @@ describe("test AutoLayoutDraggingUtils methods", () => { [movedWidget], widgets, "0", + false, + false, + mainCanvasWidth, ); expect(result[movedWidget].parentId === "0").toBeTruthy; if (result[oldParent]) { @@ -92,6 +96,8 @@ describe("test AutoLayoutDraggingUtils methods", () => { widgets, "0", true, + false, + mainCanvasWidth, ); expect(result[movedWidget].parentId === "0").toBeFalsy; if (result[oldParent]) { diff --git a/app/client/src/utils/autoLayout/autoLayoutUtils.test.ts b/app/client/src/utils/autoLayout/autoLayoutUtils.test.ts index e2e05c85f10e..de556b342a22 100644 --- a/app/client/src/utils/autoLayout/autoLayoutUtils.test.ts +++ b/app/client/src/utils/autoLayout/autoLayoutUtils.test.ts @@ -8,6 +8,7 @@ import { import { data } from "./testData"; describe("test AutoLayoutUtils methods", () => { + const mainCanvasWidth = 960; describe("test updateFlexLayersOnDelete method", () => { it("should remove deleted widgets from flex layers of the parent", () => { const widgets = { ...data }; @@ -18,6 +19,7 @@ describe("test AutoLayoutUtils methods", () => { deletedWidgetId, parentId, false, + mainCanvasWidth, ); expect(result[parentId].flexLayers?.length).toEqual(1); const layerIndex = getLayerIndexOfWidget( @@ -35,6 +37,7 @@ describe("test AutoLayoutUtils methods", () => { deletedWidgetId, parentId, false, + mainCanvasWidth, ); expect(result[parentId].flexLayers?.length).toEqual(1); expect(result[parentId].flexLayers[0]?.children.length).toEqual(2); @@ -47,12 +50,14 @@ describe("test AutoLayoutUtils methods", () => { "pt32jvs72k", parentId, false, + mainCanvasWidth, ); const result: CanvasWidgetsReduxState = updateFlexLayersOnDelete( updatedWidgets, "tg6jcd1kjp", parentId, false, + mainCanvasWidth, ); expect(result[parentId].flexLayers?.length).toEqual(0); }); @@ -83,6 +88,7 @@ describe("test AutoLayoutUtils methods", () => { copiedWidget, originalWidgetId, false, + mainCanvasWidth, ); expect(result[newParentId].flexLayers?.length).toEqual(1); @@ -133,6 +139,7 @@ describe("test AutoLayoutUtils methods", () => { copiedWidget, originalWidgetId, false, + mainCanvasWidth, ); // layer count should remain the same. => no new layer is created. expect(result[parentId].flexLayers?.length).toEqual(2); diff --git a/app/client/src/utils/autoLayout/positionUtils.test.ts b/app/client/src/utils/autoLayout/positionUtils.test.ts index 2c95d03302f4..cfc9da39ec02 100644 --- a/app/client/src/utils/autoLayout/positionUtils.test.ts +++ b/app/client/src/utils/autoLayout/positionUtils.test.ts @@ -21,6 +21,8 @@ import { import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; describe("test PositionUtils methods", () => { + const mainCanvasWidth = 960; + const columnSpace = 10; describe("test extractAlignmentInfo method", () => { it("should extract children and required columns for each alignment", () => { const widgets = { @@ -390,7 +392,16 @@ describe("test PositionUtils methods", () => { const result: { height: number; widgets: CanvasWidgetsReduxState; - } = placeWidgetsWithoutWrap(widgets, arr, 0, 64, false, 0); + } = placeWidgetsWithoutWrap( + widgets, + arr, + 0, + 64, + false, + mainCanvasWidth, + columnSpace, + 0, + ); expect(result.height).toEqual(7); expect(result.widgets["2"].leftColumn).toEqual(16); expect(result.widgets["2"].rightColumn).toEqual(40); @@ -465,7 +476,16 @@ describe("test PositionUtils methods", () => { const result: { height: number; widgets: CanvasWidgetsReduxState; - } = placeWidgetsWithoutWrap(widgets, arr, 0, 64, false, 0); + } = placeWidgetsWithoutWrap( + widgets, + arr, + 0, + 64, + false, + mainCanvasWidth, + columnSpace, + 0, + ); expect(result.height).toEqual(7); expect(result.widgets["1"].leftColumn).toEqual(8); expect(result.widgets["1"].rightColumn).toEqual(24); @@ -557,7 +577,16 @@ describe("test PositionUtils methods", () => { const result: { height: number; widgets: CanvasWidgetsReduxState; - } = placeWidgetsWithoutWrap(widgets, arr, 0, 64, true, 0); + } = placeWidgetsWithoutWrap( + widgets, + arr, + 0, + 64, + true, + mainCanvasWidth, + columnSpace, + 0, + ); expect(result.height).toEqual(7); expect(result.widgets["1"].mobileLeftColumn).toEqual(8); expect(result.widgets["1"].mobileRightColumn).toEqual(24); @@ -645,6 +674,7 @@ describe("test PositionUtils methods", () => { 0, 64, true, + mainCanvasWidth, ); expect(result.height).toEqual(18); expect(result.widgets["1"].mobileLeftColumn).toEqual(48); @@ -762,7 +792,12 @@ describe("test PositionUtils methods", () => { ], }, }; - const result = updateWidgetPositions(widgets, "3", false); + const result = updateWidgetPositions( + widgets, + "3", + false, + mainCanvasWidth, + ); expect(result["1"].leftColumn).toEqual(24); expect(result["1"].rightColumn).toEqual(40); expect(result["2"].leftColumn).toEqual(40); @@ -895,7 +930,7 @@ describe("test PositionUtils methods", () => { useAutoLayout: true, }, }; - const result = updateWidgetPositions(widgets, "3", true); + const result = updateWidgetPositions(widgets, "3", true, mainCanvasWidth); expect(result["3"].mobileBottomRow).toEqual(120); expect(result["4"].mobileBottomRow).toEqual(13); }); From 89d5d070ee5409b866fd14a42cbb9862fcef788d Mon Sep 17 00:00:00 2001 From: Aswath K Date: Thu, 9 Feb 2023 13:40:41 +0530 Subject: [PATCH 480/708] removes unwanted config --- app/client/src/widgets/CurrencyInputWidget/index.ts | 10 ---------- app/client/src/widgets/InputWidgetV2/index.ts | 10 ---------- app/client/src/widgets/PhoneInputWidget/index.ts | 10 ---------- 3 files changed, 30 deletions(-) diff --git a/app/client/src/widgets/CurrencyInputWidget/index.ts b/app/client/src/widgets/CurrencyInputWidget/index.ts index 852a2488fbe0..e82de9e82719 100644 --- a/app/client/src/widgets/CurrencyInputWidget/index.ts +++ b/app/client/src/widgets/CurrencyInputWidget/index.ts @@ -1,6 +1,5 @@ import { LabelPosition } from "components/constants"; import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; -import { layoutConfigurations } from "constants/WidgetConstants"; import { getDefaultResponsiveBehavior } from "utils/layoutPropertiesUtils"; import { DynamicHeight } from "utils/WidgetFeatures"; import { CONFIG as BaseConfig } from "widgets/BaseInputWidget"; @@ -54,15 +53,6 @@ export const CONFIG = { }; }, }, - { - viewportMinWidth: layoutConfigurations.MOBILE.maxWidth, - configuration: () => { - return { - minWidth: "60px", - minHeight: "80px", - }; - }, - }, ], }, }; diff --git a/app/client/src/widgets/InputWidgetV2/index.ts b/app/client/src/widgets/InputWidgetV2/index.ts index b2305d0be570..206a77b52ebd 100644 --- a/app/client/src/widgets/InputWidgetV2/index.ts +++ b/app/client/src/widgets/InputWidgetV2/index.ts @@ -1,6 +1,5 @@ import { LabelPosition } from "components/constants"; import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; -import { layoutConfigurations } from "constants/WidgetConstants"; import { getDefaultResponsiveBehavior } from "utils/layoutPropertiesUtils"; import { DynamicHeight } from "utils/WidgetFeatures"; import { CONFIG as BaseConfig } from "widgets/BaseInputWidget"; @@ -51,15 +50,6 @@ export const CONFIG = { }; }, }, - { - viewportMinWidth: layoutConfigurations.MOBILE.maxWidth, - configuration: () => { - return { - minWidth: "60px", - minHeight: "80px", - }; - }, - }, ], }, }; diff --git a/app/client/src/widgets/PhoneInputWidget/index.ts b/app/client/src/widgets/PhoneInputWidget/index.ts index 577065610bc4..09bf4ea38183 100644 --- a/app/client/src/widgets/PhoneInputWidget/index.ts +++ b/app/client/src/widgets/PhoneInputWidget/index.ts @@ -1,6 +1,5 @@ import { LabelPosition } from "components/constants"; import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; -import { layoutConfigurations } from "constants/WidgetConstants"; import { getDefaultResponsiveBehavior } from "utils/layoutPropertiesUtils"; import { DynamicHeight } from "utils/WidgetFeatures"; import { CONFIG as BaseConfig } from "widgets/BaseInputWidget"; @@ -53,15 +52,6 @@ export const CONFIG = { }; }, }, - { - viewportMinWidth: layoutConfigurations.MOBILE.maxWidth, - configuration: () => { - return { - minWidth: "60px", - minHeight: "80px", - }; - }, - }, ], }, }; From a2d35cccf025cc5fb3a5a63c11aa558bafcaa611 Mon Sep 17 00:00:00 2001 From: Arsalan Date: Thu, 9 Feb 2023 14:29:06 +0530 Subject: [PATCH 481/708] fix: fix content as children issue. --- app/client/src/widgets/BaseWidget.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index fcb0f70ba70b..04de4f6fe818 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -363,7 +363,7 @@ abstract class BaseWidget< {content} ) : ( - { content } + content ); } From 1bbf096a4a0a5e64f27a021f27343c23e9d6a0c9 Mon Sep 17 00:00:00 2001 From: Aswath K Date: Thu, 9 Feb 2023 17:13:02 +0530 Subject: [PATCH 482/708] feat: Responsive Calendar Widget (#20537) Sets a minWidth to Calendar Widget --- app/client/src/widgets/DatePickerWidget2/index.ts | 12 ++++++++++++ .../src/widgets/DatePickerWidget2/widget/index.tsx | 3 +++ 2 files changed, 15 insertions(+) diff --git a/app/client/src/widgets/DatePickerWidget2/index.ts b/app/client/src/widgets/DatePickerWidget2/index.ts index b373d2cef336..ed1043745c1c 100644 --- a/app/client/src/widgets/DatePickerWidget2/index.ts +++ b/app/client/src/widgets/DatePickerWidget2/index.ts @@ -46,6 +46,18 @@ export const CONFIG = { responsiveBehavior: getDefaultResponsiveBehavior(Widget.getWidgetType()), minWidth: FILL_WIDGET_MIN_WIDTH, }, + autoLayout: { + widgetSize: [ + { + viewportMinWidth: 0, + configuration: () => { + return { + minWidth: "60px", + }; + }, + }, + ], + }, properties: { derived: Widget.getDerivedPropertiesMap(), default: Widget.getDefaultPropertiesMap(), diff --git a/app/client/src/widgets/DatePickerWidget2/widget/index.tsx b/app/client/src/widgets/DatePickerWidget2/widget/index.tsx index 100ceefabc15..5b23d41c950f 100644 --- a/app/client/src/widgets/DatePickerWidget2/widget/index.tsx +++ b/app/client/src/widgets/DatePickerWidget2/widget/index.tsx @@ -17,6 +17,7 @@ import { isAutoHeightEnabledForWidget } from "widgets/WidgetUtils"; import { DatePickerType, TimePrecision } from "../constants"; import { DateFormatOptions } from "./constants"; import derivedProperties from "./parseDerivedProperties"; +import { isAutoLayout } from "selectors/mainCanvasSelectors"; function allowedRange(value: any) { const allowedValues = [0, 1, 2, 3, 4, 5, 6]; @@ -137,6 +138,7 @@ class DatePickerWidget extends BaseWidget { label: "Position", controlType: "ICON_TABS", fullWidth: true, + hidden: isAutoLayout, options: [ { label: "Auto", value: LabelPosition.Auto }, { label: "Left", value: LabelPosition.Left }, @@ -353,6 +355,7 @@ class DatePickerWidget extends BaseWidget { helpText: "Control the font size of the label associated", controlType: "DROP_DOWN", defaultValue: "0.875rem", + hidden: isAutoLayout, options: [ { label: "S", From 22a240d2a20ecdd3091e1bd17227933b6a37e4f4 Mon Sep 17 00:00:00 2001 From: Aswath K Date: Thu, 9 Feb 2023 17:17:09 +0530 Subject: [PATCH 483/708] removes minHeight since it is a 1d widget and its height cannot be resized --- app/client/src/widgets/CurrencyInputWidget/index.ts | 1 - app/client/src/widgets/InputWidgetV2/index.ts | 1 - app/client/src/widgets/PhoneInputWidget/index.ts | 1 - 3 files changed, 3 deletions(-) diff --git a/app/client/src/widgets/CurrencyInputWidget/index.ts b/app/client/src/widgets/CurrencyInputWidget/index.ts index e82de9e82719..47356b5827c8 100644 --- a/app/client/src/widgets/CurrencyInputWidget/index.ts +++ b/app/client/src/widgets/CurrencyInputWidget/index.ts @@ -49,7 +49,6 @@ export const CONFIG = { configuration: () => { return { minWidth: "60px", - minHeight: "70px", }; }, }, diff --git a/app/client/src/widgets/InputWidgetV2/index.ts b/app/client/src/widgets/InputWidgetV2/index.ts index 206a77b52ebd..001e64fafe7b 100644 --- a/app/client/src/widgets/InputWidgetV2/index.ts +++ b/app/client/src/widgets/InputWidgetV2/index.ts @@ -46,7 +46,6 @@ export const CONFIG = { configuration: () => { return { minWidth: "60px", - minHeight: "70px", }; }, }, diff --git a/app/client/src/widgets/PhoneInputWidget/index.ts b/app/client/src/widgets/PhoneInputWidget/index.ts index 09bf4ea38183..c9bedfd78450 100644 --- a/app/client/src/widgets/PhoneInputWidget/index.ts +++ b/app/client/src/widgets/PhoneInputWidget/index.ts @@ -48,7 +48,6 @@ export const CONFIG = { configuration: () => { return { minWidth: "60px", - minHeight: "70px", }; }, }, From 35fb011466e39b295c69823b14f3235028d22da9 Mon Sep 17 00:00:00 2001 From: Aswath K Date: Thu, 9 Feb 2023 17:30:51 +0530 Subject: [PATCH 484/708] Increase minWidth for Currency & Phone input --- app/client/src/widgets/CurrencyInputWidget/index.ts | 2 +- app/client/src/widgets/PhoneInputWidget/index.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/client/src/widgets/CurrencyInputWidget/index.ts b/app/client/src/widgets/CurrencyInputWidget/index.ts index 47356b5827c8..9ecf5a92bb36 100644 --- a/app/client/src/widgets/CurrencyInputWidget/index.ts +++ b/app/client/src/widgets/CurrencyInputWidget/index.ts @@ -48,7 +48,7 @@ export const CONFIG = { viewportMinWidth: 0, configuration: () => { return { - minWidth: "60px", + minWidth: "180px", }; }, }, diff --git a/app/client/src/widgets/PhoneInputWidget/index.ts b/app/client/src/widgets/PhoneInputWidget/index.ts index c9bedfd78450..55cbffbf6704 100644 --- a/app/client/src/widgets/PhoneInputWidget/index.ts +++ b/app/client/src/widgets/PhoneInputWidget/index.ts @@ -47,7 +47,7 @@ export const CONFIG = { viewportMinWidth: 0, configuration: () => { return { - minWidth: "60px", + minWidth: "180px", }; }, }, From 1eb68f94b75f1b5fcfaf350d97697cbe68a7657e Mon Sep 17 00:00:00 2001 From: Arsalan Yaldram Date: Thu, 9 Feb 2023 18:22:47 +0530 Subject: [PATCH 485/708] feat: audio widget responsive styles (#19864) --- .../src/widgets/AudioWidget/component/index.tsx | 6 +++--- app/client/src/widgets/AudioWidget/index.tsx | 13 +++++++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/app/client/src/widgets/AudioWidget/component/index.tsx b/app/client/src/widgets/AudioWidget/component/index.tsx index 7d3ddc8d5ba2..fa85a4bb0e19 100644 --- a/app/client/src/widgets/AudioWidget/component/index.tsx +++ b/app/client/src/widgets/AudioWidget/component/index.tsx @@ -1,7 +1,9 @@ import ReactPlayer from "react-player"; import React, { Ref } from "react"; import styled from "styled-components"; + import { createMessage, ENTER_AUDIO_URL } from "@appsmith/constants/messages"; + export interface AudioComponentProps { url?: string; playing?: boolean; @@ -25,8 +27,6 @@ const ErrorContainer = styled.div` height: 100%; `; -const Error = styled.span``; - export default function AudioComponent(props: AudioComponentProps) { const { controls, @@ -70,7 +70,7 @@ export default function AudioComponent(props: AudioComponentProps) { /> ) : ( - {createMessage(ENTER_AUDIO_URL)} + {createMessage(ENTER_AUDIO_URL)} ); } diff --git a/app/client/src/widgets/AudioWidget/index.tsx b/app/client/src/widgets/AudioWidget/index.tsx index 2bfffca54df5..7544b26cfa39 100644 --- a/app/client/src/widgets/AudioWidget/index.tsx +++ b/app/client/src/widgets/AudioWidget/index.tsx @@ -1,5 +1,6 @@ import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; import { getDefaultResponsiveBehavior } from "utils/layoutPropertiesUtils"; + import IconSVG from "./icon.svg"; import Widget from "./widget"; @@ -27,6 +28,18 @@ export const CONFIG = { config: Widget.getPropertyPaneConfig(), contentConfig: Widget.getPropertyPaneContentConfig(), }, + autoLayout: { + widgetSize: [ + { + viewportMinWidth: 0, + configuration: () => { + return { + minWidth: "70px", + }; + }, + }, + ], + }, }; export default Widget; From b306ca1bf8d4f35e3b167f872fae20c677ae0037 Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 9 Feb 2023 08:05:44 -0500 Subject: [PATCH 486/708] fix size tests --- .../Widgets/Multiselect/MultiSelect3_spec.js | 2 +- .../Widgets/Widgets_Labels_spec.js | 24 +++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Multiselect/MultiSelect3_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Multiselect/MultiSelect3_spec.js index e591d4869105..7165b8685303 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Multiselect/MultiSelect3_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Multiselect/MultiSelect3_spec.js @@ -28,7 +28,7 @@ describe("Dropdown Widget Functionality", function() { .find(widgetLocators.menuButton) .invoke("outerWidth") .then((width) => { - expect(parseInt(width)).to.equal(147); + expect(parseInt(width)).to.equal(146); }); cy.get(formWidgetsPage.menuButtonWidget) .find(widgetLocators.menuButton) diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Widgets_Labels_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Widgets_Labels_spec.js index 1104871406f1..70642df2991f 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Widgets_Labels_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Widgets_Labels_spec.js @@ -8,7 +8,7 @@ describe("Label feature", () => { it("CheckboxGroupWidget label properties: Text, Position, Alignment, Width", () => { const options = { widgetName: "checkboxgroupwidget", - parentColumnSpace: 11.9375, + parentColumnSpace: 11.90625, containerSelector: "[data-testid='checkboxgroup-container']", isCompact: true, labelText: "Name", @@ -21,7 +21,7 @@ describe("Label feature", () => { it("CurrencyInputWidget label properties: Text, Position, Alignment, Width", () => { const options = { widgetName: "currencyinputwidget", - parentColumnSpace: 11.9375, + parentColumnSpace: 11.90625, containerSelector: "[data-testid='input-container']", isCompact: true, labelText: "Name", @@ -34,7 +34,7 @@ describe("Label feature", () => { it("DatePickerWidget2 label properties: Text, Position, Alignment, Width", () => { const options = { widgetName: "datepickerwidget2", - parentColumnSpace: 11.9375, + parentColumnSpace: 11.90625, containerSelector: "[data-testid='datepicker-container']", isCompact: true, labelText: "Name", @@ -47,7 +47,7 @@ describe("Label feature", () => { it("InputWidgetV2 label properties: Text, Position, Alignment, Width", () => { const options = { widgetName: "inputwidgetv2", - parentColumnSpace: 11.9375, + parentColumnSpace: 11.90625, containerSelector: "[data-testid='input-container']", isCompact: true, labelText: "Name", @@ -60,7 +60,7 @@ describe("Label feature", () => { it("MultiSelectTreeWidget label properties: Text, Position, Alignment, Width", () => { const options = { widgetName: "multiselecttreewidget", - parentColumnSpace: 11.9375, + parentColumnSpace: 11.90625, containerSelector: "[data-testid='multitreeselect-container']", isCompact: true, labelText: "Name", @@ -73,7 +73,7 @@ describe("Label feature", () => { it("MultiSelectWidgetV2 label properties: Text, Position, Alignment, Width", () => { const options = { widgetName: "multiselectwidgetv2", - parentColumnSpace: 11.9375, + parentColumnSpace: 11.90625, containerSelector: "[data-testid='multiselect-container']", isCompact: true, labelText: "Name", @@ -86,7 +86,7 @@ describe("Label feature", () => { it("PhoneInputWidget label properties: Text, Position, Alignment, Width", () => { const options = { widgetName: "phoneinputwidget", - parentColumnSpace: 11.9375, + parentColumnSpace: 11.90625, containerSelector: "[data-testid='input-container']", isCompact: true, labelText: "Name", @@ -99,7 +99,7 @@ describe("Label feature", () => { it("RadioGroupWidget label properties: Text, Position, Alignment, Width", () => { const options = { widgetName: "radiogroupwidget", - parentColumnSpace: 11.9375, + parentColumnSpace: 11.90625, containerSelector: "[data-testid='radiogroup-container']", isCompact: true, labelText: "Name", @@ -112,7 +112,7 @@ describe("Label feature", () => { it("RichTextEditorWidget label properties: Text, Position, Alignment, Width", () => { const options = { widgetName: "richtexteditorwidget", - parentColumnSpace: 11.9375, + parentColumnSpace: 11.90625, containerSelector: "[data-testid='rte-container']", isCompact: false, labelText: "Name", @@ -125,7 +125,7 @@ describe("Label feature", () => { it("SelectWidget label properties: Text, Position, Alignment, Width", () => { const options = { widgetName: "selectwidget", - parentColumnSpace: 11.9375, + parentColumnSpace: 11.90625, containerSelector: "[data-testid='select-container']", isCompact: true, labelText: "Name", @@ -138,7 +138,7 @@ describe("Label feature", () => { it("SingleSelectTreeWidget label properties: Text, Position, Alignment, Width", () => { const options = { widgetName: "singleselecttreewidget", - parentColumnSpace: 11.9375, + parentColumnSpace: 11.90625, containerSelector: "[data-testid='treeselect-container']", isCompact: true, labelText: "Name", @@ -151,7 +151,7 @@ describe("Label feature", () => { it("SwitchGroupWidget label properties: Text, Position, Alignment, Width", () => { const options = { widgetName: "switchgroupwidget", - parentColumnSpace: 11.9375, + parentColumnSpace: 11.90625, containerSelector: "[data-testid='switchgroup-container']", isCompact: true, labelText: "Name", From df251f7d6104255a9355caf2971dc4d69d02b8a2 Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 9 Feb 2023 08:24:44 -0500 Subject: [PATCH 487/708] fix dynamic layout spec --- .../ClientSideTests/OtherUIFeatures/DynamicLayout_spec.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/OtherUIFeatures/DynamicLayout_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/OtherUIFeatures/DynamicLayout_spec.js index 33332700cecf..59f1cb6ecf6f 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/OtherUIFeatures/DynamicLayout_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/OtherUIFeatures/DynamicLayout_spec.js @@ -1,15 +1,10 @@ -const widgetsPage = require("../../../../locators/Widgets.json"); const commonlocators = require("../../../../locators/commonlocators.json"); -const dsl = require("../../../../fixtures/newFormDsl.json"); -import homePage from "../../../../locators/HomePage"; const pages = require("../../../../locators/Pages.json"); -const publishPage = require("../../../../locators/publishWidgetspage.json"); -const modalWidgetPage = require("../../../../locators/ModalWidget.json"); describe("Dynamic Layout Functionality", function() { it("Dynamic Layout - Change Layout", function() { cy.get(commonlocators.layoutControls) - .eq(4) + .eq(6) .click(); cy.get(commonlocators.canvas) .invoke("width") From 8d54d4b98c5081571b3255f9b18fd565e49415a8 Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 9 Feb 2023 08:58:11 -0500 Subject: [PATCH 488/708] update dynamic layout spec --- .../ClientSideTests/OtherUIFeatures/DynamicLayout_spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/OtherUIFeatures/DynamicLayout_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/OtherUIFeatures/DynamicLayout_spec.js index 59f1cb6ecf6f..880006c100df 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/OtherUIFeatures/DynamicLayout_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/OtherUIFeatures/DynamicLayout_spec.js @@ -4,7 +4,7 @@ const pages = require("../../../../locators/Pages.json"); describe("Dynamic Layout Functionality", function() { it("Dynamic Layout - Change Layout", function() { cy.get(commonlocators.layoutControls) - .eq(6) + .last() .click(); cy.get(commonlocators.canvas) .invoke("width") From d647a81c8356b6a672b077261328173122055bd8 Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 9 Feb 2023 11:55:10 -0500 Subject: [PATCH 489/708] more fixes --- .../Widgets/Dropdown/DropDownWidget_value_reset_spec.js | 5 +++-- .../Widgets/JSONForm/JSONForm_Footer_spec.js | 6 +++--- .../Widgets/TableV2/TableV2_Widget_Copy_Paste_spec.js | 1 + 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Dropdown/DropDownWidget_value_reset_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Dropdown/DropDownWidget_value_reset_spec.js index 36d1646ba23b..7a902269f295 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Dropdown/DropDownWidget_value_reset_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Dropdown/DropDownWidget_value_reset_spec.js @@ -9,8 +9,9 @@ describe("Dropdown Widget Check value does not reset on navigation", function() it("check if the dropdown value does not change on navigation", function() { //Change the value of drop down; cy.wait(4000); //settling time for dsl into layout + cy.get(commonlocators.selectButton) - .first() + .last() .click(); cy.selectWidgetOnClickOption("Red"); cy.wait(200); @@ -25,7 +26,7 @@ describe("Dropdown Widget Check value does not reset on navigation", function() cy.get( `.t--draggable-selectwidget .bp3-popover-target ${commonlocators.menuSelection}`, ) - .first() + .last() .should("have.text", "Red"); }); }); diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/JSONForm/JSONForm_Footer_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/JSONForm/JSONForm_Footer_spec.js index 11cc4a2e221e..c6bfa82588ab 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/JSONForm/JSONForm_Footer_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/JSONForm/JSONForm_Footer_spec.js @@ -46,7 +46,7 @@ describe("JSONForm Footer spec", () => { cy.get(".t--jsonform-footer").then(($footer) => { const gap = $footer.prop("offsetTop") - $body.prop("scrollHeight"); - expect(gap).equals(0); + expect(gap).equals(1); }); }); }); @@ -72,7 +72,7 @@ describe("JSONForm Footer spec", () => { $footer.prop("offsetHeight") - $form.prop("offsetHeight"); - expect(gap).equals(0); + expect(gap).equals(1); }); }); }); @@ -91,7 +91,7 @@ describe("JSONForm Footer spec", () => { $footer.prop("offsetHeight") - $form.prop("scrollHeight"); - expect(gap).equals(0); + expect(gap).equals(1); }); }); }); diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/TableV2/TableV2_Widget_Copy_Paste_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/TableV2/TableV2_Widget_Copy_Paste_spec.js index 7f678ead7f77..c3dc35dbc578 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/TableV2/TableV2_Widget_Copy_Paste_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/TableV2/TableV2_Widget_Copy_Paste_spec.js @@ -43,6 +43,7 @@ describe("Test Suite to validate copy/paste table Widget V2", function() { .trigger("mouseover"); cy.hoverAndClickParticularIndex(1); cy.selectAction("Show Bindings"); + cy.wait(200); cy.get(apiwidget.propertyList).then(function($lis) { expect($lis).to.have.length(20); expect($lis.eq(0)).to.contain("{{Table1Copy.selectedRow}}"); From 8f4b16f21c425441f76d8739cf8cf70fea1194e4 Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 9 Feb 2023 15:24:34 -0500 Subject: [PATCH 490/708] more fixes 2 --- .../Widgets/Button/Button_tooltip_spec.js | 2 +- ...ect_TreeSelect_MultiSelect_OnFocus_OnBlur_spec.js | 12 ++++++------ app/client/cypress/locators/commonlocators.json | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Button/Button_tooltip_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Button/Button_tooltip_spec.js index 1937e5ecea59..3564381b167d 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Button/Button_tooltip_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Button/Button_tooltip_spec.js @@ -18,7 +18,7 @@ describe("Button Widget Functionality - Validate tooltip visibility", function() // Hover in cy.get(widgetsPage.buttonWidget).trigger("mouseover"); // Check if a tooltip is displayed - cy.get(".bp3-popover2-content").should( + cy.get(".btnTooltipContainer .bp3-popover2-content").should( "have.text", "Lorem Ipsum is simply dummy text of the printing and typesetting industry", ); diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Select/Select_TreeSelect_MultiSelect_OnFocus_OnBlur_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Select/Select_TreeSelect_MultiSelect_OnFocus_OnBlur_spec.js index bc88c24efc27..f35c89dcdcbc 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Select/Select_TreeSelect_MultiSelect_OnFocus_OnBlur_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Select/Select_TreeSelect_MultiSelect_OnFocus_OnBlur_spec.js @@ -60,13 +60,13 @@ describe("Select, MultiSelect, Tree Select and Multi Tree Select Widget Property "ondropdownclose", "{{showAlert('TreeSelect1 dropdown closed', 'success')}}", ); - + cy.wait(500); cy.get(formWidgetsPage.treeSelect) - .last() + .first() .click({ force: true }); cy.validateToastMessage("TreeSelect1 dropdown opened"); cy.get(formWidgetsPage.treeSelect) - .last() + .first() .click({ force: true }); cy.validateToastMessage("TreeSelect1 dropdown closed"); }); @@ -84,13 +84,13 @@ describe("Select, MultiSelect, Tree Select and Multi Tree Select Widget Property "ondropdownclose", "{{showAlert('MultiTreeSelect1 dropdown closed', 'success')}}", ); - + cy.wait(500); cy.get(formWidgetsPage.multiTreeSelect) - .first() + .last() .click({ force: true }); cy.validateToastMessage("MultiTreeSelect1 dropdown opened"); cy.get(formWidgetsPage.multiTreeSelect) - .first() + .last() .click({ force: true }); cy.validateToastMessage("MultiTreeSelect1 dropdown closed"); }); diff --git a/app/client/cypress/locators/commonlocators.json b/app/client/cypress/locators/commonlocators.json index 6c6c2cfea72b..335c43666c14 100644 --- a/app/client/cypress/locators/commonlocators.json +++ b/app/client/cypress/locators/commonlocators.json @@ -66,7 +66,7 @@ "widgetNameTag": "span.t--widget-name", "serverSidePaginationCheckbox": ".t--property-control-serversidepagination input", "rightArrowBtn": "span[icon='chevron-right']", - "toastMsg": ".Toastify__toast.Toastify__toast--default span", + "toastMsg": ".Toastify__toast.Toastify__toast--default span.cs-text", "callApi": ".t--property-control-onpagechange .t--open-dropdown-Select-Action", "singleSelectMenuItem": ".bp3-menu-item.single-select div", "singleSelectWidgetMenuItem": ".menu-item-link", From a2f040a0749d2bbbab80267500dacb9b835b2ea8 Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 9 Feb 2023 17:51:29 -0500 Subject: [PATCH 491/708] fixes 3 --- .../ClientSideTests/Widgets/Migration_Spec.js | 24 +++++++++---------- .../ClientSideTests/Widgets/Modal_spec.js | 7 +++--- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Migration_Spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Migration_Spec.js index 7e23b3e0d450..ce009d518da2 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Migration_Spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Migration_Spec.js @@ -155,7 +155,7 @@ describe("Migration Validate", function() { cy.waitUntil( () => cy - .xpath("//div[contains(@class, ' t--widget-textwidget')][2]") + .xpath("//div[contains(@class, ' t--widget-textwidget')][1]") .eq(0) .contains("State:", { timeout: 30000 }) .should("exist"), @@ -169,7 +169,7 @@ describe("Migration Validate", function() { cy.get(selector + " button span") .invoke("text") .then((statetxt) => { - cy.xpath("//div[contains(@class, ' t--widget-textwidget')][2]") + cy.xpath("//div[contains(@class, ' t--widget-textwidget')][1]") .eq(0) .invoke("text") .then((txtWidtxt) => { @@ -227,7 +227,7 @@ describe("Migration Validate", function() { cy.waitUntil( () => cy - .xpath("//div[contains(@class, ' t--widget-textwidget')][1]", { + .xpath("//div[contains(@class, ' t--widget-textwidget')][2]", { timeout: 50000, }) .eq(0) @@ -240,7 +240,7 @@ describe("Migration Validate", function() { }, ).then(() => cy.wait(500)); //allow time for n/w to finish - cy.xpath("//div[contains(@class, ' t--widget-textwidget')][1]", { + cy.xpath("//div[contains(@class, ' t--widget-textwidget')][2]", { timeout: 30000, }) .eq(0) @@ -265,7 +265,7 @@ describe("Migration Validate", function() { cy.waitUntil( () => cy - .xpath("//div[contains(@class, ' t--widget-textwidget')][1]", { + .xpath("//div[contains(@class, ' t--widget-textwidget')][2]", { timeout: 50000, }) .eq(0) @@ -278,7 +278,7 @@ describe("Migration Validate", function() { }, ).then(() => cy.wait(500)); //allow time for n/w to finish - cy.xpath("//div[contains(@class, ' t--widget-textwidget')][1]", { + cy.xpath("//div[contains(@class, ' t--widget-textwidget')][2]", { timeout: 30000, }) .eq(0) @@ -331,7 +331,7 @@ describe("Migration Validate", function() { cy.waitUntil( () => cy - .xpath("//div[contains(@class, ' t--widget-textwidget')][2]", { + .xpath("//div[contains(@class, ' t--widget-textwidget')][1]", { timeout: 50000, }) .eq(0) @@ -347,7 +347,7 @@ describe("Migration Validate", function() { cy.get(selector + " button span") .invoke("text") .then((statetxt) => { - cy.xpath("//div[contains(@class, ' t--widget-textwidget')][2]") + cy.xpath("//div[contains(@class, ' t--widget-textwidget')][1]") .eq(0) .invoke("text") .then((txtWidtxt) => { @@ -406,7 +406,7 @@ describe("Migration Validate", function() { cy.waitUntil( () => cy - .xpath("//div[contains(@class, ' t--widget-textwidget')][1]", { + .xpath("//div[contains(@class, ' t--widget-textwidget')][2]", { timeout: 50000, }) .eq(0) @@ -419,7 +419,7 @@ describe("Migration Validate", function() { }, ).then(() => cy.wait(500)); //allow time for n/w to finish - cy.xpath("//div[contains(@class, ' t--widget-textwidget')][1]", { + cy.xpath("//div[contains(@class, ' t--widget-textwidget')][2]", { timeout: 30000, }) .eq(0) @@ -444,7 +444,7 @@ describe("Migration Validate", function() { cy.waitUntil( () => cy - .xpath("//div[contains(@class, ' t--widget-textwidget')][1]", { + .xpath("//div[contains(@class, ' t--widget-textwidget')][2]", { timeout: 50000, }) .eq(0) @@ -457,7 +457,7 @@ describe("Migration Validate", function() { }, ).then(() => cy.wait(500)); //allow time for n/w to finish - cy.xpath("//div[contains(@class, ' t--widget-textwidget')][1]", { + cy.xpath("//div[contains(@class, ' t--widget-textwidget')][2]", { timeout: 30000, }) .eq(0) diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Modal_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Modal_spec.js index 49330c3267d8..7f55f9ca68af 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Modal_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Modal_spec.js @@ -79,10 +79,9 @@ describe("Modal Widget Functionality", function() { .should("have.length", 1); //click on modal's widgetName Label - cy.get( - "div[data-testid='t--settings-controls-positioned-wrapper']:contains(Modal1)", - ).click({ - ctrlKey: true, + cy.get(".t--modal-widget").click(); + cy.get(widgets.widgetNameSpan).click({ + force: true, }); //verify the modal1 is selected From 89774d7e26dcf358e20186274b49142a6d83871b Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 9 Feb 2023 23:07:49 -0500 Subject: [PATCH 492/708] fixes 4 --- .../ClientSideTests/Widgets/Others/Statbox_spec.js | 11 +++++++---- .../Widgets/TableV1/Table_Widget_Copy_Paste_spec.js | 2 +- .../Widgets/TableV2/TableV2_Widget_Copy_Paste_spec.js | 2 +- .../ClientSideTests/Widgets/WidgetGrouping_spec.js | 9 ++++----- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Others/Statbox_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Others/Statbox_spec.js index 39317f7b1456..b40c3284b21c 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Others/Statbox_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Others/Statbox_spec.js @@ -58,11 +58,14 @@ describe("Statbox Widget Functionality", function() { cy.createModal("Modal", "Modal1"); }); // verifying the changed icon - cy.get(".bp3-icon-arrow-up").should("be.visible"); + cy.get(".bp3-icon-arrow-up") + .should("be.visible") + .click({ force: true }); + // verifying modal has been added - cy.get(".t--draggable-iconbuttonwidget") - .last() - .click(); + cy.get(".t--modal-widget .t--draggable-iconbuttonwidget").click({ + force: true, + }); cy.get("span:contains('Close')") .closest("div") .click(); diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/TableV1/Table_Widget_Copy_Paste_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/TableV1/Table_Widget_Copy_Paste_spec.js index 57ebab152d6d..c4a606d9dc2f 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/TableV1/Table_Widget_Copy_Paste_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/TableV1/Table_Widget_Copy_Paste_spec.js @@ -42,7 +42,7 @@ describe("Test Suite to validate copy/paste table Widget", function() { cy.get(".t--entity-name") .contains("Table1Copy") .trigger("mouseover"); - cy.hoverAndClickParticularIndex(1); + cy.hoverAndClickParticularIndex(2); cy.selectAction("Show Bindings"); cy.get(apiwidget.propertyList).then(function($lis) { expect($lis).to.have.length(13); diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/TableV2/TableV2_Widget_Copy_Paste_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/TableV2/TableV2_Widget_Copy_Paste_spec.js index c3dc35dbc578..8ec10fdd44dc 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/TableV2/TableV2_Widget_Copy_Paste_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/TableV2/TableV2_Widget_Copy_Paste_spec.js @@ -41,7 +41,7 @@ describe("Test Suite to validate copy/paste table Widget V2", function() { cy.get(".t--entity-name") .contains("Table1Copy") .trigger("mouseover"); - cy.hoverAndClickParticularIndex(1); + cy.hoverAndClickParticularIndex(2); cy.selectAction("Show Bindings"); cy.wait(200); cy.get(apiwidget.propertyList).then(function($lis) { diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/WidgetGrouping_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/WidgetGrouping_spec.js index 8b7460069489..30b63e48c609 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/WidgetGrouping_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/WidgetGrouping_spec.js @@ -38,11 +38,10 @@ describe("Widget Grouping", function() { .eq(1) .then((element) => { const elementTop = parseFloat(element.css("top")); - const elementHeight = parseFloat(element.css("height")); - const containerBottom = (elementTop + elementHeight).toString() + "px"; - cy.get(`.t--widget-camerawidget`) - .invoke("attr", "style") - .should("contain", `top: ${containerBottom}`); + cy.get(`.t--widget-camerawidget`).then((element2) => { + const containerTop = parseFloat(element2.css("top")); + expect(containerTop).to.be.greaterThan(elementTop); + }); }); }); }); From c224537b683a0f8cd887cd622c28ba22e78ca136 Mon Sep 17 00:00:00 2001 From: Preet Date: Fri, 10 Feb 2023 11:53:27 -0500 Subject: [PATCH 493/708] fix jsfunction execution spec --- .../JSFunctionExecution_spec.ts | 34 ++++++++----------- .../cypress/support/Pages/AggregateHelper.ts | 7 ++++ 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/app/client/cypress/integration/Regression_TestSuite/ServerSideTests/JsFunctionExecution/JSFunctionExecution_spec.ts b/app/client/cypress/integration/Regression_TestSuite/ServerSideTests/JsFunctionExecution/JSFunctionExecution_spec.ts index 237172afa3ce..2fb8de4a7b9a 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ServerSideTests/JsFunctionExecution/JSFunctionExecution_spec.ts +++ b/app/client/cypress/integration/Regression_TestSuite/ServerSideTests/JsFunctionExecution/JSFunctionExecution_spec.ts @@ -357,27 +357,23 @@ describe("JS Function Execution", function() { // Switch to settings tab agHelper.GetNClick(jsEditor._settingsTab); // Add settings for each function (according to data) - Object.values(FUNCTIONS_SETTINGS_DEFAULT_DATA).forEach( - (functionSetting) => { - jsEditor.EnableDisableAsyncFuncSettings( - functionSetting.name, - functionSetting.onPageLoad, - functionSetting.confirmBeforeExecute, - ); - }, - ); + for (const functionSetting of Object.values( + FUNCTIONS_SETTINGS_DEFAULT_DATA, + )) { + jsEditor.EnableDisableAsyncFuncSettings( + functionSetting.name, + functionSetting.onPageLoad, + functionSetting.confirmBeforeExecute, + ); + } // Switch to settings tab agHelper.GetNClick(jsEditor._settingsTab); //After JSObj is created - check methods are in alphabetical order assertAsyncFunctionsOrder(FUNCTIONS_SETTINGS_DEFAULT_DATA); - agHelper.RefreshPage(); // click "Yes" button for all onPageload && ConfirmExecute functions - for (let i = 0; i <= onPageLoadAndConfirmExecuteFunctionsLength - 1; i++) { - //agHelper.AssertElementPresence(jsEditor._dialog("Confirmation Dialog")); // Not working in edit mode - agHelper.ClickButton("Yes"); - agHelper.Sleep(); - } + agHelper.clickMultipleButtons("Yes"); + agHelper.Sleep(); // Switch to settings tab and assert order agHelper.GetNClick(jsEditor._settingsTab); assertAsyncFunctionsOrder(FUNCTIONS_SETTINGS_DEFAULT_DATA); @@ -414,12 +410,10 @@ describe("JS Function Execution", function() { // clone page and assert order of functions ee.ClonePage(); + // click "Yes" button for all onPageload && ConfirmExecute functions - for (let i = 0; i <= onPageLoadAndConfirmExecuteFunctionsLength - 1; i++) { - //agHelper.AssertElementPresence(jsEditor._dialog("Confirmation Dialog")); // Not working in edit mode - agHelper.ClickButton("Yes"); - agHelper.Sleep(); - } + agHelper.clickMultipleButtons("Yes"); + agHelper.Sleep(); ee.SelectEntityByName(jsObj, "Queries/JS"); agHelper.GetNClick(jsEditor._settingsTab); diff --git a/app/client/cypress/support/Pages/AggregateHelper.ts b/app/client/cypress/support/Pages/AggregateHelper.ts index 75247fe764dc..7109375dc95e 100644 --- a/app/client/cypress/support/Pages/AggregateHelper.ts +++ b/app/client/cypress/support/Pages/AggregateHelper.ts @@ -205,6 +205,13 @@ export class AggregateHelper { shouldSleep && this.Sleep(); } + public clickMultipleButtons(btnVisibleText: string, shouldSleep = true) { + cy.xpath(this.locator._spanButton(btnVisibleText)).each(($el) => { + $el.trigger("click", { force: true }); + }); + shouldSleep && this.Sleep(); + } + public Paste(selector: any, pastePayload: string) { cy.wrap(selector).then(($destination) => { const pasteEvent = Object.assign( From 3e46d63c9f168318296d6083d80d5ad1dc64e209 Mon Sep 17 00:00:00 2001 From: Preet Date: Fri, 10 Feb 2023 15:35:12 -0500 Subject: [PATCH 494/708] fixes 6 --- .../Widgets/Modal/Modal_focus_spec.js | 4 +- .../Widgets/Multiselect/MultiSelect3_spec.js | 44 +++++++++++++------ .../ServerSideTests/ApiTests/API_Bugs_Spec.js | 6 ++- app/client/cypress/support/widgetCommands.js | 18 ++++++++ 4 files changed, 54 insertions(+), 18 deletions(-) diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Modal/Modal_focus_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Modal/Modal_focus_spec.js index 2d624c65efc3..07ce560161c9 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Modal/Modal_focus_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Modal/Modal_focus_spec.js @@ -48,7 +48,7 @@ describe("Modal focus", function() { it("should focus on the input field when autofocus for the input field is enabled", () => { setupModalWithInputWidget(); - cy.openPropertyPane("inputwidgetv2"); + cy.openPropertyPaneFromModal("inputwidgetv2"); // autofocus for input field is enabled cy.get(".t--property-control-autofocus") @@ -70,7 +70,7 @@ describe("Modal focus", function() { cy.focused().should("have.value", someInputText); }); it("should not focus on the input field if autofocus is disabled", () => { - cy.openPropertyPane("inputwidgetv2"); + cy.openPropertyPaneFromModal("inputwidgetv2"); // autofocus for input field is disabled cy.get(".t--property-control-autofocus") diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Multiselect/MultiSelect3_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Multiselect/MultiSelect3_spec.js index 7165b8685303..a3f120d392f0 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Multiselect/MultiSelect3_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Multiselect/MultiSelect3_spec.js @@ -32,19 +32,21 @@ describe("Dropdown Widget Functionality", function() { }); cy.get(formWidgetsPage.menuButtonWidget) .find(widgetLocators.menuButton) + .last() .click({ force: true, }); + cy.log(cy.get(formWidgetsPage.menuButtonWidget)); cy.get(".menu-button-popover") .invoke("outerWidth") .then((width) => { - expect(parseInt(width)).to.equal(147); + expect(parseInt(width)).to.equal(146); }); // MultiSelect cy.get(formWidgetsPage.multiselectwidgetv2) .find(".rc-select-multiple") - .invoke("width") + .invoke("innerWidth") .then((inputWidth) => { cy.get(formWidgetsPage.multiselectwidgetv2) .find(".rc-select-selection-search-input") @@ -52,9 +54,11 @@ describe("Dropdown Widget Functionality", function() { .focus({ force: true }) .type("{uparrow}", { force: true }); cy.get(".multi-select-dropdown") - .invoke("width") + .invoke("outerWidth") .then((dropdownWidth) => { - expect(Math.floor(inputWidth)).to.equal(Math.floor(dropdownWidth)); + expect( + Math.floor(inputWidth) - Math.floor(dropdownWidth), + ).to.be.lessThan(1); }); }); //Multi tree Select @@ -69,7 +73,9 @@ describe("Dropdown Widget Functionality", function() { .type("{uparrow}", { force: true }); cy.get(".tree-multiselect-dropdown") .invoke("outerWidth") - .should("eq", val); + .then((dropdownWidth) => { + expect(val - dropdownWidth).to.be.within(-1, 1); + }); }); // Tree Select @@ -84,7 +90,9 @@ describe("Dropdown Widget Functionality", function() { .type("{uparrow}", { force: true }); cy.get(".single-tree-select-dropdown") .invoke("outerWidth") - .should("eq", val); + .then((dropdownWidth) => { + expect(val - dropdownWidth).to.be.within(-1, 1); + }); }); }); @@ -103,7 +111,9 @@ describe("Dropdown Widget Functionality", function() { }); cy.get(".select-popover-wrapper") .invoke("outerWidth") - .should("eq", val); + .then((dropdownWidth) => { + expect(val - dropdownWidth).to.be.within(-1, 1); + }); }); // MultiSelect @@ -111,7 +121,7 @@ describe("Dropdown Widget Functionality", function() { cy.testJsontext("text", "Label"); cy.get(formWidgetsPage.multiselectwidgetv2) .find(".rc-select-multiple") - .invoke("width") + .invoke("innerWidth") .then((val) => { cy.get(formWidgetsPage.multiselectwidgetv2) .find(".rc-select-selection-search-input") @@ -119,15 +129,17 @@ describe("Dropdown Widget Functionality", function() { .focus({ force: true }) .type("{uparrow}", { force: true }); cy.get(".multi-select-dropdown") - .invoke("width") - .should("eq", val); + .invoke("outerWidth") + .then((dropdownWidth) => { + expect(val - dropdownWidth).to.be.within(-1, 1); + }); }); //Multi tree Select cy.openPropertyPane("multiselecttreewidget"); cy.testJsontext("text", "Label"); cy.get(formWidgetsPage.multiselecttreeWidget) .find(".rc-tree-select-multiple") - .invoke("width") + .invoke("innerWidth") .then((val) => { cy.get(formWidgetsPage.multiselecttreeWidget) .find(".rc-tree-select-selection-search-input") @@ -136,14 +148,16 @@ describe("Dropdown Widget Functionality", function() { .type("{uparrow}", { force: true }); cy.get(".tree-multiselect-dropdown") .invoke("outerWidth") - .should("eq", val); + .then((dropdownWidth) => { + expect(val - dropdownWidth).to.be.within(-1, 1); + }); }); // Tree Select cy.openPropertyPane("singleselecttreewidget"); cy.testJsontext("text", "Label"); cy.get(formWidgetsPage.singleselecttreeWidget) .find(".rc-tree-select-single") - .invoke("outerWidth") + .invoke("innerWidth") .then((val) => { cy.get(formWidgetsPage.singleselecttreeWidget) .find(".rc-tree-select-selection-search-input") @@ -152,7 +166,9 @@ describe("Dropdown Widget Functionality", function() { .type("{uparrow}", { force: true }); cy.get(".single-tree-select-dropdown") .invoke("outerWidth") - .should("eq", val); + .then((dropdownWidth) => { + expect(val - dropdownWidth).to.be.within(-1, 1); + }); }); }); }); diff --git a/app/client/cypress/integration/Regression_TestSuite/ServerSideTests/ApiTests/API_Bugs_Spec.js b/app/client/cypress/integration/Regression_TestSuite/ServerSideTests/ApiTests/API_Bugs_Spec.js index ab1d44336fe1..d854186b3aa3 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ServerSideTests/ApiTests/API_Bugs_Spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ServerSideTests/ApiTests/API_Bugs_Spec.js @@ -87,10 +87,12 @@ describe("Rest Bugs tests", function() { // expect(response.body.data.body.length).to.be.above(0); //Number fact // }); - cy.get(".t--draggable-textwidget") + cy.get(".t--widget-buttonwidget").scrollIntoView(); + + cy.get(".t--widget-textwidget") .eq(0) .invoke("text") - .then(($txt) => expect($txt).to.have.length.greaterThan(25)); + .then(($txt) => expect($txt).to.have.length.greaterThan(20)); // cy.wait("@postExecute").then(({ response }) => { // //cy.log("Response is :"+ JSON.stringify(response.body)) diff --git a/app/client/cypress/support/widgetCommands.js b/app/client/cypress/support/widgetCommands.js index 886549ab0cb7..0f397e75b826 100644 --- a/app/client/cypress/support/widgetCommands.js +++ b/app/client/cypress/support/widgetCommands.js @@ -1272,6 +1272,24 @@ Cypress.Commands.add("openPropertyPane", (widgetType) => { cy.wait(1000); }); +Cypress.Commands.add("openPropertyPaneFromModal", (widgetType) => { + const selector = `.t--draggable-${widgetType}`; + cy.wait(500); + cy.get(selector) + .first() + .trigger("mouseover", { force: true }) + .wait(500); + cy.get(`${selector}:first-of-type`) + .first() + .click({ force: true }) + .wait(500); + cy.get(".t--widget-propertypane-toggle > .t--widget-name") + .last() + .click({ force: true }); + // eslint-disable-next-line cypress/no-unnecessary-waiting + cy.wait(1000); +}); + Cypress.Commands.add("openPropertyPaneCopy", (widgetType) => { if (widgetType === "List1Copy") { cy.SearchEntityandOpen(widgetType); From 1b49502fbb5cb340eb19ab5201dec249b32d1f35 Mon Sep 17 00:00:00 2001 From: Preet Date: Sun, 12 Feb 2023 18:46:59 -0500 Subject: [PATCH 495/708] fixes 7 --- .../DynamicHeight_List_TextWidget_Spec.js | 13 +++++++++++-- app/client/cypress/support/widgetCommands.js | 1 + 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/DynamicHeight/DynamicHeight_List_TextWidget_Spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/DynamicHeight/DynamicHeight_List_TextWidget_Spec.js index 831298946ccd..8b68a4d2990d 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/DynamicHeight/DynamicHeight_List_TextWidget_Spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/DynamicHeight/DynamicHeight_List_TextWidget_Spec.js @@ -85,9 +85,13 @@ describe("Dynamic Height Width validation list widget", function() { cy.wait(2000); cy.get(commonlocators.generalSectionHeight).should("be.visible"); cy.get(".t--widget-textwidget") - .last() + .first() + .click({ force: true }); + cy.get(".t--widget-textwidget") + .first() .invoke("css", "height") .then((height) => { + cy.log("height", height); cy.changeLayoutHeight(commonlocators.autoHeight); cy.wait("@updateLayout").should( "have.nested.property", @@ -96,9 +100,14 @@ describe("Dynamic Height Width validation list widget", function() { ); cy.wait(3000); cy.get(".t--widget-textwidget") - .last() + .first() + .click({ force: true }); + cy.get(".t--widget-textwidget") + .first() + .wait(1000) .invoke("css", "height") .then((newheight) => { + cy.log("newheight", newheight); expect(height).to.not.equal(newheight); }); }); diff --git a/app/client/cypress/support/widgetCommands.js b/app/client/cypress/support/widgetCommands.js index 0f397e75b826..c8daf74269e8 100644 --- a/app/client/cypress/support/widgetCommands.js +++ b/app/client/cypress/support/widgetCommands.js @@ -338,6 +338,7 @@ Cypress.Commands.add("getCodeMirror", () => { Cypress.Commands.add("testCodeMirror", (value) => { const modifierKey = Cypress.platform === "darwin" ? "meta" : "ctrl"; cy.EnableAllCodeEditors(); + cy.wait(2000); cy.get(".CodeMirror textarea") .first() .focus() From 95b2154955849e7b58f005b23e2887368c829fb1 Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 13 Feb 2023 09:32:06 -0500 Subject: [PATCH 496/708] fixes 8 --- .../DynamicHeight/DynamicHeight_Modal_Widget_spec.js | 1 + 1 file changed, 1 insertion(+) diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/DynamicHeight/DynamicHeight_Modal_Widget_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/DynamicHeight/DynamicHeight_Modal_Widget_spec.js index 8409b75eaec9..cab83daf3d3b 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/DynamicHeight/DynamicHeight_Modal_Widget_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/DynamicHeight/DynamicHeight_Modal_Widget_spec.js @@ -14,6 +14,7 @@ describe("Dynamic Height Width validation with limits", function() { cy.openPropertyPane("textwidget"); cy.get(commonlocators.generalSectionHeight).should("be.visible"); cy.changeLayoutHeightWithoutWait(commonlocators.autoHeight); + cy.openPropertyPaneFromModal("textwidget"); cy.get(".t--widget-textwidget") .invoke("css", "height") .then((theight) => { From 8634f851270fabba275b807a5e10ba162e5cc21f Mon Sep 17 00:00:00 2001 From: Preet Date: Mon, 13 Feb 2023 10:01:14 -0500 Subject: [PATCH 497/708] fixes 9 --- .../Widgets/Button/Button_onClickAction_spec.js | 11 ++++++++++- .../ClientSideTests/Widgets/List/List4_spec.js | 1 + 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Button/Button_onClickAction_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Button/Button_onClickAction_spec.js index 72036e7ba8de..569092a06164 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Button/Button_onClickAction_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Button/Button_onClickAction_spec.js @@ -17,6 +17,11 @@ describe("Button Widget Functionality", function() { //creating the Modal and verify Modal name cy.createModal(this.data.ModalName); cy.PublishtheApp(); + cy.get("body").then(($ele) => { + if ($ele.find(modalWidgetPage.modelTextField).length <= 0) { + cy.get(publishPage.buttonWidget).click(); + } + }); cy.get(publishPage.buttonWidget).click(); cy.get(modalWidgetPage.modelTextField).should( "have.text", @@ -44,7 +49,11 @@ describe("Button Widget Functionality", function() { cy.onClickActions("Success", "Error", "onclick"); cy.PublishtheApp(); - + cy.get("body").then(($ele) => { + if ($ele.find(widgetsPage.apiCallToast).length <= 0) { + cy.get(publishPage.buttonWidget).click(); + } + }); // Clicking the button to verify the success message cy.get(publishPage.buttonWidget).click(); cy.get(widgetsPage.apiCallToast).should("have.text", "Success"); diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/List/List4_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/List/List4_spec.js index 86416739171f..8116d0d5d547 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/List/List4_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/List/List4_spec.js @@ -113,6 +113,7 @@ describe("Container Widget Functionality", function() { //cy.SearchEntityandOpen("Button1"); cy.testJsontext("label", `{{currentItem.last_name}}`); cy.addAction("{{currentItem.last_name}}", "onclick"); + cy.wait(3000); cy.PublishtheApp(); // Verify Widget Button by clicking on it cy.get(widgetsPage.widgetBtn) From d24292771b6aa7281502177e8242f840be47f5b9 Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 14 Feb 2023 11:39:02 -0500 Subject: [PATCH 498/708] merge fix --- app/client/src/widgets/FormWidget/index.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/client/src/widgets/FormWidget/index.ts b/app/client/src/widgets/FormWidget/index.ts index 0193db693870..fa382e0aeaaa 100644 --- a/app/client/src/widgets/FormWidget/index.ts +++ b/app/client/src/widgets/FormWidget/index.ts @@ -1,8 +1,10 @@ import { ButtonVariantTypes, RecaptchaTypes } from "components/constants"; import { Colors } from "constants/Colors"; import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; +import { GridDefaults } from "constants/WidgetConstants"; import { Positioning } from "utils/autoLayout/constants"; import { getDefaultResponsiveBehavior } from "utils/layoutPropertiesUtils"; +import { WidgetProps } from "widgets/BaseWidget"; import IconSVG from "./icon.svg"; import Widget from "./widget"; From ee95dca31b40fa8a0b22de6c71cd64f30170922c Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 14 Feb 2023 11:40:15 -0500 Subject: [PATCH 499/708] merge fix for container --- app/client/src/widgets/ContainerWidget/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/client/src/widgets/ContainerWidget/index.ts b/app/client/src/widgets/ContainerWidget/index.ts index b0b04e241786..1d599feedb12 100644 --- a/app/client/src/widgets/ContainerWidget/index.ts +++ b/app/client/src/widgets/ContainerWidget/index.ts @@ -1,8 +1,9 @@ import { ButtonBoxShadowTypes } from "components/constants"; import { Colors } from "constants/Colors"; import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; -import { WidgetHeightLimits } from "constants/WidgetConstants"; +import { GridDefaults, WidgetHeightLimits } from "constants/WidgetConstants"; import { getDefaultResponsiveBehavior } from "utils/layoutPropertiesUtils"; +import { WidgetProps } from "widgets/BaseWidget"; import IconSVG from "./icon.svg"; import Widget from "./widget"; From 816ed7a3448a57c998768d8063ee1bc4888351e1 Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 14 Feb 2023 12:08:36 -0500 Subject: [PATCH 500/708] remove duplicate imports --- .../ServerSideTests/GenerateCRUD/Postgres2_Spec.ts | 4 ++-- app/client/src/widgets/BaseWidget.tsx | 11 ----------- .../src/widgets/ContainerWidget/component/index.tsx | 6 +----- app/client/src/widgets/constants.ts | 2 +- 4 files changed, 4 insertions(+), 19 deletions(-) diff --git a/app/client/cypress/integration/Regression_TestSuite/ServerSideTests/GenerateCRUD/Postgres2_Spec.ts b/app/client/cypress/integration/Regression_TestSuite/ServerSideTests/GenerateCRUD/Postgres2_Spec.ts index 2eb6f0b3d37d..b0de470f62b5 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ServerSideTests/GenerateCRUD/Postgres2_Spec.ts +++ b/app/client/cypress/integration/Regression_TestSuite/ServerSideTests/GenerateCRUD/Postgres2_Spec.ts @@ -541,7 +541,7 @@ describe("Validate Postgres Generate CRUD with JSON Form", () => { it("15. Verify Update fields/Delete from Deploy page - on Vessels - newly inserted record", () => { table.SelectTableRow(0); - agHelper.Sleep(2000);//since table taking time to display JSON form + agHelper.Sleep(2000); //since table taking time to display JSON form //validating update happened fine! dataSources.AssertJSONFormHeader(0, 0, "ship_id", "159180"); //Validaing new record got inserted in 1st position due to id used @@ -691,7 +691,7 @@ describe("Validate Postgres Generate CRUD with JSON Form", () => { ) { agHelper.ClickButton("Update"); //Update does not work, Bug 14063 agHelper.AssertElementAbsence(locator._toastMsg); //Validating fix for Bug 14063 - for common table columns - agHelper.AssertElementAbsence(locator._spinner, 10000);//10 secs for update to reflect! + agHelper.AssertElementAbsence(locator._spinner, 10000); //10 secs for update to reflect! agHelper.ValidateNetworkStatus("@postExecute", 200); agHelper.ValidateNetworkStatus("@postExecute", 200); table.AssertSelectedRow(rowIndex); //Validate Primary key column selection diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index 532a2d3077f7..12c13bb56dc0 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -15,7 +15,6 @@ import { import FlexComponent from "components/designSystems/appsmith/autoLayout/FlexComponent"; import PositionedContainer from "components/designSystems/appsmith/PositionedContainer"; import DraggableComponent from "components/editorComponents/DraggableComponent"; -import { EditorContext } from "components/editorComponents/EditorContextProvider"; import ErrorBoundary from "components/editorComponents/ErrorBoundry"; import ResizableComponent from "components/editorComponents/ResizableComponent"; import SnipeableComponent from "components/editorComponents/SnipeableComponent"; @@ -44,7 +43,6 @@ import { EditorContext, EditorContextType, } from "components/editorComponents/EditorContextProvider"; -import ErrorBoundary from "components/editorComponents/ErrorBoundry"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; import { DataTreeEvaluationProps, @@ -52,7 +50,6 @@ import { EVAL_ERROR_PATH, WidgetDynamicPathListProps, } from "utils/DynamicBindingUtils"; -import { DerivedPropertiesMap } from "utils/WidgetFactory"; import { CanvasWidgetStructure } from "./constants"; import Skeleton from "./Skeleton"; import { @@ -61,15 +58,7 @@ import { isAutoHeightEnabledForWidget, shouldUpdateWidgetHeightAutomatically, } from "./WidgetUtils"; -import { CanvasWidgetStructure } from "./constants"; -import { DataTreeWidget } from "entities/DataTree/dataTreeFactory"; -import Skeleton from "./Skeleton"; import { Stylesheet } from "entities/AppTheming"; -import { CSSProperties } from "styled-components"; -import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants"; -import AnalyticsUtil from "utils/AnalyticsUtil"; -import AutoHeightOverlayContainer from "components/autoHeightOverlay"; -import AutoHeightContainerWrapper from "components/autoHeight/AutoHeightContainerWrapper"; import { FlattenedWidgetProps } from "./constants"; import { ModifyMetaWidgetPayload, diff --git a/app/client/src/widgets/ContainerWidget/component/index.tsx b/app/client/src/widgets/ContainerWidget/component/index.tsx index ea80bcf06e2a..836258409f0e 100644 --- a/app/client/src/widgets/ContainerWidget/component/index.tsx +++ b/app/client/src/widgets/ContainerWidget/component/index.tsx @@ -6,8 +6,6 @@ import React, { useCallback, useEffect, useRef, - useEffect, - RefObject, } from "react"; import styled from "styled-components"; import tinycolor from "tinycolor2"; @@ -26,7 +24,7 @@ const StyledContainerComponent = styled.div< width: 100%; overflow: hidden; ${(props) => (!!props.dropDisabled ? `position: relative;` : ``)} - + ${(props) => (props.shouldScrollContents ? scrollCSS : ``)} opacity: ${(props) => (props.resizeDisabled ? "0.8" : "1")}; @@ -207,8 +205,6 @@ export interface ContainerComponentProps extends WidgetStyleContainerProps { backgroundColor?: string; type: WidgetType; noScroll?: boolean; - selected?: boolean; - focused?: boolean; minHeight?: number; useAutoLayout?: boolean; direction?: string; diff --git a/app/client/src/widgets/constants.ts b/app/client/src/widgets/constants.ts index 17dd4aeafc32..331dc874a93b 100644 --- a/app/client/src/widgets/constants.ts +++ b/app/client/src/widgets/constants.ts @@ -80,7 +80,7 @@ export type CanvasWidgetStructure = Pick< children?: CanvasWidgetStructure[]; selected?: boolean; onClickCapture?: (event: React.MouseEvent) => void; -}; + }; export enum FileDataTypes { Base64 = "Base64", From 2ee5f77585c54c2c649b535dc71b822acaaf18c3 Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 14 Feb 2023 17:02:24 -0500 Subject: [PATCH 501/708] fix property pane widget command --- app/client/cypress/support/widgetCommands.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/client/cypress/support/widgetCommands.js b/app/client/cypress/support/widgetCommands.js index c3b52d84b2b9..9810a8baf816 100644 --- a/app/client/cypress/support/widgetCommands.js +++ b/app/client/cypress/support/widgetCommands.js @@ -1298,8 +1298,9 @@ Cypress.Commands.add( cy.wait(500); cy.get(selector) .trigger("mouseover", { force: true }) + .click({ force: true }) .wait(500); - cy.get(`${selector} .t--widget-propertypane-toggle > .t--widget-name`) + cy.get(".t--widget-propertypane-toggle > .t--widget-name") .first() .click({ force: true }); // eslint-disable-next-line cypress/no-unnecessary-waiting From 6981f225f0a749ae05c39cb7e19b1fc9fce2c609 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Wed, 15 Feb 2023 14:23:29 +0530 Subject: [PATCH 502/708] fixing canvas resizer when used post preview mode. --- app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx index a7772afb1ca3..d536b4994a5e 100644 --- a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx +++ b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx @@ -150,10 +150,12 @@ function CanvasContainer() { useEffect(() => { if (appPositioningType === AppPositioningTypes.AUTO) { let buffer = 0; + const ele: any = document.getElementById("canvas-viewport"); if (isPreviewMode) { - const ele: any = document.getElementById("canvas-viewport"); ele.style.width = "inherit"; buffer = AUTOLAYOUT_RESIZER_WIDTH_BUFFER; + } else { + ele.style.width = "100%"; } if (appLayout?.type === "FLUID") { const smallestWidth = layoutConfigurations.MOBILE.minWidth; From 60a3cd04a8ac66b7d823684a64a6ae8768145fb7 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Wed, 15 Feb 2023 15:20:06 +0530 Subject: [PATCH 503/708] Added reflow while resizing to limit overflowing of widgets. --- app/client/src/resizable/resizenreflow/index.tsx | 13 +++++++------ app/client/src/utils/hooks/useReflow.ts | 10 +++++----- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/app/client/src/resizable/resizenreflow/index.tsx b/app/client/src/resizable/resizenreflow/index.tsx index 3ea3c48a4f8c..c0dfeb9baa74 100644 --- a/app/client/src/resizable/resizenreflow/index.tsx +++ b/app/client/src/resizable/resizenreflow/index.tsx @@ -229,7 +229,7 @@ type ResizableProps = { export function ReflowResizable(props: ResizableProps) { const resizableRef = useRef(null); const [isResizing, setResizing] = useState(false); - const isAutoHeight = + const isAutoLayout = useSelector(getCurrentAppPositioningType) === AppPositioningTypes.AUTO; const occupiedSpacesBySiblingWidgets = useSelector( getContainerOccupiedSpacesSelectorWhileResizing(props.parentId), @@ -277,6 +277,7 @@ export function ReflowResizable(props: ResizableProps) { [props.originalPositions], props.parentId || "", props.gridProps, + !isAutoLayout, ); useEffect(() => { @@ -399,7 +400,7 @@ export function ReflowResizable(props: ResizableProps) { if (bottomMostRow) { props.updateBottomRow(bottomMostRow); } - if (isAutoHeight && resizedPositions) { + if (isAutoLayout && resizedPositions) { triggerAutoLayoutBasedReflow(resizedPositions); } @@ -423,7 +424,7 @@ export function ReflowResizable(props: ResizableProps) { const handles = []; const widget = allWidgets[props.widgetId]; - if (!(isAutoHeight && widget.leftColumn === 0) && props.handles.left) { + if (!(isAutoLayout && widget.leftColumn === 0) && props.handles.left) { handles.push({ dragCallback: (x: number) => { setNewDimensions({ @@ -440,7 +441,7 @@ export function ReflowResizable(props: ResizableProps) { }); } - if (!isAutoHeight && props.handles.top) { + if (!isAutoLayout && props.handles.top) { handles.push({ dragCallback: (x: number, y: number) => { setNewDimensions({ @@ -459,7 +460,7 @@ export function ReflowResizable(props: ResizableProps) { if ( !( - isAutoHeight && + isAutoLayout && widget.leftColumn !== 0 && widget.rightColumn === GridDefaults.DEFAULT_GRID_COLUMNS ) && @@ -571,7 +572,7 @@ export function ReflowResizable(props: ResizableProps) { } const onResizeStop = () => { togglePointerEvents(true); - if (isAutoHeight) { + if (isAutoLayout) { dispatch(stopReflowAction()); } props.onStop( diff --git a/app/client/src/utils/hooks/useReflow.ts b/app/client/src/utils/hooks/useReflow.ts index 7cf4dd0ce1a3..5fdf6f141981 100644 --- a/app/client/src/utils/hooks/useReflow.ts +++ b/app/client/src/utils/hooks/useReflow.ts @@ -1,9 +1,9 @@ +import { AppState } from "@appsmith/reducers"; import { reflowMoveAction, stopReflowAction } from "actions/reflowActions"; import { OccupiedSpace, WidgetSpace } from "constants/CanvasEditorConstants"; import { isEmpty, throttle } from "lodash"; import { useEffect, useRef } from "react"; import { useDispatch, useSelector } from "react-redux"; -import { getContainerWidgetSpacesSelectorWhileMoving } from "selectors/editorSelectors"; import { reflow } from "reflow"; import { BlockSpace, @@ -23,10 +23,10 @@ import { getSpacesMapFromArray, willItCauseUndroppableState, } from "reflow/reflowUtils"; -import { getBottomRowAfterReflow } from "utils/reflowHookUtils"; -import { getIsReflowing } from "selectors/widgetReflowSelectors"; -import { AppState } from "@appsmith/reducers"; import { isCurrentCanvasDragging } from "sagas/selectors"; +import { getContainerWidgetSpacesSelectorWhileMoving } from "selectors/editorSelectors"; +import { getIsReflowing } from "selectors/widgetReflowSelectors"; +import { getBottomRowAfterReflow } from "utils/reflowHookUtils"; type WidgetCollidingSpace = CollidingSpace & { type: string; @@ -67,6 +67,7 @@ export const useReflow = ( OGPositions: OccupiedSpace[], parentId: string, gridProps: GridProps, + shouldResize = true, ): { reflowSpaces: ReflowInterface; resetReflow: () => void } => { const dispatch = useDispatch(); const isReflowingGlobal = useSelector(getIsReflowing); @@ -119,7 +120,6 @@ export const useReflow = ( }, [isReflowingGlobal, isDraggingCanvas]); // will become a state if we decide that resize should be a "toggle on-demand" feature - const shouldResize = true; return { reflowSpaces: ( newPositions: BlockSpace[], From 0da71c311b768e3f50662d7024f4b3b5081d3152 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Wed, 15 Feb 2023 15:27:20 +0530 Subject: [PATCH 504/708] enabling dynamic height under the hood. --- app/client/src/widgets/WidgetUtils.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/app/client/src/widgets/WidgetUtils.ts b/app/client/src/widgets/WidgetUtils.ts index 05b9c0329cda..5981d000eefa 100644 --- a/app/client/src/widgets/WidgetUtils.ts +++ b/app/client/src/widgets/WidgetUtils.ts @@ -27,13 +27,13 @@ import { find, isArray, isEmpty } from "lodash"; import generate from "nanoid/generate"; import { createGlobalStyle, css } from "styled-components"; import tinycolor from "tinycolor2"; -import { SchemaItem } from "./JSONFormWidget/constants"; -import { rgbaMigrationConstantV56 } from "./constants"; import { DynamicPath } from "utils/DynamicBindingUtils"; import { getLocale } from "utils/helpers"; import { DynamicHeight } from "utils/WidgetFeatures"; import { WidgetPositionProps, WidgetProps } from "./BaseWidget"; +import { rgbaMigrationConstantV56 } from "./constants"; import { ContainerWidgetProps } from "./ContainerWidget/widget"; +import { SchemaItem } from "./JSONFormWidget/constants"; const punycode = require("punycode/"); @@ -743,9 +743,6 @@ export const isAutoHeightEnabledForWidget = ( props: WidgetProps, shouldCheckIfEnabledWithLimits = false, ) => { - if (props.isFlexChild) { - return false; - } if (shouldCheckIfEnabledWithLimits) { return props.dynamicHeight === DynamicHeight.AUTO_HEIGHT_WITH_LIMITS; } From a1500d851cf669d300704c2f2747cbd9e0170774 Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 15 Feb 2023 07:58:46 -0500 Subject: [PATCH 505/708] removing dynamic height --- app/client/src/widgets/WidgetUtils.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/app/client/src/widgets/WidgetUtils.ts b/app/client/src/widgets/WidgetUtils.ts index 5981d000eefa..e667155a11da 100644 --- a/app/client/src/widgets/WidgetUtils.ts +++ b/app/client/src/widgets/WidgetUtils.ts @@ -743,6 +743,7 @@ export const isAutoHeightEnabledForWidget = ( props: WidgetProps, shouldCheckIfEnabledWithLimits = false, ) => { + if (props.isFlexChild) return false; if (shouldCheckIfEnabledWithLimits) { return props.dynamicHeight === DynamicHeight.AUTO_HEIGHT_WITH_LIMITS; } From eddaf0fb5021ac004e48004f70cf723364c1090d Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 15 Feb 2023 10:50:16 -0500 Subject: [PATCH 506/708] change wrapper width --- .../designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx index b2ca659e85a7..5bc9371d5ba2 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx @@ -40,7 +40,7 @@ const SubWrapper = styled.div<{ flexDirection: FlexDirection; wrap?: boolean; }>` - flex: ${({ wrap }) => `1 1 ${wrap ? "100" : "33.3"}%`}; + flex: ${({ wrap }) => `1 1 ${wrap ? "100" : "33.3333"}%`}; display: flex; flex-direction: ${({ flexDirection }) => flexDirection || "row"}; align-items: flex-start; From d165f584578ce288b45cb4fc95d9b86b288ef451 Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 15 Feb 2023 16:33:21 -0500 Subject: [PATCH 507/708] cypress fixes 1 --- .../ClientSideTests/ThemingTests/Basic_spec.js | 4 ++-- .../ServerSideTests/GenerateCRUD/Postgres2_Spec.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/ThemingTests/Basic_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/ThemingTests/Basic_spec.js index ac27b9e7b643..5333345acb47 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/ThemingTests/Basic_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/ThemingTests/Basic_spec.js @@ -865,7 +865,7 @@ describe("App Theming funtionality", function() { }); cy.assertPageSave(); - + cy.wait(2000); cy.PublishtheApp(); //Verify Background color @@ -876,7 +876,7 @@ describe("App Theming funtionality", function() { ); //new widget with its own color ////old widgets still conforming to theme color - cy.get(".t--widget-buttonwidget:nth-child(2) button").should( + cy.get(".t--widget-buttonwidget button").should( "have.css", "background-color", "rgb(126, 34, 206)", diff --git a/app/client/cypress/integration/Regression_TestSuite/ServerSideTests/GenerateCRUD/Postgres2_Spec.ts b/app/client/cypress/integration/Regression_TestSuite/ServerSideTests/GenerateCRUD/Postgres2_Spec.ts index b0de470f62b5..33d8b16b28ad 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ServerSideTests/GenerateCRUD/Postgres2_Spec.ts +++ b/app/client/cypress/integration/Regression_TestSuite/ServerSideTests/GenerateCRUD/Postgres2_Spec.ts @@ -438,12 +438,12 @@ describe("Validate Postgres Generate CRUD with JSON Form", () => { agHelper.AssertElementVisible(locator._visibleTextDiv("Insert Row")); //Checking Required field validations - deployMode.ClearJSONFieldValue("Shipname", 1); + deployMode.ClearJSONFieldValue("Shipname", 0); cy.xpath(locator._spanButton("Submit") + "/parent::div").should( "have.attr", "disabled", ); - deployMode.EnterJSONInputValue("Shipname", "MALTESE FALCON", 1); + deployMode.EnterJSONInputValue("Shipname", "MALTESE FALCON", 0); cy.xpath(locator._spanButton("Submit") + "/parent::div").should( "not.have.attr", "disabled", From a253d82f11c182c28874c216a844b8b2855c90a2 Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 15 Feb 2023 17:08:19 -0500 Subject: [PATCH 508/708] promisesapp_spec fix --- .../Regression_TestSuite/Application/PromisesApp_spec.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/client/cypress/integration/Regression_TestSuite/Application/PromisesApp_spec.js b/app/client/cypress/integration/Regression_TestSuite/Application/PromisesApp_spec.js index ca6415c1b554..71a51092b278 100644 --- a/app/client/cypress/integration/Regression_TestSuite/Application/PromisesApp_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/Application/PromisesApp_spec.js @@ -53,7 +53,7 @@ describe("JSEditor tests", function() { _.ee.SelectEntityByName("Page1", "Pages"); // verify text in the text widget cy.get(".t--draggable-textwidget span") - .eq(2) + .eq(5) .invoke("text") .then((text) => { expect(text).to.equal( @@ -77,7 +77,7 @@ describe("JSEditor tests", function() { cy.wait(2000); // verify text in the text widget cy.get(".t--draggable-textwidget span") - .eq(2) + .eq(5) .invoke("text") .then((text) => { expect(text).to.equal( @@ -129,7 +129,7 @@ describe("JSEditor tests", function() { ); cy.xpath("//span[text()='Clear store']").click({ force: true }); cy.get(".t--draggable-textwidget span") - .eq(2) + .eq(5) .invoke("text") .then((text) => { expect(text).to.equal( From 05d3af6ea4ddbea4fd213ae066f11aa67b6ae82e Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 15 Feb 2023 22:04:39 -0500 Subject: [PATCH 509/708] cypress fixes 2 --- .../ThemingTests/Basic_spec.js | 10 ++-- .../GenerateCRUD/Postgres2_Spec.ts | 56 +++++++++---------- 2 files changed, 32 insertions(+), 34 deletions(-) diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/ThemingTests/Basic_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/ThemingTests/Basic_spec.js index 5333345acb47..9d9291ace90b 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/ThemingTests/Basic_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/ThemingTests/Basic_spec.js @@ -1115,16 +1115,14 @@ describe("App Theming funtionality", function() { cy.PublishtheApp(); //Verify Background color - cy.get(".t--widget-buttonwidget:nth-child(2) button") + cy.get(".t--widget-buttonwidget button") .eq(0) .should("have.css", "background-color", "rgb(252, 165, 165)"); //new widget with its own color ////old widgets still conforming to theme color - cy.get(".t--widget-buttonwidget:nth-child(4) button").should( - "have.css", - "background-color", - "rgb(239, 68, 68)", - ); + cy.get(".t--widget-buttonwidget button") + .eq(1) + .should("have.css", "background-color", "rgb(239, 68, 68)"); cy.get(publish.iconWidgetBtn).should( "have.css", "background-color", diff --git a/app/client/cypress/integration/Regression_TestSuite/ServerSideTests/GenerateCRUD/Postgres2_Spec.ts b/app/client/cypress/integration/Regression_TestSuite/ServerSideTests/GenerateCRUD/Postgres2_Spec.ts index 33d8b16b28ad..4c40ef3e47d9 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ServerSideTests/GenerateCRUD/Postgres2_Spec.ts +++ b/app/client/cypress/integration/Regression_TestSuite/ServerSideTests/GenerateCRUD/Postgres2_Spec.ts @@ -459,59 +459,59 @@ describe("Validate Postgres Generate CRUD with JSON Form", () => { }); it("14. Verify Add/Insert from Deploy page - on Vessels - new record", () => { - deployMode.ClearJSONFieldValue("Callsign", 1); - deployMode.EnterJSONInputValue("Callsign", "9HUQ9", 1); + deployMode.ClearJSONFieldValue("Callsign", 0); + deployMode.EnterJSONInputValue("Callsign", "9HUQ9", 0); - deployMode.ClearJSONFieldValue("Country", 1); - deployMode.EnterJSONInputValue("Country", "Malta", 1); + deployMode.ClearJSONFieldValue("Country", 0); + deployMode.EnterJSONInputValue("Country", "Malta", 0); - deployMode.ClearJSONFieldValue("Next Port Name", 1); - deployMode.EnterJSONInputValue("Next Port Name", "CORFU", 1); + deployMode.ClearJSONFieldValue("Next Port Name", 0); + deployMode.EnterJSONInputValue("Next Port Name", "CORFU", 0); - deployMode.ClearJSONFieldValue("Destination", 1); - deployMode.EnterJSONInputValue("Destination", "CORFU", 1); + deployMode.ClearJSONFieldValue("Destination", 0); + deployMode.EnterJSONInputValue("Destination", "CORFU", 0); - deployMode.SelectJsonFormDropDown("Special Craft", 1); + deployMode.SelectJsonFormDropDown("Special Craft", 0); - deployMode.ClearJSONFieldValue("Timezone", 1); - deployMode.EnterJSONInputValue("Timezone", "-12", 1); + deployMode.ClearJSONFieldValue("Timezone", 0); + deployMode.EnterJSONInputValue("Timezone", "-12", 0); agHelper.AssertElementVisible( locator._visibleTextDiv("Not a valid timezone!"), ); - deployMode.ClearJSONFieldValue("Timezone", 1); - deployMode.EnterJSONInputValue("Timezone", "-2", 1); + deployMode.ClearJSONFieldValue("Timezone", 0); + deployMode.EnterJSONInputValue("Timezone", "-2", 0); - deployMode.ClearJSONFieldValue("Status Name", 1); - deployMode.EnterJSONInputValue("Status Name", "Moored", 1); + deployMode.ClearJSONFieldValue("Status Name", 0); + deployMode.EnterJSONInputValue("Status Name", "Moored", 0); - deployMode.ClearJSONFieldValue("Year Built", 1); - deployMode.EnterJSONInputValue("Year Built", "1967", 1); + deployMode.ClearJSONFieldValue("Year Built", 0); + deployMode.EnterJSONInputValue("Year Built", "1967", 0); - deployMode.ClearJSONFieldValue("Area Code", 1); - deployMode.EnterJSONInputValue("Area Code", "USG - Gulf of Mexico", 1); + deployMode.ClearJSONFieldValue("Area Code", 0); + deployMode.EnterJSONInputValue("Area Code", "USG - Gulf of Mexico", 0); - deployMode.ClearJSONFieldValue("Speed", 1); - deployMode.EnterJSONInputValue("Speed", "0.6", 1); + deployMode.ClearJSONFieldValue("Speed", 0); + deployMode.EnterJSONInputValue("Speed", "0.6", 0); agHelper.GetNClick( deployMode._jsonFormDatepickerFieldByName("Eta Updated"), 1, ); agHelper.GetNClick(locator._datePicker(2)); - agHelper.GetNClick(deployMode._jsonFieldName("Distance To Go"), 1); + agHelper.GetNClick(deployMode._jsonFieldName("Distance To Go"), 0); - deployMode.ClearJSONFieldValue("Distance To Go", 1); - deployMode.EnterJSONInputValue("Distance To Go", "18.1", 1); + deployMode.ClearJSONFieldValue("Distance To Go", 0); + deployMode.EnterJSONInputValue("Distance To Go", "18.1", 0); - deployMode.ClearJSONFieldValue("Current Port", 1); - deployMode.EnterJSONInputValue("Current Port", "GALVESTON", 1); + deployMode.ClearJSONFieldValue("Current Port", 0); + deployMode.EnterJSONInputValue("Current Port", "GALVESTON", 0); cy.xpath(deployMode._jsonFormFieldByName("Callsign", true)) .eq(1) .invoke("attr", "type") .should("eq", "password"); - deployMode.ClearJSONFieldValue("Shipname", 1); + deployMode.ClearJSONFieldValue("Shipname", 0); agHelper.AssertElementVisible( locator._visibleTextDiv("This field is required"), ); @@ -519,7 +519,7 @@ describe("Validate Postgres Generate CRUD with JSON Form", () => { "have.attr", "disabled", ); - deployMode.EnterJSONInputValue("Shipname", "MALTESE FALCON", 1); + deployMode.EnterJSONInputValue("Shipname", "MALTESE FALCON", 0); agHelper.ClickButton("Submit"); agHelper.AssertContains( From fb0a8e64fda50b76257a6589d4a71f1ef32673e2 Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 15 Feb 2023 23:59:49 -0500 Subject: [PATCH 510/708] cypress fixes 3 --- .../ServerSideTests/GenerateCRUD/Postgres2_Spec.ts | 2 +- app/client/cypress/support/Pages/AggregateHelper.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/client/cypress/integration/Regression_TestSuite/ServerSideTests/GenerateCRUD/Postgres2_Spec.ts b/app/client/cypress/integration/Regression_TestSuite/ServerSideTests/GenerateCRUD/Postgres2_Spec.ts index 4c40ef3e47d9..be382a65f943 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ServerSideTests/GenerateCRUD/Postgres2_Spec.ts +++ b/app/client/cypress/integration/Regression_TestSuite/ServerSideTests/GenerateCRUD/Postgres2_Spec.ts @@ -495,7 +495,7 @@ describe("Validate Postgres Generate CRUD with JSON Form", () => { agHelper.GetNClick( deployMode._jsonFormDatepickerFieldByName("Eta Updated"), - 1, + 0, ); agHelper.GetNClick(locator._datePicker(2)); agHelper.GetNClick(deployMode._jsonFieldName("Distance To Go"), 0); diff --git a/app/client/cypress/support/Pages/AggregateHelper.ts b/app/client/cypress/support/Pages/AggregateHelper.ts index 14c36b4cb652..319964faaac5 100644 --- a/app/client/cypress/support/Pages/AggregateHelper.ts +++ b/app/client/cypress/support/Pages/AggregateHelper.ts @@ -208,6 +208,7 @@ export class AggregateHelper { public clickMultipleButtons(btnVisibleText: string, shouldSleep = true) { cy.xpath(this.locator._spanButton(btnVisibleText)).each(($el) => { $el.trigger("click", { force: true }); + cy.wait(200); }); shouldSleep && this.Sleep(); } From f60089caf3f88144306532fa25e0c877db3ee8f4 Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 16 Feb 2023 00:17:13 -0500 Subject: [PATCH 511/708] remove unused actions --- app/client/src/actions/autoLayoutActions.ts | 12 --- .../src/ce/constants/ReduxActionConstants.tsx | 3 - .../src/sagas/AutoLayoutUpdateSagas.tsx | 91 +------------------ app/client/src/utils/layoutPropertiesUtils.ts | 60 ++++++------ 4 files changed, 31 insertions(+), 135 deletions(-) diff --git a/app/client/src/actions/autoLayoutActions.ts b/app/client/src/actions/autoLayoutActions.ts index ebb39faecf0a..45c0ea55b541 100644 --- a/app/client/src/actions/autoLayoutActions.ts +++ b/app/client/src/actions/autoLayoutActions.ts @@ -1,17 +1,5 @@ import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants"; -export const removeWrappersAction = (parentId: string) => ({ - type: ReduxActionTypes.REMOVE_CHILD_WRAPPERS, - payload: { parentId }, -}); - -export const addWrappersAction = (parentId: string) => ({ - type: ReduxActionTypes.ADD_CHILD_WRAPPERS, - payload: { - parentId, - }, -}); - export const updateLayoutForMobileBreakpointAction = ( parentId: string, isMobile: boolean, diff --git a/app/client/src/ce/constants/ReduxActionConstants.tsx b/app/client/src/ce/constants/ReduxActionConstants.tsx index defd158502c7..1157610b7b06 100644 --- a/app/client/src/ce/constants/ReduxActionConstants.tsx +++ b/app/client/src/ce/constants/ReduxActionConstants.tsx @@ -760,9 +760,6 @@ export const ReduxActionTypes = { SET_PANE_COUNT: "SET_PANE_COUNT", AUTOLAYOUT_REORDER_WIDGETS: "AUTOLAYOUT_REORDER_WIDGETS", AUTOLAYOUT_ADD_NEW_WIDGETS: "AUTOLAYOUT_ADD_NEW_WIDGETS", - REMOVE_CHILD_WRAPPERS: "REMOVE_CHILD_WRAPPERS", - ADD_CHILD_WRAPPERS: "ADD_CHILD_WRAPPERS", - UPDATE_FILL_CHILD_LAYER: "UPDATE_FILL_CHILD_LAYER", RECALCULATE_COLUMNS: "RECALCULATE_COLUMNS", }; diff --git a/app/client/src/sagas/AutoLayoutUpdateSagas.tsx b/app/client/src/sagas/AutoLayoutUpdateSagas.tsx index 42fa64c56c16..9fa2e06f83d7 100644 --- a/app/client/src/sagas/AutoLayoutUpdateSagas.tsx +++ b/app/client/src/sagas/AutoLayoutUpdateSagas.tsx @@ -4,101 +4,15 @@ import { ReduxActionErrorTypes, ReduxActionTypes, } from "ce/constants/ReduxActionConstants"; -import { ResponsiveBehavior } from "utils/autoLayout/constants"; import log from "loglevel"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; -import { all, call, put, select, takeLatest } from "redux-saga/effects"; -import { getIsMobile } from "selectors/mainCanvasSelectors"; +import { all, put, select, takeLatest } from "redux-saga/effects"; import { alterLayoutForDesktop, alterLayoutForMobile, - removeChildLayers, - updateFillChildStatus, - wrapChildren, } from "../utils/autoLayout/AutoLayoutUtils"; import { getWidgets } from "./selectors"; -type LayoutUpdatePayload = { - parentId: string; -}; - -function* removeChildWrappers(actionPayload: ReduxAction) { - try { - const start = performance.now(); - const { parentId } = actionPayload.payload; - const allWidgets: CanvasWidgetsReduxState = yield select(getWidgets); - const updatedWidgets: CanvasWidgetsReduxState = removeChildLayers( - allWidgets, - parentId, - ); - yield put(updateAndSaveLayout(updatedWidgets)); - log.debug("empty wrapper removal took", performance.now() - start, "ms"); - } catch (error) { - yield put({ - type: ReduxActionErrorTypes.WIDGET_OPERATION_ERROR, - payload: { - action: ReduxActionTypes.REMOVE_CHILD_WRAPPERS, - error, - }, - }); - } -} - -function* addChildWrappers(actionPayload: ReduxAction) { - try { - const start = performance.now(); - const { parentId } = actionPayload.payload; - const allWidgets: CanvasWidgetsReduxState = yield select(getWidgets); - const isMobile: boolean = yield select(getIsMobile); - const updatedWidgets: CanvasWidgetsReduxState = yield call( - wrapChildren, - allWidgets, - parentId, - isMobile, - ); - yield put(updateAndSaveLayout(updatedWidgets)); - log.debug("empty wrapper removal took", performance.now() - start, "ms"); - } catch (error) { - yield put({ - type: ReduxActionErrorTypes.WIDGET_OPERATION_ERROR, - payload: { - action: ReduxActionTypes.ADD_CHILD_WRAPPERS, - error, - }, - }); - } -} - -export function* updateFillChildInfo( - actionPayload: ReduxAction<{ - widgetId: string; - responsiveBehavior: ResponsiveBehavior; - }>, -) { - try { - const start = performance.now(); - const { responsiveBehavior, widgetId } = actionPayload.payload; - const allWidgets: CanvasWidgetsReduxState = yield select(getWidgets); - const isMobile: boolean = yield select(getIsMobile); - const updatedWidgets: CanvasWidgetsReduxState = updateFillChildStatus( - allWidgets, - widgetId, - responsiveBehavior === ResponsiveBehavior.Fill, - isMobile, - ); - yield put(updateAndSaveLayout(updatedWidgets)); - log.debug("updating fill child info took", performance.now() - start, "ms"); - } catch (error) { - yield put({ - type: ReduxActionErrorTypes.WIDGET_OPERATION_ERROR, - payload: { - action: ReduxActionTypes.UPDATE_FILL_CHILD_LAYER, - error, - }, - }); - } -} - export function* updateLayoutForMobileCheckpoint( actionPayload: ReduxAction<{ parentId: string; @@ -132,9 +46,6 @@ export function* updateLayoutForMobileCheckpoint( export default function* layoutUpdateSagas() { yield all([ - takeLatest(ReduxActionTypes.ADD_CHILD_WRAPPERS, addChildWrappers), - takeLatest(ReduxActionTypes.REMOVE_CHILD_WRAPPERS, removeChildWrappers), - takeLatest(ReduxActionTypes.UPDATE_FILL_CHILD_LAYER, updateFillChildInfo), takeLatest( ReduxActionTypes.RECALCULATE_COLUMNS, updateLayoutForMobileCheckpoint, diff --git a/app/client/src/utils/layoutPropertiesUtils.ts b/app/client/src/utils/layoutPropertiesUtils.ts index b48348ba6e56..9275d450fc9d 100644 --- a/app/client/src/utils/layoutPropertiesUtils.ts +++ b/app/client/src/utils/layoutPropertiesUtils.ts @@ -102,18 +102,18 @@ export const generateResponsiveBehaviorConfig = ( isBindProperty: false, isTriggerProperty: true, validation: { type: ValidationTypes.TEXT }, - additionalAction: ( - props: any, - propertyName?: string, - propertyValue?: any, - ) => ({ - type: ReduxActionTypes.UPDATE_FILL_CHILD_LAYER, - payload: { - widgetId: props.widgetId, - responsiveBehavior: propertyValue, - }, - }), - dependencies: ["widgetId"], + // additionalAction: ( + // props: any, + // propertyName?: string, + // propertyValue?: any, + // ) => ({ + // type: ReduxActionTypes.UPDATE_FILL_CHILD_LAYER, + // payload: { + // widgetId: props.widgetId, + // responsiveBehavior: propertyValue, + // }, + // }), + // dependencies: ["widgetId"], }; }; @@ -179,24 +179,24 @@ export const generatePositioningConfig = ( isBindProperty: true, isTriggerProperty: true, validation: { type: ValidationTypes.TEXT }, - additionalAction: ( - props: any, - propertyName?: string, - propertyValue?: any, - ) => { - if (!propertyName || !propertyValue) return; - const positioning: Positioning = propertyValue as Positioning; - return { - type: - positioning === Positioning.Vertical - ? ReduxActionTypes.ADD_CHILD_WRAPPERS - : ReduxActionTypes.REMOVE_CHILD_WRAPPERS, - payload: { - parentId: props.widgetId, - }, - }; - }, - dependencies: ["widgetId"], + // additionalAction: ( + // props: any, + // propertyName?: string, + // propertyValue?: any, + // ) => { + // if (!propertyName || !propertyValue) return; + // const positioning: Positioning = propertyValue as Positioning; + // return { + // type: + // positioning === Positioning.Vertical + // ? ReduxActionTypes.ADD_CHILD_WRAPPERS + // : ReduxActionTypes.REMOVE_CHILD_WRAPPERS, + // payload: { + // parentId: props.widgetId, + // }, + // }; + // }, + // dependencies: ["widgetId"], }; }; From c27e754cd69f68fcec23c7e8c5198bb7fff3c2b9 Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 16 Feb 2023 08:22:28 -0500 Subject: [PATCH 512/708] cypress fixes 3 --- .../ThemingTests/Basic_spec.js | 14 ++++---- .../JSFunctionExecution_spec.ts | 34 +++++++++++-------- 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/ThemingTests/Basic_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/ThemingTests/Basic_spec.js index 9d9291ace90b..49a62f11cf0e 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/ThemingTests/Basic_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/ThemingTests/Basic_spec.js @@ -1121,7 +1121,7 @@ describe("App Theming funtionality", function() { ////old widgets still conforming to theme color cy.get(".t--widget-buttonwidget button") - .eq(1) + .last() .should("have.css", "background-color", "rgb(239, 68, 68)"); cy.get(publish.iconWidgetBtn).should( "have.css", @@ -1135,7 +1135,7 @@ describe("App Theming funtionality", function() { .should("have.css", "border-radius", "6px"); cy.get(publish.iconWidgetBtn).should("have.css", "border-radius", "24px"); cy.get(widgetsPage.widgetBtn) - .eq(1) + .last() .should("have.css", "border-radius", "24px"); //Verify Box shadow @@ -1148,7 +1148,7 @@ describe("App Theming funtionality", function() { ); cy.get(publish.iconWidgetBtn).should("have.css", "box-shadow", "none"); cy.get(widgetsPage.widgetBtn) - .eq(1) + .last() .should( "have.css", "box-shadow", @@ -1196,10 +1196,10 @@ describe("App Theming funtionality", function() { .eq(0) .should("have.css", "background-color", "rgb(239, 68, 68)"); //Widget Color cy.get(widgetsPage.widgetBtn) - .eq(1) + .last() .should("be.visible"); cy.get(widgetsPage.widgetBtn) - .eq(1) + .last() .should("have.css", "background-color", "rgb(239, 68, 68)"); //Widget Color cy.get(publish.iconWidgetBtn).should( "have.css", @@ -1211,7 +1211,7 @@ describe("App Theming funtionality", function() { .eq(0) .should("have.css", "border-radius", "24px"); //Border Radius cy.get(widgetsPage.widgetBtn) - .eq(1) + .last() .should("have.css", "border-radius", "24px"); //Border Radius cy.get(publish.iconWidgetBtn).should("have.css", "border-radius", "24px"); //Border Radius @@ -1223,7 +1223,7 @@ describe("App Theming funtionality", function() { "rgba(0, 0, 0, 0.1) 0px 1px 3px 0px, rgba(0, 0, 0, 0.06) 0px 1px 2px 0px", ); //Shadow cy.get(widgetsPage.widgetBtn) - .eq(1) + .last() .should( "have.css", "box-shadow", diff --git a/app/client/cypress/integration/Regression_TestSuite/ServerSideTests/JsFunctionExecution/JSFunctionExecution_spec.ts b/app/client/cypress/integration/Regression_TestSuite/ServerSideTests/JsFunctionExecution/JSFunctionExecution_spec.ts index 2fb8de4a7b9a..237172afa3ce 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ServerSideTests/JsFunctionExecution/JSFunctionExecution_spec.ts +++ b/app/client/cypress/integration/Regression_TestSuite/ServerSideTests/JsFunctionExecution/JSFunctionExecution_spec.ts @@ -357,23 +357,27 @@ describe("JS Function Execution", function() { // Switch to settings tab agHelper.GetNClick(jsEditor._settingsTab); // Add settings for each function (according to data) - for (const functionSetting of Object.values( - FUNCTIONS_SETTINGS_DEFAULT_DATA, - )) { - jsEditor.EnableDisableAsyncFuncSettings( - functionSetting.name, - functionSetting.onPageLoad, - functionSetting.confirmBeforeExecute, - ); - } + Object.values(FUNCTIONS_SETTINGS_DEFAULT_DATA).forEach( + (functionSetting) => { + jsEditor.EnableDisableAsyncFuncSettings( + functionSetting.name, + functionSetting.onPageLoad, + functionSetting.confirmBeforeExecute, + ); + }, + ); // Switch to settings tab agHelper.GetNClick(jsEditor._settingsTab); //After JSObj is created - check methods are in alphabetical order assertAsyncFunctionsOrder(FUNCTIONS_SETTINGS_DEFAULT_DATA); + agHelper.RefreshPage(); // click "Yes" button for all onPageload && ConfirmExecute functions - agHelper.clickMultipleButtons("Yes"); - agHelper.Sleep(); + for (let i = 0; i <= onPageLoadAndConfirmExecuteFunctionsLength - 1; i++) { + //agHelper.AssertElementPresence(jsEditor._dialog("Confirmation Dialog")); // Not working in edit mode + agHelper.ClickButton("Yes"); + agHelper.Sleep(); + } // Switch to settings tab and assert order agHelper.GetNClick(jsEditor._settingsTab); assertAsyncFunctionsOrder(FUNCTIONS_SETTINGS_DEFAULT_DATA); @@ -410,10 +414,12 @@ describe("JS Function Execution", function() { // clone page and assert order of functions ee.ClonePage(); - // click "Yes" button for all onPageload && ConfirmExecute functions - agHelper.clickMultipleButtons("Yes"); - agHelper.Sleep(); + for (let i = 0; i <= onPageLoadAndConfirmExecuteFunctionsLength - 1; i++) { + //agHelper.AssertElementPresence(jsEditor._dialog("Confirmation Dialog")); // Not working in edit mode + agHelper.ClickButton("Yes"); + agHelper.Sleep(); + } ee.SelectEntityByName(jsObj, "Queries/JS"); agHelper.GetNClick(jsEditor._settingsTab); From eb72ff7a48998f6fa64fee1ab3b9319df7029a93 Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 16 Feb 2023 08:34:20 -0500 Subject: [PATCH 513/708] remove unused import --- app/client/src/utils/layoutPropertiesUtils.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/app/client/src/utils/layoutPropertiesUtils.ts b/app/client/src/utils/layoutPropertiesUtils.ts index 9275d450fc9d..97aef177a3d7 100644 --- a/app/client/src/utils/layoutPropertiesUtils.ts +++ b/app/client/src/utils/layoutPropertiesUtils.ts @@ -1,4 +1,3 @@ -import { ReduxActionTypes } from "ce/constants/ReduxActionConstants"; import { AlignItems, Alignment, From d633e7ee62cc4e8e00ac7085aee758ba23f5cb6f Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 16 Feb 2023 11:32:19 -0500 Subject: [PATCH 514/708] Fix highlight positions in nested containers --- .../hooks/useAutoLayoutHighlights.ts | 20 ++-- .../CanvasArenas/hooks/useCanvasDragging.ts | 15 +-- .../utils/autoLayout/highlightUtils.test.ts | 33 ++---- .../src/utils/autoLayout/highlightUtils.ts | 101 +++--------------- 4 files changed, 46 insertions(+), 123 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index 24063b4d9d80..deb3625483ef 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -1,7 +1,6 @@ import { ResponsiveBehavior } from "utils/autoLayout/constants"; import { useSelector } from "react-redux"; import { getWidgets } from "sagas/selectors"; -import { getCanvasWidth } from "selectors/editorSelectors"; import { getIsMobile } from "selectors/mainCanvasSelectors"; import { deriveHighlightsFromLayers } from "utils/autoLayout/highlightUtils"; import WidgetFactory from "utils/WidgetFactory"; @@ -34,7 +33,6 @@ export const useAutoLayoutHighlights = ({ useAutoLayout, }: AutoLayoutHighlightProps) => { const allWidgets = useSelector(getWidgets); - const canvasWidth: number = useSelector(getCanvasWidth); const isMobile = useSelector(getIsMobile); let highlights: HighlightInfo[] = []; let lastActiveHighlight: HighlightInfo | undefined; @@ -73,7 +71,7 @@ export const useAutoLayoutHighlights = ({ return flag; }; - const calculateHighlights = (): HighlightInfo[] => { + const calculateHighlights = (snapColumnSpace: number): HighlightInfo[] => { cleanUpTempStyles(); if (useAutoLayout && isDragging && isCurrentDraggedCanvas) { if (!blocksToDraw || !blocksToDraw.length) return []; @@ -81,7 +79,7 @@ export const useAutoLayoutHighlights = ({ highlights = deriveHighlightsFromLayers( allWidgets, canvasId, - canvasWidth, + snapColumnSpace, blocksToDraw.map((block) => block?.widgetId), isFillWidget, isMobile, @@ -100,12 +98,15 @@ export const useAutoLayoutHighlights = ({ * @param e | MouseMoveEvent * @returns HighlightInfo | undefined */ - const highlightDropPosition = (e: any): HighlightInfo | undefined => { + const highlightDropPosition = ( + e: any, + snapColumnSpace: number, + ): HighlightInfo | undefined => { if (!highlights || !highlights.length) highlights = deriveHighlightsFromLayers( allWidgets, canvasId, - canvasWidth, + snapColumnSpace, blocksToDraw.map((block) => block?.widgetId), isFillWidget, isMobile, @@ -121,14 +122,17 @@ export const useAutoLayoutHighlights = ({ return highlight; }; - const getDropInfo = (val: Point): HighlightInfo | undefined => { + const getDropInfo = ( + val: Point, + snapColumnSpace: number, + ): HighlightInfo | undefined => { if (lastActiveHighlight) return lastActiveHighlight; if (!highlights || !highlights.length) highlights = deriveHighlightsFromLayers( allWidgets, canvasId, - canvasWidth, + snapColumnSpace, blocksToDraw.map((block) => block?.widgetId), isFillWidget, isMobile, diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index 9d49b2a04715..b4386214356d 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -124,7 +124,7 @@ export const useCanvasDragging = ( if (useAutoLayout) { setTimeout(() => { - calculateHighlights(); + calculateHighlights(snapColumnSpace); }, 0); if (!isDragging || !isCurrentDraggedCanvas) { @@ -220,10 +220,13 @@ export const useCanvasDragging = ( const onMouseUp = () => { if (isDragging && canvasIsDragging) { if (useAutoLayout) { - const dropInfo: HighlightInfo | undefined = getDropInfo({ - x: currentRectanglesToDraw[0].top, - y: currentRectanglesToDraw[0].left, - }); + const dropInfo: HighlightInfo | undefined = getDropInfo( + { + x: currentRectanglesToDraw[0].top, + y: currentRectanglesToDraw[0].left, + }, + snapColumnSpace, + ); if (dropInfo !== undefined) updateChildrenPositions(dropInfo, currentRectanglesToDraw); } else { @@ -433,7 +436,7 @@ export const useCanvasDragging = ( if (useAutoLayout && isCurrentDraggedCanvas) { setTimeout(() => { - selectedHighlight = highlightDropPosition(e); + selectedHighlight = highlightDropPosition(e, snapColumnSpace); }, 50); } } diff --git a/app/client/src/utils/autoLayout/highlightUtils.test.ts b/app/client/src/utils/autoLayout/highlightUtils.test.ts index 296e39372f15..ec4984c8fea3 100644 --- a/app/client/src/utils/autoLayout/highlightUtils.test.ts +++ b/app/client/src/utils/autoLayout/highlightUtils.test.ts @@ -9,10 +9,8 @@ import { deriveHighlightsFromLayers, generateHighlightsForAlignment, generateVerticalHighlights, - getCanvasWidth, VerticalHighlightsPayload, } from "./highlightUtils"; -import { data } from "./testData"; describe("test HighlightUtils methods", () => { describe("test deriveHighlightsFromLayers method", () => { @@ -45,15 +43,16 @@ describe("test HighlightUtils methods", () => { const highlights: HighlightInfo[] = deriveHighlightsFromLayers( widgets, "1", - 99, + 9.875, ); - // ToDo(Preet): These numbers have to be more of a config to make more sense of them. expect(highlights.length).toEqual(3); expect(highlights[0].isVertical).toBeFalsy; - expect(highlights[0].width).toEqual(33); + // width of each horizontal highlight = container width / 3 - padding. + expect(Math.round(highlights[0].width)).toEqual(211); expect(highlights[0].height).toEqual(4); - expect(highlights[1].posX).toEqual(35); - expect(highlights[2].posX).toEqual(68); + // x position of each horizontal highlight = (container width / 3) * index + padding. + expect(Math.round(highlights[1].posX)).toEqual(215); + expect(Math.round(highlights[2].posX)).toEqual(425); }); it("should add horizontal heights before every layer and below the last layer", () => { const widgets = { @@ -107,7 +106,7 @@ describe("test HighlightUtils methods", () => { const highlights: HighlightInfo[] = deriveHighlightsFromLayers( widgets, "1", - 99, + 10, ); expect(highlights.length).toEqual(10); expect( @@ -168,7 +167,6 @@ describe("test HighlightUtils methods", () => { maxHeight: 40, offsetTop: 4, parentColumnSpace: 10, - canvasWidth: 640, avoidInitialHighlight: false, isMobile: false, startPosition: 0, @@ -244,7 +242,7 @@ describe("test HighlightUtils methods", () => { getWidgetHeight(children[1], false) * children[1].parentRowSpace, offsetTop: 4, parentColumnSpace: 10, - canvasWidth: 640, + avoidInitialHighlight: false, isMobile: false, startPosition: 0, @@ -264,7 +262,7 @@ describe("test HighlightUtils methods", () => { maxHeight: 40, offsetTop: 4, parentColumnSpace: 10, - canvasWidth: 640, + avoidInitialHighlight: true, isMobile: false, startPosition: 0, @@ -356,7 +354,7 @@ describe("test HighlightUtils methods", () => { childCount: 0, layerIndex: 0, offsetTop: 4, - canvasWidth: 640, + canvasId: "1", columnSpace: 10, draggedWidgets: [], @@ -446,9 +444,8 @@ describe("test HighlightUtils methods", () => { childCount: 0, layerIndex: 0, offsetTop: 4, - canvasWidth: 640, canvasId: "1", - columnSpace: 10, + columnSpace: 9.875, draggedWidgets: [], isMobile: true, }); @@ -466,12 +463,4 @@ describe("test HighlightUtils methods", () => { expect(result.childCount).toEqual(2); }); }); - - describe("test getCanvasWidth method", () => { - it("should measure the width of canvas and account for paddings", () => { - expect(getCanvasWidth(data["0"], data, 1174, false)).toEqual(1166); - expect(getCanvasWidth(data["mglfryj65c"], data, 1174, true)).toEqual(569); - expect(getCanvasWidth(data["a3lldg1wg9"], data, 1174, true)).toEqual(569); - }); - }); }); diff --git a/app/client/src/utils/autoLayout/highlightUtils.ts b/app/client/src/utils/autoLayout/highlightUtils.ts index 709dfce843a6..29e8a3c8b3b3 100644 --- a/app/client/src/utils/autoLayout/highlightUtils.ts +++ b/app/client/src/utils/autoLayout/highlightUtils.ts @@ -4,7 +4,6 @@ import { FLEXBOX_PADDING, GridDefaults, MAIN_CONTAINER_WIDGET_ID, - WIDGET_PADDING, } from "constants/WidgetConstants"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; import { @@ -39,7 +38,7 @@ import { export function deriveHighlightsFromLayers( allWidgets: CanvasWidgetsReduxState, canvasId: string, - mainCanvasWidth = 0, + snapColumnSpace: number, draggedWidgets: string[] = [], hasFillWidget = false, isMobile = false, @@ -48,12 +47,6 @@ export function deriveHighlightsFromLayers( const canvas = widgets[canvasId]; if (!canvas) return []; - const { canvasWidth, columnSpace } = getCanvasDimensions( - canvas, - widgets, - mainCanvasWidth, - isMobile, - ); const layers: FlexLayer[] = canvas.flexLayers || []; let highlights: HighlightInfo[] = []; let childCount = 0; @@ -81,9 +74,8 @@ export function deriveHighlightsFromLayers( childCount, layerIndex, offsetTop, - canvasWidth, canvasId, - columnSpace, + columnSpace: snapColumnSpace, draggedWidgets, isMobile, }); @@ -98,7 +90,7 @@ export function deriveHighlightsFromLayers( childCount, layerIndex, offsetTop, - canvasWidth, + snapColumnSpace * GridDefaults.DEFAULT_GRID_COLUMNS, canvasId, hasFillWidget, childrenRows * GridDefaults.DEFAULT_GRID_ROW_HEIGHT, @@ -118,7 +110,7 @@ export function deriveHighlightsFromLayers( childCount, layerIndex, offsetTop, - canvasWidth, + snapColumnSpace * GridDefaults.DEFAULT_GRID_COLUMNS, canvasId, hasFillWidget, -1, @@ -143,7 +135,6 @@ export function generateVerticalHighlights(data: { childCount: number; layerIndex: number; offsetTop: number; - canvasWidth: number; canvasId: string; columnSpace: number; draggedWidgets: string[]; @@ -151,7 +142,6 @@ export function generateVerticalHighlights(data: { }): VerticalHighlightsPayload { const { canvasId, - canvasWidth, childCount, columnSpace, draggedWidgets, @@ -259,7 +249,6 @@ export function generateVerticalHighlights(data: { offsetTop, canvasId, parentColumnSpace: columnSpace, - canvasWidth, isMobile, avoidInitialHighlight, startPosition, @@ -268,7 +257,7 @@ export function generateVerticalHighlights(data: { index += 1; } } - highlights = updateVerticalHighlightDropZone(highlights, canvasWidth); + highlights = updateVerticalHighlightDropZone(highlights, columnSpace * 64); return { highlights, childCount: count }; } @@ -288,7 +277,6 @@ export function generateHighlightsForAlignment(data: { offsetTop: number; canvasId: string; parentColumnSpace: number; - canvasWidth: number; avoidInitialHighlight?: boolean; isMobile: boolean; startPosition: number | undefined; @@ -298,7 +286,6 @@ export function generateHighlightsForAlignment(data: { arr, avoidInitialHighlight, canvasId, - canvasWidth, childCount, isMobile, layerIndex, @@ -346,7 +333,6 @@ export function generateHighlightsForAlignment(data: { ? getRightColumn(lastChild, isMobile) * parentColumnSpace + FLEXBOX_PADDING / 2 : 0, - canvasWidth, canvasId, parentColumnSpace, startPosition, @@ -382,13 +368,16 @@ function getPositionForInitialHighlight( highlights: HighlightInfo[], alignment: FlexLayerAlignment, posX: number, - containerWidth: number, canvasId: string, columnSpace: number, startPosition: number | undefined, ): number { + const containerWidth = 64 * columnSpace; const endPosition = - 64 * columnSpace - (canvasId !== MAIN_CONTAINER_WIDGET_ID ? 6 : -2); + containerWidth + + (canvasId !== MAIN_CONTAINER_WIDGET_ID + ? FLEXBOX_PADDING + : FLEXBOX_PADDING / 2); if (alignment === FlexLayerAlignment.End) { return endPosition; } else if (alignment === FlexLayerAlignment.Center) { @@ -443,12 +432,11 @@ function generateHorizontalHighlights( alignment, posX: hasFillWidget ? alignment === FlexLayerAlignment.Start - ? FLEXBOX_PADDING - : containerWidth - : width * index + - (canvasId === MAIN_CONTAINER_WIDGET_ID + ? canvasId === MAIN_CONTAINER_WIDGET_ID ? FLEXBOX_PADDING - : FLEXBOX_PADDING / 2), + : FLEXBOX_PADDING * 1.5 + : containerWidth + : width * index + FLEXBOX_PADDING, posY: offsetTop, width: hasFillWidget ? alignment === FlexLayerAlignment.Start @@ -465,67 +453,6 @@ function generateHorizontalHighlights( return arr; } -function getCanvasDimensions( - canvas: Widget, - widgets: CanvasWidgetsReduxState, - mainCanvasWidth: number, - isMobile: boolean, -): { canvasWidth: number; columnSpace: number } { - const canvasWidth: number = getCanvasWidth( - canvas, - widgets, - mainCanvasWidth, - isMobile, - ); - - const columnSpace: number = canvasWidth / GridDefaults.DEFAULT_GRID_COLUMNS; - - return { canvasWidth: canvasWidth, columnSpace }; -} - -export function getCanvasWidth( - canvas: Widget, - widgets: CanvasWidgetsReduxState, - mainCanvasWidth: number, - isMobile: boolean, -): number { - if (!mainCanvasWidth) return 0; - if (canvas.widgetId === MAIN_CONTAINER_WIDGET_ID) - return mainCanvasWidth - getPadding(canvas); - let widget = canvas; - let columns = 0; - let width = 1; - let padding = 0; - while (widget.parentId) { - columns = getWidgetWidth(widget, isMobile); - padding += getPadding(widget); - width *= columns > 64 ? 1 : columns / GridDefaults.DEFAULT_GRID_COLUMNS; - widget = widgets[widget.parentId]; - } - const totalWidth = width * mainCanvasWidth; - if (widget.widgetId === MAIN_CONTAINER_WIDGET_ID) - padding += getPadding(widget); - return totalWidth - padding; -} - -function getPadding(canvas: Widget): number { - let padding = 0; - if ( - canvas.widgetId === MAIN_CONTAINER_WIDGET_ID || - canvas.type === "CONTAINER_WIDGET" - ) { - //For MainContainer and any Container Widget padding doesn't exist coz there is already container padding. - padding = FLEXBOX_PADDING * 2; - } - if (canvas.noPad) { - // Widgets like ListWidget choose to have no container padding so will only have widget padding - padding = WIDGET_PADDING * 2; - } - // Account for container border. - padding += canvas.type === "CONTAINER_WIDGET" ? 2 : 0; - return padding; -} - /** * Calculate drop zones for vertical highlights. * Drop zone of vertical highlights span 35% of the distance between two consecutive highlights. From 935ebe1d2319eab858303211a36aed6aecc37dc4 Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 16 Feb 2023 15:33:21 -0500 Subject: [PATCH 515/708] fix basic spec --- .../ThemingTests/Basic_spec.js | 370 +++++++++--------- 1 file changed, 195 insertions(+), 175 deletions(-) diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/ThemingTests/Basic_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/ThemingTests/Basic_spec.js index 49a62f11cf0e..99a08e163d63 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/ThemingTests/Basic_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/ThemingTests/Basic_spec.js @@ -799,17 +799,17 @@ describe("App Theming funtionality", function() { .eq(33) .then(($elem) => { cy.get($elem).click({ force: true }); - cy.get(widgetsPage.widgetBtn) - .eq(1) - .should( - "have.css", - "background-color", - $elem.css("background-color"), //rgb(134, 239, 172) - ); //new widget with its own color + cy.get(".t--widget-button2 button").should( + "have.css", + "background-color", + $elem.css("background-color"), //rgb(134, 239, 172) + ); //new widget with its own color - cy.get(widgetsPage.widgetBtn) - .eq(0) - .should("have.css", "background-color", "rgb(126, 34, 206)"); //old widgets still conforming to theme color + cy.get(".t--widget-button1 button").should( + "have.css", + "background-color", + "rgb(126, 34, 206)", + ); //old widgets still conforming to theme color cy.get(widgetsPage.iconWidgetBtn).should( "have.css", "background-color", @@ -824,44 +824,44 @@ describe("App Theming funtionality", function() { .eq(0) .invoke("css", "border-top-left-radius") .then((borderRadius) => { - cy.get(widgetsPage.widgetBtn) - .eq(1) - .should( - "have.css", - "border-radius", - borderRadius, //0px - ); + cy.get(".t--widget-button2 button").should( + "have.css", + "border-radius", + borderRadius, //0px + ); cy.get(widgetsPage.iconWidgetBtn).should( "have.css", "border-radius", "24px", ); - cy.get(widgetsPage.widgetBtn) - .eq(0) - .should("have.css", "border-radius", "24px"); + cy.get(".t--widget-button1 button").should( + "have.css", + "border-radius", + "24px", + ); }); //Change Shadow & verify - cy.get(".t--button-group-0.10px").click(); + cy.get(".t--button-group-0.10px").click({ force: true }); cy.get(".t--button-group-0.10px div") .eq(0) .invoke("css", "box-shadow") .then((boxshadow) => { - cy.get(widgetsPage.widgetBtn) - .eq(1) - .should( - "have.css", - "box-shadow", - boxshadow, //rgba(0, 0, 0, 0.1) 0px 10px 15px -3px, rgba(0, 0, 0, 0.05) 0px 4px 6px -2px - ); + cy.get(".t--widget-button2 button").should( + "have.css", + "box-shadow", + boxshadow, //rgba(0, 0, 0, 0.1) 0px 10px 15px -3px, rgba(0, 0, 0, 0.05) 0px 4px 6px -2px + ); cy.get(widgetsPage.iconWidgetBtn).should( "have.css", "box-shadow", "none", ); - cy.get(widgetsPage.widgetBtn) - .eq(0) - .should("have.css", "box-shadow", "none"); + cy.get(".t--widget-button1 button").should( + "have.css", + "box-shadow", + "none", + ); }); cy.assertPageSave(); @@ -889,26 +889,30 @@ describe("App Theming funtionality", function() { ); //Verify Border radius - cy.get(widgetsPage.widgetBtn) - .eq(1) - .should("have.css", "border-radius", "0px"); + cy.get(".t--widget-button2 button").should( + "have.css", + "border-radius", + "0px", + ); cy.get(publish.iconWidgetBtn).should("have.css", "border-radius", "24px"); - cy.get(widgetsPage.widgetBtn) - .eq(0) - .should("have.css", "border-radius", "24px"); + cy.get(".t--widget-button1 button").should( + "have.css", + "border-radius", + "24px", + ); //Verify Box shadow - cy.get(widgetsPage.widgetBtn) - .eq(1) - .should( - "have.css", - "box-shadow", - "rgba(0, 0, 0, 0.1) 0px 10px 15px -3px, rgba(0, 0, 0, 0.05) 0px 4px 6px -2px", - ); + cy.get(".t--widget-button2 button").should( + "have.css", + "box-shadow", + "rgba(0, 0, 0, 0.1) 0px 10px 15px -3px, rgba(0, 0, 0, 0.05) 0px 4px 6px -2px", + ); cy.get(publish.iconWidgetBtn).should("have.css", "box-shadow", "none"); - cy.get(widgetsPage.widgetBtn) - .eq(0) - .should("have.css", "box-shadow", "none"); + cy.get(".t--widget-button1 button").should( + "have.css", + "box-shadow", + "none", + ); cy.get(publish.backToEditor) .click({ force: true }) @@ -924,16 +928,20 @@ describe("App Theming funtionality", function() { $elem[0].click(); }); - cy.get(widgetsPage.widgetBtn) - .eq(1) - .should("have.css", "background-color", "rgb(126, 34, 206)"); //verify widget reverted to theme color + cy.get(".t--widget-button2 button").should( + "have.css", + "background-color", + "rgb(126, 34, 206)", + ); //verify widget reverted to theme color cy.get(".t--property-control-borderradius .reset-button").then(($elem) => { $elem[0].removeAttribute("display: none"); $elem[0].click(); }); - cy.get(widgetsPage.widgetBtn) - .eq(1) - .should("have.css", "border-radius", "24px"); + cy.get(".t--widget-button2 button").should( + "have.css", + "border-radius", + "24px", + ); //the new widget with reverted styles also conforming to theme cy.PublishtheApp(); @@ -946,39 +954,45 @@ describe("App Theming funtionality", function() { "background-color", "rgb(253, 224, 71)", ); //Background Color - cy.get(widgetsPage.widgetBtn) - .eq(0) - .should("have.css", "background-color", "rgb(126, 34, 206)"); //Widget Color - cy.get(widgetsPage.widgetBtn) - .eq(1) - .should("be.visible"); - cy.get(widgetsPage.widgetBtn) - .eq(1) - .should("have.css", "background-color", "rgb(126, 34, 206)"); //Widget Color + cy.get(".t--widget-button1 button").should( + "have.css", + "background-color", + "rgb(126, 34, 206)", + ); //Widget Color + cy.get(".t--widget-button2 button").should("be.visible"); + cy.get(".t--widget-button2 button").should( + "have.css", + "background-color", + "rgb(126, 34, 206)", + ); //Widget Color cy.get(publish.iconWidgetBtn).should( "have.css", "background-color", "rgb(126, 34, 206)", ); //Widget Color - cy.get(widgetsPage.widgetBtn) - .eq(0) - .should("have.css", "border-radius", "24px"); //Border Radius - cy.get(widgetsPage.widgetBtn) - .eq(1) - .should("have.css", "border-radius", "24px"); //Border Radius + cy.get(".t--widget-button1 button").should( + "have.css", + "border-radius", + "24px", + ); //Border Radius + cy.get(".t--widget-button2 button").should( + "have.css", + "border-radius", + "24px", + ); //Border Radius cy.get(publish.iconWidgetBtn).should("have.css", "border-radius", "24px"); //Border Radius - cy.get(widgetsPage.widgetBtn) - .eq(0) - .should("have.css", "box-shadow", "none"); //Shadow - cy.get(widgetsPage.widgetBtn) - .eq(1) - .should( - "have.css", - "box-shadow", - "rgba(0, 0, 0, 0.1) 0px 10px 15px -3px, rgba(0, 0, 0, 0.05) 0px 4px 6px -2px", - ); //Since Shadow revert option does not exixts + cy.get(".t--widget-button1 button").should( + "have.css", + "box-shadow", + "none", + ); //Shadow + cy.get(".t--widget-button2 button").should( + "have.css", + "box-shadow", + "rgba(0, 0, 0, 0.1) 0px 10px 15px -3px, rgba(0, 0, 0, 0.05) 0px 4px 6px -2px", + ); //Since Shadow revert option does not exixts cy.get(publish.iconWidgetBtn).should("have.css", "box-shadow", "none"); //Shadow //Verify Share button @@ -1035,17 +1049,17 @@ describe("App Theming funtionality", function() { .eq(13) .then(($elem) => { cy.get($elem).click({ force: true }); - cy.get(widgetsPage.widgetBtn) - .eq(0) - .should( - "have.css", - "background-color", - $elem.css("background-color"), - ); //new widget with its own color + cy.get(".t--widget-button1 button").should( + "have.css", + "background-color", + $elem.css("background-color"), + ); //new widget with its own color - cy.get(widgetsPage.widgetBtn) - .eq(1) - .should("have.css", "background-color", "rgb(239, 68, 68)"); //old widgets still conforming to theme color + cy.get(".t--widget-button2 button").should( + "have.css", + "background-color", + "rgb(239, 68, 68)", + ); //old widgets still conforming to theme color cy.get(widgetsPage.iconWidgetBtn).should( "have.css", "background-color", @@ -1062,21 +1076,21 @@ describe("App Theming funtionality", function() { .eq(0) .invoke("css", "border-top-left-radius") .then((borderRadius) => { - cy.get(widgetsPage.widgetBtn) - .eq(0) - .should( - "have.css", - "border-radius", - borderRadius, //6px - ); + cy.get(".t--widget-button1 button").should( + "have.css", + "border-radius", + borderRadius, //6px + ); cy.get(widgetsPage.iconWidgetBtn).should( "have.css", "border-radius", "24px", ); - cy.get(widgetsPage.widgetBtn) - .eq(1) - .should("have.css", "border-radius", "24px"); + cy.get(".t--widget-button2 button").should( + "have.css", + "border-radius", + "24px", + ); }); //Change Shadow & verify @@ -1086,27 +1100,23 @@ describe("App Theming funtionality", function() { cy.get(".t--button-group-0.1px div") .invoke("css", "box-shadow") .then((boxshadow) => { - cy.get(widgetsPage.widgetBtn) - .eq(0) - .should( - "have.css", - "box-shadow", - boxshadow, //rgba(0, 0, 0, 0.1) 0px 1px 3px 0px, rgba(0, 0, 0, 0.06) 0px 1px 2px 0px - ); + cy.get(".t--widget-button1 button").should( + "have.css", + "box-shadow", + boxshadow, //rgba(0, 0, 0, 0.1) 0px 1px 3px 0px, rgba(0, 0, 0, 0.06) 0px 1px 2px 0px + ); cy.get(widgetsPage.iconWidgetBtn).should( "have.css", "box-shadow", "none", ); - cy.get(widgetsPage.widgetBtn) - .eq(1) - .should( - "have.css", - "box-shadow", - //same value as previous box shadow selection - //since revertion is not possible for box shadow - hence this widget maintains the same value - "rgba(0, 0, 0, 0.1) 0px 10px 15px -3px, rgba(0, 0, 0, 0.05) 0px 4px 6px -2px", - ); + cy.get(".t--widget-button2 button").should( + "have.css", + "box-shadow", + //same value as previous box shadow selection + //since revertion is not possible for box shadow - hence this widget maintains the same value + "rgba(0, 0, 0, 0.1) 0px 10px 15px -3px, rgba(0, 0, 0, 0.05) 0px 4px 6px -2px", + ); }); cy.assertPageSave(); @@ -1115,14 +1125,18 @@ describe("App Theming funtionality", function() { cy.PublishtheApp(); //Verify Background color - cy.get(".t--widget-buttonwidget button") - .eq(0) - .should("have.css", "background-color", "rgb(252, 165, 165)"); //new widget with its own color + cy.get(".t--widget-button1 button").should( + "have.css", + "background-color", + "rgb(252, 165, 165)", + ); //new widget with its own color ////old widgets still conforming to theme color - cy.get(".t--widget-buttonwidget button") - .last() - .should("have.css", "background-color", "rgb(239, 68, 68)"); + cy.get(".t--widget-button2 button").should( + "have.css", + "background-color", + "rgb(239, 68, 68)", + ); cy.get(publish.iconWidgetBtn).should( "have.css", "background-color", @@ -1130,30 +1144,30 @@ describe("App Theming funtionality", function() { ); //Verify Border radius - cy.get(widgetsPage.widgetBtn) - .eq(0) - .should("have.css", "border-radius", "6px"); + cy.get(".t--widget-button1 button").should( + "have.css", + "border-radius", + "6px", + ); cy.get(publish.iconWidgetBtn).should("have.css", "border-radius", "24px"); - cy.get(widgetsPage.widgetBtn) - .last() - .should("have.css", "border-radius", "24px"); + cy.get(".t--widget-button2 button").should( + "have.css", + "border-radius", + "24px", + ); //Verify Box shadow - cy.get(widgetsPage.widgetBtn) - .eq(0) - .should( - "have.css", - "box-shadow", - "rgba(0, 0, 0, 0.1) 0px 1px 3px 0px, rgba(0, 0, 0, 0.06) 0px 1px 2px 0px", - ); + cy.get(".t--widget-button1 button").should( + "have.css", + "box-shadow", + "rgba(0, 0, 0, 0.1) 0px 1px 3px 0px, rgba(0, 0, 0, 0.06) 0px 1px 2px 0px", + ); cy.get(publish.iconWidgetBtn).should("have.css", "box-shadow", "none"); - cy.get(widgetsPage.widgetBtn) - .last() - .should( - "have.css", - "box-shadow", - "rgba(0, 0, 0, 0.1) 0px 10px 15px -3px, rgba(0, 0, 0, 0.05) 0px 4px 6px -2px", - ); + cy.get(".t--widget-button2 button").should( + "have.css", + "box-shadow", + "rgba(0, 0, 0, 0.1) 0px 10px 15px -3px, rgba(0, 0, 0, 0.05) 0px 4px 6px -2px", + ); cy.get(publish.backToEditor) .click({ force: true }) @@ -1169,17 +1183,21 @@ describe("App Theming funtionality", function() { $elem[0].click(); }); - cy.get(widgetsPage.widgetBtn) - .eq(0) - .should("have.css", "background-color", "rgb(239, 68, 68)"); //verify widget reverted to theme color + cy.get(".t--widget-button1 button").should( + "have.css", + "background-color", + "rgb(239, 68, 68)", + ); //verify widget reverted to theme color cy.get(".t--property-control-borderradius .reset-button").then(($elem) => { $elem[0].removeAttribute("display: none"); $elem[0].click(); }); - cy.get(widgetsPage.widgetBtn) - .eq(0) - .should("have.css", "border-radius", "24px"); + cy.get(".t--widget-button1 button").should( + "have.css", + "border-radius", + "24px", + ); //the new widget with reverted styles also conforming to theme cy.PublishtheApp(); @@ -1192,43 +1210,45 @@ describe("App Theming funtionality", function() { "background-color", "rgb(255, 241, 242)", ); //Background Color of canvas - cy.get(widgetsPage.widgetBtn) - .eq(0) - .should("have.css", "background-color", "rgb(239, 68, 68)"); //Widget Color - cy.get(widgetsPage.widgetBtn) - .last() - .should("be.visible"); - cy.get(widgetsPage.widgetBtn) - .last() - .should("have.css", "background-color", "rgb(239, 68, 68)"); //Widget Color + cy.get(".t--widget-button1 button").should( + "have.css", + "background-color", + "rgb(239, 68, 68)", + ); //Widget Color + cy.get(".t--widget-button2 button").should("be.visible"); + cy.get(".t--widget-button2 button").should( + "have.css", + "background-color", + "rgb(239, 68, 68)", + ); //Widget Color cy.get(publish.iconWidgetBtn).should( "have.css", "background-color", "rgb(239, 68, 68)", ); //Widget Color - cy.get(widgetsPage.widgetBtn) - .eq(0) - .should("have.css", "border-radius", "24px"); //Border Radius - cy.get(widgetsPage.widgetBtn) - .last() - .should("have.css", "border-radius", "24px"); //Border Radius + cy.get(".t--widget-button1 button").should( + "have.css", + "border-radius", + "24px", + ); //Border Radius + cy.get(".t--widget-button2 button").should( + "have.css", + "border-radius", + "24px", + ); //Border Radius cy.get(publish.iconWidgetBtn).should("have.css", "border-radius", "24px"); //Border Radius - cy.get(widgetsPage.widgetBtn) - .eq(0) - .should( - "have.css", - "box-shadow", - "rgba(0, 0, 0, 0.1) 0px 1px 3px 0px, rgba(0, 0, 0, 0.06) 0px 1px 2px 0px", - ); //Shadow - cy.get(widgetsPage.widgetBtn) - .last() - .should( - "have.css", - "box-shadow", - "rgba(0, 0, 0, 0.1) 0px 10px 15px -3px, rgba(0, 0, 0, 0.05) 0px 4px 6px -2px", - ); //Since Shadow revert option does not exixts + cy.get(".t--widget-button1 button").should( + "have.css", + "box-shadow", + "rgba(0, 0, 0, 0.1) 0px 1px 3px 0px, rgba(0, 0, 0, 0.06) 0px 1px 2px 0px", + ); //Shadow + cy.get(".t--widget-button2 button").should( + "have.css", + "box-shadow", + "rgba(0, 0, 0, 0.1) 0px 10px 15px -3px, rgba(0, 0, 0, 0.05) 0px 4px 6px -2px", + ); //Since Shadow revert option does not exixts cy.get(publish.iconWidgetBtn).should("have.css", "box-shadow", "none"); //Shadow //Verify Share button From 142d6fe01ba4225079f05054e1d36586f96ca5a7 Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 16 Feb 2023 15:35:06 -0500 Subject: [PATCH 516/708] add widgetName selector --- .../designSystems/appsmith/PositionedContainer.tsx | 6 ++++-- .../appsmith/autoLayout/FlexComponent.tsx | 13 ++++++++++--- app/client/src/widgets/BaseWidget.tsx | 1 + 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/PositionedContainer.tsx b/app/client/src/components/designSystems/appsmith/PositionedContainer.tsx index 1d69eb4fca7c..76ee8302b4cb 100644 --- a/app/client/src/components/designSystems/appsmith/PositionedContainer.tsx +++ b/app/client/src/components/designSystems/appsmith/PositionedContainer.tsx @@ -98,9 +98,11 @@ export function PositionedContainer( const containerClassName = useMemo(() => { return ( generateClassName(props.widgetId) + - ` ${POSITIONED_WIDGET} ${widgetTypeClassname(props.widgetType)}` + ` ${POSITIONED_WIDGET} ${widgetTypeClassname( + props.widgetType, + )} t--widget-${props.widgetName.toLowerCase()}` ); - }, [props.widgetType, props.widgetId]); + }, [props.widgetType, props.widgetId, props.widgetName]); const isDropTarget = checkIsDropTarget(props.widgetType); const { onHoverZIndex, zIndex } = usePositionedContainerZIndex( diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx index 610206587d8d..c07f0fd81d00 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx @@ -29,6 +29,7 @@ export type AutoLayoutProps = { responsiveBehavior?: ResponsiveBehavior; selected?: boolean; widgetId: string; + widgetName: string; widgetType: WidgetType; parentColumnSpace: number; flexVerticalAlignment: FlexVerticalAlignment; @@ -62,9 +63,15 @@ export function FlexComponent(props: AutoLayoutProps) { !isSnipingMode && e.stopPropagation(); }; - const className = `auto-layout-parent-${props.parentId} auto-layout-child-${ - props.widgetId - } ${widgetTypeClassname(props.widgetType)}`; + const className = useMemo( + () => + `auto-layout-parent-${props.parentId} auto-layout-child-${ + props.widgetId + } ${widgetTypeClassname( + props.widgetType, + )} t--widget-${props.widgetName.toLowerCase()}`, + [props.parentId, props.widgetId, props.widgetType, props.widgetName], + ); const isResizing = useSelector(getIsResizing); diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index 12c13bb56dc0..a9ef65015358 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -568,6 +568,7 @@ abstract class BaseWidget< responsiveBehavior={this.props.responsiveBehavior} selected={this.props.selected} widgetId={this.props.widgetId} + widgetName={this.props.widgetName} widgetType={this.props.type} > {content} From be53889460657728c3b38ce41a9a32c949ec42d1 Mon Sep 17 00:00:00 2001 From: rahulramesha Date: Mon, 20 Feb 2023 16:10:21 +0530 Subject: [PATCH 517/708] fix pasting order should retain copied order --- app/client/src/sagas/WidgetOperationSagas.tsx | 66 ++++++- .../src/sagas/WidgetOperationUtils.test.ts | 166 ++++++++++++++++++ app/client/src/sagas/WidgetOperationUtils.ts | 74 +++++++- app/client/src/utils/storage.ts | 6 +- 4 files changed, 303 insertions(+), 9 deletions(-) diff --git a/app/client/src/sagas/WidgetOperationSagas.tsx b/app/client/src/sagas/WidgetOperationSagas.tsx index debb275cfc50..f66075e11195 100644 --- a/app/client/src/sagas/WidgetOperationSagas.tsx +++ b/app/client/src/sagas/WidgetOperationSagas.tsx @@ -130,7 +130,9 @@ import { getCanvasIdForContainer, getContainerIdForCanvas, getDefaultCanvas, + getFlexLayersForSelectedWidgets, getMousePositions, + getNewFlexLayers, getNewPositionsForCopiedWidgets, getNextWidgetName, getOccupiedSpacesFromProps, @@ -155,6 +157,7 @@ import { } from "./WidgetOperationUtils"; import { widgetSelectionSagas } from "./WidgetSelectionSagas"; import { SelectionRequestType } from "./WidgetSelectUtils"; +import { FlexLayer } from "utils/autoLayout/autoLayoutTypes"; export function* resizeSaga(resizeAction: ReduxAction) { try { @@ -837,7 +840,10 @@ function* updateCanvasSize( } } -function* createSelectedWidgetsCopy(selectedWidgets: FlattenedWidgetProps[]) { +function* createSelectedWidgetsCopy( + selectedWidgets: FlattenedWidgetProps[], + flexLayers: FlexLayer[], +) { if (!selectedWidgets || !selectedWidgets.length) return; const widgetListsToStore: { widgetId: string; @@ -846,7 +852,10 @@ function* createSelectedWidgetsCopy(selectedWidgets: FlattenedWidgetProps[]) { }[] = yield all(selectedWidgets.map((each) => call(createWidgetCopy, each))); const saveResult: boolean = yield saveCopiedWidgets( - JSON.stringify(widgetListsToStore), + JSON.stringify({ + widgets: widgetListsToStore, + flexLayers, + }), ); return saveResult; } @@ -885,8 +894,16 @@ function* copyWidgetSaga(action: ReduxAction<{ isShortcut: boolean }>) { } const selectedWidgetProps = selectedWidgets.map((each) => allWidgets[each]); + const canvasId = selectedWidgetProps?.[0]?.parentId || ""; + + const flexLayers: FlexLayer[] = getFlexLayersForSelectedWidgets( + selectedWidgets, + canvasId ? allWidgets[canvasId] : undefined, + ); + const saveResult: boolean = yield createSelectedWidgetsCopy( selectedWidgetProps, + flexLayers, ); selectedWidgetProps.forEach((each) => { @@ -1313,7 +1330,15 @@ function* pasteWidgetSaga( mouseLocation: { x: number; y: number }; }>, ) { - let copiedWidgetGroups: CopiedWidgetGroup[] = yield getCopiedWidgets(); + const { + flexLayers, + widgets: copiedWidgets, + }: { + widgets: CopiedWidgetGroup[]; + flexLayers: FlexLayer[]; + } = yield getCopiedWidgets(); + + let copiedWidgetGroups = [...copiedWidgets]; const shouldGroup: boolean = action.payload.groupWidgets; const newlyCreatedWidgetIds: string[] = []; @@ -1405,6 +1430,8 @@ function* pasteWidgetSaga( if (canvasId) pastingIntoWidgetId = canvasId; } + const widgetIdMap: Record = {}; + yield all( copiedWidgetGroups.map((copiedWidgets) => call(function*() { @@ -1452,7 +1479,6 @@ function* pasteWidgetSaga( // Get a flat list of all the widgets to be updated const widgetList = copiedWidgets.list; - const widgetIdMap: Record = {}; const reverseWidgetIdMap: Record = {}; const widgetNameMap: Record = {}; const newWidgetList: FlattenedWidgetProps[] = []; @@ -1650,7 +1676,13 @@ function* pasteWidgetSaga( */ if (widget.parentId) { const pastingIntoWidget = widgets[widget.parentId]; - if (pastingIntoWidget && isStack(widgets, pastingIntoWidget)) { + if ( + pastingIntoWidget && + isStack(widgets, pastingIntoWidget) && + (pastingIntoWidgetId !== pastingIntoWidget.widgetId || + !flexLayers || + flexLayers.length <= 0) + ) { if (widget.widgetId === widgetIdMap[copiedWidget.widgetId]) widgets = pasteWidgetInFlexLayers( widgets, @@ -1686,6 +1718,22 @@ function* pasteWidgetSaga( ), ); + if ( + pastingIntoWidgetId && + widgets[pastingIntoWidgetId] && + flexLayers && + flexLayers.length > 0 + ) { + const newFlexLayers = getNewFlexLayers(flexLayers, widgetIdMap); + widgets[pastingIntoWidgetId] = { + ...widgets[pastingIntoWidgetId], + flexLayers: [ + ...(widgets[pastingIntoWidgetId]?.flexLayers || []), + ...newFlexLayers, + ], + }; + } + //calculate the new positions of the reflowed widgets const reflowedWidgets = getReflowedPositions( widgets, @@ -1758,8 +1806,16 @@ function* cutWidgetSaga() { const selectedWidgetProps = selectedWidgets.map((each) => allWidgets[each]); + const canvasId = selectedWidgetProps?.[0]?.parentId || ""; + + const flexLayers: FlexLayer[] = getFlexLayersForSelectedWidgets( + selectedWidgets, + canvasId ? allWidgets[canvasId] : undefined, + ); + const saveResult: boolean = yield createSelectedWidgetsCopy( selectedWidgetProps, + flexLayers, ); selectedWidgetProps.forEach((each) => { diff --git a/app/client/src/sagas/WidgetOperationUtils.test.ts b/app/client/src/sagas/WidgetOperationUtils.test.ts index 0e9da9adb60c..8686509e5478 100644 --- a/app/client/src/sagas/WidgetOperationUtils.test.ts +++ b/app/client/src/sagas/WidgetOperationUtils.test.ts @@ -2,6 +2,7 @@ import { OccupiedSpace } from "constants/CanvasEditorConstants"; import { klona } from "klona"; import { get } from "lodash"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; +import { FlexLayerAlignment } from "utils/autoLayout/constants"; import { WidgetProps } from "widgets/BaseWidget"; import { FlattenedWidgetProps } from "widgets/constants"; import { @@ -23,6 +24,8 @@ import { getWidgetsFromIds, getValueFromTree, resizePublishedMainCanvasToLowestWidget, + getFlexLayersForSelectedWidgets, + getNewFlexLayers, } from "./WidgetOperationUtils"; describe("WidgetOperationSaga", () => { @@ -1096,6 +1099,169 @@ describe("WidgetOperationSaga", () => { ]), ).toBe(false); }); + + it("should test getFlexLayersForSelectedWidgets", () => { + const parentCanvas = ({ + flexLayers: [ + { + children: [ + { + id: "1", + align: FlexLayerAlignment.Start, + }, + { + id: "2", + align: FlexLayerAlignment.Start, + }, + { + id: "3", + align: FlexLayerAlignment.Center, + }, + ], + }, + { + children: [ + { + id: "4", + align: FlexLayerAlignment.Start, + }, + ], + }, + { + children: [ + { + id: "5", + align: FlexLayerAlignment.Center, + }, + { + id: "6", + align: FlexLayerAlignment.End, + }, + ], + }, + ], + } as any) as FlattenedWidgetProps; + + const selectedWidgets = ["2", "3", "6"]; + + const selectedFlexLayers = [ + { + children: [ + { + id: "2", + align: FlexLayerAlignment.Start, + }, + { + id: "3", + align: FlexLayerAlignment.Center, + }, + ], + }, + { + children: [ + { + id: "6", + align: FlexLayerAlignment.End, + }, + ], + }, + ]; + + expect( + getFlexLayersForSelectedWidgets(selectedWidgets, parentCanvas), + ).toEqual(selectedFlexLayers); + }); + + it("should test getNewFlexLayers", () => { + const flexLayers = [ + { + children: [ + { + id: "1", + align: FlexLayerAlignment.Start, + }, + { + id: "2", + align: FlexLayerAlignment.Start, + }, + { + id: "3", + align: FlexLayerAlignment.Center, + }, + ], + }, + { + children: [ + { + id: "4", + align: FlexLayerAlignment.Start, + }, + ], + }, + { + children: [ + { + id: "5", + align: FlexLayerAlignment.Center, + }, + { + id: "6", + align: FlexLayerAlignment.End, + }, + ], + }, + ]; + + const widgetIdMap = { + "1": "11", + "2": "22", + "3": "33", + "4": "44", + "5": "55", + "6": "66", + }; + + const newFlexLayers = [ + { + children: [ + { + id: "11", + align: FlexLayerAlignment.Start, + }, + { + id: "22", + align: FlexLayerAlignment.Start, + }, + { + id: "33", + align: FlexLayerAlignment.Center, + }, + ], + }, + { + children: [ + { + id: "44", + align: FlexLayerAlignment.Start, + }, + ], + }, + { + children: [ + { + id: "55", + align: FlexLayerAlignment.Center, + }, + { + id: "66", + align: FlexLayerAlignment.End, + }, + ], + }, + ]; + + expect(getNewFlexLayers(flexLayers, widgetIdMap)).toEqual(newFlexLayers); + }); }); describe("getValueFromTree - ", () => { diff --git a/app/client/src/sagas/WidgetOperationUtils.ts b/app/client/src/sagas/WidgetOperationUtils.ts index d3f6519d8761..2fcc55d2d3cf 100644 --- a/app/client/src/sagas/WidgetOperationUtils.ts +++ b/app/client/src/sagas/WidgetOperationUtils.ts @@ -55,6 +55,7 @@ import { DataTreeWidget } from "entities/DataTree/dataTreeFactory"; import { isWidget } from "@appsmith/workers/Evaluation/evaluationUtils"; import { CANVAS_DEFAULT_MIN_HEIGHT_PX } from "constants/AppConstants"; import { MetaState } from "reducers/entityReducers/metaReducer"; +import { FlexLayer } from "utils/autoLayout/autoLayoutTypes"; export interface CopiedWidgetGroup { widgetId: string; @@ -661,7 +662,9 @@ export function getBoundariesFromSelectedWidgets( */ export const getSelectedWidgetWhenPasting = function*() { const canvasWidgets: CanvasWidgetsReduxState = yield select(getWidgets); - const copiedWidgetGroups: CopiedWidgetGroup[] = yield getCopiedWidgets(); + const { + widgets: copiedWidgetGroups, + }: { widgets: CopiedWidgetGroup[] } = yield getCopiedWidgets(); let selectedWidget: FlattenedWidgetProps | undefined = yield select( getSelectedWidget, @@ -1796,3 +1799,72 @@ const updateListWidgetBindings = ( return widgets; }; + +/** + * This method preserves the flexLayers of the parent canvas, + * but only for the selected widgets + * @param selectedWidgets + * @param parentCanvas + * @returns + */ +export function getFlexLayersForSelectedWidgets( + selectedWidgets: string[], + parentCanvas: FlattenedWidgetProps | undefined, +): FlexLayer[] { + if ( + !parentCanvas || + !parentCanvas.flexLayers || + parentCanvas.flexLayers.length <= 0 + ) + return []; + + const currFlexLayers: FlexLayer[] = parentCanvas.flexLayers; + + const selectedFlexLayers: FlexLayer[] = []; + + for (const flexLayer of currFlexLayers) { + const layerChildren = []; + + for (const layerChild of flexLayer.children) { + if (selectedWidgets.indexOf(layerChild.id) > -1) { + layerChildren.push(layerChild); + } + } + + if (layerChildren.length > 0) { + selectedFlexLayers.push({ children: layerChildren }); + } + } + + return selectedFlexLayers; +} + +/** + * This method helps in Converting the widgetId inside flexLayers + * to the new corresponding widgetIds in widgetIdMap + * @param flexLayers + * @param widgetIdMap + * @returns + */ +export function getNewFlexLayers( + flexLayers: FlexLayer[], + widgetIdMap: Record, +) { + const newFlexLayers: FlexLayer[] = []; + + for (const flexLayer of flexLayers) { + const newChildren = []; + + for (const layerChild of flexLayer.children) { + if (widgetIdMap[layerChild.id]) { + newChildren.push({ + id: widgetIdMap[layerChild.id], + align: layerChild.align, + }); + } + } + newFlexLayers.push({ children: newChildren }); + } + + return newFlexLayers; +} diff --git a/app/client/src/utils/storage.ts b/app/client/src/utils/storage.ts index 55522189dde7..886f70b0cf51 100644 --- a/app/client/src/utils/storage.ts +++ b/app/client/src/utils/storage.ts @@ -88,11 +88,11 @@ export const getReflowOnBoardingFlag = async (email: any) => { export const getCopiedWidgets = async () => { try { - const widget: string | null = await store.getItem( + const copiedWidgetData: string | null = await store.getItem( STORAGE_KEYS.COPIED_WIDGET, ); - if (widget && widget.length > 0) { - return JSON.parse(widget); + if (copiedWidgetData && copiedWidgetData.length > 0) { + return JSON.parse(copiedWidgetData); } } catch (error) { log.error("An error occurred when fetching copied widget: ", error); From e46aa1c3b7e42729033d939361be51f054496ffb Mon Sep 17 00:00:00 2001 From: rahulramesha Date: Mon, 20 Feb 2023 17:56:07 +0530 Subject: [PATCH 518/708] fix modal widget conversion --- app/client/src/utils/DSLConversions/autoToFixedLayout.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/client/src/utils/DSLConversions/autoToFixedLayout.ts b/app/client/src/utils/DSLConversions/autoToFixedLayout.ts index b90136e5a065..be6ebef2305e 100644 --- a/app/client/src/utils/DSLConversions/autoToFixedLayout.ts +++ b/app/client/src/utils/DSLConversions/autoToFixedLayout.ts @@ -298,6 +298,9 @@ function processIndividualLayer( //update alignment layer map by iterating each children within layer for (const child of flexChildren) { const widget = widgets[child.id]; + + if (widget.detachFromLayout) continue; + currChildren.push(child.id); if (child.align === "end") { From 509b16bcc7b4513fd5f16fcd4bb2766df9a643d4 Mon Sep 17 00:00:00 2001 From: rahulramesha Date: Mon, 20 Feb 2023 18:17:32 +0530 Subject: [PATCH 519/708] adjust widget topRow based on vertical alignmnet of the widget in layer --- .../utils/DSLConversions/autoToFixedLayout.ts | 50 +++++++++++++++++-- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/app/client/src/utils/DSLConversions/autoToFixedLayout.ts b/app/client/src/utils/DSLConversions/autoToFixedLayout.ts index be6ebef2305e..409d2bd96377 100644 --- a/app/client/src/utils/DSLConversions/autoToFixedLayout.ts +++ b/app/client/src/utils/DSLConversions/autoToFixedLayout.ts @@ -13,7 +13,11 @@ import { alterLayoutForDesktop, alterLayoutForMobile, } from "utils/autoLayout/AutoLayoutUtils"; -import { FlexLayerAlignment, Positioning } from "utils/autoLayout/constants"; +import { + FlexLayerAlignment, + FlexVerticalAlignment, + Positioning, +} from "utils/autoLayout/constants"; import { getWidgetWidth, getWidgetHeight, @@ -289,6 +293,8 @@ function processIndividualLayer( let currChildren: string[] = []; + let layerHeight = 0; + const alignmentLayerMap: AlignmentLayerMap = { start: { widgets: [], width: 0, type: FlexLayerAlignment.Start }, center: { widgets: [], width: 0, type: FlexLayerAlignment.Center }, @@ -302,6 +308,7 @@ function processIndividualLayer( if (widget.detachFromLayout) continue; currChildren.push(child.id); + layerHeight = Math.max(layerHeight, getWidgetHeight(widget, false)); if (child.align === "end") { alignmentLayerMap.end.widgets.push(widget); @@ -320,6 +327,7 @@ function processIndividualLayer( widgets, alignmentLayerMap, currentBottomRow, + layerHeight, ); currChildren = [...children]; @@ -351,6 +359,7 @@ function placeWidgetsWithoutWrap( widgets: CanvasWidgetsReduxState, alignmentLayerMap: AlignmentLayerMap, currentBottomRow: number, + layerHeight: number, ): { currWidgets: CanvasWidgetsReduxState; nextBottomRow: number; @@ -381,6 +390,7 @@ function placeWidgetsWithoutWrap( width, type, currentBottomRow, + layerHeight, ); currWidgets = { ...convertedWidgets }; children = [...children, ...currChildren]; @@ -409,6 +419,7 @@ function calculateWidgetDimensionForAlignment( alignedWidth: number, flexLayerAlignment: FlexLayerAlignment, currentBottomRow: number, + layerHeight: number, ): { convertedWidgets: CanvasWidgetsReduxState; children: string[]; @@ -451,12 +462,18 @@ function calculateWidgetDimensionForAlignment( currentAvailableLeftColumn + width, GridDefaults.DEFAULT_GRID_COLUMNS, ); + const topRow = getTopRowFromVerticalAlignment( + currentBottomRow, + widget.flexVerticalAlignment, + layerHeight, + getWidgetHeight(widget, false), + ); //update positions currWidgets[widget.widgetId] = { ...widget, - topRow: currentBottomRow, - bottomRow: currentBottomRow + height, + topRow, + bottomRow: topRow + height, leftColumn: leftColumn, rightColumn: rightColumn, }; @@ -500,3 +517,30 @@ function getCalculatedLeftColumn( return totalWidth - width; } } + +/** + * Get topRow based on vertical alignment of widget + * @param currentBottomRow + * @param flexVerticalAlignment + * @param layerHeight + * @param widgetHeight + * @returns + */ +function getTopRowFromVerticalAlignment( + currentBottomRow: number, + flexVerticalAlignment: FlexVerticalAlignment | undefined, + layerHeight: number, + widgetHeight: number, +): number { + if (!flexVerticalAlignment || !layerHeight || layerHeight <= widgetHeight) { + return currentBottomRow; + } + + if (flexVerticalAlignment === FlexVerticalAlignment.Center) { + return Math.floor(currentBottomRow + (layerHeight - widgetHeight) / 2); + } else if (flexVerticalAlignment === FlexVerticalAlignment.Bottom) { + return currentBottomRow + layerHeight - widgetHeight; + } else { + return currentBottomRow; + } +} From 6d83f67165a29325712cab375992f4289284aa77 Mon Sep 17 00:00:00 2001 From: Arsalan Yaldram Date: Tue, 21 Feb 2023 12:48:59 +0530 Subject: [PATCH 520/708] feat: responsive styles for slider widgets. (#19979) --- app/client/src/utils/layoutPropertiesUtils.ts | 6 ++++ .../src/widgets/CategorySliderWidget/index.ts | 31 +++++++++++++------ .../widget/propertyConfig/contentConfig.ts | 2 ++ .../widget/propertyConfig/styleConfig.ts | 2 ++ .../src/widgets/NumberSliderWidget/index.ts | 19 ++++++++++-- .../widget/propertyConfig/contentConfig.ts | 3 ++ .../widget/propertyConfig/styleConfig.ts | 2 ++ .../src/widgets/RangeSliderWidget/index.ts | 19 ++++++++++-- .../widget/propertyConfig/contentConfig.ts | 2 ++ .../widget/propertyConfig/styleConfig.ts | 2 ++ 10 files changed, 75 insertions(+), 13 deletions(-) diff --git a/app/client/src/utils/layoutPropertiesUtils.ts b/app/client/src/utils/layoutPropertiesUtils.ts index 97aef177a3d7..59589a5a72a5 100644 --- a/app/client/src/utils/layoutPropertiesUtils.ts +++ b/app/client/src/utils/layoutPropertiesUtils.ts @@ -246,6 +246,9 @@ export const NonResizableWidgets = [ "SWITCH_WIDGET", "TEXT_WIDGET", "SINGLE_SELECT_TREE_WIDGET", + "CATEGORY_SLIDER_WIDGET", + "RANGE_SLIDER_WIDGET", + "NUMBER_SLIDER_WIDGET", ]; export const DefaultFillWidgets = [ @@ -280,6 +283,9 @@ export const DefaultFillWidgets = [ "TABLE_WIDGET_V2", "PROGRESS_WIDGET", "SWITCH_WIDGET", + "CATEGORY_SLIDER_WIDGET", + "RANGE_SLIDER_WIDGET", + "NUMBER_SLIDER_WIDGET", ]; export function getDefaultResponsiveBehavior(widgetType: string) { diff --git a/app/client/src/widgets/CategorySliderWidget/index.ts b/app/client/src/widgets/CategorySliderWidget/index.ts index 55260a0e5149..bee81cd7c8c9 100644 --- a/app/client/src/widgets/CategorySliderWidget/index.ts +++ b/app/client/src/widgets/CategorySliderWidget/index.ts @@ -1,9 +1,10 @@ -import { LabelPosition } from "components/constants"; import { Alignment } from "@blueprintjs/core"; +import { LabelPosition } from "components/constants"; +import { getDefaultResponsiveBehavior } from "utils/layoutPropertiesUtils"; + import IconSVG from "./icon.svg"; import Widget from "./widget"; -import { ResponsiveBehavior } from "utils/autoLayout/constants"; export const CONFIG = { type: Widget.getWidgetType(), @@ -13,11 +14,11 @@ export const CONFIG = { iconSVG: IconSVG, defaults: { options: [ - { label: "Extra Small", value: "xs" }, - { label: "Small", value: "sm" }, - { label: "Medium", value: "md" }, - { label: "Large", value: "lg" }, - { label: "Extra Large", value: "xl" }, + { label: "xs", value: "xs" }, + { label: "sm", value: "sm" }, + { label: "md", value: "md" }, + { label: "lg", value: "lg" }, + { label: "xl", value: "xl" }, ], defaultOptionValue: "md", isVisible: true, @@ -31,12 +32,12 @@ export const CONFIG = { version: 1, animateLoading: true, labelText: "Size", - labelPosition: LabelPosition.Left, + labelPosition: LabelPosition.Top, labelAlignment: Alignment.LEFT, labelWidth: 5, labelTextSize: "0.875rem", sliderSize: "m", - responsiveBehavior: ResponsiveBehavior.Fill, + responsiveBehavior: getDefaultResponsiveBehavior(Widget.getWidgetType()), }, properties: { derived: Widget.getDerivedPropertiesMap(), @@ -46,6 +47,18 @@ export const CONFIG = { styleConfig: Widget.getPropertyPaneStyleConfig(), stylesheetConfig: Widget.getStylesheetConfig(), }, + autoLayout: { + widgetSize: [ + { + viewportMinWidth: 0, + configuration: () => { + return { + minWidth: "150px", + }; + }, + }, + ], + }, }; export default Widget; diff --git a/app/client/src/widgets/CategorySliderWidget/widget/propertyConfig/contentConfig.ts b/app/client/src/widgets/CategorySliderWidget/widget/propertyConfig/contentConfig.ts index 42a7ccb4ebb1..850d3014ccba 100644 --- a/app/client/src/widgets/CategorySliderWidget/widget/propertyConfig/contentConfig.ts +++ b/app/client/src/widgets/CategorySliderWidget/widget/propertyConfig/contentConfig.ts @@ -2,6 +2,7 @@ import { Alignment } from "@blueprintjs/core"; import { LabelPosition } from "components/constants"; import { ValidationTypes } from "constants/WidgetValidation"; import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; +import { isAutoLayout } from "selectors/mainCanvasSelectors"; import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService"; import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import { CategorySliderWidgetProps } from ".."; @@ -81,6 +82,7 @@ export default [ label: "Position", controlType: "ICON_TABS", fullWidth: true, + hidden: isAutoLayout, options: [ { label: "Left", value: LabelPosition.Left }, { label: "Top", value: LabelPosition.Top }, diff --git a/app/client/src/widgets/CategorySliderWidget/widget/propertyConfig/styleConfig.ts b/app/client/src/widgets/CategorySliderWidget/widget/propertyConfig/styleConfig.ts index 634586e40b31..8410e4f26475 100644 --- a/app/client/src/widgets/CategorySliderWidget/widget/propertyConfig/styleConfig.ts +++ b/app/client/src/widgets/CategorySliderWidget/widget/propertyConfig/styleConfig.ts @@ -1,4 +1,5 @@ import { ValidationTypes } from "constants/WidgetValidation"; +import { isAutoLayout } from "selectors/mainCanvasSelectors"; export default [ { @@ -53,6 +54,7 @@ export default [ helpText: "Control the font size of the label associated", controlType: "DROP_DOWN", defaultValue: "0.875rem", + hidden: isAutoLayout, options: [ { label: "S", diff --git a/app/client/src/widgets/NumberSliderWidget/index.ts b/app/client/src/widgets/NumberSliderWidget/index.ts index d7a9be5aa153..aed9a980e987 100644 --- a/app/client/src/widgets/NumberSliderWidget/index.ts +++ b/app/client/src/widgets/NumberSliderWidget/index.ts @@ -1,6 +1,8 @@ -import { LabelPosition } from "components/constants"; import { Alignment } from "@blueprintjs/core"; +import { LabelPosition } from "components/constants"; +import { getDefaultResponsiveBehavior } from "utils/layoutPropertiesUtils"; + import IconSVG from "./icon.svg"; import Widget from "./widget"; @@ -32,11 +34,12 @@ export const CONFIG = { version: 1, animateLoading: true, labelText: "Percentage", - labelPosition: LabelPosition.Left, + labelPosition: LabelPosition.Top, labelAlignment: Alignment.LEFT, labelWidth: 8, labelTextSize: "0.875rem", sliderSize: "m", + responsiveBehavior: getDefaultResponsiveBehavior(Widget.getWidgetType()), }, properties: { derived: Widget.getDerivedPropertiesMap(), @@ -46,6 +49,18 @@ export const CONFIG = { styleConfig: Widget.getPropertyPaneStyleConfig(), stylesheetConfig: Widget.getStylesheetConfig(), }, + autoLayout: { + widgetSize: [ + { + viewportMinWidth: 0, + configuration: () => { + return { + minWidth: "150px", + }; + }, + }, + ], + }, }; export default Widget; diff --git a/app/client/src/widgets/NumberSliderWidget/widget/propertyConfig/contentConfig.ts b/app/client/src/widgets/NumberSliderWidget/widget/propertyConfig/contentConfig.ts index f363b9cec673..b5b92eb6e065 100644 --- a/app/client/src/widgets/NumberSliderWidget/widget/propertyConfig/contentConfig.ts +++ b/app/client/src/widgets/NumberSliderWidget/widget/propertyConfig/contentConfig.ts @@ -1,6 +1,8 @@ import { Alignment } from "@blueprintjs/core"; + import { LabelPosition } from "components/constants"; import { ValidationTypes } from "constants/WidgetValidation"; +import { isAutoLayout } from "selectors/mainCanvasSelectors"; import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService"; import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import { NumberSliderWidgetProps } from ".."; @@ -116,6 +118,7 @@ export default [ label: "Position", controlType: "ICON_TABS", fullWidth: true, + hidden: isAutoLayout, options: [ { label: "Left", value: LabelPosition.Left }, { label: "Top", value: LabelPosition.Top }, diff --git a/app/client/src/widgets/NumberSliderWidget/widget/propertyConfig/styleConfig.ts b/app/client/src/widgets/NumberSliderWidget/widget/propertyConfig/styleConfig.ts index 634586e40b31..8410e4f26475 100644 --- a/app/client/src/widgets/NumberSliderWidget/widget/propertyConfig/styleConfig.ts +++ b/app/client/src/widgets/NumberSliderWidget/widget/propertyConfig/styleConfig.ts @@ -1,4 +1,5 @@ import { ValidationTypes } from "constants/WidgetValidation"; +import { isAutoLayout } from "selectors/mainCanvasSelectors"; export default [ { @@ -53,6 +54,7 @@ export default [ helpText: "Control the font size of the label associated", controlType: "DROP_DOWN", defaultValue: "0.875rem", + hidden: isAutoLayout, options: [ { label: "S", diff --git a/app/client/src/widgets/RangeSliderWidget/index.ts b/app/client/src/widgets/RangeSliderWidget/index.ts index 494c14cf056a..94c28ea7a7cd 100644 --- a/app/client/src/widgets/RangeSliderWidget/index.ts +++ b/app/client/src/widgets/RangeSliderWidget/index.ts @@ -1,6 +1,8 @@ -import { LabelPosition } from "components/constants"; import { Alignment } from "@blueprintjs/core"; +import { LabelPosition } from "components/constants"; +import { getDefaultResponsiveBehavior } from "utils/layoutPropertiesUtils"; + import IconSVG from "./icon.svg"; import Widget from "./widget"; @@ -26,7 +28,7 @@ export const CONFIG = { isDisabled: false, tooltipAlwaysOn: false, labelText: "Percentage", - labelPosition: LabelPosition.Left, + labelPosition: LabelPosition.Top, labelAlignment: Alignment.LEFT, labelWidth: 8, labelTextSize: "0.875rem", @@ -38,6 +40,7 @@ export const CONFIG = { version: 1, animateLoading: true, sliderSize: "m", + responsiveBehavior: getDefaultResponsiveBehavior(Widget.getWidgetType()), }, properties: { derived: Widget.getDerivedPropertiesMap(), @@ -47,6 +50,18 @@ export const CONFIG = { styleConfig: Widget.getPropertyPaneStyleConfig(), stylesheetConfig: Widget.getStylesheetConfig(), }, + autoLayout: { + widgetSize: [ + { + viewportMinWidth: 0, + configuration: () => { + return { + minWidth: "150px", + }; + }, + }, + ], + }, }; export default Widget; diff --git a/app/client/src/widgets/RangeSliderWidget/widget/propertyConfig/contentConfig.ts b/app/client/src/widgets/RangeSliderWidget/widget/propertyConfig/contentConfig.ts index 1f7d9ed7add5..348fbcedc528 100644 --- a/app/client/src/widgets/RangeSliderWidget/widget/propertyConfig/contentConfig.ts +++ b/app/client/src/widgets/RangeSliderWidget/widget/propertyConfig/contentConfig.ts @@ -1,6 +1,7 @@ import { Alignment } from "@blueprintjs/core"; import { LabelPosition } from "components/constants"; import { ValidationTypes } from "constants/WidgetValidation"; +import { isAutoLayout } from "selectors/mainCanvasSelectors"; import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService"; import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import { RangeSliderWidgetProps } from ".."; @@ -158,6 +159,7 @@ export default [ label: "Position", controlType: "ICON_TABS", fullWidth: true, + hidden: isAutoLayout, options: [ { label: "Left", value: LabelPosition.Left }, { label: "Top", value: LabelPosition.Top }, diff --git a/app/client/src/widgets/RangeSliderWidget/widget/propertyConfig/styleConfig.ts b/app/client/src/widgets/RangeSliderWidget/widget/propertyConfig/styleConfig.ts index 551f00845689..2990ec5c0467 100644 --- a/app/client/src/widgets/RangeSliderWidget/widget/propertyConfig/styleConfig.ts +++ b/app/client/src/widgets/RangeSliderWidget/widget/propertyConfig/styleConfig.ts @@ -1,4 +1,5 @@ import { ValidationTypes } from "constants/WidgetValidation"; +import { isAutoLayout } from "selectors/mainCanvasSelectors"; export default [ { @@ -53,6 +54,7 @@ export default [ helpText: "Control the font size of the label associated", controlType: "DROP_DOWN", defaultValue: "0.875rem", + hidden: isAutoLayout, options: [ { label: "S", From 2a8916e842e5ecd20c641ba809a841a76f296021 Mon Sep 17 00:00:00 2001 From: Arsalan Yaldram Date: Tue, 21 Feb 2023 13:13:36 +0530 Subject: [PATCH 521/708] feat: responsive styles for rating widget (#19921) --- .../src/widgets/ButtonWidget/component/index.tsx | 8 ++------ .../widgets/IconButtonWidget/component/index.tsx | 2 -- .../widgets/MenuButtonWidget/component/index.tsx | 10 +++++----- .../src/widgets/RateWidget/component/index.tsx | 8 ++++++++ app/client/src/widgets/RateWidget/index.ts | 14 +++++++++++++- app/client/src/widgets/RateWidget/widget/index.tsx | 3 +++ 6 files changed, 31 insertions(+), 14 deletions(-) diff --git a/app/client/src/widgets/ButtonWidget/component/index.tsx b/app/client/src/widgets/ButtonWidget/component/index.tsx index be1e21b8f645..cd4abf4ba949 100644 --- a/app/client/src/widgets/ButtonWidget/component/index.tsx +++ b/app/client/src/widgets/ButtonWidget/component/index.tsx @@ -73,7 +73,6 @@ const TooltipStyles = createGlobalStyle` `; const buttonBaseStyle = css` -height: 100%; background-image: none !important; font-weight: ${(props) => props.theme.fontWeights[2]}; outline: none; @@ -121,13 +120,10 @@ ${({ buttonColor, buttonVariant, theme }) => ` } & > span { - max-height: 100%; - max-width: 99%; + display: inline-block; text-overflow: ellipsis; overflow: hidden; - display: -webkit-box; - -webkit-line-clamp: 1; - -webkit-box-orient: vertical; + white-space: nowrap; line-height: normal; color: ${ diff --git a/app/client/src/widgets/IconButtonWidget/component/index.tsx b/app/client/src/widgets/IconButtonWidget/component/index.tsx index f8e2a7208151..e335d3ff1cd2 100644 --- a/app/client/src/widgets/IconButtonWidget/component/index.tsx +++ b/app/client/src/widgets/IconButtonWidget/component/index.tsx @@ -135,8 +135,6 @@ export const StyledButton = styled((props) => ( line-height: ${({ compactMode }) => compactMode === "SHORT" ? "24px" : "28px"}; - - ${({ buttonColor, buttonVariant, compactMode, hasOnClickAction, theme }) => ` &:enabled { background: ${ diff --git a/app/client/src/widgets/MenuButtonWidget/component/index.tsx b/app/client/src/widgets/MenuButtonWidget/component/index.tsx index e1a434607200..4e6fb64b9642 100644 --- a/app/client/src/widgets/MenuButtonWidget/component/index.tsx +++ b/app/client/src/widgets/MenuButtonWidget/component/index.tsx @@ -148,14 +148,14 @@ const BaseButton = styled(Button)` ? `1px solid ${theme.colors.button.primary.secondary.borderColor}` : "none" } !important; + & > span { + display: inline-block; text-overflow: ellipsis; - display: -webkit-box; - -webkit-line-clamp: 1; - -webkit-box-orient: vertical; - - max-height: 100%; overflow: hidden; + white-space: nowrap; + line-height: normal; + color: ${ buttonVariant === ButtonVariantTypes.PRIMARY ? getComplementaryGrayscaleColor(buttonColor) diff --git a/app/client/src/widgets/RateWidget/component/index.tsx b/app/client/src/widgets/RateWidget/component/index.tsx index efecc219c503..c93ec643d8db 100644 --- a/app/client/src/widgets/RateWidget/component/index.tsx +++ b/app/client/src/widgets/RateWidget/component/index.tsx @@ -30,6 +30,14 @@ export const RateContainer = styled.div` align-content: flex-start; overflow: auto; + .auto-layout && { + overflow: unset; + + > span { + flex-wrap: nowrap; + } + } + > span { display: flex !important; flex-wrap: wrap; diff --git a/app/client/src/widgets/RateWidget/index.ts b/app/client/src/widgets/RateWidget/index.ts index 603505b344b1..8ef74571aa18 100644 --- a/app/client/src/widgets/RateWidget/index.ts +++ b/app/client/src/widgets/RateWidget/index.ts @@ -1,6 +1,6 @@ import { Colors } from "constants/Colors"; import IconSVG from "./icon.svg"; -import Widget from "./widget"; +import Widget, { RateWidgetProps } from "./widget"; export const CONFIG = { features: { @@ -30,6 +30,18 @@ export const CONFIG = { tooltips: ["Terrible", "Bad", "Neutral", "Good", "Great"], widgetName: "Rating", }, + // A sample widgetSize configuration for AutoLayout + widgetSize: [ + { + viewportMinWidth: 0, + configuration: (props: RateWidgetProps) => { + return { + // 20 is the size of a star, 5 is the margin between stars, 8 is the total padding of the widget + minWidth: `${props.maxCount * 21 + (props.maxCount + 1) * 5 + 8}px`, + }; + }, + }, + ], properties: { derived: Widget.getDerivedPropertiesMap(), default: Widget.getDefaultPropertiesMap(), diff --git a/app/client/src/widgets/RateWidget/widget/index.tsx b/app/client/src/widgets/RateWidget/widget/index.tsx index 8a8dabc2a7a9..7c9d7cccf0ed 100644 --- a/app/client/src/widgets/RateWidget/widget/index.tsx +++ b/app/client/src/widgets/RateWidget/widget/index.tsx @@ -10,6 +10,7 @@ import { Stylesheet } from "entities/AppTheming"; import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService"; import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; +import { isAutoLayout } from "selectors/mainCanvasSelectors"; function validateDefaultRate(value: unknown, props: any, _: any) { try { @@ -82,6 +83,7 @@ class RateWidget extends BaseWidget { placeholderText: "5", isBindProperty: true, isTriggerProperty: false, + hidden: isAutoLayout, validation: { type: ValidationTypes.NUMBER, params: { natural: true }, @@ -208,6 +210,7 @@ class RateWidget extends BaseWidget { helpText: "Controls the size of the stars in the widget", controlType: "ICON_TABS", fullWidth: true, + hidden: isAutoLayout, options: [ { label: "Small", From 08c972cb381b0c42d4a65bc2fa518138d1c9ef27 Mon Sep 17 00:00:00 2001 From: Arsalan Yaldram Date: Tue, 21 Feb 2023 13:14:45 +0530 Subject: [PATCH 522/708] feat: responsive config for map & map-chart (#20682) --- app/client/src/utils/layoutPropertiesUtils.ts | 1 + app/client/src/widgets/MapChartWidget/index.ts | 18 ++++++++++++++++++ app/client/src/widgets/MapWidget/index.ts | 14 ++++++++++++++ 3 files changed, 33 insertions(+) diff --git a/app/client/src/utils/layoutPropertiesUtils.ts b/app/client/src/utils/layoutPropertiesUtils.ts index 59589a5a72a5..452558cd709f 100644 --- a/app/client/src/utils/layoutPropertiesUtils.ts +++ b/app/client/src/utils/layoutPropertiesUtils.ts @@ -270,6 +270,7 @@ export const DefaultFillWidgets = [ "JSON_FORM_WIDGET", "LIST_WIDGET", "MAP_WIDGET", + "MAP_CHART_WIDGET", "MULTI_SELECT_TREE_WIDGET", "MULTI_SELECT_WIDGET", "MULTI_SELECT_WIDGET_V2", diff --git a/app/client/src/widgets/MapChartWidget/index.ts b/app/client/src/widgets/MapChartWidget/index.ts index db634284977c..4ad9c60e86a7 100644 --- a/app/client/src/widgets/MapChartWidget/index.ts +++ b/app/client/src/widgets/MapChartWidget/index.ts @@ -1,3 +1,6 @@ +import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; +import { getDefaultResponsiveBehavior } from "utils/layoutPropertiesUtils"; + import { dataSetForWorld, MapTypes } from "./constants"; import IconSVG from "./icon.svg"; import Widget from "./widget"; @@ -35,6 +38,8 @@ export const CONFIG = { code: "#E65100", }, ], + responsiveBehavior: getDefaultResponsiveBehavior(Widget.getWidgetType()), + minWidth: FILL_WIDGET_MIN_WIDTH, }, properties: { derived: Widget.getDerivedPropertiesMap(), @@ -45,6 +50,19 @@ export const CONFIG = { styleConfig: Widget.getPropertyPaneStyleConfig(), stylesheetConfig: Widget.getStylesheetConfig(), }, + autoLayout: { + widgetSize: [ + { + viewportMinWidth: 0, + configuration: () => { + return { + minWidth: "400px", + minHeight: "300px", + }; + }, + }, + ], + }, }; export default Widget; diff --git a/app/client/src/widgets/MapWidget/index.ts b/app/client/src/widgets/MapWidget/index.ts index 5469b6f6cf75..992c3ee1c5f1 100644 --- a/app/client/src/widgets/MapWidget/index.ts +++ b/app/client/src/widgets/MapWidget/index.ts @@ -1,5 +1,6 @@ import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; import { getDefaultResponsiveBehavior } from "utils/layoutPropertiesUtils"; + import IconSVG from "./icon.svg"; import Widget from "./widget"; @@ -35,6 +36,19 @@ export const CONFIG = { styleConfig: Widget.getPropertyPaneStyleConfig(), stylesheetConfig: Widget.getStylesheetConfig(), }, + autoLayout: { + widgetSize: [ + { + viewportMinWidth: 0, + configuration: () => { + return { + minWidth: "400px", + minHeight: "300px", + }; + }, + }, + ], + }, }; export default Widget; From edff46fe308bf7df3f71ea12d39101d78927efc9 Mon Sep 17 00:00:00 2001 From: Arsalan Yaldram Date: Tue, 21 Feb 2023 13:15:28 +0530 Subject: [PATCH 523/708] feat: responsive config for group widgets (#20683) --- .../src/widgets/CheckboxGroupWidget/index.ts | 17 +++++++++++++---- .../src/widgets/RadioGroupWidget/index.ts | 17 +++++++++++++++-- .../src/widgets/SwitchGroupWidget/index.ts | 13 +++++++++++++ 3 files changed, 41 insertions(+), 6 deletions(-) diff --git a/app/client/src/widgets/CheckboxGroupWidget/index.ts b/app/client/src/widgets/CheckboxGroupWidget/index.ts index 9c6cad1bf981..5203170a56c2 100644 --- a/app/client/src/widgets/CheckboxGroupWidget/index.ts +++ b/app/client/src/widgets/CheckboxGroupWidget/index.ts @@ -1,7 +1,6 @@ import { Alignment } from "@blueprintjs/core"; import { LabelPosition } from "components/constants"; -import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; -import { getDefaultResponsiveBehavior } from "utils/layoutPropertiesUtils"; + import IconSVG from "./icon.svg"; import Widget from "./widget"; @@ -37,8 +36,6 @@ export const CONFIG = { labelWidth: 5, widgetName: "CheckboxGroup", version: 2, - responsiveBehavior: getDefaultResponsiveBehavior(Widget.getWidgetType()), - minWidth: FILL_WIDGET_MIN_WIDTH, }, properties: { derived: Widget.getDerivedPropertiesMap(), @@ -49,6 +46,18 @@ export const CONFIG = { styleConfig: Widget.getPropertyPaneStyleConfig(), stylesheetConfig: Widget.getStylesheetConfig(), }, + autoLayout: { + widgetSize: [ + { + viewportMinWidth: 0, + configuration: () => { + return { + minWidth: "240px", + }; + }, + }, + ], + }, }; export default Widget; diff --git a/app/client/src/widgets/RadioGroupWidget/index.ts b/app/client/src/widgets/RadioGroupWidget/index.ts index 865fbc5abaf5..2f73ba63444a 100644 --- a/app/client/src/widgets/RadioGroupWidget/index.ts +++ b/app/client/src/widgets/RadioGroupWidget/index.ts @@ -1,8 +1,9 @@ -import IconSVG from "./icon.svg"; import { Alignment } from "@blueprintjs/core"; -import Widget from "./widget"; import { LabelPosition } from "components/constants"; +import IconSVG from "./icon.svg"; +import Widget from "./widget"; + export const CONFIG = { type: Widget.getWidgetType(), name: "Radio Group", @@ -45,6 +46,18 @@ export const CONFIG = { styleConfig: Widget.getPropertyPaneStyleConfig(), stylesheetConfig: Widget.getStylesheetConfig(), }, + autoLayout: { + widgetSize: [ + { + viewportMinWidth: 0, + configuration: () => { + return { + minWidth: "240px", + }; + }, + }, + ], + }, }; export default Widget; diff --git a/app/client/src/widgets/SwitchGroupWidget/index.ts b/app/client/src/widgets/SwitchGroupWidget/index.ts index c57a97ddea01..e182e2b905bf 100644 --- a/app/client/src/widgets/SwitchGroupWidget/index.ts +++ b/app/client/src/widgets/SwitchGroupWidget/index.ts @@ -1,5 +1,6 @@ import { Alignment } from "@blueprintjs/core"; import { LabelPosition } from "components/constants"; + import IconSVG from "./icon.svg"; import Widget from "./widget"; @@ -47,6 +48,18 @@ export const CONFIG = { styleConfig: Widget.getPropertyPaneStyleConfig(), stylesheetConfig: Widget.getStylesheetConfig(), }, + autoLayout: { + widgetSize: [ + { + viewportMinWidth: 0, + configuration: () => { + return { + minWidth: "240px", + }; + }, + }, + ], + }, }; export default Widget; From 8b083b772dc3d2fab78f2c911f2b0e6ae16ed13a Mon Sep 17 00:00:00 2001 From: Arsalan Yaldram Date: Tue, 21 Feb 2023 14:33:11 +0530 Subject: [PATCH 524/708] feat: responsive styles for button widget (#19892) --- app/client/src/actions/autoLayoutActions.ts | 15 ++ .../src/ce/constants/ReduxActionConstants.tsx | 3 + .../AutoLayoutDimensionObeserver.tsx | 88 +++++++ .../EditorContextProvider.tsx | 8 + .../src/sagas/AutoLayoutUpdateSagas.tsx | 226 ++++++++++++++++-- .../src/utils/autoLayout/AutoLayoutUtils.ts | 63 +++++ .../src/utils/autoLayout/flexWidgetUtils.ts | 69 +++++- .../src/utils/autoLayout/positionUtils.ts | 5 +- app/client/src/utils/layoutPropertiesUtils.ts | 2 +- app/client/src/widgets/BaseWidget.tsx | 23 ++ .../ButtonWidget/component/DragContainer.tsx | 9 +- app/client/src/widgets/ButtonWidget/index.ts | 13 + .../src/widgets/FilePickerWidgetV2/index.ts | 17 +- .../src/widgets/IconButtonWidget/index.ts | 16 +- .../src/widgets/MenuButtonWidget/index.ts | 13 + 15 files changed, 533 insertions(+), 37 deletions(-) create mode 100644 app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutDimensionObeserver.tsx diff --git a/app/client/src/actions/autoLayoutActions.ts b/app/client/src/actions/autoLayoutActions.ts index f90c11313b61..396a603369c6 100644 --- a/app/client/src/actions/autoLayoutActions.ts +++ b/app/client/src/actions/autoLayoutActions.ts @@ -19,3 +19,18 @@ export const widgetViolatedMinDimentionsAction = (parentId: string) => ({ parentId, }, }); + +export function updateWidgetDimensionAction( + widgetId: string, + width: number, + height: number, +) { + return { + type: ReduxActionTypes.UPDATE_WIDGET_DIMENSIONS, + payload: { + widgetId, + width, + height, + }, + }; +} diff --git a/app/client/src/ce/constants/ReduxActionConstants.tsx b/app/client/src/ce/constants/ReduxActionConstants.tsx index e13fd209ae98..a4347e7b9aa6 100644 --- a/app/client/src/ce/constants/ReduxActionConstants.tsx +++ b/app/client/src/ce/constants/ReduxActionConstants.tsx @@ -762,6 +762,9 @@ export const ReduxActionTypes = { AUTOLAYOUT_ADD_NEW_WIDGETS: "AUTOLAYOUT_ADD_NEW_WIDGETS", RECALCULATE_COLUMNS: "RECALCULATE_COLUMNS", WIDGET_VIOLATED_MIN_DIMENSIONS: "WIDGET_VIOLATED_MIN_DIMENSIONS", + UPDATE_WIDGET_DIMENSIONS: "UPDATE_WIDGET_DIMENSIONS", + PROCESS_AUTO_LAYOUT_DIMENSION_UPDATES: + "PROCESS_AUTO_LAYOUT_DIMENSION_UPDATES", }; export type ReduxActionType = typeof ReduxActionTypes[keyof typeof ReduxActionTypes]; diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutDimensionObeserver.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutDimensionObeserver.tsx new file mode 100644 index 000000000000..a657da1c9c5d --- /dev/null +++ b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutDimensionObeserver.tsx @@ -0,0 +1,88 @@ +import { FLEXBOX_PADDING } from "constants/WidgetConstants"; +import React, { useState } from "react"; +import { PropsWithChildren, useEffect, useRef } from "react"; +import styled from "styled-components"; +import { ResponsiveBehavior } from "utils/autoLayout/constants"; +import { WidgetProps } from "widgets/BaseWidget"; + +const SimpleContainer = styled.div` + width: fit-content; + &.fill-widget { + width: 100%; + } +`; + +interface AutoLayoutDimensionObserverProps { + onDimensionUpdate: (width: number, height: number) => void; + widgetProps: WidgetProps; +} + +export default function AutoLayoutDimensionObserver( + props: PropsWithChildren, +) { + const { onDimensionUpdate } = props; + const [currentDimension, setCurrentDimension] = useState({ + width: 0, + height: 0, + }); + + const ref = useRef(null); + + const observer = useRef( + new ResizeObserver((entries) => { + const width = entries[0].contentRect.width; + const height = entries[0].contentRect.height; + if (width === 0 || height === 0) return; + setCurrentDimension({ width, height }); + onDimensionUpdate(width, height); + }), + ); + + const boundingBoxWidth = + (props.widgetProps.rightColumn - props.widgetProps.leftColumn) * + props.widgetProps.parentColumnSpace - + 2 * FLEXBOX_PADDING; + + useEffect(() => { + const diff = Math.abs(boundingBoxWidth - currentDimension.width); + console.log( + "##### width", + props.widgetProps.widgetName, + boundingBoxWidth, + currentDimension.width, + boundingBoxWidth - currentDimension.width, + ); + if (currentDimension.width === 0 || currentDimension.height === 0) return; + if (diff > 2) + onDimensionUpdate(currentDimension.width, currentDimension.height); + }, [boundingBoxWidth, currentDimension.width]); + + console.log( + "##### AutoLayoutDimensionObserver", + props.widgetProps.widgetName, + props.widgetProps.rightColumn, + ); + + useEffect(() => { + if (ref.current) { + observer.current.observe(ref.current); + } + + return () => { + observer.current.disconnect(); + }; + }, []); + + return ( + + {props.children} + + ); +} diff --git a/app/client/src/components/editorComponents/EditorContextProvider.tsx b/app/client/src/components/editorComponents/EditorContextProvider.tsx index d5cfe85a1271..e037a8716764 100644 --- a/app/client/src/components/editorComponents/EditorContextProvider.tsx +++ b/app/client/src/components/editorComponents/EditorContextProvider.tsx @@ -48,6 +48,7 @@ import { selectWidgetInitAction, WidgetSelectionRequest, } from "actions/widgetSelectionActions"; +import { updateWidgetDimensionAction } from "actions/autoLayoutActions"; export type EditorContextType = { executeAction?: (triggerPayload: ExecuteTriggerPayload) => void; @@ -77,6 +78,11 @@ export type EditorContextType = { propertyValue: any, ) => void; updateWidgetAutoHeight?: (widgetId: string, height: number) => void; + updateWidgetDimension?: ( + widgetId: string, + width: number, + height: number, + ) => void; checkContainersForAutoHeight?: () => void; modifyMetaWidgets?: (modifications: ModifyMetaWidgetPayload) => void; setWidgetCache?: ( @@ -111,6 +117,7 @@ const COMMON_API_METHODS: EditorContextTypeKey[] = [ "syncUpdateWidgetMetaProperty", "triggerEvalOnMetaUpdate", "updateWidgetAutoHeight", + "updateWidgetDimension", "checkContainersForAutoHeight", "selectWidgetRequest", ]; @@ -206,6 +213,7 @@ const mapDispatchToProps = { batchUpdateWidgetProperty: batchUpdatePropertyAction, triggerEvalOnMetaUpdate: triggerEvalOnMetaUpdate, updateWidgetAutoHeight: updateWidgetAutoHeightAction, + updateWidgetDimension: updateWidgetDimensionAction, checkContainersForAutoHeight: checkContainersForAutoHeightAction, modifyMetaWidgets, updateMetaWidgetProperty, diff --git a/app/client/src/sagas/AutoLayoutUpdateSagas.tsx b/app/client/src/sagas/AutoLayoutUpdateSagas.tsx index d40800dfab4c..694ff5d2b311 100644 --- a/app/client/src/sagas/AutoLayoutUpdateSagas.tsx +++ b/app/client/src/sagas/AutoLayoutUpdateSagas.tsx @@ -6,15 +6,20 @@ import { } from "ce/constants/ReduxActionConstants"; import log from "loglevel"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; -import { all, put, select, takeLatest } from "redux-saga/effects"; +import { all, debounce, put, select, takeLatest } from "redux-saga/effects"; import { alterLayoutForDesktop, alterLayoutForMobile, + getCanvasDimensions, } from "../utils/autoLayout/AutoLayoutUtils"; import { getWidgets } from "./selectors"; import { updateWidgetPositions } from "utils/autoLayout/positionUtils"; -import { getCanvasWidth } from "selectors/editorSelectors"; import { getIsMobile } from "selectors/mainCanvasSelectors"; +import { getCanvasWidth as getMainCanvasWidth } from "selectors/editorSelectors"; +import { getWidgetMinMaxDimensionsInPixel } from "utils/autoLayout/flexWidgetUtils"; +import { getIsDraggingOrResizing } from "selectors/widgetSelectors"; +import { updateMultipleWidgetPropertiesAction } from "actions/controlActions"; +import { diff } from "deep-diff"; export function* updateLayoutForMobileCheckpoint( actionPayload: ReduxAction<{ @@ -27,7 +32,7 @@ export function* updateLayoutForMobileCheckpoint( const start = performance.now(); const { canvasWidth, isMobile, parentId } = actionPayload.payload; const allWidgets: CanvasWidgetsReduxState = yield select(getWidgets); - const mainCanvasWidth: number = yield select(getCanvasWidth); + const mainCanvasWidth: number = yield select(getMainCanvasWidth); const updatedWidgets: CanvasWidgetsReduxState = isMobile ? alterLayoutForMobile(allWidgets, parentId, canvasWidth, canvasWidth) : alterLayoutForDesktop(allWidgets, parentId, mainCanvasWidth); @@ -48,26 +53,198 @@ export function* updateLayoutForMobileCheckpoint( } } -const processedParentIds = new Map(); +// const processedParentIds = new Map(); -function* widgetViolatedMinDimensionsSaga( - action: ReduxAction<{ parentId: string }>, +// function* widgetViolatedMinDimensionsSaga( +// action: ReduxAction<{ parentId: string }>, +// ) { +// const isMobile: boolean = yield select(getIsMobile); +// const allWidgets: CanvasWidgetsReduxState = yield select(getWidgets); +// const mainCanvasWidth: number = yield select(getCanvasWidth); + +// const parentId = action.payload.parentId; +// if (processedParentIds.has(parentId)) return; +// processedParentIds.set(parentId, true); +// setTimeout(() => processedParentIds.delete(parentId), 1000); +// const updatedWidgets = updateWidgetPositions( +// allWidgets, +// parentId, +// isMobile, +// mainCanvasWidth, +// ); +// yield put(updateAndSaveLayout(updatedWidgets)); +// } + +let autoLayoutWidgetDimensionUpdateBatch: Record< + string, + { width: number; height: number } +> = {}; + +function addWidgetToAutoLayoutDimensionUpdateBatch( + widgetId: string, + width: number, + height: number, ) { - const isMobile: boolean = yield select(getIsMobile); + autoLayoutWidgetDimensionUpdateBatch[widgetId] = { width, height }; +} + +function* updateWidgetDimensionsSaga( + action: ReduxAction<{ widgetId: string; width: number; height: number }>, +) { + let { height, width } = action.payload; + const { widgetId } = action.payload; const allWidgets: CanvasWidgetsReduxState = yield select(getWidgets); - const mainCanvasWidth: number = yield select(getCanvasWidth); - - const parentId = action.payload.parentId; - if (processedParentIds.has(parentId)) return; - processedParentIds.set(parentId, true); - setTimeout(() => processedParentIds.delete(parentId), 1000); - const updatedWidgets = updateWidgetPositions( - allWidgets, - parentId, - isMobile, + const mainCanvasWidth: number = yield select(getMainCanvasWidth); + const isLayoutUpdating: boolean = yield select(getIsDraggingOrResizing); + + const widget = allWidgets[widgetId]; + const widgetMinMaxDimensions = getWidgetMinMaxDimensionsInPixel( + widget, mainCanvasWidth, ); - yield put(updateAndSaveLayout(updatedWidgets)); + + console.log("#### addtobatch", widget.widgetName, width, height); + + if ( + widgetMinMaxDimensions.minHeight && + height < widgetMinMaxDimensions.minHeight + ) { + height = widgetMinMaxDimensions.minHeight; + } + if ( + widgetMinMaxDimensions.maxHeight && + height > widgetMinMaxDimensions.maxHeight + ) { + height = widgetMinMaxDimensions.maxHeight; + } + if ( + widgetMinMaxDimensions.minWidth && + width < widgetMinMaxDimensions.minWidth + ) { + width = widgetMinMaxDimensions.minWidth; + } + if ( + widgetMinMaxDimensions.maxWidth && + width > widgetMinMaxDimensions.maxWidth + ) { + width = widgetMinMaxDimensions.maxWidth; + } + + addWidgetToAutoLayoutDimensionUpdateBatch(widgetId, width, height); + console.log("#### addtobatch done", widget.widgetName, width, height); + if (isLayoutUpdating) return; + yield put({ + type: ReduxActionTypes.PROCESS_AUTO_LAYOUT_DIMENSION_UPDATES, + }); +} + +function* processAutoLayoutDimensionUpdatesSaga() { + const allWidgets: CanvasWidgetsReduxState = yield select(getWidgets); + const mainCanvasWidth: number = yield select(getMainCanvasWidth); + const isMobile: boolean = yield select(getIsMobile); + + let widgets = allWidgets; + const widgets1 = { ...widgets }; + const parentIds = new Set(); + // Initialise a list of changes so far. + // This contains a map of widgetIds with their new topRow and bottomRow + const changesSoFar: Record< + string, + { bottomRow?: number; rightColumn?: number } + > = {}; + for (const widgetId in autoLayoutWidgetDimensionUpdateBatch) { + const { height, width } = autoLayoutWidgetDimensionUpdateBatch[widgetId]; + const widget = allWidgets[widgetId]; + console.log("#### parentId", widget.widgetName, widget?.parentId, widget); + const parentId = widget.parentId; + if (parentId === undefined) continue; + if (parentId) parentIds.add(parentId); + + const { columnSpace } = getCanvasDimensions( + widgets[parentId], + widgets, + mainCanvasWidth, + isMobile, + ); + + const newBottomRow = widget.topRow + height / widget.parentRowSpace; + const newRightColumn = widget.leftColumn + width / columnSpace; + + if ( + widget.bottomRow !== newBottomRow || + widget.rightColumn !== newRightColumn + ) { + changesSoFar[widgetId] = { + bottomRow: newBottomRow, + rightColumn: newRightColumn, + }; + } + + console.log( + "#### columnSpace", + widget.widgetName, + width, + widget.parentColumnSpace, + columnSpace, + ); + + widgets = { + ...widgets, + [widgetId]: { + ...widget, + bottomRow: widget.topRow + height / widget.parentRowSpace, + rightColumn: widget.leftColumn + width / columnSpace, + }, + }; + } + + for (const parentId of parentIds) { + console.log( + "#### calling updateWidgetPositions for ", + widgets[parentId].widgetName, + ); + widgets = updateWidgetPositions( + widgets, + parentId, + isMobile, + mainCanvasWidth, + ); + } + + const widgetsToUpdate: any = []; + for (const changedWidgetId in changesSoFar) { + widgetsToUpdate[changedWidgetId] = [ + { + propertyPath: "topRow", + propertyValue: widgets[changedWidgetId].topRow, + }, + { + propertyPath: "bottomRow", + propertyValue: changesSoFar[changedWidgetId].bottomRow, + }, + { + propertyPath: "leftColumn", + propertyValue: widgets[changedWidgetId].leftColumn, + }, + { + propertyPath: "rightColumn", + propertyValue: changesSoFar[changedWidgetId].rightColumn, + }, + ]; + } + + console.log("#### difff", diff(widgets, widgets1)); + + // 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)); + + // Save the layout + yield put(updateAndSaveLayout(widgets)); + + // clear the batch after processing + autoLayoutWidgetDimensionUpdateBatch = {}; } export default function* layoutUpdateSagas() { @@ -76,9 +253,18 @@ export default function* layoutUpdateSagas() { ReduxActionTypes.RECALCULATE_COLUMNS, updateLayoutForMobileCheckpoint, ), + // takeLatest( + // ReduxActionTypes.WIDGET_VIOLATED_MIN_DIMENSIONS, + // widgetViolatedMinDimensionsSaga, + // ), takeLatest( - ReduxActionTypes.WIDGET_VIOLATED_MIN_DIMENSIONS, - widgetViolatedMinDimensionsSaga, + ReduxActionTypes.UPDATE_WIDGET_DIMENSIONS, + updateWidgetDimensionsSaga, + ), + debounce( + 50, + ReduxActionTypes.PROCESS_AUTO_LAYOUT_DIMENSION_UPDATES, + processAutoLayoutDimensionUpdatesSaga, ), ]); } diff --git a/app/client/src/utils/autoLayout/AutoLayoutUtils.ts b/app/client/src/utils/autoLayout/AutoLayoutUtils.ts index 547edebd6a3b..10d0aad58ba3 100644 --- a/app/client/src/utils/autoLayout/AutoLayoutUtils.ts +++ b/app/client/src/utils/autoLayout/AutoLayoutUtils.ts @@ -4,6 +4,7 @@ import { layoutConfigurations, GridDefaults, MAIN_CONTAINER_WIDGET_ID, + WIDGET_PADDING, } from "constants/WidgetConstants"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; @@ -16,6 +17,7 @@ import { import { updateWidgetPositions } from "utils/autoLayout/positionUtils"; import { getWidgetWidth } from "./flexWidgetUtils"; import { AlignmentColumnInfo } from "./autoLayoutTypes"; +import { Widget } from "./positionUtils"; function getCanvas(widgets: CanvasWidgetsReduxState, containerId: string) { const container = widgets[containerId]; @@ -517,3 +519,64 @@ export function getAlignmentColumnInfo( [FlexLayerAlignment.End]: end, }; } + +export function getCanvasDimensions( + canvas: Widget, + widgets: CanvasWidgetsReduxState, + mainCanvasWidth: number, + isMobile: boolean, +): { canvasWidth: number; columnSpace: number } { + const canvasWidth: number = getCanvasWidth( + canvas, + widgets, + mainCanvasWidth, + isMobile, + ); + + const columnSpace: number = canvasWidth / GridDefaults.DEFAULT_GRID_COLUMNS; + + return { canvasWidth: canvasWidth, columnSpace }; +} + +function getCanvasWidth( + canvas: Widget, + widgets: CanvasWidgetsReduxState, + mainCanvasWidth: number, + isMobile: boolean, +): number { + if (!mainCanvasWidth) return 0; + if (canvas.widgetId === MAIN_CONTAINER_WIDGET_ID) + return mainCanvasWidth - getPadding(canvas); + let widget = canvas; + let columns = 0; + let width = 1; + let padding = 0; + while (widget.parentId) { + columns = getWidgetWidth(widget, isMobile); + padding += getPadding(widget); + width *= columns > 64 ? 1 : columns / GridDefaults.DEFAULT_GRID_COLUMNS; + widget = widgets[widget.parentId]; + } + const totalWidth = width * mainCanvasWidth; + if (widget.widgetId === MAIN_CONTAINER_WIDGET_ID) + padding += getPadding(widget); + return totalWidth - padding; +} + +function getPadding(canvas: Widget): number { + let padding = 0; + if ( + canvas.widgetId === MAIN_CONTAINER_WIDGET_ID || + canvas.type === "CONTAINER_WIDGET" + ) { + //For MainContainer and any Container Widget padding doesn't exist coz there is already container padding. + padding = FLEXBOX_PADDING * 2; + } + if (canvas.noPad) { + // Widgets like ListWidget choose to have no container padding so will only have widget padding + padding = WIDGET_PADDING * 2; + } + // Account for container border. + padding += canvas.type === "CONTAINER_WIDGET" ? 2 : 0; + return padding; +} diff --git a/app/client/src/utils/autoLayout/flexWidgetUtils.ts b/app/client/src/utils/autoLayout/flexWidgetUtils.ts index 5a32108fd53e..380566a76bb1 100644 --- a/app/client/src/utils/autoLayout/flexWidgetUtils.ts +++ b/app/client/src/utils/autoLayout/flexWidgetUtils.ts @@ -1,9 +1,11 @@ import WidgetFactory from "utils/WidgetFactory"; import { WidgetSizeConfig } from "widgets/constants"; -export interface MinSize { +export interface MinMaxSize { minHeight: number | string; + maxHeight: number | string; minWidth: number | string; + maxWidth: number | string; } export function getRightColumn(widget: any, isMobile: boolean): number { @@ -121,23 +123,25 @@ export function getWidgetRows(widget: any, isMobile: boolean): number { } /** - * Calculates the minimum size of a widget based on the widget type and the canvas width. + * Calculates the minimum & maximum size of a widget based on the widget type and the canvas width. * @param widget | Widget props * @param canvasWidth | number : main canvas width. * @returns MinSize | undefined */ -export function getMinSize( +export function getMinMaxSize( widget: any, canvasWidth: number, -): MinSize | undefined { +): MinMaxSize | undefined { // Get the widget size configuration. const sizeConfig = getCurrentSizeConfig(widget, canvasWidth); if (!sizeConfig) return; - // Get the minimum size for the widget at this breakpoint. - const { minHeight, minWidth } = sizeConfig.configuration(widget); + // Get the minimum & maximum size for the widget at this breakpoint. + const { maxHeight, maxWidth, minHeight, minWidth } = sizeConfig.configuration( + widget, + ); - return { minHeight, minWidth }; + return { maxHeight, maxWidth, minHeight, minWidth }; } export function getCurrentSizeConfig( @@ -167,7 +171,7 @@ export function getMinPixelWidth( canvasWidth: number, ): number | undefined { if (!widget) return; - const minSize = getMinSize(widget, canvasWidth); + const minSize = getMinMaxSize(widget, canvasWidth); if (!minSize) return; const arr: string[] = typeof minSize.minWidth === "string" ? minSize.minWidth.split("px") : []; @@ -175,3 +179,52 @@ export function getMinPixelWidth( if (typeof minSize.minWidth === "number") return minSize.minWidth * widget.parentColumnSpace; } + +function getPxValue(val: string | number, factor: number): number | undefined { + const arr: string[] = typeof val === "string" ? val.split("px") : []; + if (arr.length) return parseInt(arr[0]); + if (typeof val === "number") return val * factor; +} + +/** + * Return the widget dimension constraints based on the widget type and the canvas width. + * size can be configured in columns (number) or pixels (string). + * Return an appropriate pixel width & height based on the size type. + */ +export function getWidgetMinMaxDimensionsInPixel( + widget: any, + canvasWidth: number, +): { [key in keyof MinMaxSize]: number | undefined } { + const returnValue: { [key in keyof MinMaxSize]: number | undefined } = { + minWidth: undefined, + minHeight: undefined, + maxWidth: undefined, + maxHeight: undefined, + }; + + if (!widget) return returnValue; + const minMaxSize = getMinMaxSize(widget, canvasWidth); + if (!minMaxSize) return returnValue; + + returnValue.minWidth = getPxValue( + minMaxSize.minWidth, + widget.parentColumnSpace, + ); + + returnValue.maxWidth = getPxValue( + minMaxSize.maxWidth, + widget.parentColumnSpace, + ); + + returnValue.minHeight = getPxValue( + minMaxSize.minHeight, + widget.parentRowSpace, + ); + + returnValue.maxHeight = getPxValue( + minMaxSize.maxHeight, + widget.parentRowSpace, + ); + + return returnValue; +} diff --git a/app/client/src/utils/autoLayout/positionUtils.ts b/app/client/src/utils/autoLayout/positionUtils.ts index 6ba0fabe18a1..3f18deb5c8ea 100644 --- a/app/client/src/utils/autoLayout/positionUtils.ts +++ b/app/client/src/utils/autoLayout/positionUtils.ts @@ -19,7 +19,7 @@ import { getWidgetWidth, setDimensions, } from "./flexWidgetUtils"; -import { getCanvasWidth } from "./highlightUtils"; +import { getCanvasDimensions } from "./AutoLayoutUtils"; export type Widget = WidgetProps & { children?: string[] | undefined; @@ -59,13 +59,12 @@ export function updateWidgetPositions( const parent = widgets[parentId]; if (!parent) return widgets; - const canvasWidth = getCanvasWidth( + const { columnSpace } = getCanvasDimensions( parent, widgets, mainCanvasWidth, isMobile, ); - const columnSpace = canvasWidth / GridDefaults.DEFAULT_GRID_COLUMNS; let height = 0; if (parent.flexLayers && parent.flexLayers?.length) { diff --git a/app/client/src/utils/layoutPropertiesUtils.ts b/app/client/src/utils/layoutPropertiesUtils.ts index 452558cd709f..e6c5e18cfbe0 100644 --- a/app/client/src/utils/layoutPropertiesUtils.ts +++ b/app/client/src/utils/layoutPropertiesUtils.ts @@ -227,6 +227,7 @@ export function getLayoutConfig(alignment: Alignment, spacing: Spacing): any[] { export const NonResizableWidgets = [ "AUDIO_WIDGET", "BUTTON_WIDGET", + "FILE_PICKER_WIDGET_V2", "BUTTON_GROUP_WIDGET", "CHECKBOX_WIDGET", "CURRENCY_INPUT_WIDGET", @@ -265,7 +266,6 @@ export const DefaultFillWidgets = [ "DATE_PICKER_WIDGET2", "DIVIDER_WIDGET", "FORM_WIDGET", - "FILE_PICKER_WIDGET_V2", "INPUT_WIDGET_V2", "JSON_FORM_WIDGET", "LIST_WIDGET", diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index cdfbc234c4c4..630c9e10eb73 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -23,6 +23,7 @@ import { ExecuteTriggerPayload } from "constants/AppsmithActionConstants/ActionC import { PropertyPaneConfig } from "constants/PropertyControlConstants"; import { CSSUnit, + FLEXBOX_PADDING, GridDefaults, PositionType, RenderMode, @@ -66,6 +67,7 @@ import { UpdateMetaWidgetPropertyPayload, } from "reducers/entityReducers/metaWidgetsReducer"; import { SelectionRequestType } from "sagas/WidgetSelectUtils"; +import AutoLayoutDimensionObserver from "components/designSystems/appsmith/autoLayout/AutoLayoutDimensionObeserver"; /*** * BaseWidget @@ -607,6 +609,7 @@ abstract class BaseWidget< // Adding a check for the Modal Widget early // to avoid deselect Modal in its unmount effect. if ( + !this.props.isFlexChild && isAutoHeightEnabledForWidget(this.props) && !this.props.isAutoGeneratedWidget && // To skip list widget's auto generated widgets !this.props.detachFromLayout // To skip Modal widget issue #18697 @@ -620,9 +623,29 @@ abstract class BaseWidget< ); } + if (this.props.isFlexChild && !this.props.detachFromLayout) { + return ( + + {content} + + ); + } return this.addErrorBoundary(content); }; + private updateWidgetDimensions = (width: number, height: number) => { + const { updateWidgetDimension } = this.context; + if (!updateWidgetDimension) return; + updateWidgetDimension( + this.props.widgetId, + width + 2 * FLEXBOX_PADDING, + height + 2 * FLEXBOX_PADDING, + ); + }; + private getWidgetView(): ReactNode { let content: ReactNode; diff --git a/app/client/src/widgets/ButtonWidget/component/DragContainer.tsx b/app/client/src/widgets/ButtonWidget/component/DragContainer.tsx index d4eb417d1147..046b44f21ee2 100644 --- a/app/client/src/widgets/ButtonWidget/component/DragContainer.tsx +++ b/app/client/src/widgets/ButtonWidget/component/DragContainer.tsx @@ -23,7 +23,7 @@ import { buttonHoverActiveStyles } from "./utils"; /* For the Button Widget we don't remove the DragContainer - because of the Tooltip issue - + because of the Tooltip issue - https://github.com/appsmithorg/appsmith/pull/12372 For this reason we pass the showInAllModes prop. */ @@ -45,6 +45,13 @@ const ButtonContainer = styled.div` height: 100%; } + .auto-layout & > .bp3-button.bp3-fill { + display: flex; + width: auto; + max-width: 352px; + min-width: 112px; + } + position: relative; &:after { content: ""; diff --git a/app/client/src/widgets/ButtonWidget/index.ts b/app/client/src/widgets/ButtonWidget/index.ts index 084bb982d583..929437d9c3de 100644 --- a/app/client/src/widgets/ButtonWidget/index.ts +++ b/app/client/src/widgets/ButtonWidget/index.ts @@ -41,6 +41,19 @@ export const CONFIG = { styleConfig: Widget.getPropertyPaneStyleConfig(), stylesheetConfig: Widget.getStylesheetConfig(), }, + autoLayout: { + widgetSize: [ + { + viewportMinWidth: 0, + configuration: () => { + return { + minWidth: "120px", + maxWidth: "360px", + }; + }, + }, + ], + }, }; export default Widget; diff --git a/app/client/src/widgets/FilePickerWidgetV2/index.ts b/app/client/src/widgets/FilePickerWidgetV2/index.ts index a4cf9c0f90fe..25efec97f96a 100644 --- a/app/client/src/widgets/FilePickerWidgetV2/index.ts +++ b/app/client/src/widgets/FilePickerWidgetV2/index.ts @@ -1,4 +1,4 @@ -import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; +import { BUTTON_MIN_WIDTH } from "constants/minWidthConstants"; import { getDefaultResponsiveBehavior } from "utils/layoutPropertiesUtils"; import FileDataTypes from "./constants"; import IconSVG from "./icon.svg"; @@ -28,7 +28,7 @@ export const CONFIG = { isDisabled: false, animateLoading: true, responsiveBehavior: getDefaultResponsiveBehavior(Widget.getWidgetType()), - minWidth: FILL_WIDGET_MIN_WIDTH, + minWidth: BUTTON_MIN_WIDTH, }, properties: { derived: Widget.getDerivedPropertiesMap(), @@ -39,6 +39,19 @@ export const CONFIG = { contentConfig: Widget.getPropertyPaneContentConfig(), stylesheetConfig: Widget.getStylesheetConfig(), }, + autoLayout: { + widgetSize: [ + { + viewportMinWidth: 0, + configuration: () => { + return { + minWidth: "42px", + maxWidth: "320px", + }; + }, + }, + ], + }, }; export default Widget; diff --git a/app/client/src/widgets/IconButtonWidget/index.ts b/app/client/src/widgets/IconButtonWidget/index.ts index e30e7052d06d..f43e2bea2bf4 100644 --- a/app/client/src/widgets/IconButtonWidget/index.ts +++ b/app/client/src/widgets/IconButtonWidget/index.ts @@ -14,8 +14,8 @@ export const CONFIG = { buttonVariant: ButtonVariantTypes.PRIMARY, isDisabled: false, isVisible: true, - rows: 4, - columns: 4, + rows: 5, + columns: 5, widgetName: "IconButton", version: 1, animateLoading: true, @@ -30,6 +30,18 @@ export const CONFIG = { styleConfig: Widget.getPropertyPaneStyleConfig(), stylesheetConfig: Widget.getStylesheetConfig(), }, + autoLayout: { + widgetSize: [ + { + viewportMinWidth: 0, + configuration: () => { + return { + minWidth: "40px", + }; + }, + }, + ], + }, }; export default Widget; diff --git a/app/client/src/widgets/MenuButtonWidget/index.ts b/app/client/src/widgets/MenuButtonWidget/index.ts index b10f0ff3b22b..eac25359ffbe 100644 --- a/app/client/src/widgets/MenuButtonWidget/index.ts +++ b/app/client/src/widgets/MenuButtonWidget/index.ts @@ -56,6 +56,19 @@ export const CONFIG = { styleConfig: Widget.getPropertyPaneStyleConfig(), stylesheetConfig: Widget.getStylesheetConfig(), }, + autoLayout: { + widgetSize: [ + { + viewportMinWidth: 0, + configuration: () => { + return { + minWidth: "42px", + maxWidth: "320px", + }; + }, + }, + ], + }, }; export default Widget; From 18bf404c60f33c93691301e6fc8c4ef50c7d93fe Mon Sep 17 00:00:00 2001 From: Aswath K Date: Tue, 21 Feb 2023 14:35:20 +0530 Subject: [PATCH 525/708] Revert "feat: responsive styles for button widget" (#20819) Reverts appsmithorg/appsmith#19892 --- app/client/src/actions/autoLayoutActions.ts | 15 -- .../src/ce/constants/ReduxActionConstants.tsx | 3 - .../AutoLayoutDimensionObeserver.tsx | 88 ------- .../EditorContextProvider.tsx | 8 - .../src/sagas/AutoLayoutUpdateSagas.tsx | 226 ++---------------- .../src/utils/autoLayout/AutoLayoutUtils.ts | 63 ----- .../src/utils/autoLayout/flexWidgetUtils.ts | 69 +----- .../src/utils/autoLayout/positionUtils.ts | 5 +- app/client/src/utils/layoutPropertiesUtils.ts | 2 +- app/client/src/widgets/BaseWidget.tsx | 23 -- .../ButtonWidget/component/DragContainer.tsx | 9 +- app/client/src/widgets/ButtonWidget/index.ts | 13 - .../src/widgets/FilePickerWidgetV2/index.ts | 17 +- .../src/widgets/IconButtonWidget/index.ts | 16 +- .../src/widgets/MenuButtonWidget/index.ts | 13 - 15 files changed, 37 insertions(+), 533 deletions(-) delete mode 100644 app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutDimensionObeserver.tsx diff --git a/app/client/src/actions/autoLayoutActions.ts b/app/client/src/actions/autoLayoutActions.ts index 396a603369c6..f90c11313b61 100644 --- a/app/client/src/actions/autoLayoutActions.ts +++ b/app/client/src/actions/autoLayoutActions.ts @@ -19,18 +19,3 @@ export const widgetViolatedMinDimentionsAction = (parentId: string) => ({ parentId, }, }); - -export function updateWidgetDimensionAction( - widgetId: string, - width: number, - height: number, -) { - return { - type: ReduxActionTypes.UPDATE_WIDGET_DIMENSIONS, - payload: { - widgetId, - width, - height, - }, - }; -} diff --git a/app/client/src/ce/constants/ReduxActionConstants.tsx b/app/client/src/ce/constants/ReduxActionConstants.tsx index a4347e7b9aa6..e13fd209ae98 100644 --- a/app/client/src/ce/constants/ReduxActionConstants.tsx +++ b/app/client/src/ce/constants/ReduxActionConstants.tsx @@ -762,9 +762,6 @@ export const ReduxActionTypes = { AUTOLAYOUT_ADD_NEW_WIDGETS: "AUTOLAYOUT_ADD_NEW_WIDGETS", RECALCULATE_COLUMNS: "RECALCULATE_COLUMNS", WIDGET_VIOLATED_MIN_DIMENSIONS: "WIDGET_VIOLATED_MIN_DIMENSIONS", - UPDATE_WIDGET_DIMENSIONS: "UPDATE_WIDGET_DIMENSIONS", - PROCESS_AUTO_LAYOUT_DIMENSION_UPDATES: - "PROCESS_AUTO_LAYOUT_DIMENSION_UPDATES", }; export type ReduxActionType = typeof ReduxActionTypes[keyof typeof ReduxActionTypes]; diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutDimensionObeserver.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutDimensionObeserver.tsx deleted file mode 100644 index a657da1c9c5d..000000000000 --- a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutDimensionObeserver.tsx +++ /dev/null @@ -1,88 +0,0 @@ -import { FLEXBOX_PADDING } from "constants/WidgetConstants"; -import React, { useState } from "react"; -import { PropsWithChildren, useEffect, useRef } from "react"; -import styled from "styled-components"; -import { ResponsiveBehavior } from "utils/autoLayout/constants"; -import { WidgetProps } from "widgets/BaseWidget"; - -const SimpleContainer = styled.div` - width: fit-content; - &.fill-widget { - width: 100%; - } -`; - -interface AutoLayoutDimensionObserverProps { - onDimensionUpdate: (width: number, height: number) => void; - widgetProps: WidgetProps; -} - -export default function AutoLayoutDimensionObserver( - props: PropsWithChildren, -) { - const { onDimensionUpdate } = props; - const [currentDimension, setCurrentDimension] = useState({ - width: 0, - height: 0, - }); - - const ref = useRef(null); - - const observer = useRef( - new ResizeObserver((entries) => { - const width = entries[0].contentRect.width; - const height = entries[0].contentRect.height; - if (width === 0 || height === 0) return; - setCurrentDimension({ width, height }); - onDimensionUpdate(width, height); - }), - ); - - const boundingBoxWidth = - (props.widgetProps.rightColumn - props.widgetProps.leftColumn) * - props.widgetProps.parentColumnSpace - - 2 * FLEXBOX_PADDING; - - useEffect(() => { - const diff = Math.abs(boundingBoxWidth - currentDimension.width); - console.log( - "##### width", - props.widgetProps.widgetName, - boundingBoxWidth, - currentDimension.width, - boundingBoxWidth - currentDimension.width, - ); - if (currentDimension.width === 0 || currentDimension.height === 0) return; - if (diff > 2) - onDimensionUpdate(currentDimension.width, currentDimension.height); - }, [boundingBoxWidth, currentDimension.width]); - - console.log( - "##### AutoLayoutDimensionObserver", - props.widgetProps.widgetName, - props.widgetProps.rightColumn, - ); - - useEffect(() => { - if (ref.current) { - observer.current.observe(ref.current); - } - - return () => { - observer.current.disconnect(); - }; - }, []); - - return ( - - {props.children} - - ); -} diff --git a/app/client/src/components/editorComponents/EditorContextProvider.tsx b/app/client/src/components/editorComponents/EditorContextProvider.tsx index e037a8716764..d5cfe85a1271 100644 --- a/app/client/src/components/editorComponents/EditorContextProvider.tsx +++ b/app/client/src/components/editorComponents/EditorContextProvider.tsx @@ -48,7 +48,6 @@ import { selectWidgetInitAction, WidgetSelectionRequest, } from "actions/widgetSelectionActions"; -import { updateWidgetDimensionAction } from "actions/autoLayoutActions"; export type EditorContextType = { executeAction?: (triggerPayload: ExecuteTriggerPayload) => void; @@ -78,11 +77,6 @@ export type EditorContextType = { propertyValue: any, ) => void; updateWidgetAutoHeight?: (widgetId: string, height: number) => void; - updateWidgetDimension?: ( - widgetId: string, - width: number, - height: number, - ) => void; checkContainersForAutoHeight?: () => void; modifyMetaWidgets?: (modifications: ModifyMetaWidgetPayload) => void; setWidgetCache?: ( @@ -117,7 +111,6 @@ const COMMON_API_METHODS: EditorContextTypeKey[] = [ "syncUpdateWidgetMetaProperty", "triggerEvalOnMetaUpdate", "updateWidgetAutoHeight", - "updateWidgetDimension", "checkContainersForAutoHeight", "selectWidgetRequest", ]; @@ -213,7 +206,6 @@ const mapDispatchToProps = { batchUpdateWidgetProperty: batchUpdatePropertyAction, triggerEvalOnMetaUpdate: triggerEvalOnMetaUpdate, updateWidgetAutoHeight: updateWidgetAutoHeightAction, - updateWidgetDimension: updateWidgetDimensionAction, checkContainersForAutoHeight: checkContainersForAutoHeightAction, modifyMetaWidgets, updateMetaWidgetProperty, diff --git a/app/client/src/sagas/AutoLayoutUpdateSagas.tsx b/app/client/src/sagas/AutoLayoutUpdateSagas.tsx index 694ff5d2b311..d40800dfab4c 100644 --- a/app/client/src/sagas/AutoLayoutUpdateSagas.tsx +++ b/app/client/src/sagas/AutoLayoutUpdateSagas.tsx @@ -6,20 +6,15 @@ import { } from "ce/constants/ReduxActionConstants"; import log from "loglevel"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; -import { all, debounce, put, select, takeLatest } from "redux-saga/effects"; +import { all, put, select, takeLatest } from "redux-saga/effects"; import { alterLayoutForDesktop, alterLayoutForMobile, - getCanvasDimensions, } from "../utils/autoLayout/AutoLayoutUtils"; import { getWidgets } from "./selectors"; import { updateWidgetPositions } from "utils/autoLayout/positionUtils"; +import { getCanvasWidth } from "selectors/editorSelectors"; import { getIsMobile } from "selectors/mainCanvasSelectors"; -import { getCanvasWidth as getMainCanvasWidth } from "selectors/editorSelectors"; -import { getWidgetMinMaxDimensionsInPixel } from "utils/autoLayout/flexWidgetUtils"; -import { getIsDraggingOrResizing } from "selectors/widgetSelectors"; -import { updateMultipleWidgetPropertiesAction } from "actions/controlActions"; -import { diff } from "deep-diff"; export function* updateLayoutForMobileCheckpoint( actionPayload: ReduxAction<{ @@ -32,7 +27,7 @@ export function* updateLayoutForMobileCheckpoint( const start = performance.now(); const { canvasWidth, isMobile, parentId } = actionPayload.payload; const allWidgets: CanvasWidgetsReduxState = yield select(getWidgets); - const mainCanvasWidth: number = yield select(getMainCanvasWidth); + const mainCanvasWidth: number = yield select(getCanvasWidth); const updatedWidgets: CanvasWidgetsReduxState = isMobile ? alterLayoutForMobile(allWidgets, parentId, canvasWidth, canvasWidth) : alterLayoutForDesktop(allWidgets, parentId, mainCanvasWidth); @@ -53,198 +48,26 @@ export function* updateLayoutForMobileCheckpoint( } } -// const processedParentIds = new Map(); +const processedParentIds = new Map(); -// function* widgetViolatedMinDimensionsSaga( -// action: ReduxAction<{ parentId: string }>, -// ) { -// const isMobile: boolean = yield select(getIsMobile); -// const allWidgets: CanvasWidgetsReduxState = yield select(getWidgets); -// const mainCanvasWidth: number = yield select(getCanvasWidth); - -// const parentId = action.payload.parentId; -// if (processedParentIds.has(parentId)) return; -// processedParentIds.set(parentId, true); -// setTimeout(() => processedParentIds.delete(parentId), 1000); -// const updatedWidgets = updateWidgetPositions( -// allWidgets, -// parentId, -// isMobile, -// mainCanvasWidth, -// ); -// yield put(updateAndSaveLayout(updatedWidgets)); -// } - -let autoLayoutWidgetDimensionUpdateBatch: Record< - string, - { width: number; height: number } -> = {}; - -function addWidgetToAutoLayoutDimensionUpdateBatch( - widgetId: string, - width: number, - height: number, -) { - autoLayoutWidgetDimensionUpdateBatch[widgetId] = { width, height }; -} - -function* updateWidgetDimensionsSaga( - action: ReduxAction<{ widgetId: string; width: number; height: number }>, +function* widgetViolatedMinDimensionsSaga( + action: ReduxAction<{ parentId: string }>, ) { - let { height, width } = action.payload; - const { widgetId } = action.payload; + const isMobile: boolean = yield select(getIsMobile); const allWidgets: CanvasWidgetsReduxState = yield select(getWidgets); - const mainCanvasWidth: number = yield select(getMainCanvasWidth); - const isLayoutUpdating: boolean = yield select(getIsDraggingOrResizing); - - const widget = allWidgets[widgetId]; - const widgetMinMaxDimensions = getWidgetMinMaxDimensionsInPixel( - widget, + const mainCanvasWidth: number = yield select(getCanvasWidth); + + const parentId = action.payload.parentId; + if (processedParentIds.has(parentId)) return; + processedParentIds.set(parentId, true); + setTimeout(() => processedParentIds.delete(parentId), 1000); + const updatedWidgets = updateWidgetPositions( + allWidgets, + parentId, + isMobile, mainCanvasWidth, ); - - console.log("#### addtobatch", widget.widgetName, width, height); - - if ( - widgetMinMaxDimensions.minHeight && - height < widgetMinMaxDimensions.minHeight - ) { - height = widgetMinMaxDimensions.minHeight; - } - if ( - widgetMinMaxDimensions.maxHeight && - height > widgetMinMaxDimensions.maxHeight - ) { - height = widgetMinMaxDimensions.maxHeight; - } - if ( - widgetMinMaxDimensions.minWidth && - width < widgetMinMaxDimensions.minWidth - ) { - width = widgetMinMaxDimensions.minWidth; - } - if ( - widgetMinMaxDimensions.maxWidth && - width > widgetMinMaxDimensions.maxWidth - ) { - width = widgetMinMaxDimensions.maxWidth; - } - - addWidgetToAutoLayoutDimensionUpdateBatch(widgetId, width, height); - console.log("#### addtobatch done", widget.widgetName, width, height); - if (isLayoutUpdating) return; - yield put({ - type: ReduxActionTypes.PROCESS_AUTO_LAYOUT_DIMENSION_UPDATES, - }); -} - -function* processAutoLayoutDimensionUpdatesSaga() { - const allWidgets: CanvasWidgetsReduxState = yield select(getWidgets); - const mainCanvasWidth: number = yield select(getMainCanvasWidth); - const isMobile: boolean = yield select(getIsMobile); - - let widgets = allWidgets; - const widgets1 = { ...widgets }; - const parentIds = new Set(); - // Initialise a list of changes so far. - // This contains a map of widgetIds with their new topRow and bottomRow - const changesSoFar: Record< - string, - { bottomRow?: number; rightColumn?: number } - > = {}; - for (const widgetId in autoLayoutWidgetDimensionUpdateBatch) { - const { height, width } = autoLayoutWidgetDimensionUpdateBatch[widgetId]; - const widget = allWidgets[widgetId]; - console.log("#### parentId", widget.widgetName, widget?.parentId, widget); - const parentId = widget.parentId; - if (parentId === undefined) continue; - if (parentId) parentIds.add(parentId); - - const { columnSpace } = getCanvasDimensions( - widgets[parentId], - widgets, - mainCanvasWidth, - isMobile, - ); - - const newBottomRow = widget.topRow + height / widget.parentRowSpace; - const newRightColumn = widget.leftColumn + width / columnSpace; - - if ( - widget.bottomRow !== newBottomRow || - widget.rightColumn !== newRightColumn - ) { - changesSoFar[widgetId] = { - bottomRow: newBottomRow, - rightColumn: newRightColumn, - }; - } - - console.log( - "#### columnSpace", - widget.widgetName, - width, - widget.parentColumnSpace, - columnSpace, - ); - - widgets = { - ...widgets, - [widgetId]: { - ...widget, - bottomRow: widget.topRow + height / widget.parentRowSpace, - rightColumn: widget.leftColumn + width / columnSpace, - }, - }; - } - - for (const parentId of parentIds) { - console.log( - "#### calling updateWidgetPositions for ", - widgets[parentId].widgetName, - ); - widgets = updateWidgetPositions( - widgets, - parentId, - isMobile, - mainCanvasWidth, - ); - } - - const widgetsToUpdate: any = []; - for (const changedWidgetId in changesSoFar) { - widgetsToUpdate[changedWidgetId] = [ - { - propertyPath: "topRow", - propertyValue: widgets[changedWidgetId].topRow, - }, - { - propertyPath: "bottomRow", - propertyValue: changesSoFar[changedWidgetId].bottomRow, - }, - { - propertyPath: "leftColumn", - propertyValue: widgets[changedWidgetId].leftColumn, - }, - { - propertyPath: "rightColumn", - propertyValue: changesSoFar[changedWidgetId].rightColumn, - }, - ]; - } - - console.log("#### difff", diff(widgets, widgets1)); - - // 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)); - - // Save the layout - yield put(updateAndSaveLayout(widgets)); - - // clear the batch after processing - autoLayoutWidgetDimensionUpdateBatch = {}; + yield put(updateAndSaveLayout(updatedWidgets)); } export default function* layoutUpdateSagas() { @@ -253,18 +76,9 @@ export default function* layoutUpdateSagas() { ReduxActionTypes.RECALCULATE_COLUMNS, updateLayoutForMobileCheckpoint, ), - // takeLatest( - // ReduxActionTypes.WIDGET_VIOLATED_MIN_DIMENSIONS, - // widgetViolatedMinDimensionsSaga, - // ), takeLatest( - ReduxActionTypes.UPDATE_WIDGET_DIMENSIONS, - updateWidgetDimensionsSaga, - ), - debounce( - 50, - ReduxActionTypes.PROCESS_AUTO_LAYOUT_DIMENSION_UPDATES, - processAutoLayoutDimensionUpdatesSaga, + ReduxActionTypes.WIDGET_VIOLATED_MIN_DIMENSIONS, + widgetViolatedMinDimensionsSaga, ), ]); } diff --git a/app/client/src/utils/autoLayout/AutoLayoutUtils.ts b/app/client/src/utils/autoLayout/AutoLayoutUtils.ts index 10d0aad58ba3..547edebd6a3b 100644 --- a/app/client/src/utils/autoLayout/AutoLayoutUtils.ts +++ b/app/client/src/utils/autoLayout/AutoLayoutUtils.ts @@ -4,7 +4,6 @@ import { layoutConfigurations, GridDefaults, MAIN_CONTAINER_WIDGET_ID, - WIDGET_PADDING, } from "constants/WidgetConstants"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; @@ -17,7 +16,6 @@ import { import { updateWidgetPositions } from "utils/autoLayout/positionUtils"; import { getWidgetWidth } from "./flexWidgetUtils"; import { AlignmentColumnInfo } from "./autoLayoutTypes"; -import { Widget } from "./positionUtils"; function getCanvas(widgets: CanvasWidgetsReduxState, containerId: string) { const container = widgets[containerId]; @@ -519,64 +517,3 @@ export function getAlignmentColumnInfo( [FlexLayerAlignment.End]: end, }; } - -export function getCanvasDimensions( - canvas: Widget, - widgets: CanvasWidgetsReduxState, - mainCanvasWidth: number, - isMobile: boolean, -): { canvasWidth: number; columnSpace: number } { - const canvasWidth: number = getCanvasWidth( - canvas, - widgets, - mainCanvasWidth, - isMobile, - ); - - const columnSpace: number = canvasWidth / GridDefaults.DEFAULT_GRID_COLUMNS; - - return { canvasWidth: canvasWidth, columnSpace }; -} - -function getCanvasWidth( - canvas: Widget, - widgets: CanvasWidgetsReduxState, - mainCanvasWidth: number, - isMobile: boolean, -): number { - if (!mainCanvasWidth) return 0; - if (canvas.widgetId === MAIN_CONTAINER_WIDGET_ID) - return mainCanvasWidth - getPadding(canvas); - let widget = canvas; - let columns = 0; - let width = 1; - let padding = 0; - while (widget.parentId) { - columns = getWidgetWidth(widget, isMobile); - padding += getPadding(widget); - width *= columns > 64 ? 1 : columns / GridDefaults.DEFAULT_GRID_COLUMNS; - widget = widgets[widget.parentId]; - } - const totalWidth = width * mainCanvasWidth; - if (widget.widgetId === MAIN_CONTAINER_WIDGET_ID) - padding += getPadding(widget); - return totalWidth - padding; -} - -function getPadding(canvas: Widget): number { - let padding = 0; - if ( - canvas.widgetId === MAIN_CONTAINER_WIDGET_ID || - canvas.type === "CONTAINER_WIDGET" - ) { - //For MainContainer and any Container Widget padding doesn't exist coz there is already container padding. - padding = FLEXBOX_PADDING * 2; - } - if (canvas.noPad) { - // Widgets like ListWidget choose to have no container padding so will only have widget padding - padding = WIDGET_PADDING * 2; - } - // Account for container border. - padding += canvas.type === "CONTAINER_WIDGET" ? 2 : 0; - return padding; -} diff --git a/app/client/src/utils/autoLayout/flexWidgetUtils.ts b/app/client/src/utils/autoLayout/flexWidgetUtils.ts index 380566a76bb1..5a32108fd53e 100644 --- a/app/client/src/utils/autoLayout/flexWidgetUtils.ts +++ b/app/client/src/utils/autoLayout/flexWidgetUtils.ts @@ -1,11 +1,9 @@ import WidgetFactory from "utils/WidgetFactory"; import { WidgetSizeConfig } from "widgets/constants"; -export interface MinMaxSize { +export interface MinSize { minHeight: number | string; - maxHeight: number | string; minWidth: number | string; - maxWidth: number | string; } export function getRightColumn(widget: any, isMobile: boolean): number { @@ -123,25 +121,23 @@ export function getWidgetRows(widget: any, isMobile: boolean): number { } /** - * Calculates the minimum & maximum size of a widget based on the widget type and the canvas width. + * Calculates the minimum size of a widget based on the widget type and the canvas width. * @param widget | Widget props * @param canvasWidth | number : main canvas width. * @returns MinSize | undefined */ -export function getMinMaxSize( +export function getMinSize( widget: any, canvasWidth: number, -): MinMaxSize | undefined { +): MinSize | undefined { // Get the widget size configuration. const sizeConfig = getCurrentSizeConfig(widget, canvasWidth); if (!sizeConfig) return; - // Get the minimum & maximum size for the widget at this breakpoint. - const { maxHeight, maxWidth, minHeight, minWidth } = sizeConfig.configuration( - widget, - ); + // Get the minimum size for the widget at this breakpoint. + const { minHeight, minWidth } = sizeConfig.configuration(widget); - return { maxHeight, maxWidth, minHeight, minWidth }; + return { minHeight, minWidth }; } export function getCurrentSizeConfig( @@ -171,7 +167,7 @@ export function getMinPixelWidth( canvasWidth: number, ): number | undefined { if (!widget) return; - const minSize = getMinMaxSize(widget, canvasWidth); + const minSize = getMinSize(widget, canvasWidth); if (!minSize) return; const arr: string[] = typeof minSize.minWidth === "string" ? minSize.minWidth.split("px") : []; @@ -179,52 +175,3 @@ export function getMinPixelWidth( if (typeof minSize.minWidth === "number") return minSize.minWidth * widget.parentColumnSpace; } - -function getPxValue(val: string | number, factor: number): number | undefined { - const arr: string[] = typeof val === "string" ? val.split("px") : []; - if (arr.length) return parseInt(arr[0]); - if (typeof val === "number") return val * factor; -} - -/** - * Return the widget dimension constraints based on the widget type and the canvas width. - * size can be configured in columns (number) or pixels (string). - * Return an appropriate pixel width & height based on the size type. - */ -export function getWidgetMinMaxDimensionsInPixel( - widget: any, - canvasWidth: number, -): { [key in keyof MinMaxSize]: number | undefined } { - const returnValue: { [key in keyof MinMaxSize]: number | undefined } = { - minWidth: undefined, - minHeight: undefined, - maxWidth: undefined, - maxHeight: undefined, - }; - - if (!widget) return returnValue; - const minMaxSize = getMinMaxSize(widget, canvasWidth); - if (!minMaxSize) return returnValue; - - returnValue.minWidth = getPxValue( - minMaxSize.minWidth, - widget.parentColumnSpace, - ); - - returnValue.maxWidth = getPxValue( - minMaxSize.maxWidth, - widget.parentColumnSpace, - ); - - returnValue.minHeight = getPxValue( - minMaxSize.minHeight, - widget.parentRowSpace, - ); - - returnValue.maxHeight = getPxValue( - minMaxSize.maxHeight, - widget.parentRowSpace, - ); - - return returnValue; -} diff --git a/app/client/src/utils/autoLayout/positionUtils.ts b/app/client/src/utils/autoLayout/positionUtils.ts index 3f18deb5c8ea..6ba0fabe18a1 100644 --- a/app/client/src/utils/autoLayout/positionUtils.ts +++ b/app/client/src/utils/autoLayout/positionUtils.ts @@ -19,7 +19,7 @@ import { getWidgetWidth, setDimensions, } from "./flexWidgetUtils"; -import { getCanvasDimensions } from "./AutoLayoutUtils"; +import { getCanvasWidth } from "./highlightUtils"; export type Widget = WidgetProps & { children?: string[] | undefined; @@ -59,12 +59,13 @@ export function updateWidgetPositions( const parent = widgets[parentId]; if (!parent) return widgets; - const { columnSpace } = getCanvasDimensions( + const canvasWidth = getCanvasWidth( parent, widgets, mainCanvasWidth, isMobile, ); + const columnSpace = canvasWidth / GridDefaults.DEFAULT_GRID_COLUMNS; let height = 0; if (parent.flexLayers && parent.flexLayers?.length) { diff --git a/app/client/src/utils/layoutPropertiesUtils.ts b/app/client/src/utils/layoutPropertiesUtils.ts index e6c5e18cfbe0..452558cd709f 100644 --- a/app/client/src/utils/layoutPropertiesUtils.ts +++ b/app/client/src/utils/layoutPropertiesUtils.ts @@ -227,7 +227,6 @@ export function getLayoutConfig(alignment: Alignment, spacing: Spacing): any[] { export const NonResizableWidgets = [ "AUDIO_WIDGET", "BUTTON_WIDGET", - "FILE_PICKER_WIDGET_V2", "BUTTON_GROUP_WIDGET", "CHECKBOX_WIDGET", "CURRENCY_INPUT_WIDGET", @@ -266,6 +265,7 @@ export const DefaultFillWidgets = [ "DATE_PICKER_WIDGET2", "DIVIDER_WIDGET", "FORM_WIDGET", + "FILE_PICKER_WIDGET_V2", "INPUT_WIDGET_V2", "JSON_FORM_WIDGET", "LIST_WIDGET", diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index 630c9e10eb73..cdfbc234c4c4 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -23,7 +23,6 @@ import { ExecuteTriggerPayload } from "constants/AppsmithActionConstants/ActionC import { PropertyPaneConfig } from "constants/PropertyControlConstants"; import { CSSUnit, - FLEXBOX_PADDING, GridDefaults, PositionType, RenderMode, @@ -67,7 +66,6 @@ import { UpdateMetaWidgetPropertyPayload, } from "reducers/entityReducers/metaWidgetsReducer"; import { SelectionRequestType } from "sagas/WidgetSelectUtils"; -import AutoLayoutDimensionObserver from "components/designSystems/appsmith/autoLayout/AutoLayoutDimensionObeserver"; /*** * BaseWidget @@ -609,7 +607,6 @@ abstract class BaseWidget< // Adding a check for the Modal Widget early // to avoid deselect Modal in its unmount effect. if ( - !this.props.isFlexChild && isAutoHeightEnabledForWidget(this.props) && !this.props.isAutoGeneratedWidget && // To skip list widget's auto generated widgets !this.props.detachFromLayout // To skip Modal widget issue #18697 @@ -623,29 +620,9 @@ abstract class BaseWidget< ); } - if (this.props.isFlexChild && !this.props.detachFromLayout) { - return ( - - {content} - - ); - } return this.addErrorBoundary(content); }; - private updateWidgetDimensions = (width: number, height: number) => { - const { updateWidgetDimension } = this.context; - if (!updateWidgetDimension) return; - updateWidgetDimension( - this.props.widgetId, - width + 2 * FLEXBOX_PADDING, - height + 2 * FLEXBOX_PADDING, - ); - }; - private getWidgetView(): ReactNode { let content: ReactNode; diff --git a/app/client/src/widgets/ButtonWidget/component/DragContainer.tsx b/app/client/src/widgets/ButtonWidget/component/DragContainer.tsx index 046b44f21ee2..d4eb417d1147 100644 --- a/app/client/src/widgets/ButtonWidget/component/DragContainer.tsx +++ b/app/client/src/widgets/ButtonWidget/component/DragContainer.tsx @@ -23,7 +23,7 @@ import { buttonHoverActiveStyles } from "./utils"; /* For the Button Widget we don't remove the DragContainer - because of the Tooltip issue - + because of the Tooltip issue - https://github.com/appsmithorg/appsmith/pull/12372 For this reason we pass the showInAllModes prop. */ @@ -45,13 +45,6 @@ const ButtonContainer = styled.div` height: 100%; } - .auto-layout & > .bp3-button.bp3-fill { - display: flex; - width: auto; - max-width: 352px; - min-width: 112px; - } - position: relative; &:after { content: ""; diff --git a/app/client/src/widgets/ButtonWidget/index.ts b/app/client/src/widgets/ButtonWidget/index.ts index 929437d9c3de..084bb982d583 100644 --- a/app/client/src/widgets/ButtonWidget/index.ts +++ b/app/client/src/widgets/ButtonWidget/index.ts @@ -41,19 +41,6 @@ export const CONFIG = { styleConfig: Widget.getPropertyPaneStyleConfig(), stylesheetConfig: Widget.getStylesheetConfig(), }, - autoLayout: { - widgetSize: [ - { - viewportMinWidth: 0, - configuration: () => { - return { - minWidth: "120px", - maxWidth: "360px", - }; - }, - }, - ], - }, }; export default Widget; diff --git a/app/client/src/widgets/FilePickerWidgetV2/index.ts b/app/client/src/widgets/FilePickerWidgetV2/index.ts index 25efec97f96a..a4cf9c0f90fe 100644 --- a/app/client/src/widgets/FilePickerWidgetV2/index.ts +++ b/app/client/src/widgets/FilePickerWidgetV2/index.ts @@ -1,4 +1,4 @@ -import { BUTTON_MIN_WIDTH } from "constants/minWidthConstants"; +import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; import { getDefaultResponsiveBehavior } from "utils/layoutPropertiesUtils"; import FileDataTypes from "./constants"; import IconSVG from "./icon.svg"; @@ -28,7 +28,7 @@ export const CONFIG = { isDisabled: false, animateLoading: true, responsiveBehavior: getDefaultResponsiveBehavior(Widget.getWidgetType()), - minWidth: BUTTON_MIN_WIDTH, + minWidth: FILL_WIDGET_MIN_WIDTH, }, properties: { derived: Widget.getDerivedPropertiesMap(), @@ -39,19 +39,6 @@ export const CONFIG = { contentConfig: Widget.getPropertyPaneContentConfig(), stylesheetConfig: Widget.getStylesheetConfig(), }, - autoLayout: { - widgetSize: [ - { - viewportMinWidth: 0, - configuration: () => { - return { - minWidth: "42px", - maxWidth: "320px", - }; - }, - }, - ], - }, }; export default Widget; diff --git a/app/client/src/widgets/IconButtonWidget/index.ts b/app/client/src/widgets/IconButtonWidget/index.ts index f43e2bea2bf4..e30e7052d06d 100644 --- a/app/client/src/widgets/IconButtonWidget/index.ts +++ b/app/client/src/widgets/IconButtonWidget/index.ts @@ -14,8 +14,8 @@ export const CONFIG = { buttonVariant: ButtonVariantTypes.PRIMARY, isDisabled: false, isVisible: true, - rows: 5, - columns: 5, + rows: 4, + columns: 4, widgetName: "IconButton", version: 1, animateLoading: true, @@ -30,18 +30,6 @@ export const CONFIG = { styleConfig: Widget.getPropertyPaneStyleConfig(), stylesheetConfig: Widget.getStylesheetConfig(), }, - autoLayout: { - widgetSize: [ - { - viewportMinWidth: 0, - configuration: () => { - return { - minWidth: "40px", - }; - }, - }, - ], - }, }; export default Widget; diff --git a/app/client/src/widgets/MenuButtonWidget/index.ts b/app/client/src/widgets/MenuButtonWidget/index.ts index eac25359ffbe..b10f0ff3b22b 100644 --- a/app/client/src/widgets/MenuButtonWidget/index.ts +++ b/app/client/src/widgets/MenuButtonWidget/index.ts @@ -56,19 +56,6 @@ export const CONFIG = { styleConfig: Widget.getPropertyPaneStyleConfig(), stylesheetConfig: Widget.getStylesheetConfig(), }, - autoLayout: { - widgetSize: [ - { - viewportMinWidth: 0, - configuration: () => { - return { - minWidth: "42px", - maxWidth: "320px", - }; - }, - }, - ], - }, }; export default Widget; From 07a8624e6a9c9be30975829fcd6f15b2ce4e386d Mon Sep 17 00:00:00 2001 From: Aswath K Date: Tue, 21 Feb 2023 14:39:29 +0530 Subject: [PATCH 526/708] feat: Button widget responsiveness (#20820) Co-authored-by: Arsalan --- app/client/src/actions/autoLayoutActions.ts | 15 ++ .../src/ce/constants/ReduxActionConstants.tsx | 3 + .../AutoLayoutDimensionObeserver.tsx | 86 ++++++++ .../EditorContextProvider.tsx | 8 + .../src/sagas/AutoLayoutUpdateSagas.tsx | 207 ++++++++++++++++-- .../src/utils/autoLayout/AutoLayoutUtils.ts | 63 ++++++ .../src/utils/autoLayout/flexWidgetUtils.ts | 69 +++++- .../src/utils/autoLayout/positionUtils.ts | 5 +- app/client/src/utils/layoutPropertiesUtils.ts | 9 +- app/client/src/widgets/BaseWidget.tsx | 36 +++ .../ButtonWidget/component/DragContainer.tsx | 10 +- app/client/src/widgets/ButtonWidget/index.ts | 13 ++ .../src/widgets/FilePickerWidgetV2/index.ts | 17 +- .../IconButtonWidget/component/index.tsx | 7 + .../src/widgets/IconButtonWidget/index.ts | 16 +- .../src/widgets/MenuButtonWidget/index.ts | 13 ++ 16 files changed, 540 insertions(+), 37 deletions(-) create mode 100644 app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutDimensionObeserver.tsx diff --git a/app/client/src/actions/autoLayoutActions.ts b/app/client/src/actions/autoLayoutActions.ts index f90c11313b61..396a603369c6 100644 --- a/app/client/src/actions/autoLayoutActions.ts +++ b/app/client/src/actions/autoLayoutActions.ts @@ -19,3 +19,18 @@ export const widgetViolatedMinDimentionsAction = (parentId: string) => ({ parentId, }, }); + +export function updateWidgetDimensionAction( + widgetId: string, + width: number, + height: number, +) { + return { + type: ReduxActionTypes.UPDATE_WIDGET_DIMENSIONS, + payload: { + widgetId, + width, + height, + }, + }; +} diff --git a/app/client/src/ce/constants/ReduxActionConstants.tsx b/app/client/src/ce/constants/ReduxActionConstants.tsx index e13fd209ae98..a4347e7b9aa6 100644 --- a/app/client/src/ce/constants/ReduxActionConstants.tsx +++ b/app/client/src/ce/constants/ReduxActionConstants.tsx @@ -762,6 +762,9 @@ export const ReduxActionTypes = { AUTOLAYOUT_ADD_NEW_WIDGETS: "AUTOLAYOUT_ADD_NEW_WIDGETS", RECALCULATE_COLUMNS: "RECALCULATE_COLUMNS", WIDGET_VIOLATED_MIN_DIMENSIONS: "WIDGET_VIOLATED_MIN_DIMENSIONS", + UPDATE_WIDGET_DIMENSIONS: "UPDATE_WIDGET_DIMENSIONS", + PROCESS_AUTO_LAYOUT_DIMENSION_UPDATES: + "PROCESS_AUTO_LAYOUT_DIMENSION_UPDATES", }; export type ReduxActionType = typeof ReduxActionTypes[keyof typeof ReduxActionTypes]; diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutDimensionObeserver.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutDimensionObeserver.tsx new file mode 100644 index 000000000000..9af7257775b6 --- /dev/null +++ b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutDimensionObeserver.tsx @@ -0,0 +1,86 @@ +import { FLEXBOX_PADDING } from "constants/WidgetConstants"; +import React, { useState } from "react"; +import { PropsWithChildren, useEffect, useRef } from "react"; +import styled from "styled-components"; +import { ResponsiveBehavior } from "utils/autoLayout/constants"; +import { WidgetProps } from "widgets/BaseWidget"; + +const SimpleContainer = styled.div` + width: fit-content; + &.fill-widget { + width: 100%; + } +`; + +interface AutoLayoutDimensionObserverProps { + onDimensionUpdate: (width: number, height: number) => void; + widgetProps: WidgetProps; +} + +export default function AutoLayoutDimensionObserver( + props: PropsWithChildren, +) { + const { onDimensionUpdate } = props; + const [currentDimension, setCurrentDimension] = useState({ + width: 0, + height: 0, + }); + + const ref = useRef(null); + + const observer = useRef( + new ResizeObserver((entries) => { + const width = entries[0].contentRect.width; + const height = entries[0].contentRect.height; + if (width === 0 || height === 0) return; + setCurrentDimension({ width, height }); + }), + ); + + const boundingBoxWidth = + (props.widgetProps.rightColumn - props.widgetProps.leftColumn) * + props.widgetProps.parentColumnSpace - + 2 * FLEXBOX_PADDING; + + const boundingBoxHeight = + (props.widgetProps.bottomRow - props.widgetProps.topRow) * + props.widgetProps.parentRowSpace - + 2 * FLEXBOX_PADDING; + + useEffect(() => { + const widthDiff = Math.abs(boundingBoxWidth - currentDimension.width); + const heightDiff = Math.abs(boundingBoxHeight - currentDimension.height); + if (currentDimension.width === 0 || currentDimension.height === 0) return; + if (widthDiff > 2 || heightDiff > 2) { + onDimensionUpdate(currentDimension.width, currentDimension.height); + } + }, [ + boundingBoxWidth, + boundingBoxHeight, + currentDimension.width, + currentDimension.height, + ]); + + useEffect(() => { + if (ref.current) { + observer.current.observe(ref.current); + } + + return () => { + observer.current.disconnect(); + }; + }, []); + + return ( + + {props.children} + + ); +} diff --git a/app/client/src/components/editorComponents/EditorContextProvider.tsx b/app/client/src/components/editorComponents/EditorContextProvider.tsx index d5cfe85a1271..e037a8716764 100644 --- a/app/client/src/components/editorComponents/EditorContextProvider.tsx +++ b/app/client/src/components/editorComponents/EditorContextProvider.tsx @@ -48,6 +48,7 @@ import { selectWidgetInitAction, WidgetSelectionRequest, } from "actions/widgetSelectionActions"; +import { updateWidgetDimensionAction } from "actions/autoLayoutActions"; export type EditorContextType = { executeAction?: (triggerPayload: ExecuteTriggerPayload) => void; @@ -77,6 +78,11 @@ export type EditorContextType = { propertyValue: any, ) => void; updateWidgetAutoHeight?: (widgetId: string, height: number) => void; + updateWidgetDimension?: ( + widgetId: string, + width: number, + height: number, + ) => void; checkContainersForAutoHeight?: () => void; modifyMetaWidgets?: (modifications: ModifyMetaWidgetPayload) => void; setWidgetCache?: ( @@ -111,6 +117,7 @@ const COMMON_API_METHODS: EditorContextTypeKey[] = [ "syncUpdateWidgetMetaProperty", "triggerEvalOnMetaUpdate", "updateWidgetAutoHeight", + "updateWidgetDimension", "checkContainersForAutoHeight", "selectWidgetRequest", ]; @@ -206,6 +213,7 @@ const mapDispatchToProps = { batchUpdateWidgetProperty: batchUpdatePropertyAction, triggerEvalOnMetaUpdate: triggerEvalOnMetaUpdate, updateWidgetAutoHeight: updateWidgetAutoHeightAction, + updateWidgetDimension: updateWidgetDimensionAction, checkContainersForAutoHeight: checkContainersForAutoHeightAction, modifyMetaWidgets, updateMetaWidgetProperty, diff --git a/app/client/src/sagas/AutoLayoutUpdateSagas.tsx b/app/client/src/sagas/AutoLayoutUpdateSagas.tsx index d40800dfab4c..3c1909e59ddd 100644 --- a/app/client/src/sagas/AutoLayoutUpdateSagas.tsx +++ b/app/client/src/sagas/AutoLayoutUpdateSagas.tsx @@ -6,15 +6,18 @@ import { } from "ce/constants/ReduxActionConstants"; import log from "loglevel"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; -import { all, put, select, takeLatest } from "redux-saga/effects"; +import { all, debounce, put, select, takeLatest } from "redux-saga/effects"; import { alterLayoutForDesktop, alterLayoutForMobile, + getCanvasDimensions, } from "../utils/autoLayout/AutoLayoutUtils"; import { getWidgets } from "./selectors"; import { updateWidgetPositions } from "utils/autoLayout/positionUtils"; -import { getCanvasWidth } from "selectors/editorSelectors"; import { getIsMobile } from "selectors/mainCanvasSelectors"; +import { getCanvasWidth as getMainCanvasWidth } from "selectors/editorSelectors"; +import { getWidgetMinMaxDimensionsInPixel } from "utils/autoLayout/flexWidgetUtils"; +import { getIsDraggingOrResizing } from "selectors/widgetSelectors"; export function* updateLayoutForMobileCheckpoint( actionPayload: ReduxAction<{ @@ -27,7 +30,7 @@ export function* updateLayoutForMobileCheckpoint( const start = performance.now(); const { canvasWidth, isMobile, parentId } = actionPayload.payload; const allWidgets: CanvasWidgetsReduxState = yield select(getWidgets); - const mainCanvasWidth: number = yield select(getCanvasWidth); + const mainCanvasWidth: number = yield select(getMainCanvasWidth); const updatedWidgets: CanvasWidgetsReduxState = isMobile ? alterLayoutForMobile(allWidgets, parentId, canvasWidth, canvasWidth) : alterLayoutForDesktop(allWidgets, parentId, mainCanvasWidth); @@ -48,26 +51,181 @@ export function* updateLayoutForMobileCheckpoint( } } -const processedParentIds = new Map(); +// const processedParentIds = new Map(); -function* widgetViolatedMinDimensionsSaga( - action: ReduxAction<{ parentId: string }>, +// function* widgetViolatedMinDimensionsSaga( +// action: ReduxAction<{ parentId: string }>, +// ) { +// const isMobile: boolean = yield select(getIsMobile); +// const allWidgets: CanvasWidgetsReduxState = yield select(getWidgets); +// const mainCanvasWidth: number = yield select(getCanvasWidth); + +// const parentId = action.payload.parentId; +// if (processedParentIds.has(parentId)) return; +// processedParentIds.set(parentId, true); +// setTimeout(() => processedParentIds.delete(parentId), 1000); +// const updatedWidgets = updateWidgetPositions( +// allWidgets, +// parentId, +// isMobile, +// mainCanvasWidth, +// ); +// yield put(updateAndSaveLayout(updatedWidgets)); +// } + +let autoLayoutWidgetDimensionUpdateBatch: Record< + string, + { width: number; height: number } +> = {}; + +function addWidgetToAutoLayoutDimensionUpdateBatch( + widgetId: string, + width: number, + height: number, ) { - const isMobile: boolean = yield select(getIsMobile); + autoLayoutWidgetDimensionUpdateBatch[widgetId] = { width, height }; +} + +function* updateWidgetDimensionsSaga( + action: ReduxAction<{ widgetId: string; width: number; height: number }>, +) { + let { height, width } = action.payload; + const { widgetId } = action.payload; const allWidgets: CanvasWidgetsReduxState = yield select(getWidgets); - const mainCanvasWidth: number = yield select(getCanvasWidth); - - const parentId = action.payload.parentId; - if (processedParentIds.has(parentId)) return; - processedParentIds.set(parentId, true); - setTimeout(() => processedParentIds.delete(parentId), 1000); - const updatedWidgets = updateWidgetPositions( - allWidgets, - parentId, - isMobile, + const mainCanvasWidth: number = yield select(getMainCanvasWidth); + const isLayoutUpdating: boolean = yield select(getIsDraggingOrResizing); + + const widget = allWidgets[widgetId]; + const widgetMinMaxDimensions = getWidgetMinMaxDimensionsInPixel( + widget, mainCanvasWidth, ); - yield put(updateAndSaveLayout(updatedWidgets)); + + if ( + widgetMinMaxDimensions.minHeight && + height < widgetMinMaxDimensions.minHeight + ) { + height = widgetMinMaxDimensions.minHeight; + } + if ( + widgetMinMaxDimensions.maxHeight && + height > widgetMinMaxDimensions.maxHeight + ) { + height = widgetMinMaxDimensions.maxHeight; + } + if ( + widgetMinMaxDimensions.minWidth && + width < widgetMinMaxDimensions.minWidth + ) { + width = widgetMinMaxDimensions.minWidth; + } + if ( + widgetMinMaxDimensions.maxWidth && + width > widgetMinMaxDimensions.maxWidth + ) { + width = widgetMinMaxDimensions.maxWidth; + } + + addWidgetToAutoLayoutDimensionUpdateBatch(widgetId, width, height); + if (isLayoutUpdating) return; + yield put({ + type: ReduxActionTypes.PROCESS_AUTO_LAYOUT_DIMENSION_UPDATES, + }); +} + +function* processAutoLayoutDimensionUpdatesSaga() { + const allWidgets: CanvasWidgetsReduxState = yield select(getWidgets); + const mainCanvasWidth: number = yield select(getMainCanvasWidth); + const isMobile: boolean = yield select(getIsMobile); + + let widgets = allWidgets; + const parentIds = new Set(); + // Initialise a list of changes so far. + // This contains a map of widgetIds with their new topRow and bottomRow + const changesSoFar: Record< + string, + { bottomRow?: number; rightColumn?: number } + > = {}; + for (const widgetId in autoLayoutWidgetDimensionUpdateBatch) { + const { height, width } = autoLayoutWidgetDimensionUpdateBatch[widgetId]; + const widget = allWidgets[widgetId]; + if (!widget) continue; + const parentId = widget.parentId; + if (parentId === undefined) continue; + if (parentId) parentIds.add(parentId); + + const { columnSpace } = getCanvasDimensions( + widgets[parentId], + widgets, + mainCanvasWidth, + isMobile, + ); + + const newBottomRow = widget.topRow + height / widget.parentRowSpace; + const newRightColumn = widget.leftColumn + width / columnSpace; + + if ( + widget.bottomRow !== newBottomRow || + widget.rightColumn !== newRightColumn + ) { + changesSoFar[widgetId] = { + bottomRow: newBottomRow, + rightColumn: newRightColumn, + }; + } + + widgets = { + ...widgets, + [widgetId]: { + ...widget, + bottomRow: widget.topRow + height / widget.parentRowSpace, + rightColumn: widget.leftColumn + width / columnSpace, + }, + }; + } + + for (const parentId of parentIds) { + widgets = updateWidgetPositions( + widgets, + parentId, + isMobile, + mainCanvasWidth, + ); + } + + const widgetsToUpdate: any = []; + for (const changedWidgetId in changesSoFar) { + widgetsToUpdate[changedWidgetId] = [ + { + propertyPath: "topRow", + propertyValue: widgets[changedWidgetId].topRow, + }, + { + propertyPath: "bottomRow", + propertyValue: changesSoFar[changedWidgetId].bottomRow, + }, + { + propertyPath: "leftColumn", + propertyValue: widgets[changedWidgetId].leftColumn, + }, + { + propertyPath: "rightColumn", + propertyValue: changesSoFar[changedWidgetId].rightColumn, + }, + ]; + } + + // TODO(aswathkk): Use updateMultipleWidgetPropertiesAction instead of updateAndSaveLayout + // 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)); + + // Save the layout + yield put(updateAndSaveLayout(widgets)); + + // clear the batch after processing + autoLayoutWidgetDimensionUpdateBatch = {}; } export default function* layoutUpdateSagas() { @@ -76,9 +234,18 @@ export default function* layoutUpdateSagas() { ReduxActionTypes.RECALCULATE_COLUMNS, updateLayoutForMobileCheckpoint, ), + // takeLatest( + // ReduxActionTypes.WIDGET_VIOLATED_MIN_DIMENSIONS, + // widgetViolatedMinDimensionsSaga, + // ), takeLatest( - ReduxActionTypes.WIDGET_VIOLATED_MIN_DIMENSIONS, - widgetViolatedMinDimensionsSaga, + ReduxActionTypes.UPDATE_WIDGET_DIMENSIONS, + updateWidgetDimensionsSaga, + ), + debounce( + 50, + ReduxActionTypes.PROCESS_AUTO_LAYOUT_DIMENSION_UPDATES, + processAutoLayoutDimensionUpdatesSaga, ), ]); } diff --git a/app/client/src/utils/autoLayout/AutoLayoutUtils.ts b/app/client/src/utils/autoLayout/AutoLayoutUtils.ts index 547edebd6a3b..10d0aad58ba3 100644 --- a/app/client/src/utils/autoLayout/AutoLayoutUtils.ts +++ b/app/client/src/utils/autoLayout/AutoLayoutUtils.ts @@ -4,6 +4,7 @@ import { layoutConfigurations, GridDefaults, MAIN_CONTAINER_WIDGET_ID, + WIDGET_PADDING, } from "constants/WidgetConstants"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; @@ -16,6 +17,7 @@ import { import { updateWidgetPositions } from "utils/autoLayout/positionUtils"; import { getWidgetWidth } from "./flexWidgetUtils"; import { AlignmentColumnInfo } from "./autoLayoutTypes"; +import { Widget } from "./positionUtils"; function getCanvas(widgets: CanvasWidgetsReduxState, containerId: string) { const container = widgets[containerId]; @@ -517,3 +519,64 @@ export function getAlignmentColumnInfo( [FlexLayerAlignment.End]: end, }; } + +export function getCanvasDimensions( + canvas: Widget, + widgets: CanvasWidgetsReduxState, + mainCanvasWidth: number, + isMobile: boolean, +): { canvasWidth: number; columnSpace: number } { + const canvasWidth: number = getCanvasWidth( + canvas, + widgets, + mainCanvasWidth, + isMobile, + ); + + const columnSpace: number = canvasWidth / GridDefaults.DEFAULT_GRID_COLUMNS; + + return { canvasWidth: canvasWidth, columnSpace }; +} + +function getCanvasWidth( + canvas: Widget, + widgets: CanvasWidgetsReduxState, + mainCanvasWidth: number, + isMobile: boolean, +): number { + if (!mainCanvasWidth) return 0; + if (canvas.widgetId === MAIN_CONTAINER_WIDGET_ID) + return mainCanvasWidth - getPadding(canvas); + let widget = canvas; + let columns = 0; + let width = 1; + let padding = 0; + while (widget.parentId) { + columns = getWidgetWidth(widget, isMobile); + padding += getPadding(widget); + width *= columns > 64 ? 1 : columns / GridDefaults.DEFAULT_GRID_COLUMNS; + widget = widgets[widget.parentId]; + } + const totalWidth = width * mainCanvasWidth; + if (widget.widgetId === MAIN_CONTAINER_WIDGET_ID) + padding += getPadding(widget); + return totalWidth - padding; +} + +function getPadding(canvas: Widget): number { + let padding = 0; + if ( + canvas.widgetId === MAIN_CONTAINER_WIDGET_ID || + canvas.type === "CONTAINER_WIDGET" + ) { + //For MainContainer and any Container Widget padding doesn't exist coz there is already container padding. + padding = FLEXBOX_PADDING * 2; + } + if (canvas.noPad) { + // Widgets like ListWidget choose to have no container padding so will only have widget padding + padding = WIDGET_PADDING * 2; + } + // Account for container border. + padding += canvas.type === "CONTAINER_WIDGET" ? 2 : 0; + return padding; +} diff --git a/app/client/src/utils/autoLayout/flexWidgetUtils.ts b/app/client/src/utils/autoLayout/flexWidgetUtils.ts index 5a32108fd53e..380566a76bb1 100644 --- a/app/client/src/utils/autoLayout/flexWidgetUtils.ts +++ b/app/client/src/utils/autoLayout/flexWidgetUtils.ts @@ -1,9 +1,11 @@ import WidgetFactory from "utils/WidgetFactory"; import { WidgetSizeConfig } from "widgets/constants"; -export interface MinSize { +export interface MinMaxSize { minHeight: number | string; + maxHeight: number | string; minWidth: number | string; + maxWidth: number | string; } export function getRightColumn(widget: any, isMobile: boolean): number { @@ -121,23 +123,25 @@ export function getWidgetRows(widget: any, isMobile: boolean): number { } /** - * Calculates the minimum size of a widget based on the widget type and the canvas width. + * Calculates the minimum & maximum size of a widget based on the widget type and the canvas width. * @param widget | Widget props * @param canvasWidth | number : main canvas width. * @returns MinSize | undefined */ -export function getMinSize( +export function getMinMaxSize( widget: any, canvasWidth: number, -): MinSize | undefined { +): MinMaxSize | undefined { // Get the widget size configuration. const sizeConfig = getCurrentSizeConfig(widget, canvasWidth); if (!sizeConfig) return; - // Get the minimum size for the widget at this breakpoint. - const { minHeight, minWidth } = sizeConfig.configuration(widget); + // Get the minimum & maximum size for the widget at this breakpoint. + const { maxHeight, maxWidth, minHeight, minWidth } = sizeConfig.configuration( + widget, + ); - return { minHeight, minWidth }; + return { maxHeight, maxWidth, minHeight, minWidth }; } export function getCurrentSizeConfig( @@ -167,7 +171,7 @@ export function getMinPixelWidth( canvasWidth: number, ): number | undefined { if (!widget) return; - const minSize = getMinSize(widget, canvasWidth); + const minSize = getMinMaxSize(widget, canvasWidth); if (!minSize) return; const arr: string[] = typeof minSize.minWidth === "string" ? minSize.minWidth.split("px") : []; @@ -175,3 +179,52 @@ export function getMinPixelWidth( if (typeof minSize.minWidth === "number") return minSize.minWidth * widget.parentColumnSpace; } + +function getPxValue(val: string | number, factor: number): number | undefined { + const arr: string[] = typeof val === "string" ? val.split("px") : []; + if (arr.length) return parseInt(arr[0]); + if (typeof val === "number") return val * factor; +} + +/** + * Return the widget dimension constraints based on the widget type and the canvas width. + * size can be configured in columns (number) or pixels (string). + * Return an appropriate pixel width & height based on the size type. + */ +export function getWidgetMinMaxDimensionsInPixel( + widget: any, + canvasWidth: number, +): { [key in keyof MinMaxSize]: number | undefined } { + const returnValue: { [key in keyof MinMaxSize]: number | undefined } = { + minWidth: undefined, + minHeight: undefined, + maxWidth: undefined, + maxHeight: undefined, + }; + + if (!widget) return returnValue; + const minMaxSize = getMinMaxSize(widget, canvasWidth); + if (!minMaxSize) return returnValue; + + returnValue.minWidth = getPxValue( + minMaxSize.minWidth, + widget.parentColumnSpace, + ); + + returnValue.maxWidth = getPxValue( + minMaxSize.maxWidth, + widget.parentColumnSpace, + ); + + returnValue.minHeight = getPxValue( + minMaxSize.minHeight, + widget.parentRowSpace, + ); + + returnValue.maxHeight = getPxValue( + minMaxSize.maxHeight, + widget.parentRowSpace, + ); + + return returnValue; +} diff --git a/app/client/src/utils/autoLayout/positionUtils.ts b/app/client/src/utils/autoLayout/positionUtils.ts index 6ba0fabe18a1..3f18deb5c8ea 100644 --- a/app/client/src/utils/autoLayout/positionUtils.ts +++ b/app/client/src/utils/autoLayout/positionUtils.ts @@ -19,7 +19,7 @@ import { getWidgetWidth, setDimensions, } from "./flexWidgetUtils"; -import { getCanvasWidth } from "./highlightUtils"; +import { getCanvasDimensions } from "./AutoLayoutUtils"; export type Widget = WidgetProps & { children?: string[] | undefined; @@ -59,13 +59,12 @@ export function updateWidgetPositions( const parent = widgets[parentId]; if (!parent) return widgets; - const canvasWidth = getCanvasWidth( + const { columnSpace } = getCanvasDimensions( parent, widgets, mainCanvasWidth, isMobile, ); - const columnSpace = canvasWidth / GridDefaults.DEFAULT_GRID_COLUMNS; let height = 0; if (parent.flexLayers && parent.flexLayers?.length) { diff --git a/app/client/src/utils/layoutPropertiesUtils.ts b/app/client/src/utils/layoutPropertiesUtils.ts index 452558cd709f..0060c03e46a6 100644 --- a/app/client/src/utils/layoutPropertiesUtils.ts +++ b/app/client/src/utils/layoutPropertiesUtils.ts @@ -227,6 +227,7 @@ export function getLayoutConfig(alignment: Alignment, spacing: Spacing): any[] { export const NonResizableWidgets = [ "AUDIO_WIDGET", "BUTTON_WIDGET", + "FILE_PICKER_WIDGET_V2", "BUTTON_GROUP_WIDGET", "CHECKBOX_WIDGET", "CURRENCY_INPUT_WIDGET", @@ -265,7 +266,6 @@ export const DefaultFillWidgets = [ "DATE_PICKER_WIDGET2", "DIVIDER_WIDGET", "FORM_WIDGET", - "FILE_PICKER_WIDGET_V2", "INPUT_WIDGET_V2", "JSON_FORM_WIDGET", "LIST_WIDGET", @@ -289,6 +289,13 @@ export const DefaultFillWidgets = [ "NUMBER_SLIDER_WIDGET", ]; +// TODO(aswathkk): See if this needs to be moved to widget config +export const WIDGET_WITH_DYNAMIC_WIDTH = ["BUTTON_WIDGET"]; + +// TODO(aswathkk): See if this needs to be moved to widget config +// This is used only for autoLayout +export const WIDGET_WITH_DYNAMIC_HEIGHT = ["CHECKBOX_GROUP_WIDGET"]; + export function getDefaultResponsiveBehavior(widgetType: string) { return DefaultFillWidgets.includes(widgetType) ? ResponsiveBehavior.Fill diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index cdfbc234c4c4..b8af83fc1615 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -23,6 +23,7 @@ import { ExecuteTriggerPayload } from "constants/AppsmithActionConstants/ActionC import { PropertyPaneConfig } from "constants/PropertyControlConstants"; import { CSSUnit, + FLEXBOX_PADDING, GridDefaults, PositionType, RenderMode, @@ -66,6 +67,11 @@ import { UpdateMetaWidgetPropertyPayload, } from "reducers/entityReducers/metaWidgetsReducer"; import { SelectionRequestType } from "sagas/WidgetSelectUtils"; +import AutoLayoutDimensionObserver from "components/designSystems/appsmith/autoLayout/AutoLayoutDimensionObeserver"; +import { + WIDGET_WITH_DYNAMIC_HEIGHT, + WIDGET_WITH_DYNAMIC_WIDTH, +} from "utils/layoutPropertiesUtils"; /*** * BaseWidget @@ -607,6 +613,7 @@ abstract class BaseWidget< // Adding a check for the Modal Widget early // to avoid deselect Modal in its unmount effect. if ( + !this.props.isFlexChild && isAutoHeightEnabledForWidget(this.props) && !this.props.isAutoGeneratedWidget && // To skip list widget's auto generated widgets !this.props.detachFromLayout // To skip Modal widget issue #18697 @@ -620,9 +627,38 @@ abstract class BaseWidget< ); } + if (this.props.isFlexChild && !this.props.detachFromLayout) { + const shouldObserveWidth = WIDGET_WITH_DYNAMIC_WIDTH.includes( + this.props.type, + ); + const shouldObserveHeight = WIDGET_WITH_DYNAMIC_HEIGHT.includes( + this.props.type, + ); + + if (!shouldObserveHeight && !shouldObserveWidth) return content; + + return ( + + {content} + + ); + } return this.addErrorBoundary(content); }; + private updateWidgetDimensions = (width: number, height: number) => { + const { updateWidgetDimension } = this.context; + if (!updateWidgetDimension) return; + updateWidgetDimension( + this.props.widgetId, + width + 2 * FLEXBOX_PADDING, + height + 2 * FLEXBOX_PADDING, + ); + }; + private getWidgetView(): ReactNode { let content: ReactNode; diff --git a/app/client/src/widgets/ButtonWidget/component/DragContainer.tsx b/app/client/src/widgets/ButtonWidget/component/DragContainer.tsx index d4eb417d1147..0efda4fbdb89 100644 --- a/app/client/src/widgets/ButtonWidget/component/DragContainer.tsx +++ b/app/client/src/widgets/ButtonWidget/component/DragContainer.tsx @@ -23,7 +23,7 @@ import { buttonHoverActiveStyles } from "./utils"; /* For the Button Widget we don't remove the DragContainer - because of the Tooltip issue - + because of the Tooltip issue - https://github.com/appsmithorg/appsmith/pull/12372 For this reason we pass the showInAllModes prop. */ @@ -45,6 +45,14 @@ const ButtonContainer = styled.div` height: 100%; } + .auto-layout & > .bp3-button.bp3-fill { + display: flex; + width: auto; + max-width: 352px; + min-width: 112px; + min-height: 32px; + } + position: relative; &:after { content: ""; diff --git a/app/client/src/widgets/ButtonWidget/index.ts b/app/client/src/widgets/ButtonWidget/index.ts index 084bb982d583..929437d9c3de 100644 --- a/app/client/src/widgets/ButtonWidget/index.ts +++ b/app/client/src/widgets/ButtonWidget/index.ts @@ -41,6 +41,19 @@ export const CONFIG = { styleConfig: Widget.getPropertyPaneStyleConfig(), stylesheetConfig: Widget.getStylesheetConfig(), }, + autoLayout: { + widgetSize: [ + { + viewportMinWidth: 0, + configuration: () => { + return { + minWidth: "120px", + maxWidth: "360px", + }; + }, + }, + ], + }, }; export default Widget; diff --git a/app/client/src/widgets/FilePickerWidgetV2/index.ts b/app/client/src/widgets/FilePickerWidgetV2/index.ts index a4cf9c0f90fe..63e3fa8b3f79 100644 --- a/app/client/src/widgets/FilePickerWidgetV2/index.ts +++ b/app/client/src/widgets/FilePickerWidgetV2/index.ts @@ -1,4 +1,4 @@ -import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; +import { BUTTON_MIN_WIDTH } from "constants/minWidthConstants"; import { getDefaultResponsiveBehavior } from "utils/layoutPropertiesUtils"; import FileDataTypes from "./constants"; import IconSVG from "./icon.svg"; @@ -28,7 +28,7 @@ export const CONFIG = { isDisabled: false, animateLoading: true, responsiveBehavior: getDefaultResponsiveBehavior(Widget.getWidgetType()), - minWidth: FILL_WIDGET_MIN_WIDTH, + minWidth: BUTTON_MIN_WIDTH, }, properties: { derived: Widget.getDerivedPropertiesMap(), @@ -39,6 +39,19 @@ export const CONFIG = { contentConfig: Widget.getPropertyPaneContentConfig(), stylesheetConfig: Widget.getStylesheetConfig(), }, + autoLayout: { + widgetSize: [ + { + viewportMinWidth: 0, + configuration: () => { + return { + minWidth: "120px", + maxWidth: "360px", + }; + }, + }, + ], + }, }; export default Widget; diff --git a/app/client/src/widgets/IconButtonWidget/component/index.tsx b/app/client/src/widgets/IconButtonWidget/component/index.tsx index e335d3ff1cd2..0ec34e8d13ce 100644 --- a/app/client/src/widgets/IconButtonWidget/component/index.tsx +++ b/app/client/src/widgets/IconButtonWidget/component/index.tsx @@ -135,6 +135,13 @@ export const StyledButton = styled((props) => ( line-height: ${({ compactMode }) => compactMode === "SHORT" ? "24px" : "28px"}; + .auto-layout & { + min-height: 32px; + min-width: 32px; + height: 32px; + width: 32px; + } + ${({ buttonColor, buttonVariant, compactMode, hasOnClickAction, theme }) => ` &:enabled { background: ${ diff --git a/app/client/src/widgets/IconButtonWidget/index.ts b/app/client/src/widgets/IconButtonWidget/index.ts index e30e7052d06d..f43e2bea2bf4 100644 --- a/app/client/src/widgets/IconButtonWidget/index.ts +++ b/app/client/src/widgets/IconButtonWidget/index.ts @@ -14,8 +14,8 @@ export const CONFIG = { buttonVariant: ButtonVariantTypes.PRIMARY, isDisabled: false, isVisible: true, - rows: 4, - columns: 4, + rows: 5, + columns: 5, widgetName: "IconButton", version: 1, animateLoading: true, @@ -30,6 +30,18 @@ export const CONFIG = { styleConfig: Widget.getPropertyPaneStyleConfig(), stylesheetConfig: Widget.getStylesheetConfig(), }, + autoLayout: { + widgetSize: [ + { + viewportMinWidth: 0, + configuration: () => { + return { + minWidth: "40px", + }; + }, + }, + ], + }, }; export default Widget; diff --git a/app/client/src/widgets/MenuButtonWidget/index.ts b/app/client/src/widgets/MenuButtonWidget/index.ts index b10f0ff3b22b..95afcf673e31 100644 --- a/app/client/src/widgets/MenuButtonWidget/index.ts +++ b/app/client/src/widgets/MenuButtonWidget/index.ts @@ -56,6 +56,19 @@ export const CONFIG = { styleConfig: Widget.getPropertyPaneStyleConfig(), stylesheetConfig: Widget.getStylesheetConfig(), }, + autoLayout: { + widgetSize: [ + { + viewportMinWidth: 0, + configuration: () => { + return { + minWidth: "120px", + maxWidth: "360px", + }; + }, + }, + ], + }, }; export default Widget; From 0a398b0a0feb54dbf2f7eb9cc52488f480f99344 Mon Sep 17 00:00:00 2001 From: Aswath K Date: Tue, 21 Feb 2023 14:56:26 +0530 Subject: [PATCH 527/708] Adds button like widgets for auto width --- app/client/src/utils/layoutPropertiesUtils.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/client/src/utils/layoutPropertiesUtils.ts b/app/client/src/utils/layoutPropertiesUtils.ts index 0060c03e46a6..41435d8238ff 100644 --- a/app/client/src/utils/layoutPropertiesUtils.ts +++ b/app/client/src/utils/layoutPropertiesUtils.ts @@ -227,7 +227,6 @@ export function getLayoutConfig(alignment: Alignment, spacing: Spacing): any[] { export const NonResizableWidgets = [ "AUDIO_WIDGET", "BUTTON_WIDGET", - "FILE_PICKER_WIDGET_V2", "BUTTON_GROUP_WIDGET", "CHECKBOX_WIDGET", "CURRENCY_INPUT_WIDGET", @@ -290,7 +289,12 @@ export const DefaultFillWidgets = [ ]; // TODO(aswathkk): See if this needs to be moved to widget config -export const WIDGET_WITH_DYNAMIC_WIDTH = ["BUTTON_WIDGET"]; +export const WIDGET_WITH_DYNAMIC_WIDTH = [ + "BUTTON_WIDGET", + "ICON_BUTTON_WIDGET", + "MENU_BUTTON_WIDGET", + "FILE_PICKER_WIDGET_V2", +]; // TODO(aswathkk): See if this needs to be moved to widget config // This is used only for autoLayout From c67c5e785dd99cc8db9fa545737cd69b00f0ef89 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Tue, 21 Feb 2023 17:08:26 +0530 Subject: [PATCH 528/708] enabling dynamic height under the hood for autolayout. --- app/client/src/widgets/WidgetUtils.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/app/client/src/widgets/WidgetUtils.ts b/app/client/src/widgets/WidgetUtils.ts index e667155a11da..5981d000eefa 100644 --- a/app/client/src/widgets/WidgetUtils.ts +++ b/app/client/src/widgets/WidgetUtils.ts @@ -743,7 +743,6 @@ export const isAutoHeightEnabledForWidget = ( props: WidgetProps, shouldCheckIfEnabledWithLimits = false, ) => { - if (props.isFlexChild) return false; if (shouldCheckIfEnabledWithLimits) { return props.dynamicHeight === DynamicHeight.AUTO_HEIGHT_WITH_LIMITS; } From 60d4b33aa078e606c8bfea96b7a7d1be7dfd5ecb Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Tue, 21 Feb 2023 17:09:24 +0530 Subject: [PATCH 529/708] add Canvas Resizer. --- .../Editor/WidgetsEditor/CanvasContainer.tsx | 159 +--------------- app/client/src/widgets/CanvasResizer.tsx | 176 ++++++++++++++++++ 2 files changed, 181 insertions(+), 154 deletions(-) create mode 100644 app/client/src/widgets/CanvasResizer.tsx diff --git a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx index d536b4994a5e..60e9c929e74b 100644 --- a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx +++ b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx @@ -1,12 +1,9 @@ -import { ReactComponent as CanvasResizer } from "assets/icons/ads/app-icons/canvas-resizer.svg"; import React, { ReactNode, useEffect } from "react"; import { useSelector } from "react-redux"; import { getCanvasScale, getCanvasWidth, - getCurrentApplicationLayout, - getCurrentAppPositioningType, getCurrentPageId, getIsFetchingPage, getViewModePageList, @@ -30,55 +27,11 @@ import { } from "selectors/appThemingSelectors"; import { getCanvasWidgetsStructure } from "selectors/entitiesSelector"; import { getCurrentThemeDetails } from "selectors/themeSelectors"; -import { - AUTOLAYOUT_RESIZER_WIDTH_BUFFER, - useDynamicAppLayout, -} from "utils/hooks/useDynamicAppLayout"; +import { useDynamicAppLayout } from "utils/hooks/useDynamicAppLayout"; import useGoogleFont from "utils/hooks/useGoogleFont"; -// import { noop } from "utils/AppsmithUtils"; -// import useHorizontalResize from "utils/hooks/useHorizontalResize"; -import { layoutConfigurations } from "constants/WidgetConstants"; -import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; +import { CanvasResizer } from "widgets/CanvasResizer"; import Canvas from "../Canvas"; -const AutoLayoutCanvasResizer = styled.div` - position: sticky; - cursor: col-resize; - width: 2px; - height: 100%; - display: flex; - background: #d9d9d9; - align-items: center; - justify-content: flex-start; - z-index: 2; - transition: width 300ms ease; - transition: background 300ms ease; - .canvas-resizer-icon { - border-left: 2px solid; - border-color: #d7d7d7; - transition: border 300ms ease; - margin-left: 2px; - & > svg { - fill: #d7d7d7; - transition: fill 300ms ease; - } - } - &:hover, - &:active { - width: 3px; - transition: width 300ms ease; - background: #ff9b4e; - transition: background 300ms ease; - .canvas-resizer-icon { - border-color: #ff9b4e; - transition: border 300ms ease; - & > svg { - fill: #ff9b4e; - transition: fill 300ms ease; - } - } - } -`; const Container = styled.section<{ background: string; }>` @@ -145,91 +98,6 @@ function CanvasContainer() { /> ); } - const appPositioningType = useSelector(getCurrentAppPositioningType); - const appLayout = useSelector(getCurrentApplicationLayout); - useEffect(() => { - if (appPositioningType === AppPositioningTypes.AUTO) { - let buffer = 0; - const ele: any = document.getElementById("canvas-viewport"); - if (isPreviewMode) { - ele.style.width = "inherit"; - buffer = AUTOLAYOUT_RESIZER_WIDTH_BUFFER; - } else { - ele.style.width = "100%"; - } - if (appLayout?.type === "FLUID") { - const smallestWidth = layoutConfigurations.MOBILE.minWidth; - // Query the element - const ele: any = document.getElementById("canvas-viewport"); - let needsInitiation = true; - let initialWidth = ele.offsetWidth; - // The current position of mouse - let x = 0; - // let y = 0; - - // The dimension of the element - let w = 0; - // let h = 0; - let events: any = []; - - // Handle the mousedown event - // that's triggered when user drags the resizer - const mouseDownHandler = function(e: any) { - if (needsInitiation) { - initialWidth = ele.offsetWidth; - needsInitiation = false; - } - // Get the current mouse position - x = e.clientX; - // y = e.clientY; - - // Calculate the dimension of element - const styles = window.getComputedStyle(ele); - w = parseInt(styles.width, 10) + buffer; - // h = parseInt(styles.height, 10); - const mouseMove = (e: any) => mouseMoveHandler(e); - events.push(mouseMove); - // Attach the listeners to `document` - document.addEventListener("mousemove", mouseMove); - document.addEventListener("mouseup", mouseUpHandler); - // e.stopPropagation(); - }; - - const mouseMoveHandler = function(e: any) { - // How far the mouse has been moved - // const multiplier = rightHandle ? 2 : -2; - const multiplier = 2; - const dx = (e.clientX - x) * multiplier; - if (initialWidth >= w + dx && smallestWidth <= w + dx) { - // Adjust the dimension of element - ele.style.width = `${w + dx}px`; - } - if (initialWidth < w + dx) { - ele.style.width = `${initialWidth}px`; - } - if (smallestWidth > w + dx) { - ele.style.width = `${smallestWidth}px`; - } - // e.stopPropagation(); - }; - - const mouseUpHandler = function(e: any) { - // Remove the handlers of `mousemove` and `mouseup` - mouseMoveHandler(e); - document.removeEventListener("mousemove", events[0] as any); - document.removeEventListener("mouseup", mouseUpHandler); - events = []; - }; - const rightResizer: any = ele.querySelectorAll(".resizer-right")[0]; - const rightMove = (e: any) => mouseDownHandler(e); - rightResizer.addEventListener("mousedown", rightMove); - - return () => { - rightResizer.removeEventListener("mousedown", rightMove); - }; - } - } - }, [appLayout, isPreviewMode, currentPageId, appPositioningType]); // calculating exact height to not allow scroll at this component, // calculating total height minus margin on top, top bar and bottom bar @@ -264,26 +132,9 @@ function CanvasContainer() {
)} {node} - {appPositioningType === AppPositioningTypes.AUTO && ( - { - e.preventDefault(); - e.stopPropagation(); - }} - style={{ - left: isPreviewMode - ? `calc(100% - ${20}px)` - : `calc(100% - ${37}px)`, - bottom: isPreviewMode ? "-3px" : "0%", - }} - > -
- -
-
- )} + ); } diff --git a/app/client/src/widgets/CanvasResizer.tsx b/app/client/src/widgets/CanvasResizer.tsx new file mode 100644 index 000000000000..c28fc906e1b0 --- /dev/null +++ b/app/client/src/widgets/CanvasResizer.tsx @@ -0,0 +1,176 @@ +import { ReactComponent as CanvasResizerIcon } from "assets/icons/ads/app-icons/canvas-resizer.svg"; +import { layoutConfigurations } from "constants/WidgetConstants"; +import React, { useEffect, useRef } from "react"; +import { useSelector } from "react-redux"; +import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; +import { + getCurrentApplicationLayout, + getCurrentAppPositioningType, + getCurrentPageId, + previewModeSelector, +} from "selectors/editorSelectors"; +import styled from "styled-components"; +import { AUTOLAYOUT_RESIZER_WIDTH_BUFFER } from "utils/hooks/useDynamicAppLayout"; + +const AutoLayoutCanvasResizer = styled.div` + position: sticky; + cursor: col-resize; + width: 2px; + height: 100%; + display: flex; + background: #d9d9d9; + align-items: center; + justify-content: flex-start; + z-index: 2; + transition: width 300ms ease; + transition: background 300ms ease; + .canvas-resizer-icon { + border-left: 2px solid; + border-color: #d7d7d7; + transition: border 300ms ease; + margin-left: 2px; + & > svg { + fill: #d7d7d7; + transition: fill 300ms ease; + } + } + &:hover, + &:active { + width: 3px; + transition: width 300ms ease; + background: #ff9b4e; + transition: background 300ms ease; + .canvas-resizer-icon { + border-color: #ff9b4e; + transition: border 300ms ease; + & > svg { + fill: #ff9b4e; + transition: fill 300ms ease; + } + } + } +`; +export function CanvasResizer({ + isPageInitiated, +}: { + isPageInitiated: boolean; +}) { + const isPreviewMode = useSelector(previewModeSelector); + const currentPageId = useSelector(getCurrentPageId); + const appLayout = useSelector(getCurrentApplicationLayout) || "FLUID"; + const appPositioningType = useSelector(getCurrentAppPositioningType); + const ref = useRef(null); + useEffect(() => { + let ele: any = document.getElementById("canvas-viewport"); + if ( + isPageInitiated && + ele && + appPositioningType === AppPositioningTypes.AUTO + ) { + let buffer = 0; + if (isPreviewMode) { + ele.style.width = "inherit"; + buffer = AUTOLAYOUT_RESIZER_WIDTH_BUFFER; + } else { + ele.style.width = "100%"; + } + if (appLayout?.type === "FLUID") { + const smallestWidth = layoutConfigurations.MOBILE.minWidth; + // Query the element + ele = document.getElementById("canvas-viewport"); + let needsInitiation = true; + let initialWidth = ele.offsetWidth; + // The current position of mouse + let x = 0; + // let y = 0; + + // The dimension of the element + let w = 0; + // let h = 0; + let events: any = []; + + // Handle the mousedown event + // that's triggered when user drags the resizer + const mouseDownHandler = function(e: any) { + if (needsInitiation) { + initialWidth = ele.offsetWidth; + needsInitiation = false; + } + // Get the current mouse position + x = e.clientX; + // y = e.clientY; + + // Calculate the dimension of element + const styles = window.getComputedStyle(ele); + w = parseInt(styles.width, 10) + buffer; + // h = parseInt(styles.height, 10); + const mouseMove = (e: any) => mouseMoveHandler(e); + events.push(mouseMove); + // Attach the listeners to `document` + document.addEventListener("mousemove", mouseMove); + document.addEventListener("mouseup", mouseUpHandler); + // e.stopPropagation(); + }; + + const mouseMoveHandler = function(e: any) { + // How far the mouse has been moved + // const multiplier = rightHandle ? 2 : -2; + const multiplier = 2; + const dx = (e.clientX - x) * multiplier; + if (initialWidth >= w + dx && smallestWidth <= w + dx) { + // Adjust the dimension of element + ele.style.width = `${w + dx}px`; + } + if (initialWidth < w + dx) { + ele.style.width = `${initialWidth}px`; + } + if (smallestWidth > w + dx) { + ele.style.width = `${smallestWidth}px`; + } + // e.stopPropagation(); + }; + + const mouseUpHandler = function(e: any) { + // Remove the handlers of `mousemove` and `mouseup` + mouseMoveHandler(e); + document.removeEventListener("mousemove", events[0] as any); + document.removeEventListener("mouseup", mouseUpHandler); + events = []; + }; + const rightResizer: any = ref.current; + const rightMove = (e: any) => mouseDownHandler(e); + rightResizer && rightResizer.addEventListener("mousedown", rightMove); + + return () => { + rightResizer && + rightResizer.removeEventListener("mousedown", rightMove); + }; + } + } + }, [ + appLayout, + isPreviewMode, + currentPageId, + appPositioningType, + isPageInitiated, + ]); + return appPositioningType === AppPositioningTypes.AUTO ? ( + { + e.preventDefault(); + e.stopPropagation(); + }} + ref={ref} + style={{ + left: isPreviewMode ? `calc(100% - ${20}px)` : `calc(100% - ${37}px)`, + bottom: isPreviewMode ? "-3px" : "0%", + }} + > +
+ +
+
+ ) : null; +} From e350a2e675ccd7c4c72fa593afd3822177453007 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Tue, 21 Feb 2023 17:09:50 +0530 Subject: [PATCH 530/708] Adding alignment based resizing behaviour for autolayout. --- .../editorComponents/ResizableComponent.tsx | 47 +- .../editorComponents/ResizableUtils.ts | 2 +- .../WidgetNameComponent/index.tsx | 2 +- .../src/resizable/autolayoutresize/index.tsx | 646 ++++++++++++++++++ app/client/src/resizable/common.tsx | 198 ++++++ .../{resize => modalresize}/index.tsx | 2 +- .../src/resizable/resizenreflow/index.tsx | 350 ++-------- 7 files changed, 922 insertions(+), 325 deletions(-) create mode 100644 app/client/src/resizable/autolayoutresize/index.tsx create mode 100644 app/client/src/resizable/common.tsx rename app/client/src/resizable/{resize => modalresize}/index.tsx (99%) diff --git a/app/client/src/components/editorComponents/ResizableComponent.tsx b/app/client/src/components/editorComponents/ResizableComponent.tsx index eaae309b7119..910d2853a293 100644 --- a/app/client/src/components/editorComponents/ResizableComponent.tsx +++ b/app/client/src/components/editorComponents/ResizableComponent.tsx @@ -2,14 +2,18 @@ import { AppState } from "@appsmith/reducers"; import { batchUpdateMultipleWidgetProperties } from "actions/controlActions"; import { focusWidget } from "actions/widgetActions"; import { EditorContext } from "components/editorComponents/EditorContextProvider"; +import { OccupiedSpace } from "constants/CanvasEditorConstants"; import { GridDefaults } from "constants/WidgetConstants"; import { get, omit } from "lodash"; import { XYCord } from "pages/common/CanvasArenas/hooks/useRenderBlocksOnCanvas"; import React, { memo, useContext, useMemo } from "react"; import { useDispatch, useSelector } from "react-redux"; -import Resizable from "resizable/resizenreflow"; +import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; +import { ReflowResizable as AutoLayoutResizable } from "resizable/autolayoutresize"; +import { ReflowResizable as FixedLayoutResizable } from "resizable/resizenreflow"; import { SelectionRequestType } from "sagas/WidgetSelectUtils"; import { + getCurrentAppPositioningType, previewModeSelector, snipingModeSelector, } from "selectors/editorSelectors"; @@ -37,11 +41,7 @@ import { } from "widgets/BaseWidget"; import { isAutoHeightEnabledForWidget } from "widgets/WidgetUtils"; import { DropTargetContext } from "./DropTargetComponent"; -import { - computeFinalRowCols, - computeRowCols, - UIElementSize, -} from "./ResizableUtils"; +import { computeFinalRowCols, UIElementSize } from "./ResizableUtils"; import { BottomHandleStyles, BottomLeftHandleStyles, @@ -64,7 +64,9 @@ export const ResizableComponent = memo(function ResizableComponent( // Fetch information from the context const { updateWidget } = useContext(EditorContext); const dispatch = useDispatch(); - + const isAutoLayout = + useSelector(getCurrentAppPositioningType) === AppPositioningTypes.AUTO; + const Resizable = isAutoLayout ? AutoLayoutResizable : FixedLayoutResizable; const isSnipingMode = useSelector(snipingModeSelector); const isPreviewMode = useSelector(previewModeSelector); @@ -126,15 +128,7 @@ export const ResizableComponent = memo(function ResizableComponent( }; // onResize handler - const getResizedPositions = ( - newDimensions: UIElementSize, - position: XYCord, - ) => { - const delta: UIElementSize = { - height: newDimensions.height - dimensions.height, - width: newDimensions.width - dimensions.width, - }; - const newRowCols: WidgetRowCols = computeRowCols(delta, position, props); + const getResizedPositions = (resizedPositions: OccupiedSpace) => { let canResizeVertically = true; let canResizeHorizontally = true; @@ -146,27 +140,21 @@ export const ResizableComponent = memo(function ResizableComponent( }; if ( - newRowCols && - (newRowCols.rightColumn > getSnapColumns() || - newRowCols.leftColumn < 0 || - newRowCols.rightColumn - newRowCols.leftColumn < 2) + resizedPositions && + (resizedPositions.right > getSnapColumns() || + resizedPositions.left < 0 || + resizedPositions.right - resizedPositions.left < 2) ) { canResizeHorizontally = false; } if ( - newRowCols && - (newRowCols.topRow < 0 || newRowCols.bottomRow - newRowCols.topRow < 4) + resizedPositions && + (resizedPositions.top < 0 || + resizedPositions.bottom - resizedPositions.top < 4) ) { canResizeVertically = false; } - const resizedPositions = { - id: props.widgetId, - left: newRowCols.leftColumn, - top: newRowCols.topRow, - bottom: newRowCols.bottomRow, - right: newRowCols.rightColumn, - }; if (isAutoHeightEnabledForWidget(props)) { canResizeVertically = false; @@ -178,7 +166,6 @@ export const ResizableComponent = memo(function ResizableComponent( return { canResizeHorizontally, canResizeVertically, - resizedPositions, }; }; diff --git a/app/client/src/components/editorComponents/ResizableUtils.ts b/app/client/src/components/editorComponents/ResizableUtils.ts index 8f99e743ba76..de2cb4de1b3a 100644 --- a/app/client/src/components/editorComponents/ResizableUtils.ts +++ b/app/client/src/components/editorComponents/ResizableUtils.ts @@ -1,8 +1,8 @@ -import { WidgetProps, WidgetRowCols } from "widgets/BaseWidget"; import { GridDefaults } from "constants/WidgetConstants"; import { XYCord } from "pages/common/CanvasArenas/hooks/useRenderBlocksOnCanvas"; import { ReflowDirection } from "reflow/reflowTypes"; import { ResponsiveBehavior } from "utils/autoLayout/constants"; +import { WidgetProps, WidgetRowCols } from "widgets/BaseWidget"; export type UIElementSize = { height: number; width: number }; diff --git a/app/client/src/components/editorComponents/WidgetNameComponent/index.tsx b/app/client/src/components/editorComponents/WidgetNameComponent/index.tsx index 33807e5f9dac..6553e4ef1436 100644 --- a/app/client/src/components/editorComponents/WidgetNameComponent/index.tsx +++ b/app/client/src/components/editorComponents/WidgetNameComponent/index.tsx @@ -6,7 +6,7 @@ import { WidgetType } from "constants/WidgetConstants"; import React from "react"; import { useDispatch, useSelector } from "react-redux"; import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; -import { RESIZE_BORDER_BUFFER } from "resizable/resizenreflow"; +import { RESIZE_BORDER_BUFFER } from "resizable/common"; import { SelectionRequestType } from "sagas/WidgetSelectUtils"; import { hideErrors } from "selectors/debuggerSelectors"; import { diff --git a/app/client/src/resizable/autolayoutresize/index.tsx b/app/client/src/resizable/autolayoutresize/index.tsx new file mode 100644 index 000000000000..661e5d5aed41 --- /dev/null +++ b/app/client/src/resizable/autolayoutresize/index.tsx @@ -0,0 +1,646 @@ +import { reflowMoveAction, stopReflowAction } from "actions/reflowActions"; +import { isHandleResizeAllowed } from "components/editorComponents/ResizableUtils"; +import { OccupiedSpace } from "constants/CanvasEditorConstants"; +import { + GridDefaults, + WidgetHeightLimits, + WIDGET_PADDING, +} from "constants/WidgetConstants"; +import React, { useEffect, useMemo, useRef, useState } from "react"; +import { useDispatch, useSelector } from "react-redux"; +import { Spring } from "react-spring"; +import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; +import { + MovementLimitMap, + ReflowDirection, + ReflowedSpace, + ReflowedSpaceMap, +} from "reflow/reflowTypes"; +import { + DimensionUpdateProps, + ResizableHandle, + ResizableProps, + ResizeWrapper, + RESIZE_BORDER_BUFFER, +} from "resizable/common"; +import { getWidgets } from "sagas/selectors"; +import { + getContainerOccupiedSpacesSelectorWhileResizing, + getCurrentAppPositioningType, +} from "selectors/editorSelectors"; +import { getReflowSelector } from "selectors/widgetReflowSelectors"; +import { + getFillWidgetLengthForLayer, + getLayerIndexOfWidget, +} from "utils/autoLayout/AutoLayoutUtils"; +import { ResponsiveBehavior } from "utils/autoLayout/constants"; +import { useReflow } from "utils/hooks/useReflow"; +import PerformanceTracker, { + PerformanceTransactionName, +} from "utils/PerformanceTracker"; +import { isDropZoneOccupied } from "utils/WidgetPropsUtils"; + +export function ReflowResizable(props: ResizableProps) { + const resizableRef = useRef(null); + const [isResizing, setResizing] = useState(false); + const isAutoLayout = + useSelector(getCurrentAppPositioningType) === AppPositioningTypes.AUTO; + const occupiedSpacesBySiblingWidgets = useSelector( + getContainerOccupiedSpacesSelectorWhileResizing(props.parentId), + ); + const checkForCollision = (widgetNewSize: { + left: number; + top: number; + bottom: number; + right: number; + }) => { + return isDropZoneOccupied( + widgetNewSize, + props.widgetId, + occupiedSpacesBySiblingWidgets, + ); + }; + // Performance tracking start + const sentryPerfTags = props.zWidgetType + ? [{ name: "widget_type", value: props.zWidgetType }] + : []; + PerformanceTracker.startTracking( + PerformanceTransactionName.SHOW_RESIZE_HANDLES, + { widgetId: props.zWidgetId }, + true, + sentryPerfTags, + ); + const reflowSelector = getReflowSelector(props.widgetId); + + const equal = ( + reflowA: ReflowedSpace | undefined, + reflowB: ReflowedSpace | undefined, + ) => { + if ( + reflowA?.width !== reflowB?.width || + reflowA?.height !== reflowB?.height + ) + return false; + + return true; + }; + + const reflowedPosition = useSelector(reflowSelector, equal); + + const reflow = useReflow( + [props.originalPositions], + props.parentId || "", + props.gridProps, + false, + ); + + useEffect(() => { + PerformanceTracker.stopTracking( + PerformanceTransactionName.SHOW_RESIZE_HANDLES, + ); + }, []); + //end + const [pointerEvents, togglePointerEvents] = useState(true); + const [newDimensions, set] = useState({ + width: props.componentWidth, + height: props.componentHeight, + x: 0, + y: 0, + reset: false, + direction: ReflowDirection.UNSET, + reflectDimension: true, + reflectPosition: true, + }); + const allWidgets = useSelector(getWidgets); + const widgetAlignment = allWidgets[props.widgetId].alignment || "start"; + const dispatch = useDispatch(); + const layer = useMemo(() => { + const { widgetId } = props; + const widget = allWidgets[widgetId]; + if (!widget || !widget.parentId) return {}; + const parent = allWidgets[widget.parentId]; + if (!parent) return {}; + const flexLayers = parent.flexLayers; + const layerIndex = getLayerIndexOfWidget(flexLayers, widgetId); + if (layerIndex === -1) return {}; + return flexLayers[layerIndex]; + }, [props, allWidgets]); + const hasFillChild = + !!layer && + layer.children.some((each: any) => { + const widget = allWidgets[each.id]; + return widget && widget.responsiveBehavior === ResponsiveBehavior.Fill; + }); + const triggerAutoLayoutBasedReflow = (resizedPositions: OccupiedSpace) => { + let canHorizontalMove = false; + const widgets = { + ...allWidgets, + [props.widgetId]: { + ...allWidgets[props.widgetId], + leftColumn: resizedPositions.left, + rightColumn: resizedPositions.right, + topRow: resizedPositions.top, + bottomRow: resizedPositions.bottom, + }, + }; + const fillWidgetsLength = getFillWidgetLengthForLayer(layer, widgets); + if (fillWidgetsLength) { + let correctedMovementMap: ReflowedSpaceMap = {}; + for (const child of layer.children) { + const childWidget = allWidgets[child.id]; + const updatedWidth = fillWidgetsLength * widget.parentColumnSpace; + if ( + childWidget && + childWidget.responsiveBehavior === ResponsiveBehavior.Fill && + (childWidget.rightColumn - childWidget.leftColumn) * + childWidget.parentColumnSpace !== + updatedWidth + ) { + canHorizontalMove = true; + correctedMovementMap = { + ...correctedMovementMap, + [child.id]: { + width: fillWidgetsLength * widget.parentColumnSpace, + }, + }; + } + } + dispatch(reflowMoveAction(correctedMovementMap)); + } + return canHorizontalMove; + }; + + const setNewDimensions = ( + direction: ReflowDirection, + resizedPositions: OccupiedSpace, + rect: DimensionUpdateProps, + ) => { + const { + canResizeHorizontally, + canResizeVertically, + } = props.getResizedPositions(resizedPositions); + const canResize = canResizeHorizontally || canResizeVertically; + + if (canResize) { + set((prevState) => { + let newRect = { ...rect }; + let canVerticalMove = true, + canHorizontalMove = true, + bottomMostRow = 0, + movementLimitMap: MovementLimitMap | undefined = {}; + + if (resizedPositions) { + //calling reflow to update movements of reflowing widgets and get movementLimit of current resizing widget + ({ bottomMostRow, movementLimitMap } = reflow.reflowSpaces( + [resizedPositions], + direction, + true, + )); + } + + if ( + resizedPositions && + movementLimitMap && + movementLimitMap[resizedPositions.id] + ) { + ({ canHorizontalMove, canVerticalMove } = movementLimitMap[ + resizedPositions.id + ]); + } + if (isAutoLayout && hasFillChild) { + canHorizontalMove = triggerAutoLayoutBasedReflow(resizedPositions); + } + + //if it should not resize horizontally, we keep keep the previous horizontal dimensions + if (!canHorizontalMove || !canResizeHorizontally) { + console.log("I cannot move", { resizedPositions }); + newRect = { + ...newRect, + width: prevState.width, + x: prevState.x, + X: prevState.X, + }; + } else { + console.log("I can move", { resizedPositions }); + } + + //if it should not resize vertically, we keep keep the previous vertical dimensions + if (!canVerticalMove || !canResizeVertically) { + newRect = { + ...newRect, + height: prevState.height, + y: prevState.y, + Y: prevState.Y, + }; + } + + if (bottomMostRow) { + props.updateBottomRow(bottomMostRow); + } + + console.log({ newRect }); + return newRect; + }); + } + }; + + useEffect(() => { + set((prevDimensions) => { + return { + ...prevDimensions, + width: props.componentWidth, + height: props.componentHeight, + x: 0, + y: 0, + reset: true, + }; + }); + }, [props.componentHeight, props.componentWidth, isResizing]); + + const handles = []; + const widget = allWidgets[props.widgetId]; + const resizedPositions = { + left: widget.leftColumn, + right: widget.rightColumn, + top: widget.topRow, + bottom: widget.bottomRow, + id: widget.widgetId, + }; + if (!(isAutoLayout && widget.leftColumn === 0) && props.handles.left) { + handles.push({ + dragCallback: (x: number) => { + const updatedPositions = { ...resizedPositions }; + let dimensionUpdates = { + reflectDimension: true, + reflectPosition: false, + y: newDimensions.y, + direction: ReflowDirection.LEFT, + X: x, + height: newDimensions.height, + width: props.componentWidth, + x: x, + }; + if (isAutoLayout) { + if (widgetAlignment === "start" || hasFillChild) { + updatedPositions.right = + widget.rightColumn - x / widget.parentColumnSpace; + dimensionUpdates = { + ...dimensionUpdates, + width: props.componentWidth - x, + x: 0, + }; + } else if (widgetAlignment === "center") { + updatedPositions.right = + widget.rightColumn - x / widget.parentColumnSpace; + updatedPositions.left = + widget.leftColumn - x / widget.parentColumnSpace; + dimensionUpdates = { + ...dimensionUpdates, + width: props.componentWidth - 2 * x, + x: 0, + reflectDimension: true, + reflectPosition: true, + }; + } else { + updatedPositions.left = + widget.leftColumn + x / widget.parentColumnSpace; + dimensionUpdates = { + ...dimensionUpdates, + width: props.componentWidth - x, + x, + }; + } + setNewDimensions( + ReflowDirection.LEFT, + updatedPositions, + dimensionUpdates, + ); + } + }, + component: props.handles.left, + handleDirection: ReflowDirection.LEFT, + }); + } + + if ( + !( + isAutoLayout && + widget.leftColumn !== 0 && + widget.rightColumn === GridDefaults.DEFAULT_GRID_COLUMNS + ) && + props.handles.right + ) { + handles.push({ + dragCallback: (x: number) => { + const updatedPositions = { ...resizedPositions }; + let dimensionUpdates = { + reflectDimension: true, + reflectPosition: false, + y: newDimensions.y, + direction: ReflowDirection.RIGHT, + X: x, + height: newDimensions.height, + width: props.componentWidth, + x: x, + }; + if (isAutoLayout) { + if (widgetAlignment === "start" || hasFillChild) { + updatedPositions.right = + widget.rightColumn + x / widget.parentColumnSpace; + dimensionUpdates = { + ...dimensionUpdates, + width: props.componentWidth + x, + x: 0, + }; + } else if (widgetAlignment === "center") { + updatedPositions.right = + widget.rightColumn + x / widget.parentColumnSpace; + updatedPositions.left = + widget.leftColumn + x / widget.parentColumnSpace; + dimensionUpdates = { + ...dimensionUpdates, + width: props.componentWidth + 2 * x, + x: 0, + reflectDimension: true, + reflectPosition: true, + }; + } else { + updatedPositions.left = + widget.leftColumn - x / widget.parentColumnSpace; + dimensionUpdates = { + ...dimensionUpdates, + width: props.componentWidth + x, + x: 0, + }; + } + setNewDimensions( + ReflowDirection.RIGHT, + updatedPositions, + dimensionUpdates, + ); + } + }, + component: props.handles.right, + handleDirection: ReflowDirection.RIGHT, + }); + } + + if (props.handles.bottom) { + handles.push({ + dragCallback: (x: number, y: number) => { + const updatedPositions = { ...resizedPositions }; + updatedPositions.bottom = widget.bottomRow + x / widget.parentRowSpace; + setNewDimensions(ReflowDirection.RIGHT, updatedPositions, { + width: newDimensions.width, + height: props.componentHeight + y, + x: newDimensions.x, + y: newDimensions.y, + direction: ReflowDirection.BOTTOM, + Y: y, + reflectDimension: true, + reflectPosition: true, + }); + }, + component: props.handles.bottom, + handleDirection: ReflowDirection.BOTTOM, + }); + } + + if (props.handles.bottomRight) { + handles.push({ + dragCallback: (x: number, y: number) => { + const updatedPositions = { ...resizedPositions }; + let dimensionUpdates = { + reflectDimension: true, + reflectPosition: false, + y: newDimensions.y, + width: props.componentWidth + x, + height: props.componentHeight + y, + x: newDimensions.x, + direction: ReflowDirection.BOTTOMRIGHT, + X: x, + Y: y, + }; + if (isAutoLayout) { + if (widgetAlignment === "start" || hasFillChild) { + updatedPositions.right = + widget.rightColumn + x / widget.parentColumnSpace; + dimensionUpdates = { + ...dimensionUpdates, + width: props.componentWidth + x, + x: 0, + }; + } else if (widgetAlignment === "center") { + updatedPositions.right = + widget.rightColumn + x / widget.parentColumnSpace; + updatedPositions.left = + widget.leftColumn + x / widget.parentColumnSpace; + dimensionUpdates = { + ...dimensionUpdates, + width: props.componentWidth + 2 * x, + x: 0, + reflectDimension: true, + reflectPosition: true, + }; + } else { + updatedPositions.left = + widget.leftColumn - x / widget.parentColumnSpace; + dimensionUpdates = { + ...dimensionUpdates, + width: props.componentWidth + x, + x: 0, + }; + } + setNewDimensions( + ReflowDirection.BOTTOMRIGHT, + updatedPositions, + dimensionUpdates, + ); + } + }, + component: props.handles.bottomRight, + affectsWidth: true, + }); + } + + if (props.handles.bottomLeft) { + handles.push({ + dragCallback: (x: number, y: number) => { + const updatedPositions = { ...resizedPositions }; + let dimensionUpdates = { + reflectDimension: true, + reflectPosition: false, + x: x, + width: props.componentWidth - x, + height: props.componentHeight + y, + y: newDimensions.y, + direction: ReflowDirection.BOTTOMLEFT, + X: x, + Y: y, + }; + if (isAutoLayout) { + if (widgetAlignment === "start" || hasFillChild) { + updatedPositions.right = + widget.rightColumn - x / widget.parentColumnSpace; + dimensionUpdates = { + ...dimensionUpdates, + width: props.componentWidth - x, + x: 0, + }; + } else if (widgetAlignment === "center") { + updatedPositions.right = + widget.rightColumn - x / widget.parentColumnSpace; + updatedPositions.left = + widget.leftColumn - x / widget.parentColumnSpace; + dimensionUpdates = { + ...dimensionUpdates, + width: props.componentWidth - 2 * x, + x: 0, + reflectDimension: true, + reflectPosition: true, + }; + } else { + updatedPositions.left = + widget.leftColumn + x / widget.parentColumnSpace; + dimensionUpdates = { + ...dimensionUpdates, + width: props.componentWidth - x, + x, + }; + } + setNewDimensions( + ReflowDirection.BOTTOMLEFT, + updatedPositions, + dimensionUpdates, + ); + } + }, + component: props.handles.bottomLeft, + affectsWidth: true, + }); + } + const onResizeStop = () => { + togglePointerEvents(true); + if (isAutoLayout) { + dispatch(stopReflowAction()); + } + props.onStop( + { + width: newDimensions.width, + height: newDimensions.height, + }, + { + x: newDimensions.x, + y: newDimensions.y, + }, + ); + setResizing(false); + }; + + const renderHandles = handles.map((handle, index) => { + const disableDot = !isHandleResizeAllowed( + props.enableHorizontalResize, + props.enableVerticalResize, + handle.handleDirection, + props.isFlexChild, + props.responsiveBehavior, + ); + return ( + { + togglePointerEvents(false); + props.onStart(); + setResizing(true); + }} + onStop={onResizeStop} + scrollParent={resizableRef.current} + snapGrid={props.snapGrid} + /> + ); + }); + const bufferForBoundary = props.showResizeBoundary ? RESIZE_BORDER_BUFFER : 0; + const widgetWidth = + (reflowedPosition?.width === undefined + ? newDimensions.width + : reflowedPosition.width - 2 * WIDGET_PADDING) + bufferForBoundary; + const widgetHeight = + (reflowedPosition?.height === undefined + ? newDimensions.height + : reflowedPosition.height - 2 * WIDGET_PADDING) + bufferForBoundary; + return ( + + {(_props) => ( + + {props.children} + {props.enableHorizontalResize && renderHandles} + + )} + + ); +} + +export default ReflowResizable; diff --git a/app/client/src/resizable/common.tsx b/app/client/src/resizable/common.tsx new file mode 100644 index 000000000000..1eb238a7ed52 --- /dev/null +++ b/app/client/src/resizable/common.tsx @@ -0,0 +1,198 @@ +import { OccupiedSpace } from "constants/CanvasEditorConstants"; +import { Colors } from "constants/Colors"; +import React, { ReactNode } from "react"; +import { animated } from "react-spring"; +import { useDrag } from "react-use-gesture"; +import { GridProps, ReflowDirection } from "reflow/reflowTypes"; +import styled, { StyledComponent } from "styled-components"; +import { + LayoutDirection, + ResponsiveBehavior, +} from "utils/autoLayout/constants"; +import { getNearestParentCanvas } from "utils/generators"; + +const resizeBorderPadding = 1; +const resizeBorder = 1; +const resizeBoxShadow = 1; +const resizeOutline = 1; + +export const RESIZE_BORDER_BUFFER = + resizeBorderPadding + resizeBorder + resizeBoxShadow + resizeOutline; + +export const ResizeWrapper = styled(animated.div)<{ + $prevents: boolean; + isHovered: boolean; + showBoundaries: boolean; +}>` + display: block; + & { + * { + pointer-events: ${(props) => !props.$prevents && "none"}; + } + } + ${(props) => { + if (props.showBoundaries) { + return ` + box-shadow: 0px 0px 0px ${resizeBoxShadow}px ${ + props.isHovered ? Colors.WATUSI : "#f86a2b" + }; + border-radius: 0px 4px 4px 4px; + border: ${resizeBorder}px solid ${Colors.GREY_1}; + padding: ${resizeBorderPadding}px; + outline: ${resizeOutline}px solid ${Colors.GREY_1} !important; + outline-offset: 1px;`; + } else { + return ` + border: 0px solid transparent; + `; + } + }}} +`; + +const getSnappedValues = ( + x: number, + y: number, + snapGrid: { x: number; y: number }, +) => { + return { + x: Math.round(x / snapGrid.x) * snapGrid.x, + y: Math.round(y / snapGrid.y) * snapGrid.y, + }; +}; + +export type DimensionUpdateProps = { + width: number; + height: number; + x: number; + y: number; + reset?: boolean; + direction: ReflowDirection; + X?: number; + Y?: number; + reflectPosition: boolean; + reflectDimension: boolean; +}; + +type ResizableHandleProps = { + allowResize: boolean; + scrollParent: HTMLDivElement | null; + disableDot: boolean; + isHovered: boolean; + checkForCollision: (widgetNewSize: { + left: number; + top: number; + bottom: number; + right: number; + }) => boolean; + dragCallback: (x: number, y: number) => void; + component: StyledComponent<"div", Record>; + onStart: () => void; + onStop: () => void; + snapGrid: { + x: number; + y: number; + }; + direction?: ReflowDirection; +}; + +export function ResizableHandle(props: ResizableHandleProps) { + const bind = useDrag((state) => { + const { + first, + last, + dragging, + memo, + movement: [mx, my], + } = state; + if (!props.allowResize || props.disableDot) { + return; + } + const scrollParent = getNearestParentCanvas(props.scrollParent); + + const initialScrollTop = memo ? memo.scrollTop : 0; + const currentScrollTop = scrollParent?.scrollTop || 0; + + const deltaScrolledHeight = currentScrollTop - initialScrollTop; + const deltaY = my + deltaScrolledHeight; + const snapped = getSnappedValues(mx, deltaY, props.snapGrid); + if (first) { + props.onStart(); + return { scrollTop: currentScrollTop, snapped }; + } + const { snapped: snappedMemo } = memo; + + if ( + dragging && + snappedMemo && + (snapped.x !== snappedMemo.x || snapped.y !== snappedMemo.y) + ) { + props.dragCallback(snapped.x, snapped.y); + } + if (last) { + props.onStop(); + } + + return { ...memo, snapped }; + }); + const propsToPass = { + ...bind(), + showAsBorder: !props.allowResize, + disableDot: props.disableDot, + isHovered: props.isHovered, + }; + + return ( + + ); +} + +export type ResizableProps = { + allowResize: boolean; + handles: { + left?: StyledComponent<"div", Record>; + top?: StyledComponent<"div", Record>; + bottom?: StyledComponent<"div", Record>; + right?: StyledComponent<"div", Record>; + bottomRight?: StyledComponent<"div", Record>; + topLeft?: StyledComponent<"div", Record>; + topRight?: StyledComponent<"div", Record>; + bottomLeft?: StyledComponent<"div", Record>; + }; + componentWidth: number; + componentHeight: number; + children: ReactNode; + updateBottomRow: (bottomRow: number) => void; + getResizedPositions: ( + resizedPositions: OccupiedSpace, + ) => { + canResizeHorizontally: boolean; + canResizeVertically: boolean; + }; + fixedHeight: boolean; + maxDynamicHeight?: number; + originalPositions: OccupiedSpace; + onStart: (affectsWidth?: boolean) => void; + onStop: ( + size: { width: number; height: number }, + position: { x: number; y: number }, + ) => void; + snapGrid: { x: number; y: number }; + enableVerticalResize: boolean; + enableHorizontalResize: boolean; + className?: string; + parentId?: string; + widgetId: string; + gridProps: GridProps; + zWidgetType?: string; + zWidgetId?: string; + isFlexChild?: boolean; + isHovered: boolean; + responsiveBehavior?: ResponsiveBehavior; + direction?: LayoutDirection; + paddingOffset: number; + isMobile: boolean; + showResizeBoundary: boolean; +}; diff --git a/app/client/src/resizable/resize/index.tsx b/app/client/src/resizable/modalresize/index.tsx similarity index 99% rename from app/client/src/resizable/resize/index.tsx rename to app/client/src/resizable/modalresize/index.tsx index 0937906fc98c..5242ce0794fa 100644 --- a/app/client/src/resizable/resize/index.tsx +++ b/app/client/src/resizable/modalresize/index.tsx @@ -3,7 +3,7 @@ import React, { ReactNode, useEffect, useState } from "react"; import { Spring } from "react-spring"; import { useDrag } from "react-use-gesture"; import { ReflowDirection } from "reflow/reflowTypes"; -import { ResizeWrapper } from "resizable/resizenreflow"; +import { ResizeWrapper } from "resizable/common"; import { StyledComponent } from "styled-components"; import PerformanceTracker, { PerformanceTransactionName, diff --git a/app/client/src/resizable/resizenreflow/index.tsx b/app/client/src/resizable/resizenreflow/index.tsx index c0dfeb9baa74..8efee632d623 100644 --- a/app/client/src/resizable/resizenreflow/index.tsx +++ b/app/client/src/resizable/resizenreflow/index.tsx @@ -1,236 +1,40 @@ -import { reflowMoveAction, stopReflowAction } from "actions/reflowActions"; -import { isHandleResizeAllowed } from "components/editorComponents/ResizableUtils"; -import { OccupiedSpace } from "constants/CanvasEditorConstants"; -import { Colors } from "constants/Colors"; +import { + computeRowCols, + isHandleResizeAllowed, +} from "components/editorComponents/ResizableUtils"; import { GridDefaults, WidgetHeightLimits, WIDGET_PADDING, } from "constants/WidgetConstants"; -import React, { ReactNode, useEffect, useRef, useState } from "react"; -import { useDispatch, useSelector } from "react-redux"; -import { animated, Spring } from "react-spring"; -import { useDrag } from "react-use-gesture"; -import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; +import React, { useEffect, useRef, useState } from "react"; +import { useSelector } from "react-redux"; +import { Spring } from "react-spring"; import { - GridProps, MovementLimitMap, ReflowDirection, ReflowedSpace, - ReflowedSpaceMap, } from "reflow/reflowTypes"; -import { getWidgets } from "sagas/selectors"; import { - getContainerOccupiedSpacesSelectorWhileResizing, - getCurrentAppPositioningType, -} from "selectors/editorSelectors"; + DimensionUpdateProps, + ResizableHandle, + ResizableProps, + ResizeWrapper, + RESIZE_BORDER_BUFFER, +} from "resizable/common"; +import { getWidgetByID } from "sagas/selectors"; +import { getContainerOccupiedSpacesSelectorWhileResizing } from "selectors/editorSelectors"; import { getReflowSelector } from "selectors/widgetReflowSelectors"; -import styled, { StyledComponent } from "styled-components"; -import { - getFillWidgetLengthForLayer, - getLayerIndexOfWidget, -} from "utils/autoLayout/AutoLayoutUtils"; -import { - LayoutDirection, - ResponsiveBehavior, -} from "utils/autoLayout/constants"; -import { getNearestParentCanvas } from "utils/generators"; import { useReflow } from "utils/hooks/useReflow"; import PerformanceTracker, { PerformanceTransactionName, } from "utils/PerformanceTracker"; import { isDropZoneOccupied } from "utils/WidgetPropsUtils"; -const resizeBorderPadding = 1; -const resizeBorder = 1; -const resizeBoxShadow = 1; -const resizeOutline = 1; - -export const RESIZE_BORDER_BUFFER = - resizeBorderPadding + resizeBorder + resizeBoxShadow + resizeOutline; - -export const ResizeWrapper = styled(animated.div)<{ - $prevents: boolean; - isHovered: boolean; - showBoundaries: boolean; -}>` - display: block; - & { - * { - pointer-events: ${(props) => !props.$prevents && "none"}; - } - } - ${(props) => { - if (props.showBoundaries) { - return ` - box-shadow: 0px 0px 0px ${resizeBoxShadow}px ${ - props.isHovered ? Colors.WATUSI : "#f86a2b" - }; - border-radius: 0px 4px 4px 4px; - border: ${resizeBorder}px solid ${Colors.GREY_1}; - padding: ${resizeBorderPadding}px; - outline: ${resizeOutline}px solid ${Colors.GREY_1} !important; - outline-offset: 1px;`; - } else { - return ` - border: 0px solid transparent; - `; - } - }}} -`; - -const getSnappedValues = ( - x: number, - y: number, - snapGrid: { x: number; y: number }, -) => { - return { - x: Math.round(x / snapGrid.x) * snapGrid.x, - y: Math.round(y / snapGrid.y) * snapGrid.y, - }; -}; - -export type DimensionProps = { - width: number; - height: number; - x: number; - y: number; - reset?: boolean; - direction: ReflowDirection; - X?: number; - Y?: number; -}; - -type ResizableHandleProps = { - allowResize: boolean; - scrollParent: HTMLDivElement | null; - disableDot: boolean; - isHovered: boolean; - checkForCollision: (widgetNewSize: { - left: number; - top: number; - bottom: number; - right: number; - }) => boolean; - dragCallback: (x: number, y: number) => void; - component: StyledComponent<"div", Record>; - onStart: () => void; - onStop: () => void; - snapGrid: { - x: number; - y: number; - }; - direction?: ReflowDirection; -}; - -function ResizableHandle(props: ResizableHandleProps) { - const bind = useDrag((state) => { - const { - first, - last, - dragging, - memo, - movement: [mx, my], - } = state; - if (!props.allowResize || props.disableDot) { - return; - } - const scrollParent = getNearestParentCanvas(props.scrollParent); - - const initialScrollTop = memo ? memo.scrollTop : 0; - const currentScrollTop = scrollParent?.scrollTop || 0; - - const deltaScrolledHeight = currentScrollTop - initialScrollTop; - const deltaY = my + deltaScrolledHeight; - const snapped = getSnappedValues(mx, deltaY, props.snapGrid); - if (first) { - props.onStart(); - return { scrollTop: currentScrollTop, snapped }; - } - const { snapped: snappedMemo } = memo; - - if ( - dragging && - snappedMemo && - (snapped.x !== snappedMemo.x || snapped.y !== snappedMemo.y) - ) { - props.dragCallback(snapped.x, snapped.y); - } - if (last) { - props.onStop(); - } - - return { ...memo, snapped }; - }); - const propsToPass = { - ...bind(), - showAsBorder: !props.allowResize, - disableDot: props.disableDot, - isHovered: props.isHovered, - }; - - return ( - - ); -} - -type ResizableProps = { - allowResize: boolean; - handles: { - left?: StyledComponent<"div", Record>; - top?: StyledComponent<"div", Record>; - bottom?: StyledComponent<"div", Record>; - right?: StyledComponent<"div", Record>; - bottomRight?: StyledComponent<"div", Record>; - topLeft?: StyledComponent<"div", Record>; - topRight?: StyledComponent<"div", Record>; - bottomLeft?: StyledComponent<"div", Record>; - }; - componentWidth: number; - componentHeight: number; - children: ReactNode; - updateBottomRow: (bottomRow: number) => void; - getResizedPositions: ( - size: { width: number; height: number }, - position: { x: number; y: number }, - ) => { - canResizeHorizontally: boolean; - canResizeVertically: boolean; - resizedPositions?: OccupiedSpace; - }; - fixedHeight: boolean; - maxDynamicHeight?: number; - originalPositions: OccupiedSpace; - onStart: (affectsWidth?: boolean) => void; - onStop: ( - size: { width: number; height: number }, - position: { x: number; y: number }, - ) => void; - snapGrid: { x: number; y: number }; - enableVerticalResize: boolean; - enableHorizontalResize: boolean; - className?: string; - parentId?: string; - widgetId: string; - gridProps: GridProps; - zWidgetType?: string; - zWidgetId?: string; - isFlexChild?: boolean; - isHovered: boolean; - responsiveBehavior?: ResponsiveBehavior; - direction?: LayoutDirection; - paddingOffset: number; - isMobile: boolean; - showResizeBoundary: boolean; -}; export function ReflowResizable(props: ResizableProps) { const resizableRef = useRef(null); const [isResizing, setResizing] = useState(false); - const isAutoLayout = - useSelector(getCurrentAppPositioningType) === AppPositioningTypes.AUTO; + const occupiedSpacesBySiblingWidgets = useSelector( getContainerOccupiedSpacesSelectorWhileResizing(props.parentId), ); @@ -277,8 +81,8 @@ export function ReflowResizable(props: ResizableProps) { [props.originalPositions], props.parentId || "", props.gridProps, - !isAutoLayout, ); + const widget = useSelector(getWidgetByID(props.widgetId)); useEffect(() => { PerformanceTracker.stopTracking( @@ -287,66 +91,40 @@ export function ReflowResizable(props: ResizableProps) { }, []); //end const [pointerEvents, togglePointerEvents] = useState(true); - const [newDimensions, set] = useState({ + const dimensionReflectionProps = { + reflectDimension: true, + reflectPosition: true, + }; + const [newDimensions, set] = useState({ width: props.componentWidth, height: props.componentHeight, x: 0, y: 0, reset: false, direction: ReflowDirection.UNSET, + ...dimensionReflectionProps, }); - const allWidgets = useSelector(getWidgets); - const dispatch = useDispatch(); - const triggerAutoLayoutBasedReflow = (resizedPositions: OccupiedSpace) => { - const { widgetId } = props; - const widget = allWidgets[widgetId]; - if (!widget || !widget.parentId) return; - const parent = allWidgets[widget.parentId]; - if (!parent) return; - const flexLayers = parent.flexLayers; - const layerIndex = getLayerIndexOfWidget(flexLayers, widgetId); - if (layerIndex === -1) return; - const layer = flexLayers[layerIndex]; - const widgets = { - ...allWidgets, - [props.widgetId]: { - ...allWidgets[props.widgetId], - leftColumn: resizedPositions.left, - rightColumn: resizedPositions.right, - topRow: resizedPositions.top, - bottomRow: resizedPositions.bottom, - }, - }; - const fillWidgetsLength = getFillWidgetLengthForLayer(layer, widgets); - if (fillWidgetsLength) { - let correctedMovementMap: ReflowedSpaceMap = {}; - for (const child of layer.children) { - const childWidget = allWidgets[child.id]; - if ( - childWidget && - childWidget.responsiveBehavior === ResponsiveBehavior.Fill - ) { - correctedMovementMap = { - ...correctedMovementMap, - [child.id]: { - width: fillWidgetsLength * widget.parentColumnSpace, - }, - }; - } - } - dispatch(reflowMoveAction(correctedMovementMap)); - } - }; - const setNewDimensions = (rect: DimensionProps) => { + const setNewDimensions = (rect: DimensionUpdateProps) => { const { direction, height, width, x, y } = rect; - + const delta = { + height: height - props.componentHeight, + width: width - props.componentWidth, + }; + const updatedPositions = computeRowCols(delta, { x, y }, widget); + const resizedPositions = { + left: updatedPositions.leftColumn, + right: updatedPositions.rightColumn, + top: updatedPositions.topRow, + bottom: updatedPositions.bottomRow, + id: widget.widgetId, + parentId: widget.parentId, + }; //if it is reached the end of canvas const { canResizeHorizontally, canResizeVertically, - resizedPositions, - } = props.getResizedPositions({ width, height }, { x, y }); + } = props.getResizedPositions(resizedPositions); const canResize = canResizeHorizontally || canResizeVertically; if (canResize) { @@ -400,9 +178,6 @@ export function ReflowResizable(props: ResizableProps) { if (bottomMostRow) { props.updateBottomRow(bottomMostRow); } - if (isAutoLayout && resizedPositions) { - triggerAutoLayoutBasedReflow(resizedPositions); - } return newRect; }); @@ -423,8 +198,8 @@ export function ReflowResizable(props: ResizableProps) { }, [props.componentHeight, props.componentWidth, isResizing]); const handles = []; - const widget = allWidgets[props.widgetId]; - if (!(isAutoLayout && widget.leftColumn === 0) && props.handles.left) { + + if (props.handles.left) { handles.push({ dragCallback: (x: number) => { setNewDimensions({ @@ -434,6 +209,7 @@ export function ReflowResizable(props: ResizableProps) { y: newDimensions.y, direction: ReflowDirection.LEFT, X: x, + ...dimensionReflectionProps, }); }, component: props.handles.left, @@ -441,7 +217,7 @@ export function ReflowResizable(props: ResizableProps) { }); } - if (!isAutoLayout && props.handles.top) { + if (props.handles.top) { handles.push({ dragCallback: (x: number, y: number) => { setNewDimensions({ @@ -451,6 +227,7 @@ export function ReflowResizable(props: ResizableProps) { x: newDimensions.x, direction: ReflowDirection.TOP, Y: y, + ...dimensionReflectionProps, }); }, component: props.handles.top, @@ -458,14 +235,7 @@ export function ReflowResizable(props: ResizableProps) { }); } - if ( - !( - isAutoLayout && - widget.leftColumn !== 0 && - widget.rightColumn === GridDefaults.DEFAULT_GRID_COLUMNS - ) && - props.handles.right - ) { + if (props.handles.right) { handles.push({ dragCallback: (x: number) => { setNewDimensions({ @@ -475,6 +245,7 @@ export function ReflowResizable(props: ResizableProps) { y: newDimensions.y, direction: ReflowDirection.RIGHT, X: x, + ...dimensionReflectionProps, }); }, component: props.handles.right, @@ -492,6 +263,7 @@ export function ReflowResizable(props: ResizableProps) { y: newDimensions.y, direction: ReflowDirection.BOTTOM, Y: y, + ...dimensionReflectionProps, }); }, component: props.handles.bottom, @@ -510,10 +282,10 @@ export function ReflowResizable(props: ResizableProps) { direction: ReflowDirection.TOPLEFT, X: x, Y: y, + ...dimensionReflectionProps, }); }, component: props.handles.topLeft, - affectsWidth: true, }); } @@ -528,10 +300,10 @@ export function ReflowResizable(props: ResizableProps) { direction: ReflowDirection.TOPRIGHT, X: x, Y: y, + ...dimensionReflectionProps, }); }, component: props.handles.topRight, - affectsWidth: true, }); } @@ -546,10 +318,10 @@ export function ReflowResizable(props: ResizableProps) { direction: ReflowDirection.BOTTOMRIGHT, X: x, Y: y, + ...dimensionReflectionProps, }); }, component: props.handles.bottomRight, - affectsWidth: true, }); } @@ -564,17 +336,14 @@ export function ReflowResizable(props: ResizableProps) { direction: ReflowDirection.BOTTOMLEFT, X: x, Y: y, + ...dimensionReflectionProps, }); }, component: props.handles.bottomLeft, - affectsWidth: true, }); } const onResizeStop = () => { togglePointerEvents(true); - if (isAutoLayout) { - dispatch(stopReflowAction()); - } props.onStop( { width: newDimensions.width, @@ -593,19 +362,11 @@ export function ReflowResizable(props: ResizableProps) { props.enableHorizontalResize, props.enableVerticalResize, handle.handleDirection, - props.isFlexChild, - props.responsiveBehavior, ); return ( ); }); + const bufferForBoundary = props.showResizeBoundary ? RESIZE_BORDER_BUFFER : 0; const widgetWidth = (reflowedPosition?.width === undefined @@ -667,8 +429,12 @@ export function ReflowResizable(props: ResizableProps) { maxHeight: (props.maxDynamicHeight || WidgetHeightLimits.MAX_HEIGHT_IN_ROWS) * GridDefaults.DEFAULT_GRID_ROW_HEIGHT, - transform: `translate3d(${newDimensions.x - - bufferForBoundary / 2}px,${newDimensions.y - + transform: `translate3d(${(newDimensions.reflectPosition + ? newDimensions.x + : 0) - + bufferForBoundary / 2}px,${(newDimensions.reflectPosition + ? newDimensions.y + : 0) - bufferForBoundary / 2}px,0)`, }} > From 4e98bd06b817eb7720145b20239c21f082afb844 Mon Sep 17 00:00:00 2001 From: Aswath K Date: Tue, 21 Feb 2023 17:20:08 +0530 Subject: [PATCH 531/708] feat: Responsive Checkbox & Switch Widgets (#19999) Sets minWidth to Checkbox & Switch widgets. Label overflow behaviour updated for autoLayout --- app/client/src/utils/layoutPropertiesUtils.ts | 6 +++++- .../src/widgets/CheckboxWidget/component/index.tsx | 7 +++++++ app/client/src/widgets/CheckboxWidget/index.ts | 12 ++++++++++++ .../src/widgets/CheckboxWidget/widget/index.tsx | 2 ++ .../src/widgets/SwitchWidget/component/index.tsx | 7 +++++++ app/client/src/widgets/SwitchWidget/index.ts | 14 ++++++++++++++ .../src/widgets/SwitchWidget/widget/index.tsx | 2 ++ 7 files changed, 49 insertions(+), 1 deletion(-) diff --git a/app/client/src/utils/layoutPropertiesUtils.ts b/app/client/src/utils/layoutPropertiesUtils.ts index 41435d8238ff..335887303612 100644 --- a/app/client/src/utils/layoutPropertiesUtils.ts +++ b/app/client/src/utils/layoutPropertiesUtils.ts @@ -298,7 +298,11 @@ export const WIDGET_WITH_DYNAMIC_WIDTH = [ // TODO(aswathkk): See if this needs to be moved to widget config // This is used only for autoLayout -export const WIDGET_WITH_DYNAMIC_HEIGHT = ["CHECKBOX_GROUP_WIDGET"]; +export const WIDGET_WITH_DYNAMIC_HEIGHT = [ + "CHECKBOX_GROUP_WIDGET", + "CHECKBOX_WIDGET", + "SWITCH_WIDGET", +]; export function getDefaultResponsiveBehavior(widgetType: string) { return DefaultFillWidgets.includes(widgetType) diff --git a/app/client/src/widgets/CheckboxWidget/component/index.tsx b/app/client/src/widgets/CheckboxWidget/component/index.tsx index aeb23e5b0f27..030d8bae48c4 100644 --- a/app/client/src/widgets/CheckboxWidget/component/index.tsx +++ b/app/client/src/widgets/CheckboxWidget/component/index.tsx @@ -56,6 +56,13 @@ export const CheckboxLabel = styled.div<{ ${({ isDynamicHeightEnabled }) => isDynamicHeightEnabled ? "&& { word-break: break-all; }" : ""}; + + .auto-layout & { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + word-wrap: normal; + } `; export const StyledCheckbox = styled(Checkbox)` diff --git a/app/client/src/widgets/CheckboxWidget/index.ts b/app/client/src/widgets/CheckboxWidget/index.ts index 901d2135438c..5b73b9c20556 100644 --- a/app/client/src/widgets/CheckboxWidget/index.ts +++ b/app/client/src/widgets/CheckboxWidget/index.ts @@ -32,6 +32,18 @@ export const CONFIG = { responsiveBehavior: getDefaultResponsiveBehavior(Widget.getWidgetType()), minWidth: FILL_WIDGET_MIN_WIDTH, }, + autoLayout: { + widgetSize: [ + { + viewportMinWidth: 0, + configuration: () => { + return { + minWidth: "74px", + }; + }, + }, + ], + }, properties: { derived: Widget.getDerivedPropertiesMap(), default: Widget.getDefaultPropertiesMap(), diff --git a/app/client/src/widgets/CheckboxWidget/widget/index.tsx b/app/client/src/widgets/CheckboxWidget/widget/index.tsx index feae79e26da9..061cb50f9f39 100644 --- a/app/client/src/widgets/CheckboxWidget/widget/index.tsx +++ b/app/client/src/widgets/CheckboxWidget/widget/index.tsx @@ -4,6 +4,7 @@ import { WidgetType } from "constants/WidgetConstants"; import { ValidationTypes } from "constants/WidgetValidation"; import { Stylesheet } from "entities/AppTheming"; import React from "react"; +import { isAutoLayout } from "selectors/mainCanvasSelectors"; import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; import { AlignWidgetTypes } from "widgets/constants"; @@ -168,6 +169,7 @@ class CheckboxWidget extends BaseWidget { helpText: "Control the font size of the label associated", controlType: "DROP_DOWN", defaultValue: "0.875rem", + hidden: isAutoLayout, options: [ { label: "S", diff --git a/app/client/src/widgets/SwitchWidget/component/index.tsx b/app/client/src/widgets/SwitchWidget/component/index.tsx index bcd2ad0bb72d..edc3bd4c95f7 100644 --- a/app/client/src/widgets/SwitchWidget/component/index.tsx +++ b/app/client/src/widgets/SwitchWidget/component/index.tsx @@ -57,6 +57,13 @@ const SwitchLabel = styled.div<{ ${({ isDynamicHeightEnabled }) => isDynamicHeightEnabled ? "&& { word-break: break-all; }" : ""}; + + .auto-layout & { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + word-wrap: normal; + } `; export const StyledSwitch = styled(Switch)<{ diff --git a/app/client/src/widgets/SwitchWidget/index.ts b/app/client/src/widgets/SwitchWidget/index.ts index 2893e5d170d9..1e999c88739d 100644 --- a/app/client/src/widgets/SwitchWidget/index.ts +++ b/app/client/src/widgets/SwitchWidget/index.ts @@ -2,6 +2,7 @@ import IconSVG from "./icon.svg"; import Widget from "./widget"; import { LabelPosition } from "components/constants"; import { AlignWidgetTypes } from "widgets/constants"; +import { getDefaultResponsiveBehavior } from "utils/layoutPropertiesUtils"; export const CONFIG = { features: { @@ -26,6 +27,19 @@ export const CONFIG = { version: 1, isDisabled: false, animateLoading: true, + responsiveBehavior: getDefaultResponsiveBehavior(Widget.getWidgetType()), + }, + autoLayout: { + widgetSize: [ + { + viewportMinWidth: 0, + configuration: () => { + return { + minWidth: "74px", + }; + }, + }, + ], }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/SwitchWidget/widget/index.tsx b/app/client/src/widgets/SwitchWidget/widget/index.tsx index 95f84e4d81cd..0a0fe64f5493 100644 --- a/app/client/src/widgets/SwitchWidget/widget/index.tsx +++ b/app/client/src/widgets/SwitchWidget/widget/index.tsx @@ -13,6 +13,7 @@ import { AlignWidgetTypes } from "widgets/constants"; import { Stylesheet } from "entities/AppTheming"; import { isAutoHeightEnabledForWidget } from "widgets/WidgetUtils"; +import { isAutoLayout } from "selectors/mainCanvasSelectors"; class SwitchWidget extends BaseWidget { static getPropertyPaneContentConfig() { @@ -157,6 +158,7 @@ class SwitchWidget extends BaseWidget { helpText: "Control the font size of the label associated", controlType: "DROP_DOWN", defaultValue: "0.875rem", + hidden: isAutoLayout, options: [ { label: "S", From 816e6f3ff580278b2aff24d3a906e35aa1887cee Mon Sep 17 00:00:00 2001 From: Aswath K Date: Tue, 21 Feb 2023 17:25:03 +0530 Subject: [PATCH 532/708] Feat: Responsive select widgets (#19985) Adds minWidth config for Select, MuliSelect, TreeSelect, MultiSelectTree widgets. Removes label position and font size properties too from those widgets for AutoLayout. --- .../src/widgets/MultiSelectTreeWidget/index.ts | 12 ++++++++++++ .../widgets/MultiSelectTreeWidget/widget/index.tsx | 3 +++ app/client/src/widgets/MultiSelectWidgetV2/index.ts | 12 ++++++++++++ .../src/widgets/MultiSelectWidgetV2/widget/index.tsx | 3 +++ app/client/src/widgets/SelectWidget/index.ts | 12 ++++++++++++ app/client/src/widgets/SelectWidget/widget/index.tsx | 3 +++ .../src/widgets/SingleSelectTreeWidget/index.ts | 12 ++++++++++++ .../widgets/SingleSelectTreeWidget/widget/index.tsx | 3 +++ 8 files changed, 60 insertions(+) diff --git a/app/client/src/widgets/MultiSelectTreeWidget/index.ts b/app/client/src/widgets/MultiSelectTreeWidget/index.ts index d274b904f4f8..6babe387d313 100644 --- a/app/client/src/widgets/MultiSelectTreeWidget/index.ts +++ b/app/client/src/widgets/MultiSelectTreeWidget/index.ts @@ -59,6 +59,18 @@ export const CONFIG = { responsiveBehavior: getDefaultResponsiveBehavior(Widget.getWidgetType()), minWidth: FILL_WIDGET_MIN_WIDTH, }, + autoLayout: { + widgetSize: [ + { + viewportMinWidth: 0, + configuration: () => { + return { + minWidth: "120px", + }; + }, + }, + ], + }, properties: { derived: Widget.getDerivedPropertiesMap(), default: Widget.getDefaultPropertiesMap(), diff --git a/app/client/src/widgets/MultiSelectTreeWidget/widget/index.tsx b/app/client/src/widgets/MultiSelectTreeWidget/widget/index.tsx index e7c0957d1017..3faffe0b7f96 100644 --- a/app/client/src/widgets/MultiSelectTreeWidget/widget/index.tsx +++ b/app/client/src/widgets/MultiSelectTreeWidget/widget/index.tsx @@ -13,6 +13,7 @@ import { isArray, xor } from "lodash"; import { DefaultValueType } from "rc-tree-select/lib/interface"; import { CheckedStrategy } from "rc-tree-select/lib/utils/strategyUtil"; import React, { ReactNode } from "react"; +import { isAutoLayout } from "selectors/mainCanvasSelectors"; import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService"; import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; @@ -166,6 +167,7 @@ class MultiSelectTreeWidget extends BaseWidget< label: "Position", controlType: "ICON_TABS", fullWidth: true, + hidden: isAutoLayout, options: [ { label: "Auto", value: LabelPosition.Auto }, { label: "Left", value: LabelPosition.Left }, @@ -390,6 +392,7 @@ class MultiSelectTreeWidget extends BaseWidget< helpText: "Control the font size of the label associated", controlType: "DROP_DOWN", defaultValue: "0.875rem", + hidden: isAutoLayout, options: [ { label: "S", diff --git a/app/client/src/widgets/MultiSelectWidgetV2/index.ts b/app/client/src/widgets/MultiSelectWidgetV2/index.ts index dc65bd5d30cd..a290135a4673 100644 --- a/app/client/src/widgets/MultiSelectWidgetV2/index.ts +++ b/app/client/src/widgets/MultiSelectWidgetV2/index.ts @@ -44,6 +44,18 @@ export const CONFIG = { responsiveBehavior: getDefaultResponsiveBehavior(Widget.getWidgetType()), minWidth: FILL_WIDGET_MIN_WIDTH, }, + autoLayout: { + widgetSize: [ + { + viewportMinWidth: 0, + configuration: () => { + return { + minWidth: "120px", + }; + }, + }, + ], + }, properties: { derived: Widget.getDerivedPropertiesMap(), default: Widget.getDefaultPropertiesMap(), diff --git a/app/client/src/widgets/MultiSelectWidgetV2/widget/index.tsx b/app/client/src/widgets/MultiSelectWidgetV2/widget/index.tsx index 5773199cd076..bf4f67d426c5 100644 --- a/app/client/src/widgets/MultiSelectWidgetV2/widget/index.tsx +++ b/app/client/src/widgets/MultiSelectWidgetV2/widget/index.tsx @@ -13,6 +13,7 @@ import equal from "fast-deep-equal/es6"; import { isArray, isFinite, isString, LoDashStatic, xorWith } from "lodash"; import { DraftValueType, LabelInValueType } from "rc-select/lib/Select"; import React from "react"; +import { isAutoLayout } from "selectors/mainCanvasSelectors"; import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService"; import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; @@ -274,6 +275,7 @@ class MultiSelectWidget extends BaseWidget< label: "Position", controlType: "ICON_TABS", fullWidth: true, + hidden: isAutoLayout, options: [ { label: "Auto", value: LabelPosition.Auto }, { label: "Left", value: LabelPosition.Left }, @@ -504,6 +506,7 @@ class MultiSelectWidget extends BaseWidget< helpText: "Control the font size of the label associated", controlType: "DROP_DOWN", defaultValue: "0.875rem", + hidden: isAutoLayout, options: [ { label: "S", diff --git a/app/client/src/widgets/SelectWidget/index.ts b/app/client/src/widgets/SelectWidget/index.ts index 5bdb0d46cb3c..b9f44087eb60 100644 --- a/app/client/src/widgets/SelectWidget/index.ts +++ b/app/client/src/widgets/SelectWidget/index.ts @@ -44,6 +44,18 @@ export const CONFIG = { responsiveBehavior: getDefaultResponsiveBehavior(Widget.getWidgetType()), minWidth: FILL_WIDGET_MIN_WIDTH, }, + autoLayout: { + widgetSize: [ + { + viewportMinWidth: 0, + configuration: () => { + return { + minWidth: "120px", + }; + }, + }, + ], + }, properties: { derived: Widget.getDerivedPropertiesMap(), default: Widget.getDefaultPropertiesMap(), diff --git a/app/client/src/widgets/SelectWidget/widget/index.tsx b/app/client/src/widgets/SelectWidget/widget/index.tsx index f5ef43e0907a..e65e02d404f3 100644 --- a/app/client/src/widgets/SelectWidget/widget/index.tsx +++ b/app/client/src/widgets/SelectWidget/widget/index.tsx @@ -18,6 +18,7 @@ import { LoDashStatic, } from "lodash"; import React from "react"; +import { isAutoLayout } from "selectors/mainCanvasSelectors"; import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService"; import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import { GRID_DENSITY_MIGRATION_V1, MinimumPopupRows } from "widgets/constants"; @@ -216,6 +217,7 @@ class SelectWidget extends BaseWidget { { label: "Top", value: LabelPosition.Top }, { label: "Auto", value: LabelPosition.Auto }, ], + hidden: isAutoLayout, defaultValue: LabelPosition.Top, isBindProperty: false, isTriggerProperty: false, @@ -429,6 +431,7 @@ class SelectWidget extends BaseWidget { helpText: "Control the font size of the label associated", controlType: "DROP_DOWN", defaultValue: "0.875rem", + hidden: isAutoLayout, options: [ { label: "S", diff --git a/app/client/src/widgets/SingleSelectTreeWidget/index.ts b/app/client/src/widgets/SingleSelectTreeWidget/index.ts index 8c8891423d6f..f5c72c790974 100644 --- a/app/client/src/widgets/SingleSelectTreeWidget/index.ts +++ b/app/client/src/widgets/SingleSelectTreeWidget/index.ts @@ -58,6 +58,18 @@ export const CONFIG = { responsiveBehavior: getDefaultResponsiveBehavior(Widget.getWidgetType()), minWidth: FILL_WIDGET_MIN_WIDTH, }, + autoLayout: { + widgetSize: [ + { + viewportMinWidth: 0, + configuration: () => { + return { + minWidth: "120px", + }; + }, + }, + ], + }, properties: { derived: Widget.getDerivedPropertiesMap(), default: Widget.getDefaultPropertiesMap(), diff --git a/app/client/src/widgets/SingleSelectTreeWidget/widget/index.tsx b/app/client/src/widgets/SingleSelectTreeWidget/widget/index.tsx index 57a82157ed58..3f4caf6ac07d 100644 --- a/app/client/src/widgets/SingleSelectTreeWidget/widget/index.tsx +++ b/app/client/src/widgets/SingleSelectTreeWidget/widget/index.tsx @@ -12,6 +12,7 @@ import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; import { isArray } from "lodash"; import { DefaultValueType } from "rc-tree-select/lib/interface"; import React, { ReactNode } from "react"; +import { isAutoLayout } from "selectors/mainCanvasSelectors"; import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService"; import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; @@ -151,6 +152,7 @@ class SingleSelectTreeWidget extends BaseWidget< label: "Position", controlType: "ICON_TABS", fullWidth: true, + hidden: isAutoLayout, options: [ { label: "Auto", value: LabelPosition.Auto }, { label: "Left", value: LabelPosition.Left }, @@ -361,6 +363,7 @@ class SingleSelectTreeWidget extends BaseWidget< helpText: "Control the font size of the label associated", controlType: "DROP_DOWN", defaultValue: "0.875rem", + hidden: isAutoLayout, options: [ { label: "S", From 91c02764290c50c9f8f29899ef3c83bec6faac75 Mon Sep 17 00:00:00 2001 From: Aswath K Date: Tue, 21 Feb 2023 18:03:20 +0530 Subject: [PATCH 533/708] fix: Jest tests --- .../components/editorComponents/EditorContextProvider.test.tsx | 2 ++ app/client/src/selectors/mainCanvasSelectors.tsx | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/client/src/components/editorComponents/EditorContextProvider.test.tsx b/app/client/src/components/editorComponents/EditorContextProvider.test.tsx index 62508624a86f..fbbee5d31261 100644 --- a/app/client/src/components/editorComponents/EditorContextProvider.test.tsx +++ b/app/client/src/components/editorComponents/EditorContextProvider.test.tsx @@ -41,6 +41,7 @@ describe("EditorContextProvider", () => { "updateWidget", "updateWidgetProperty", "updateWidgetAutoHeight", + "updateWidgetDimension", "checkContainersForAutoHeight", ].sort(); @@ -73,6 +74,7 @@ describe("EditorContextProvider", () => { "syncUpdateWidgetMetaProperty", "triggerEvalOnMetaUpdate", "updateWidgetAutoHeight", + "updateWidgetDimension", "checkContainersForAutoHeight", ].sort(); diff --git a/app/client/src/selectors/mainCanvasSelectors.tsx b/app/client/src/selectors/mainCanvasSelectors.tsx index e42f72f528ad..156f72b968c7 100644 --- a/app/client/src/selectors/mainCanvasSelectors.tsx +++ b/app/client/src/selectors/mainCanvasSelectors.tsx @@ -9,7 +9,7 @@ export const getIsCanvasInitialized = (state: AppState) => { export const getIsMobile = (state: AppState) => state.ui.mainCanvas.isMobile; export const getUseAutoLayout = (state: AppState) => - state.entities.canvasWidgets[MAIN_CONTAINER_WIDGET_ID].useAutoLayout; + state.entities.canvasWidgets[MAIN_CONTAINER_WIDGET_ID]?.useAutoLayout; export function isAutoLayout() { const appState = store.getState(); From 0d1c31a6ae72d120823e90c3d88382df0b7c309d Mon Sep 17 00:00:00 2001 From: Aswath K Date: Tue, 21 Feb 2023 20:39:56 +0530 Subject: [PATCH 534/708] Updates widget config according to spec --- app/client/src/utils/layoutPropertiesUtils.ts | 9 +++++++ app/client/src/widgets/AudioWidget/index.tsx | 2 +- .../src/widgets/CategorySliderWidget/index.ts | 2 +- .../src/widgets/CheckboxWidget/index.ts | 2 +- .../src/widgets/CurrencyInputWidget/index.ts | 2 +- .../src/widgets/DatePickerWidget2/index.ts | 2 +- app/client/src/widgets/InputWidgetV2/index.ts | 2 +- .../src/widgets/NumberSliderWidget/index.ts | 2 +- .../src/widgets/PhoneInputWidget/index.ts | 2 +- .../src/widgets/RangeSliderWidget/index.ts | 2 +- app/client/src/widgets/RateWidget/index.ts | 26 +++++++++++-------- .../src/widgets/RateWidget/widget/index.tsx | 1 - app/client/src/widgets/SwitchWidget/index.ts | 2 +- app/client/src/widgets/constants.ts | 2 +- 14 files changed, 35 insertions(+), 23 deletions(-) diff --git a/app/client/src/utils/layoutPropertiesUtils.ts b/app/client/src/utils/layoutPropertiesUtils.ts index 335887303612..581979c74394 100644 --- a/app/client/src/utils/layoutPropertiesUtils.ts +++ b/app/client/src/utils/layoutPropertiesUtils.ts @@ -294,6 +294,7 @@ export const WIDGET_WITH_DYNAMIC_WIDTH = [ "ICON_BUTTON_WIDGET", "MENU_BUTTON_WIDGET", "FILE_PICKER_WIDGET_V2", + "RATE_WIDGET", ]; // TODO(aswathkk): See if this needs to be moved to widget config @@ -302,6 +303,14 @@ export const WIDGET_WITH_DYNAMIC_HEIGHT = [ "CHECKBOX_GROUP_WIDGET", "CHECKBOX_WIDGET", "SWITCH_WIDGET", + "TEXT_WIDGET", + "INPUT_WIDGET_V2", + "PHONE_INPUT_WIDGET", + "CURRENCY_INPUT_WIDGET", + "SELECT_WIDGET", + "MULTI_SELECT_TREE_WIDGET", + "SINGLE_SELECT_TREE_WIDGET", + "MULTI_SELECT_WIDGET_V2", ]; export function getDefaultResponsiveBehavior(widgetType: string) { diff --git a/app/client/src/widgets/AudioWidget/index.tsx b/app/client/src/widgets/AudioWidget/index.tsx index 7544b26cfa39..2f3bccdbbdd5 100644 --- a/app/client/src/widgets/AudioWidget/index.tsx +++ b/app/client/src/widgets/AudioWidget/index.tsx @@ -34,7 +34,7 @@ export const CONFIG = { viewportMinWidth: 0, configuration: () => { return { - minWidth: "70px", + minWidth: "180px", }; }, }, diff --git a/app/client/src/widgets/CategorySliderWidget/index.ts b/app/client/src/widgets/CategorySliderWidget/index.ts index bee81cd7c8c9..94ae822a5863 100644 --- a/app/client/src/widgets/CategorySliderWidget/index.ts +++ b/app/client/src/widgets/CategorySliderWidget/index.ts @@ -53,7 +53,7 @@ export const CONFIG = { viewportMinWidth: 0, configuration: () => { return { - minWidth: "150px", + minWidth: "180px", }; }, }, diff --git a/app/client/src/widgets/CheckboxWidget/index.ts b/app/client/src/widgets/CheckboxWidget/index.ts index 5b73b9c20556..fc84271cd1b8 100644 --- a/app/client/src/widgets/CheckboxWidget/index.ts +++ b/app/client/src/widgets/CheckboxWidget/index.ts @@ -38,7 +38,7 @@ export const CONFIG = { viewportMinWidth: 0, configuration: () => { return { - minWidth: "74px", + minWidth: "120px", }; }, }, diff --git a/app/client/src/widgets/CurrencyInputWidget/index.ts b/app/client/src/widgets/CurrencyInputWidget/index.ts index 9ecf5a92bb36..ced03ace7331 100644 --- a/app/client/src/widgets/CurrencyInputWidget/index.ts +++ b/app/client/src/widgets/CurrencyInputWidget/index.ts @@ -48,7 +48,7 @@ export const CONFIG = { viewportMinWidth: 0, configuration: () => { return { - minWidth: "180px", + minWidth: "160px", }; }, }, diff --git a/app/client/src/widgets/DatePickerWidget2/index.ts b/app/client/src/widgets/DatePickerWidget2/index.ts index ed1043745c1c..57a833540376 100644 --- a/app/client/src/widgets/DatePickerWidget2/index.ts +++ b/app/client/src/widgets/DatePickerWidget2/index.ts @@ -52,7 +52,7 @@ export const CONFIG = { viewportMinWidth: 0, configuration: () => { return { - minWidth: "60px", + minWidth: "120px", }; }, }, diff --git a/app/client/src/widgets/InputWidgetV2/index.ts b/app/client/src/widgets/InputWidgetV2/index.ts index 001e64fafe7b..a0ecca638011 100644 --- a/app/client/src/widgets/InputWidgetV2/index.ts +++ b/app/client/src/widgets/InputWidgetV2/index.ts @@ -45,7 +45,7 @@ export const CONFIG = { viewportMinWidth: 0, configuration: () => { return { - minWidth: "60px", + minWidth: "120px", }; }, }, diff --git a/app/client/src/widgets/NumberSliderWidget/index.ts b/app/client/src/widgets/NumberSliderWidget/index.ts index aed9a980e987..2b5dd447077d 100644 --- a/app/client/src/widgets/NumberSliderWidget/index.ts +++ b/app/client/src/widgets/NumberSliderWidget/index.ts @@ -55,7 +55,7 @@ export const CONFIG = { viewportMinWidth: 0, configuration: () => { return { - minWidth: "150px", + minWidth: "180px", }; }, }, diff --git a/app/client/src/widgets/PhoneInputWidget/index.ts b/app/client/src/widgets/PhoneInputWidget/index.ts index 55cbffbf6704..690971ae3287 100644 --- a/app/client/src/widgets/PhoneInputWidget/index.ts +++ b/app/client/src/widgets/PhoneInputWidget/index.ts @@ -47,7 +47,7 @@ export const CONFIG = { viewportMinWidth: 0, configuration: () => { return { - minWidth: "180px", + minWidth: "160px", }; }, }, diff --git a/app/client/src/widgets/RangeSliderWidget/index.ts b/app/client/src/widgets/RangeSliderWidget/index.ts index 94c28ea7a7cd..8a1174c7f25a 100644 --- a/app/client/src/widgets/RangeSliderWidget/index.ts +++ b/app/client/src/widgets/RangeSliderWidget/index.ts @@ -56,7 +56,7 @@ export const CONFIG = { viewportMinWidth: 0, configuration: () => { return { - minWidth: "150px", + minWidth: "180px", }; }, }, diff --git a/app/client/src/widgets/RateWidget/index.ts b/app/client/src/widgets/RateWidget/index.ts index 8ef74571aa18..916717ead48e 100644 --- a/app/client/src/widgets/RateWidget/index.ts +++ b/app/client/src/widgets/RateWidget/index.ts @@ -30,18 +30,22 @@ export const CONFIG = { tooltips: ["Terrible", "Bad", "Neutral", "Good", "Great"], widgetName: "Rating", }, - // A sample widgetSize configuration for AutoLayout - widgetSize: [ - { - viewportMinWidth: 0, - configuration: (props: RateWidgetProps) => { - return { - // 20 is the size of a star, 5 is the margin between stars, 8 is the total padding of the widget - minWidth: `${props.maxCount * 21 + (props.maxCount + 1) * 5 + 8}px`, - }; + autoLayout: { + widgetSize: [ + { + viewportMinWidth: 0, + configuration: (props: RateWidgetProps) => { + let maxCount = props.maxCount; + if (typeof maxCount !== "number") + maxCount = parseInt(props.maxCount as any, 10); + return { + // 21 is the size of a star, 5 is the margin between stars + minWidth: `${maxCount * 21 + (maxCount + 1) * 5}px`, + }; + }, }, - }, - ], + ], + }, properties: { derived: Widget.getDerivedPropertiesMap(), default: Widget.getDefaultPropertiesMap(), diff --git a/app/client/src/widgets/RateWidget/widget/index.tsx b/app/client/src/widgets/RateWidget/widget/index.tsx index 7c9d7cccf0ed..cf290a6e13d2 100644 --- a/app/client/src/widgets/RateWidget/widget/index.tsx +++ b/app/client/src/widgets/RateWidget/widget/index.tsx @@ -83,7 +83,6 @@ class RateWidget extends BaseWidget { placeholderText: "5", isBindProperty: true, isTriggerProperty: false, - hidden: isAutoLayout, validation: { type: ValidationTypes.NUMBER, params: { natural: true }, diff --git a/app/client/src/widgets/SwitchWidget/index.ts b/app/client/src/widgets/SwitchWidget/index.ts index 1e999c88739d..f76e5e6efc17 100644 --- a/app/client/src/widgets/SwitchWidget/index.ts +++ b/app/client/src/widgets/SwitchWidget/index.ts @@ -35,7 +35,7 @@ export const CONFIG = { viewportMinWidth: 0, configuration: () => { return { - minWidth: "74px", + minWidth: "120px", }; }, }, diff --git a/app/client/src/widgets/constants.ts b/app/client/src/widgets/constants.ts index 53e2169706f9..801d8def32ed 100644 --- a/app/client/src/widgets/constants.ts +++ b/app/client/src/widgets/constants.ts @@ -17,7 +17,7 @@ import { WidgetProps } from "./BaseWidget"; export type WidgetSizeConfig = { viewportMinWidth: number; - configuration: (props: WidgetProps) => { [key: string]: string | number }; + configuration: (props: any) => { [key: string]: string | number }; }; export type AutoLayoutConfig = { From 6456a0435b9886c702cc6817340ecf7479df8815 Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 22 Feb 2023 10:02:21 +0530 Subject: [PATCH 535/708] minor fix --- app/client/src/widgets/ModalWidget/component/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/client/src/widgets/ModalWidget/component/index.tsx b/app/client/src/widgets/ModalWidget/component/index.tsx index 084c724c9628..5a78c69d8d61 100644 --- a/app/client/src/widgets/ModalWidget/component/index.tsx +++ b/app/client/src/widgets/ModalWidget/component/index.tsx @@ -23,11 +23,11 @@ import { } from "components/editorComponents/ResizeStyledComponents"; import { Colors } from "constants/Colors"; import { Layers } from "constants/Layers"; -import Resizable from "resizable/resize"; import AnalyticsUtil from "utils/AnalyticsUtil"; import { getCanvasClassName } from "utils/generators"; import { useWidgetDragResize } from "utils/hooks/dragResizeHooks"; import { scrollCSS } from "widgets/WidgetUtils"; +import Resizable from "resizable/modalresize"; const Container = styled.div<{ width?: number; From b10ca4770ff5c8a5bc3d4e3567dec0183671b1ac Mon Sep 17 00:00:00 2001 From: Aswath K Date: Wed, 22 Feb 2023 12:01:34 +0530 Subject: [PATCH 536/708] fix: infinite loop in jest test --- app/client/src/utils/autoLayout/positionUtils.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/app/client/src/utils/autoLayout/positionUtils.test.ts b/app/client/src/utils/autoLayout/positionUtils.test.ts index cfc9da39ec02..61a06d2f042f 100644 --- a/app/client/src/utils/autoLayout/positionUtils.test.ts +++ b/app/client/src/utils/autoLayout/positionUtils.test.ts @@ -918,7 +918,6 @@ describe("test PositionUtils methods", () => { mobileLeftColumn: 0, mobileRightColumn: 640, responsiveBehavior: ResponsiveBehavior.Fill, - parentId: "4", flexLayers: [ { children: [{ id: "4", align: FlexLayerAlignment.Start }], From dfca4349f405d2a3dc931e9baba32ef5d919acf9 Mon Sep 17 00:00:00 2001 From: rahulramesha Date: Wed, 22 Feb 2023 12:55:42 +0530 Subject: [PATCH 537/708] Fix Auto layout bugs #20694, #20704, #20696 --- app/client/src/pages/Editor/Canvas.tsx | 11 +- .../Editor/WidgetsEditor/CanvasContainer.tsx | 123 ++++++++++-------- app/client/src/selectors/canvasSelectors.ts | 17 +++ 3 files changed, 92 insertions(+), 59 deletions(-) diff --git a/app/client/src/pages/Editor/Canvas.tsx b/app/client/src/pages/Editor/Canvas.tsx index 431444b6d8dc..815ef3f0bd57 100644 --- a/app/client/src/pages/Editor/Canvas.tsx +++ b/app/client/src/pages/Editor/Canvas.tsx @@ -15,6 +15,7 @@ interface CanvasProps { widgetsStructure: CanvasWidgetStructure; pageId: string; canvasWidth: number; + isAutoLayout?: boolean; canvasScale?: number; } @@ -22,13 +23,14 @@ const Container = styled.section<{ background: string; width: number; $canvasScale: number; + $isAutoLayout: boolean; }>` background: ${({ background }) => background}; - width: ${(props) => props.width}px; + width: ${({ $isAutoLayout, width }) => + $isAutoLayout ? `100%` : `${width}px`}; transform: scale(${(props) => props.$canvasScale}); transform-origin: "0 0"; `; - const Canvas = (props: CanvasProps) => { const { canvasScale = 1, canvasWidth } = props; const isPreviewMode = useSelector(previewModeSelector); @@ -47,12 +49,15 @@ const Canvas = (props: CanvasProps) => { const focusRef = useWidgetFocus(); + const marginHorizontalClass = props.isAutoLayout ? `mx-0` : `mx-auto`; + try { return ( ` - width: 100%; + width: ${({ $isAutoLayout }) => + $isAutoLayout ? `calc(100% - ${CANVAS_WIDTH_OFFSET}px)` : `100%`}; position: relative; overflow-x: auto; overflow-y: auto; @@ -103,6 +108,7 @@ function CanvasContainer() { const currentPageId = useSelector(getCurrentPageId); const isFetchingPage = useSelector(getIsFetchingPage); const canvasWidth = useSelector(getCanvasWidth); + const isAutoLayout = useSelector(getIsAutoLayout); const widgetsStructure = useSelector(getCanvasWidgetsStructure, equal); const pages = useSelector(getViewModePageList); const theme = useSelector(getCurrentThemeDetails); @@ -116,6 +122,7 @@ function CanvasContainer() { const isLayoutingInitialized = useDynamicAppLayout(); const isPageInitializing = isFetchingPage || !isLayoutingInitialized; + useEffect(() => { return () => { dispatch(forceOpenWidgetPanel(false)); @@ -140,6 +147,7 @@ function CanvasContainer() { @@ -149,20 +157,20 @@ function CanvasContainer() { const appLayout = useSelector(getCurrentApplicationLayout); useEffect(() => { if (appPositioningType === AppPositioningTypes.AUTO) { - let buffer = 0; + const buffer = isPreviewMode ? AUTOLAYOUT_RESIZER_WIDTH_BUFFER : 0; + const fullWidthCSS = `calc(100% - ${CANVAS_WIDTH_OFFSET}px)`; + const wrapperElement: any = document.getElementById("widgets-editor"); const ele: any = document.getElementById("canvas-viewport"); - if (isPreviewMode) { - ele.style.width = "inherit"; - buffer = AUTOLAYOUT_RESIZER_WIDTH_BUFFER; - } else { - ele.style.width = "100%"; + + let maxWidth = wrapperElement.offsetWidth - CANVAS_WIDTH_OFFSET; + + if (ele && ele.offsetWidth >= maxWidth) { + ele.style.width = fullWidthCSS; } + if (appLayout?.type === "FLUID") { const smallestWidth = layoutConfigurations.MOBILE.minWidth; - // Query the element - const ele: any = document.getElementById("canvas-viewport"); - let needsInitiation = true; - let initialWidth = ele.offsetWidth; + // The current position of mouse let x = 0; // let y = 0; @@ -175,10 +183,8 @@ function CanvasContainer() { // Handle the mousedown event // that's triggered when user drags the resizer const mouseDownHandler = function(e: any) { - if (needsInitiation) { - initialWidth = ele.offsetWidth; - needsInitiation = false; - } + maxWidth = wrapperElement.offsetWidth - CANVAS_WIDTH_OFFSET; + // Get the current mouse position x = e.clientX; // y = e.clientY; @@ -200,12 +206,12 @@ function CanvasContainer() { // const multiplier = rightHandle ? 2 : -2; const multiplier = 2; const dx = (e.clientX - x) * multiplier; - if (initialWidth >= w + dx && smallestWidth <= w + dx) { + if (maxWidth >= w + dx && smallestWidth <= w + dx) { // Adjust the dimension of element ele.style.width = `${w + dx}px`; } - if (initialWidth < w + dx) { - ele.style.width = `${initialWidth}px`; + if (maxWidth < w + dx) { + ele.style.width = fullWidthCSS; } if (smallestWidth > w + dx) { ele.style.width = `${smallestWidth}px`; @@ -220,12 +226,14 @@ function CanvasContainer() { document.removeEventListener("mouseup", mouseUpHandler); events = []; }; - const rightResizer: any = ele.querySelectorAll(".resizer-right")[0]; + const rightResizer: any = document.querySelectorAll( + ".resizer-right", + )[0]; const rightMove = (e: any) => mouseDownHandler(e); - rightResizer.addEventListener("mousedown", rightMove); + rightResizer?.addEventListener("mousedown", rightMove); return () => { - rightResizer.removeEventListener("mousedown", rightMove); + rightResizer?.removeEventListener("mousedown", rightMove); }; } } @@ -234,36 +242,40 @@ function CanvasContainer() { // calculating exact height to not allow scroll at this component, // calculating total height minus margin on top, top bar and bottom bar const heightWithTopMargin = `calc(100vh - 2.25rem - ${theme.smallHeaderHeight} - ${theme.bottomBarHeight})`; + const resizerTop = `calc(2.25rem + ${theme.smallHeaderHeight})`; return ( - - - {isAppThemeChanging && ( -
- -
- )} - {node} + <> + + + {isAppThemeChanging && ( +
+ +
+ )} + {node} +
{appPositioningType === AppPositioningTypes.AUTO && ( @@ -284,7 +295,7 @@ function CanvasContainer() {
)} - + ); } CanvasContainer.whyDidYouRender = { diff --git a/app/client/src/selectors/canvasSelectors.ts b/app/client/src/selectors/canvasSelectors.ts index 45cb3a969c05..2c590d51856e 100644 --- a/app/client/src/selectors/canvasSelectors.ts +++ b/app/client/src/selectors/canvasSelectors.ts @@ -1,5 +1,22 @@ import { AppState } from "@appsmith/reducers"; +import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; +import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; +import { createSelector } from "reselect"; +import { getWidgets } from "sagas/selectors"; +import { Positioning } from "utils/autoLayout/constants"; export const getIsDraggingForSelection = (state: AppState) => { return state.ui.canvasSelection.isDraggingForSelection; }; + +export const getIsAutoLayout = createSelector( + getWidgets, + (widgets: CanvasWidgetsReduxState): boolean => { + const mainContainer = widgets[MAIN_CONTAINER_WIDGET_ID]; + + return ( + mainContainer.useAutoLayout && + mainContainer.positioning === Positioning.Vertical + ); + }, +); From 1a704cbb54f0f66ba7236856b6f59f5159dd40e7 Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 22 Feb 2023 13:00:29 +0530 Subject: [PATCH 538/708] fix global hot keys test --- app/client/src/pages/Editor/GlobalHotKeys/GlobalHotKeys.test.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/app/client/src/pages/Editor/GlobalHotKeys/GlobalHotKeys.test.tsx b/app/client/src/pages/Editor/GlobalHotKeys/GlobalHotKeys.test.tsx index d93cb8e4697c..887a31f9c741 100644 --- a/app/client/src/pages/Editor/GlobalHotKeys/GlobalHotKeys.test.tsx +++ b/app/client/src/pages/Editor/GlobalHotKeys/GlobalHotKeys.test.tsx @@ -544,7 +544,6 @@ describe("Cut/Copy/Paste hotkey", () => { }); expect(spyPaste).toBeCalled(); - await component.findByTestId("t--selection-box"); act(() => { dispatchTestKeyboardEventWithCode( component.container, From bc71f0a5eaf803b064a570c8a6254f9aa7b5baae Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 22 Feb 2023 15:13:22 +0530 Subject: [PATCH 539/708] remove unused import --- app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx index 7b8fe5cd2c82..32cba3accab7 100644 --- a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx +++ b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx @@ -3,7 +3,6 @@ import React, { ReactNode, useEffect } from "react"; import { useSelector } from "react-redux"; import { - getCanvasScale, getCanvasWidth, getCurrentApplicationLayout, getCurrentAppPositioningType, From e757c428a4df5d0c2b12d11781230cc4c2666123 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Wed, 22 Feb 2023 16:40:16 +0530 Subject: [PATCH 540/708] removing console logs. --- app/client/src/resizable/autolayoutresize/index.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/client/src/resizable/autolayoutresize/index.tsx b/app/client/src/resizable/autolayoutresize/index.tsx index 661e5d5aed41..70d4914a37b1 100644 --- a/app/client/src/resizable/autolayoutresize/index.tsx +++ b/app/client/src/resizable/autolayoutresize/index.tsx @@ -213,15 +213,12 @@ export function ReflowResizable(props: ResizableProps) { //if it should not resize horizontally, we keep keep the previous horizontal dimensions if (!canHorizontalMove || !canResizeHorizontally) { - console.log("I cannot move", { resizedPositions }); newRect = { ...newRect, width: prevState.width, x: prevState.x, X: prevState.X, }; - } else { - console.log("I can move", { resizedPositions }); } //if it should not resize vertically, we keep keep the previous vertical dimensions @@ -238,7 +235,6 @@ export function ReflowResizable(props: ResizableProps) { props.updateBottomRow(bottomMostRow); } - console.log({ newRect }); return newRect; }); } From 3f1be90daf533910bedff22098d793aacfb03478 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Wed, 22 Feb 2023 16:41:01 +0530 Subject: [PATCH 541/708] Revert "enabling dynamic height under the hood for autolayout." This reverts commit c67c5e785dd99cc8db9fa545737cd69b00f0ef89. --- app/client/src/widgets/WidgetUtils.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/app/client/src/widgets/WidgetUtils.ts b/app/client/src/widgets/WidgetUtils.ts index 5981d000eefa..e667155a11da 100644 --- a/app/client/src/widgets/WidgetUtils.ts +++ b/app/client/src/widgets/WidgetUtils.ts @@ -743,6 +743,7 @@ export const isAutoHeightEnabledForWidget = ( props: WidgetProps, shouldCheckIfEnabledWithLimits = false, ) => { + if (props.isFlexChild) return false; if (shouldCheckIfEnabledWithLimits) { return props.dynamicHeight === DynamicHeight.AUTO_HEIGHT_WITH_LIMITS; } From 0c35b4d7f6556f3a4bc6ee68f50909719081aec8 Mon Sep 17 00:00:00 2001 From: rahulramesha Date: Wed, 22 Feb 2023 18:10:52 +0530 Subject: [PATCH 542/708] fix canvasResizer width related bugs --- .../src/utils/hooks/useDynamicAppLayout.tsx | 9 ++-- app/client/src/widgets/CanvasResizer.tsx | 53 ++++++++++--------- 2 files changed, 33 insertions(+), 29 deletions(-) diff --git a/app/client/src/utils/hooks/useDynamicAppLayout.tsx b/app/client/src/utils/hooks/useDynamicAppLayout.tsx index 1f682a5f75c0..22d95fbf3469 100644 --- a/app/client/src/utils/hooks/useDynamicAppLayout.tsx +++ b/app/client/src/utils/hooks/useDynamicAppLayout.tsx @@ -104,6 +104,9 @@ export const useDynamicAppLayout = () => { const { maxWidth, minWidth } = layoutWidthRange; let calculatedWidth = screenWidth - scrollbarWidth(); + const gutterWidth = + appPositioningType === AppPositioningTypes.AUTO ? 0 : GUTTER_WIDTH; + // if preview mode is not on and the app setting pane is not opened, we need to subtract the width of the property pane if ( isPreviewMode === false && @@ -142,15 +145,11 @@ export const useDynamicAppLayout = () => { calculatedWidth = ele.clientWidth; } - if (appPositioningType === AppPositioningTypes.AUTO && isPreviewMode) { - calculatedWidth -= AUTOLAYOUT_RESIZER_WIDTH_BUFFER; - } - switch (true) { case maxWidth < 0: case appLayout?.type === "FLUID": case calculatedWidth < maxWidth && calculatedWidth > minWidth: - const totalWidthToSubtract = BORDERS_WIDTH + GUTTER_WIDTH; + const totalWidthToSubtract = BORDERS_WIDTH + gutterWidth; // NOTE: gutter + border width will be only substracted when theme mode and preview mode are off return ( calculatedWidth - diff --git a/app/client/src/widgets/CanvasResizer.tsx b/app/client/src/widgets/CanvasResizer.tsx index c28fc906e1b0..9ed6ddc569fc 100644 --- a/app/client/src/widgets/CanvasResizer.tsx +++ b/app/client/src/widgets/CanvasResizer.tsx @@ -22,6 +22,7 @@ const AutoLayoutCanvasResizer = styled.div` align-items: center; justify-content: flex-start; z-index: 2; + margin-left: 2px; transition: width 300ms ease; transition: background 300ms ease; .canvas-resizer-icon { @@ -51,9 +52,15 @@ const AutoLayoutCanvasResizer = styled.div` } `; export function CanvasResizer({ + heightWithTopMargin, isPageInitiated, + resizerTop, + shouldHaveTopMargin, }: { + heightWithTopMargin: string; isPageInitiated: boolean; + resizerTop: string; + shouldHaveTopMargin: boolean; }) { const isPreviewMode = useSelector(previewModeSelector); const currentPageId = useSelector(getCurrentPageId); @@ -61,25 +68,22 @@ export function CanvasResizer({ const appPositioningType = useSelector(getCurrentAppPositioningType); const ref = useRef(null); useEffect(() => { - let ele: any = document.getElementById("canvas-viewport"); - if ( - isPageInitiated && - ele && - appPositioningType === AppPositioningTypes.AUTO - ) { - let buffer = 0; - if (isPreviewMode) { - ele.style.width = "inherit"; - buffer = AUTOLAYOUT_RESIZER_WIDTH_BUFFER; - } else { - ele.style.width = "100%"; + const ele: any = document.getElementById("canvas-viewport"); + + if (isPageInitiated && appPositioningType === AppPositioningTypes.AUTO) { + const buffer = isPreviewMode ? AUTOLAYOUT_RESIZER_WIDTH_BUFFER : 0; + const fullWidthCSS = `calc(100% - ${AUTOLAYOUT_RESIZER_WIDTH_BUFFER}px)`; + const wrapperElement: any = document.getElementById("widgets-editor"); + + let maxWidth = + wrapperElement.offsetWidth - AUTOLAYOUT_RESIZER_WIDTH_BUFFER; + + if (ele && ele.offsetWidth >= maxWidth) { + ele.style.width = fullWidthCSS; } + if (appLayout?.type === "FLUID") { const smallestWidth = layoutConfigurations.MOBILE.minWidth; - // Query the element - ele = document.getElementById("canvas-viewport"); - let needsInitiation = true; - let initialWidth = ele.offsetWidth; // The current position of mouse let x = 0; // let y = 0; @@ -92,10 +96,8 @@ export function CanvasResizer({ // Handle the mousedown event // that's triggered when user drags the resizer const mouseDownHandler = function(e: any) { - if (needsInitiation) { - initialWidth = ele.offsetWidth; - needsInitiation = false; - } + maxWidth = + wrapperElement.offsetWidth - AUTOLAYOUT_RESIZER_WIDTH_BUFFER; // Get the current mouse position x = e.clientX; // y = e.clientY; @@ -117,12 +119,12 @@ export function CanvasResizer({ // const multiplier = rightHandle ? 2 : -2; const multiplier = 2; const dx = (e.clientX - x) * multiplier; - if (initialWidth >= w + dx && smallestWidth <= w + dx) { + if (maxWidth >= w + dx && smallestWidth <= w + dx) { // Adjust the dimension of element ele.style.width = `${w + dx}px`; } - if (initialWidth < w + dx) { - ele.style.width = `${initialWidth}px`; + if (maxWidth < w + dx) { + ele.style.width = fullWidthCSS; } if (smallestWidth > w + dx) { ele.style.width = `${smallestWidth}px`; @@ -146,6 +148,8 @@ export function CanvasResizer({ rightResizer.removeEventListener("mousedown", rightMove); }; } + } else { + ele.style.removeProperty("width"); } }, [ appLayout, @@ -164,7 +168,8 @@ export function CanvasResizer({ }} ref={ref} style={{ - left: isPreviewMode ? `calc(100% - ${20}px)` : `calc(100% - ${37}px)`, + top: resizerTop, + height: shouldHaveTopMargin ? heightWithTopMargin : "100vh", bottom: isPreviewMode ? "-3px" : "0%", }} > From c1499c406e7e1a07a68e392cc22e0e4a34c21ea8 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Wed, 22 Feb 2023 19:04:53 +0530 Subject: [PATCH 543/708] Adjust widget name component border. --- .../editorComponents/WidgetNameComponent/SettingsControl.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/client/src/components/editorComponents/WidgetNameComponent/SettingsControl.tsx b/app/client/src/components/editorComponents/WidgetNameComponent/SettingsControl.tsx index 9f821a2d3bd0..81d315337f9c 100644 --- a/app/client/src/components/editorComponents/WidgetNameComponent/SettingsControl.tsx +++ b/app/client/src/components/editorComponents/WidgetNameComponent/SettingsControl.tsx @@ -30,7 +30,7 @@ const SettingsWrapper = styled.div<{ widgetWidth: number }>` display: flex; justify-content: space-between; align-items: center; - max-width: ${(props) => props.widgetWidth - BORDER_RADIUS / 2}px; + max-width: ${(props) => props.widgetWidth - BORDER_RADIUS}px; & { pre { margin: 0 5px 0 0; From d1c9fb548a900d757f97a5970054e9d7e690bcdc Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Wed, 22 Feb 2023 19:06:03 +0530 Subject: [PATCH 544/708] adjust resize handles position. --- .../editorComponents/ResizeStyledComponents.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/client/src/components/editorComponents/ResizeStyledComponents.tsx b/app/client/src/components/editorComponents/ResizeStyledComponents.tsx index 6f95de4ec2b3..670283c16e5e 100644 --- a/app/client/src/components/editorComponents/ResizeStyledComponents.tsx +++ b/app/client/src/components/editorComponents/ResizeStyledComponents.tsx @@ -127,23 +127,23 @@ export const HorizontalHandleStyles = css<{ export const LeftHandleStyles = styled.div` ${VerticalHandleStyles} - left: ${-EDGE_RESIZE_HANDLE_WIDTH / 2 - WIDGET_PADDING + 1}px; + left: ${-EDGE_RESIZE_HANDLE_WIDTH / 2 - WIDGET_PADDING + 1.5}px; `; export const RightHandleStyles = styled.div` ${VerticalHandleStyles}; - right: ${-EDGE_RESIZE_HANDLE_WIDTH / 2 - WIDGET_PADDING + 3}px; + right: ${-EDGE_RESIZE_HANDLE_WIDTH / 2 - WIDGET_PADDING + 3.5}px; height: calc(100% + ${2 * WIDGET_PADDING}px); `; export const TopHandleStyles = styled.div` ${HorizontalHandleStyles}; - top: ${-EDGE_RESIZE_HANDLE_WIDTH / 2 - WIDGET_PADDING + 1}px; + top: ${-EDGE_RESIZE_HANDLE_WIDTH / 2 - WIDGET_PADDING + 1.5}px; `; export const BottomHandleStyles = styled.div` ${HorizontalHandleStyles}; - bottom: ${-EDGE_RESIZE_HANDLE_WIDTH / 2 - WIDGET_PADDING + 3}px; + bottom: ${-EDGE_RESIZE_HANDLE_WIDTH / 2 - WIDGET_PADDING + 3.5}px; `; export const CornerHandleStyles = css` From e1ac2e0bd222fbfbcc4892a9396675074cf330d0 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Wed, 22 Feb 2023 19:20:54 +0530 Subject: [PATCH 545/708] select new widget on drop in auto layout mode. --- .../hooks/useBlocksToBeDraggedOnCanvas.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts b/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts index 31b5b158d329..3deb5b7da47b 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts @@ -1,7 +1,6 @@ import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants"; import { AppState } from "@appsmith/reducers"; import { stopReflowAction } from "actions/reflowActions"; -import { AlignItems, LayoutDirection } from "utils/autoLayout/constants"; import { DropTargetContext } from "components/editorComponents/DropTargetComponent"; import { EditorContext } from "components/editorComponents/EditorContextProvider"; import { OccupiedSpace, WidgetSpace } from "constants/CanvasEditorConstants"; @@ -17,11 +16,14 @@ import { useContext, useEffect, useRef } from "react"; import { useDispatch, useSelector } from "react-redux"; import { DragDetails } from "reducers/uiReducers/dragResizeReducer"; import { getDragDetails, getWidgetByID, getWidgets } from "sagas/selectors"; +import { SelectionRequestType } from "sagas/WidgetSelectUtils"; import { getOccupiedSpacesWhileMoving } from "selectors/editorSelectors"; import { getTableFilterState } from "selectors/tableFilterSelectors"; import { getSelectedWidgets } from "selectors/ui"; import { getIsReflowing } from "selectors/widgetReflowSelectors"; import AnalyticsUtil from "utils/AnalyticsUtil"; +import { HighlightInfo } from "utils/autoLayout/autoLayoutTypes"; +import { AlignItems, LayoutDirection } from "utils/autoLayout/constants"; import { snapToGrid } from "utils/helpers"; import { useWidgetSelection } from "utils/hooks/useWidgetSelection"; import { @@ -30,8 +32,6 @@ import { widgetOperationParams, } from "utils/WidgetPropsUtils"; import { XYCord } from "./useRenderBlocksOnCanvas"; -import { SelectionRequestType } from "sagas/WidgetSelectUtils"; -import { HighlightInfo } from "utils/autoLayout/autoLayoutTypes"; export interface WidgetDraggingUpdateParams extends WidgetDraggingBlock { updateWidgetParams: WidgetOperationParams; @@ -267,9 +267,6 @@ export const useBlocksToBeDraggedOnCanvas = ({ alignment: dropPayload.alignment, }, }; - setTimeout(() => { - selectWidget(widgetPayload.newWidgetId); - }, 100); dispatch({ type: ReduxActionTypes.AUTOLAYOUT_ADD_NEW_WIDGETS, payload: { @@ -281,6 +278,9 @@ export const useBlocksToBeDraggedOnCanvas = ({ direction, }, }); + setTimeout(() => { + selectWidget(SelectionRequestType.One, [widgetPayload.newWidgetId]); + }, 100); }; const onDrop = ( drawingBlocks: WidgetDraggingBlock[], From aecd8a6da922b48331818892790d1a2a1740734f Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 23 Feb 2023 06:53:00 +0530 Subject: [PATCH 546/708] cleanup resizable component and remove additionalAction --- .../editorComponents/ResizableComponent.tsx | 25 ++++------------ .../constants/PropertyControlConstants.tsx | 7 ----- .../Editor/PropertyPane/PropertyControl.tsx | 25 ---------------- app/client/src/utils/layoutPropertiesUtils.ts | 30 ------------------- 4 files changed, 6 insertions(+), 81 deletions(-) diff --git a/app/client/src/components/editorComponents/ResizableComponent.tsx b/app/client/src/components/editorComponents/ResizableComponent.tsx index eaae309b7119..26d833aa5b69 100644 --- a/app/client/src/components/editorComponents/ResizableComponent.tsx +++ b/app/client/src/components/editorComponents/ResizableComponent.tsx @@ -22,6 +22,10 @@ import { } from "selectors/widgetSelectors"; import AnalyticsUtil from "utils/AnalyticsUtil"; import { ResponsiveBehavior } from "utils/autoLayout/constants"; +import { + getWidgetHeight, + getWidgetWidth, +} from "utils/autoLayout/flexWidgetUtils"; import { useShowPropertyPane, useShowTableFilterPane, @@ -100,31 +104,14 @@ export const ResizableComponent = memo(function ResizableComponent( // The ResizableContainer's size prop is controlled const dimensions: UIElementSize = { width: - ((props.isFlexChild && - props.isMobile && - props.mobileRightColumn !== undefined - ? props.mobileRightColumn - : props.rightColumn) - - (props.isFlexChild && - props.isMobile && - props.mobileLeftColumn !== undefined - ? props.mobileLeftColumn - : props.leftColumn)) * + getWidgetWidth(props, !!props.isFlexChild ? !!props.isMobile : false) * props.parentColumnSpace - 2 * props.paddingOffset, height: - ((props.isFlexChild && - props.isMobile && - props.mobileBottomRow !== undefined - ? props.mobileBottomRow - : props.bottomRow) - - (props.isFlexChild && props.isMobile && props.mobileTopRow !== undefined - ? props.mobileTopRow - : props.topRow)) * + getWidgetHeight(props, !!props.isFlexChild ? !!props.isMobile : false) * props.parentRowSpace - 2 * props.paddingOffset, }; - // onResize handler const getResizedPositions = ( newDimensions: UIElementSize, diff --git a/app/client/src/constants/PropertyControlConstants.tsx b/app/client/src/constants/PropertyControlConstants.tsx index 289351960be1..bf23d1c675d1 100644 --- a/app/client/src/constants/PropertyControlConstants.tsx +++ b/app/client/src/constants/PropertyControlConstants.tsx @@ -7,8 +7,6 @@ import { ValidationResponse, ValidationTypes, } from "constants/WidgetValidation"; -import { AppTheme } from "entities/AppTheming"; -import { WidgetProps } from "widgets/BaseWidget"; import { AdditionalDynamicDataTree } from "utils/autocomplete/customTreeTypeDefCreator"; import { Stylesheet } from "entities/AppTheming"; import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; @@ -94,11 +92,6 @@ export type PropertyPaneControlConfig = { // TODO(abhinav): To fix this, rename the options property of the controls which use this // Alternatively, create a new structure options?: any; - additionalAction?: ( - props: any, - propertyName?: string, - propertyValue?: any, - ) => ReduxAction; // The following should ideally be used internally postUpdateAction?: ReduxActionType; onBlur?: () => void; diff --git a/app/client/src/pages/Editor/PropertyPane/PropertyControl.tsx b/app/client/src/pages/Editor/PropertyPane/PropertyControl.tsx index 3ba2365b1b0b..ff25c4f0f719 100644 --- a/app/client/src/pages/Editor/PropertyPane/PropertyControl.tsx +++ b/app/client/src/pages/Editor/PropertyPane/PropertyControl.tsx @@ -235,20 +235,6 @@ const PropertyControl = memo((props: Props) => { [], ); - const getAdditionalActionsToDispatch = ( - propertyName: string, - propertyValue: any, - ): ReduxAction | null => { - if (props.additionalAction) { - return props.additionalAction( - widgetProperties, - propertyName, - propertyValue, - ); - } - return null; - }; - const getWidgetsOwnUpdatesOnPropertyChange = ( propertyName: string, propertyValue: any, @@ -449,22 +435,11 @@ const PropertyControl = memo((props: Props) => { ); } } - const additionalAction = getAdditionalActionsToDispatch( - propertyName, - propertyValue, - ); if (allPropertiesToUpdates && allPropertiesToUpdates.length) { // updating properties of a widget(s) should be done only once when property value changes. // to make sure dsl updates are atomic which is a necessity for undo/redo. onBatchUpdatePropertiesOfMultipleWidgets(allPropertiesToUpdates); - // TODO: This is a temporary implementation. - // Replace it with Abhinav's implementation of the same functionality on dynamic height, when available. - if (additionalAction) { - setTimeout(() => { - dispatch(additionalAction); - }, 0); - } } }, [widgetProperties], diff --git a/app/client/src/utils/layoutPropertiesUtils.ts b/app/client/src/utils/layoutPropertiesUtils.ts index 97aef177a3d7..7d2735757367 100644 --- a/app/client/src/utils/layoutPropertiesUtils.ts +++ b/app/client/src/utils/layoutPropertiesUtils.ts @@ -101,18 +101,6 @@ export const generateResponsiveBehaviorConfig = ( isBindProperty: false, isTriggerProperty: true, validation: { type: ValidationTypes.TEXT }, - // additionalAction: ( - // props: any, - // propertyName?: string, - // propertyValue?: any, - // ) => ({ - // type: ReduxActionTypes.UPDATE_FILL_CHILD_LAYER, - // payload: { - // widgetId: props.widgetId, - // responsiveBehavior: propertyValue, - // }, - // }), - // dependencies: ["widgetId"], }; }; @@ -178,24 +166,6 @@ export const generatePositioningConfig = ( isBindProperty: true, isTriggerProperty: true, validation: { type: ValidationTypes.TEXT }, - // additionalAction: ( - // props: any, - // propertyName?: string, - // propertyValue?: any, - // ) => { - // if (!propertyName || !propertyValue) return; - // const positioning: Positioning = propertyValue as Positioning; - // return { - // type: - // positioning === Positioning.Vertical - // ? ReduxActionTypes.ADD_CHILD_WRAPPERS - // : ReduxActionTypes.REMOVE_CHILD_WRAPPERS, - // payload: { - // parentId: props.widgetId, - // }, - // }; - // }, - // dependencies: ["widgetId"], }; }; From 250619cfd712b219b6d1387035216dd86a61380a Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 23 Feb 2023 06:54:47 +0530 Subject: [PATCH 547/708] move widgetTypeClassname to widgetUtils --- .../designSystems/appsmith/PositionedContainer.tsx | 2 +- .../designSystems/appsmith/autoLayout/FlexComponent.tsx | 7 ++----- app/client/src/widgets/WidgetUtils.ts | 6 ++++++ 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/PositionedContainer.tsx b/app/client/src/components/designSystems/appsmith/PositionedContainer.tsx index 76ee8302b4cb..b1642c33d512 100644 --- a/app/client/src/components/designSystems/appsmith/PositionedContainer.tsx +++ b/app/client/src/components/designSystems/appsmith/PositionedContainer.tsx @@ -5,7 +5,6 @@ import { CSSUnits, PositionTypes, WidgetType, - widgetTypeClassname, WIDGET_PADDING, } from "constants/WidgetConstants"; import { generateClassName } from "utils/generators"; @@ -22,6 +21,7 @@ import { } from "selectors/widgetReflowSelectors"; import { POSITIONED_WIDGET } from "constants/componentClassNameConstants"; import equal from "fast-deep-equal"; +import { widgetTypeClassname } from "widgets/WidgetUtils"; const PositionedWidget = styled.div<{ zIndexOnHover: number; diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx index c07f0fd81d00..3133dadc3f50 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx @@ -1,11 +1,7 @@ import React, { CSSProperties, ReactNode, useCallback, useMemo } from "react"; import styled from "styled-components"; -import { - WidgetType, - widgetTypeClassname, - WIDGET_PADDING, -} from "constants/WidgetConstants"; +import { WidgetType, WIDGET_PADDING } from "constants/WidgetConstants"; import { useSelector } from "react-redux"; import { snipingModeSelector } from "selectors/editorSelectors"; import { getIsResizing } from "selectors/widgetSelectors"; @@ -17,6 +13,7 @@ import { import { useClickToSelectWidget } from "utils/hooks/useClickToSelectWidget"; import { usePositionedContainerZIndex } from "utils/hooks/usePositionedContainerZIndex"; import { checkIsDropTarget } from "../PositionedContainer"; +import { widgetTypeClassname } from "widgets/WidgetUtils"; export type AutoLayoutProps = { children: ReactNode; diff --git a/app/client/src/widgets/WidgetUtils.ts b/app/client/src/widgets/WidgetUtils.ts index e667155a11da..e4614bfe04f3 100644 --- a/app/client/src/widgets/WidgetUtils.ts +++ b/app/client/src/widgets/WidgetUtils.ts @@ -880,3 +880,9 @@ export const scrollCSS = css` background: transparent !important; } `; + +export const widgetTypeClassname = (widgetType: string): string => + `t--widget-${widgetType + .split("_") + .join("") + .toLowerCase()}`; From 1479e2deed86076db0739f404dd0a92ca2523446 Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 23 Feb 2023 07:16:59 +0530 Subject: [PATCH 548/708] memoize flexbox style --- .../appsmith/autoLayout/FlexBoxComponent.tsx | 68 +++++++------------ .../Editor/PropertyPane/PropertyControl.tsx | 1 - app/client/src/widgets/CanvasWidget.tsx | 5 -- 3 files changed, 26 insertions(+), 48 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx index 406a589d19f5..c3d81ebea8cb 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx @@ -1,11 +1,9 @@ import { isArray } from "lodash"; -import React, { ReactNode } from "react"; -import styled from "styled-components"; +import React, { CSSProperties, ReactNode, useMemo } from "react"; import { FlexLayerAlignment, LayoutDirection, - Overflow, } from "utils/autoLayout/constants"; import { APP_MODE } from "entities/App"; import { useSelector } from "react-redux"; @@ -25,40 +23,12 @@ export interface FlexBoxProps { useAutoLayout: boolean; children?: ReactNode; widgetId: string; - overflow: Overflow; flexLayers: FlexLayer[]; isMobile?: boolean; } export const DEFAULT_HIGHLIGHT_SIZE = 4; -export const FlexContainer = styled.div<{ - useAutoLayout?: boolean; - direction?: LayoutDirection; - stretchHeight: boolean; - overflow: Overflow; - leaveSpaceForWidgetName: boolean; - isMobile?: boolean; -}>` - display: ${({ useAutoLayout }) => (useAutoLayout ? "flex" : "block")}; - flex-direction: ${({ direction }) => - direction === LayoutDirection.Vertical ? "column" : "row"}; - justify-content: flex-start; - align-items: flex-start; - flex-wrap: ${({ overflow }) => - overflow?.indexOf("wrap") > -1 ? overflow : "nowrap"}; - - width: 100%; - height: ${({ stretchHeight }) => (stretchHeight ? "100%" : "auto")}; - - overflow: hidden; - - padding: ${({ leaveSpaceForWidgetName }) => - leaveSpaceForWidgetName - ? `${FLEXBOX_PADDING}px ${FLEXBOX_PADDING}px 22px ${FLEXBOX_PADDING}px;` - : "0px;"}; -`; - function FlexBoxComponent(props: FlexBoxProps) { const direction: LayoutDirection = props.direction || LayoutDirection.Horizontal; @@ -143,18 +113,32 @@ function FlexBoxComponent(props: FlexBoxProps) { ); } + const flexBoxStyle: CSSProperties = useMemo(() => { + return { + display: !!props.useAutoLayout ? "flex" : "block", + flexDirection: + props.direction === LayoutDirection.Vertical ? "column" : "row", + justifyContent: "flex-start", + alignItems: "flex-start", + flexWrap: "nowrap", + width: "100%", + height: props.stretchHeight ? "100%" : "auto", + overflow: "hidden", + padding: leaveSpaceForWidgetName + ? `${FLEXBOX_PADDING}px ${FLEXBOX_PADDING}px 22px ${FLEXBOX_PADDING}px` + : "0px", + }; + }, [ + props.useAutoLayout, + props.direction, + props.stretchHeight, + leaveSpaceForWidgetName, + ]); + return ( - - <>{renderChildren()} - +
+ {renderChildren()} +
); } diff --git a/app/client/src/pages/Editor/PropertyPane/PropertyControl.tsx b/app/client/src/pages/Editor/PropertyPane/PropertyControl.tsx index ff25c4f0f719..ce5dda21125b 100644 --- a/app/client/src/pages/Editor/PropertyPane/PropertyControl.tsx +++ b/app/client/src/pages/Editor/PropertyPane/PropertyControl.tsx @@ -15,7 +15,6 @@ import { } from "actions/controlActions"; import { setFocusablePropertyPaneField } from "actions/propertyPaneActions"; import { ReactComponent as ResetIcon } from "assets/icons/control/undo_2.svg"; -import { ReduxAction } from "ce/constants/ReduxActionConstants"; import { EditorTheme } from "components/editorComponents/CodeEditor/EditorConfig"; import { ControlData } from "components/propertyControls/BaseControl"; import { diff --git a/app/client/src/widgets/CanvasWidget.tsx b/app/client/src/widgets/CanvasWidget.tsx index 866f64d80119..2226711e5995 100644 --- a/app/client/src/widgets/CanvasWidget.tsx +++ b/app/client/src/widgets/CanvasWidget.tsx @@ -133,11 +133,6 @@ class CanvasWidget extends ContainerWidget { direction={direction} flexLayers={this.props.flexLayers || []} isMobile={this.props.isMobile} - overflow={ - direction === LayoutDirection.Horizontal - ? Overflow.Wrap - : Overflow.NoWrap - } stretchHeight={stretchFlexBox} useAutoLayout={this.props.useAutoLayout || false} widgetId={this.props.widgetId} From d69dc3e0763ce55c7e1f8307225496e75846a104 Mon Sep 17 00:00:00 2001 From: Aswath K Date: Thu, 23 Feb 2023 11:36:04 +0530 Subject: [PATCH 549/708] wip: tried to use updateMultipleWidgetPropertiesAction --- .../AutoLayoutDimensionObeserver.tsx | 45 ++++++++++--------- .../src/sagas/AutoLayoutUpdateSagas.tsx | 34 +++++++++++++- app/client/src/widgets/BaseWidget.tsx | 9 +++- 3 files changed, 66 insertions(+), 22 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutDimensionObeserver.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutDimensionObeserver.tsx index 9af7257775b6..a09120a0b421 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutDimensionObeserver.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutDimensionObeserver.tsx @@ -2,8 +2,6 @@ import { FLEXBOX_PADDING } from "constants/WidgetConstants"; import React, { useState } from "react"; import { PropsWithChildren, useEffect, useRef } from "react"; import styled from "styled-components"; -import { ResponsiveBehavior } from "utils/autoLayout/constants"; -import { WidgetProps } from "widgets/BaseWidget"; const SimpleContainer = styled.div` width: fit-content; @@ -14,7 +12,12 @@ const SimpleContainer = styled.div` interface AutoLayoutDimensionObserverProps { onDimensionUpdate: (width: number, height: number) => void; - widgetProps: WidgetProps; + // widgetProps: WidgetProps; + width: number; + height: number; + isFillWidget: boolean; + // TODO(aswathkk): Remove this since this is being only used for debuggind + widgetName: string; } export default function AutoLayoutDimensionObserver( @@ -34,29 +37,33 @@ export default function AutoLayoutDimensionObserver( const height = entries[0].contentRect.height; if (width === 0 || height === 0) return; setCurrentDimension({ width, height }); + onDimensionUpdate(width, height); }), ); - const boundingBoxWidth = - (props.widgetProps.rightColumn - props.widgetProps.leftColumn) * - props.widgetProps.parentColumnSpace - - 2 * FLEXBOX_PADDING; - - const boundingBoxHeight = - (props.widgetProps.bottomRow - props.widgetProps.topRow) * - props.widgetProps.parentRowSpace - - 2 * FLEXBOX_PADDING; - useEffect(() => { - const widthDiff = Math.abs(boundingBoxWidth - currentDimension.width); - const heightDiff = Math.abs(boundingBoxHeight - currentDimension.height); if (currentDimension.width === 0 || currentDimension.height === 0) return; + const widthDiff = Math.abs( + props.width - 2 * FLEXBOX_PADDING - currentDimension.width, + ); + const heightDiff = Math.abs( + props.height - 2 * FLEXBOX_PADDING - currentDimension.height, + ); if (widthDiff > 2 || heightDiff > 2) { + // console.log( + // "#### dimensionUpdateObserver", + // props.widgetName, + // "dWidth", + // widthDiff, + // "dHeight", + // heightDiff, + // currentDimension, + // ); onDimensionUpdate(currentDimension.width, currentDimension.height); } }, [ - boundingBoxWidth, - boundingBoxHeight, + props.width, + props.height, currentDimension.width, currentDimension.height, ]); @@ -74,9 +81,7 @@ export default function AutoLayoutDimensionObserver( return ( diff --git a/app/client/src/sagas/AutoLayoutUpdateSagas.tsx b/app/client/src/sagas/AutoLayoutUpdateSagas.tsx index 3c1909e59ddd..147fa815822f 100644 --- a/app/client/src/sagas/AutoLayoutUpdateSagas.tsx +++ b/app/client/src/sagas/AutoLayoutUpdateSagas.tsx @@ -18,6 +18,8 @@ import { getIsMobile } from "selectors/mainCanvasSelectors"; import { getCanvasWidth as getMainCanvasWidth } from "selectors/editorSelectors"; import { getWidgetMinMaxDimensionsInPixel } from "utils/autoLayout/flexWidgetUtils"; import { getIsDraggingOrResizing } from "selectors/widgetSelectors"; +// import { diff } from "deep-diff"; +// import { updateMultipleWidgetPropertiesAction } from "actions/controlActions"; export function* updateLayoutForMobileCheckpoint( actionPayload: ReduxAction<{ @@ -139,6 +141,7 @@ function* processAutoLayoutDimensionUpdatesSaga() { const isMobile: boolean = yield select(getIsMobile); let widgets = allWidgets; + // const widgetsOld = { ...widgets }; const parentIds = new Set(); // Initialise a list of changes so far. // This contains a map of widgetIds with their new topRow and bottomRow @@ -168,6 +171,17 @@ function* processAutoLayoutDimensionUpdatesSaga() { widget.bottomRow !== newBottomRow || widget.rightColumn !== newRightColumn ) { + // console.log( + // "#### change", + // widget.widgetName, + // { width, columnSpace, leftColumn: widget.leftColumn }, + // "rightColumn", + // widget.rightColumn, + // newRightColumn, + // "bottomRow", + // widget.bottomRow, + // newBottomRow, + // ); changesSoFar[widgetId] = { bottomRow: newBottomRow, rightColumn: newRightColumn, @@ -215,7 +229,25 @@ function* processAutoLayoutDimensionUpdatesSaga() { ]; } - // TODO(aswathkk): Use updateMultipleWidgetPropertiesAction instead of updateAndSaveLayout + // const d = diff(widgetsOld, widgets); + + // console.log( + // "#### process", + // { widgetsToUpdate, widgets, widgetsOld }, + // d?.map((x: any) => ({ + // path: `${widgets[x.path[0]]?.widgetName}.${x.path[1]}`, + // lhs: x?.lhs, + // rhs: x?.rhs, + // })), + // ); + + /** + * TODO(aswathkk): Use updateMultipleWidgetPropertiesAction instead of updateAndSaveLayout + * But, using updateMultipleWidgetPropertiesAction is causing following issues + * 1. Highlights are getting broken + * 2. widget's posision are not properly getting updated + */ + // Push all updates to the CanvasWidgetsReducer. // Note that we're not calling `UPDATE_LAYOUT` // as we don't need to trigger an eval diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index b8af83fc1615..1cb945357167 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -637,10 +637,17 @@ abstract class BaseWidget< if (!shouldObserveHeight && !shouldObserveWidth) return content; + const { componentHeight, componentWidth } = this.getComponentDimensions(); + return ( {content} From dd4ac5092c448b556e5ae53c62af45f094629677 Mon Sep 17 00:00:00 2001 From: Aswath K Date: Thu, 23 Feb 2023 12:25:05 +0530 Subject: [PATCH 550/708] fix: warning --- app/client/src/widgets/CanvasWidget.tsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/app/client/src/widgets/CanvasWidget.tsx b/app/client/src/widgets/CanvasWidget.tsx index 2226711e5995..01cdc13620e0 100644 --- a/app/client/src/widgets/CanvasWidget.tsx +++ b/app/client/src/widgets/CanvasWidget.tsx @@ -1,8 +1,4 @@ -import { - LayoutDirection, - Overflow, - Positioning, -} from "utils/autoLayout/constants"; +import { LayoutDirection, Positioning } from "utils/autoLayout/constants"; import FlexBoxComponent from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; import DropTargetComponent from "components/editorComponents/DropTargetComponent"; import { CANVAS_DEFAULT_MIN_HEIGHT_PX } from "constants/AppConstants"; From 28d621cbe849cb3bfbaffa99c9040fa9308cbe92 Mon Sep 17 00:00:00 2001 From: Arsalan Yaldram Date: Thu, 23 Feb 2023 12:36:16 +0530 Subject: [PATCH 551/708] feat: added dynamic height to group widgets, remove label position left. (#20876) --- app/client/src/utils/layoutPropertiesUtils.ts | 2 ++ app/client/src/widgets/CheckboxGroupWidget/widget/index.tsx | 2 ++ app/client/src/widgets/RadioGroupWidget/widget/index.tsx | 2 ++ app/client/src/widgets/SwitchGroupWidget/widget/index.tsx | 5 +++-- 4 files changed, 9 insertions(+), 2 deletions(-) diff --git a/app/client/src/utils/layoutPropertiesUtils.ts b/app/client/src/utils/layoutPropertiesUtils.ts index 530d2983b275..eafb3d535a27 100644 --- a/app/client/src/utils/layoutPropertiesUtils.ts +++ b/app/client/src/utils/layoutPropertiesUtils.ts @@ -271,6 +271,8 @@ export const WIDGET_WITH_DYNAMIC_WIDTH = [ // This is used only for autoLayout export const WIDGET_WITH_DYNAMIC_HEIGHT = [ "CHECKBOX_GROUP_WIDGET", + "SWITCH_GROUP_WIDGET", + "RADIO_GROUP_WIDGET", "CHECKBOX_WIDGET", "SWITCH_WIDGET", "TEXT_WIDGET", diff --git a/app/client/src/widgets/CheckboxGroupWidget/widget/index.tsx b/app/client/src/widgets/CheckboxGroupWidget/widget/index.tsx index ee91907e6c93..bb412faef4ad 100644 --- a/app/client/src/widgets/CheckboxGroupWidget/widget/index.tsx +++ b/app/client/src/widgets/CheckboxGroupWidget/widget/index.tsx @@ -22,6 +22,7 @@ import CheckboxGroupComponent from "../component"; import { OptionProps, SelectAllState, SelectAllStates } from "../constants"; import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; +import { isAutoLayout } from "selectors/mainCanvasSelectors"; export function defaultSelectedValuesValidation( value: unknown, @@ -143,6 +144,7 @@ class CheckboxGroupWidget extends BaseWidget< label: "Position", controlType: "ICON_TABS", fullWidth: true, + hidden: isAutoLayout, options: [ { label: "Auto", value: LabelPosition.Auto }, { label: "Left", value: LabelPosition.Left }, diff --git a/app/client/src/widgets/RadioGroupWidget/widget/index.tsx b/app/client/src/widgets/RadioGroupWidget/widget/index.tsx index 3397f7beba82..c08e0918b08b 100644 --- a/app/client/src/widgets/RadioGroupWidget/widget/index.tsx +++ b/app/client/src/widgets/RadioGroupWidget/widget/index.tsx @@ -18,6 +18,7 @@ import { isAutoHeightEnabledForWidget } from "widgets/WidgetUtils"; import BaseWidget, { WidgetProps, WidgetState } from "../../BaseWidget"; import RadioGroupComponent from "../component"; import { RadioOption } from "../constants"; +import { isAutoLayout } from "selectors/mainCanvasSelectors"; /** * Validation rules: @@ -252,6 +253,7 @@ class RadioGroupWidget extends BaseWidget { label: "Position", controlType: "ICON_TABS", fullWidth: true, + hidden: isAutoLayout, options: [ { label: "Auto", value: LabelPosition.Auto }, { label: "Left", value: LabelPosition.Left }, diff --git a/app/client/src/widgets/SwitchGroupWidget/widget/index.tsx b/app/client/src/widgets/SwitchGroupWidget/widget/index.tsx index 109ec9f86a1d..a8f78df5603e 100644 --- a/app/client/src/widgets/SwitchGroupWidget/widget/index.tsx +++ b/app/client/src/widgets/SwitchGroupWidget/widget/index.tsx @@ -1,12 +1,12 @@ +import React from "react"; import { Alignment } from "@blueprintjs/core"; import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; import { ValidationTypes } from "constants/WidgetValidation"; import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; import { isString, xor } from "lodash"; -import React from "react"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; - +import { isAutoLayout } from "selectors/mainCanvasSelectors"; import { LabelPosition } from "components/constants"; import { TextSize } from "constants/WidgetConstants"; import { Stylesheet } from "entities/AppTheming"; @@ -106,6 +106,7 @@ class SwitchGroupWidget extends BaseWidget< label: "Position", controlType: "ICON_TABS", fullWidth: true, + hidden: isAutoLayout, options: [ { label: "Auto", value: LabelPosition.Auto }, { label: "Left", value: LabelPosition.Left }, From 008ea8acf582fd11cc693346a7fa48c7fd022fa0 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Thu, 23 Feb 2023 14:11:28 +0530 Subject: [PATCH 552/708] remove default layout as it is already handled in the selector. --- .../src/pages/common/CanvasArenas/CanvasSelectionArena.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/CanvasSelectionArena.tsx b/app/client/src/pages/common/CanvasArenas/CanvasSelectionArena.tsx index 691ea38d48c6..a7a7d37d32b1 100644 --- a/app/client/src/pages/common/CanvasArenas/CanvasSelectionArena.tsx +++ b/app/client/src/pages/common/CanvasArenas/CanvasSelectionArena.tsx @@ -23,8 +23,8 @@ import { } from "selectors/editorSelectors"; import { getNearestParentCanvas } from "utils/generators"; import { getAbsolutePixels } from "utils/helpers"; -import { XYCord } from "./hooks/useRenderBlocksOnCanvas"; import { useCanvasDragToScroll } from "./hooks/useCanvasDragToScroll"; +import { XYCord } from "./hooks/useRenderBlocksOnCanvas"; import { StickyCanvasArena } from "./StickyCanvasArena"; export interface SelectedArenaDimensions { @@ -75,7 +75,7 @@ export function CanvasSelectionArena({ getWidget(state, widgetId), ); const currentPageId = useSelector(getCurrentPageId); - const appLayout = useSelector(getCurrentApplicationLayout) || "FLUID"; + const appLayout = useSelector(getCurrentApplicationLayout); const throttledWidgetSelection = useCallback( throttle( ( From 1fec0b1f61a61b8cde2036ac230ad4c4a6d4da7a Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 23 Feb 2023 16:11:57 +0530 Subject: [PATCH 553/708] fix build issue --- app/client/src/widgets/CanvasWidget.tsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/app/client/src/widgets/CanvasWidget.tsx b/app/client/src/widgets/CanvasWidget.tsx index 2226711e5995..01cdc13620e0 100644 --- a/app/client/src/widgets/CanvasWidget.tsx +++ b/app/client/src/widgets/CanvasWidget.tsx @@ -1,8 +1,4 @@ -import { - LayoutDirection, - Overflow, - Positioning, -} from "utils/autoLayout/constants"; +import { LayoutDirection, Positioning } from "utils/autoLayout/constants"; import FlexBoxComponent from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; import DropTargetComponent from "components/editorComponents/DropTargetComponent"; import { CANVAS_DEFAULT_MIN_HEIGHT_PX } from "constants/AppConstants"; From 1bbab808119417339793064a6a45c9c953fbc543 Mon Sep 17 00:00:00 2001 From: rahulramesha Date: Thu, 23 Feb 2023 18:27:16 +0530 Subject: [PATCH 554/708] avoid triggering drag to select while resizing canvas --- app/client/src/actions/autoLayoutActions.ts | 7 +++++++ app/client/src/ce/constants/ReduxActionConstants.tsx | 1 + app/client/src/reducers/uiReducers/dragResizeReducer.ts | 8 ++++++++ app/client/src/utils/hooks/useAllowEditorDragToSelect.ts | 5 +++++ app/client/src/widgets/CanvasResizer.tsx | 6 +++++- 5 files changed, 26 insertions(+), 1 deletion(-) diff --git a/app/client/src/actions/autoLayoutActions.ts b/app/client/src/actions/autoLayoutActions.ts index 45c0ea55b541..84a0e778aa79 100644 --- a/app/client/src/actions/autoLayoutActions.ts +++ b/app/client/src/actions/autoLayoutActions.ts @@ -12,3 +12,10 @@ export const updateLayoutForMobileBreakpointAction = ( canvasWidth, }, }); + +export const setAutoCanvasResizing = (isAutoCanvasResizing: boolean) => { + return { + type: ReduxActionTypes.SET_AUTO_CANVAS_RESIZING, + payload: isAutoCanvasResizing, + }; +}; diff --git a/app/client/src/ce/constants/ReduxActionConstants.tsx b/app/client/src/ce/constants/ReduxActionConstants.tsx index 16188e662f46..bbc997eeed9d 100644 --- a/app/client/src/ce/constants/ReduxActionConstants.tsx +++ b/app/client/src/ce/constants/ReduxActionConstants.tsx @@ -356,6 +356,7 @@ export const ReduxActionTypes = { SET_DRAGGING_CANVAS: "SET_DRAGGING_CANVAS", SET_NEW_WIDGET_DRAGGING: "SET_NEW_WIDGET_DRAGGING", SET_WIDGET_RESIZING: "SET_WIDGET_RESIZING", + SET_AUTO_CANVAS_RESIZING: "SET_AUTO_CANVAS_RESIZING", ADD_SUGGESTED_WIDGET: "ADD_SUGGESTED_WIDGET", MODIFY_META_WIDGETS: "MODIFY_META_WIDGETS", DELETE_META_WIDGETS: "DELETE_META_WIDGETS", diff --git a/app/client/src/reducers/uiReducers/dragResizeReducer.ts b/app/client/src/reducers/uiReducers/dragResizeReducer.ts index a35e2c0c4a4d..226edbccae73 100644 --- a/app/client/src/reducers/uiReducers/dragResizeReducer.ts +++ b/app/client/src/reducers/uiReducers/dragResizeReducer.ts @@ -15,6 +15,7 @@ const initialState: WidgetDragResizeState = { selectedWidgets: [], focusedWidget: undefined, selectedWidgetAncestry: [], + isAutoCanvasResizing: false, }; export const widgetDraggingReducer = createImmerReducer(initialState, { @@ -71,6 +72,12 @@ export const widgetDraggingReducer = createImmerReducer(initialState, { ) => { state.isResizing = action.payload.isResizing; }, + [ReduxActionTypes.SET_AUTO_CANVAS_RESIZING]: ( + state: WidgetDragResizeState, + action: ReduxAction, + ) => { + state.isAutoCanvasResizing = action.payload; + }, [ReduxActionTypes.SET_SELECTED_WIDGETS]: ( state: WidgetDragResizeState, action: ReduxAction<{ widgetIds: string[] }>, @@ -120,6 +127,7 @@ export type WidgetDragResizeState = { focusedWidget?: string; selectedWidgetAncestry: string[]; selectedWidgets: string[]; + isAutoCanvasResizing: boolean; }; export default widgetDraggingReducer; diff --git a/app/client/src/utils/hooks/useAllowEditorDragToSelect.ts b/app/client/src/utils/hooks/useAllowEditorDragToSelect.ts index 5c5553065537..554bb7a2a68b 100644 --- a/app/client/src/utils/hooks/useAllowEditorDragToSelect.ts +++ b/app/client/src/utils/hooks/useAllowEditorDragToSelect.ts @@ -21,6 +21,10 @@ export const useAllowEditorDragToSelect = () => { (state: AppState) => state.ui.canvasSelection.isDraggingForSelection, ); + const isAutoCanvasResizing = useSelector( + (state: AppState) => state.ui.widgetDragResize.isAutoCanvasResizing, + ); + // This state tells us to disable dragging, // This is usually true when widgets themselves implement drag/drop // This flag resolves conflicting drag/drop triggers. @@ -34,6 +38,7 @@ export const useAllowEditorDragToSelect = () => { const isPreviewMode = useSelector(previewModeSelector); return ( + !isAutoCanvasResizing && !isResizingOrDragging && !isDraggingDisabled && !isSnipingMode && diff --git a/app/client/src/widgets/CanvasResizer.tsx b/app/client/src/widgets/CanvasResizer.tsx index 9ed6ddc569fc..86e1b09afec8 100644 --- a/app/client/src/widgets/CanvasResizer.tsx +++ b/app/client/src/widgets/CanvasResizer.tsx @@ -1,7 +1,7 @@ import { ReactComponent as CanvasResizerIcon } from "assets/icons/ads/app-icons/canvas-resizer.svg"; import { layoutConfigurations } from "constants/WidgetConstants"; import React, { useEffect, useRef } from "react"; -import { useSelector } from "react-redux"; +import { useDispatch, useSelector } from "react-redux"; import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; import { getCurrentApplicationLayout, @@ -9,6 +9,7 @@ import { getCurrentPageId, previewModeSelector, } from "selectors/editorSelectors"; +import { setAutoCanvasResizing } from "actions/autoLayoutActions"; import styled from "styled-components"; import { AUTOLAYOUT_RESIZER_WIDTH_BUFFER } from "utils/hooks/useDynamicAppLayout"; @@ -67,6 +68,7 @@ export function CanvasResizer({ const appLayout = useSelector(getCurrentApplicationLayout) || "FLUID"; const appPositioningType = useSelector(getCurrentAppPositioningType); const ref = useRef(null); + const dispatch = useDispatch(); useEffect(() => { const ele: any = document.getElementById("canvas-viewport"); @@ -104,6 +106,7 @@ export function CanvasResizer({ // Calculate the dimension of element const styles = window.getComputedStyle(ele); + dispatch(setAutoCanvasResizing(true)); w = parseInt(styles.width, 10) + buffer; // h = parseInt(styles.height, 10); const mouseMove = (e: any) => mouseMoveHandler(e); @@ -135,6 +138,7 @@ export function CanvasResizer({ const mouseUpHandler = function(e: any) { // Remove the handlers of `mousemove` and `mouseup` mouseMoveHandler(e); + dispatch(setAutoCanvasResizing(false)); document.removeEventListener("mousemove", events[0] as any); document.removeEventListener("mouseup", mouseUpHandler); events = []; From 9bd17f496082bdc0d1cad8fe6bfcb00aaece770d Mon Sep 17 00:00:00 2001 From: rahulramesha Date: Thu, 23 Feb 2023 18:43:46 +0530 Subject: [PATCH 555/708] refactoring auto layout copy paste utils into autolayoututils --- app/client/src/sagas/WidgetOperationSagas.tsx | 4 +- .../src/sagas/WidgetOperationUtils.test.ts | 166 ----------------- app/client/src/sagas/WidgetOperationUtils.ts | 70 ------- .../src/utils/autoLayout/AutoLayoutUtils.ts | 74 +++++++- .../utils/autoLayout/autoLayoutUtils.test.ts | 171 +++++++++++++++++- 5 files changed, 245 insertions(+), 240 deletions(-) diff --git a/app/client/src/sagas/WidgetOperationSagas.tsx b/app/client/src/sagas/WidgetOperationSagas.tsx index 36446688907b..a9de7b4b0b5a 100644 --- a/app/client/src/sagas/WidgetOperationSagas.tsx +++ b/app/client/src/sagas/WidgetOperationSagas.tsx @@ -108,6 +108,8 @@ import { import WidgetFactory from "utils/WidgetFactory"; import { addChildToPastedFlexLayers, + getFlexLayersForSelectedWidgets, + getNewFlexLayers, isStack, pasteWidgetInFlexLayers, } from "../utils/autoLayout/AutoLayoutUtils"; @@ -127,9 +129,7 @@ import { getCanvasIdForContainer, getContainerIdForCanvas, getDefaultCanvas, - getFlexLayersForSelectedWidgets, getMousePositions, - getNewFlexLayers, getNewPositionsForCopiedWidgets, getNextWidgetName, getOccupiedSpacesFromProps, diff --git a/app/client/src/sagas/WidgetOperationUtils.test.ts b/app/client/src/sagas/WidgetOperationUtils.test.ts index 8686509e5478..0e9da9adb60c 100644 --- a/app/client/src/sagas/WidgetOperationUtils.test.ts +++ b/app/client/src/sagas/WidgetOperationUtils.test.ts @@ -2,7 +2,6 @@ import { OccupiedSpace } from "constants/CanvasEditorConstants"; import { klona } from "klona"; import { get } from "lodash"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; -import { FlexLayerAlignment } from "utils/autoLayout/constants"; import { WidgetProps } from "widgets/BaseWidget"; import { FlattenedWidgetProps } from "widgets/constants"; import { @@ -24,8 +23,6 @@ import { getWidgetsFromIds, getValueFromTree, resizePublishedMainCanvasToLowestWidget, - getFlexLayersForSelectedWidgets, - getNewFlexLayers, } from "./WidgetOperationUtils"; describe("WidgetOperationSaga", () => { @@ -1099,169 +1096,6 @@ describe("WidgetOperationSaga", () => { ]), ).toBe(false); }); - - it("should test getFlexLayersForSelectedWidgets", () => { - const parentCanvas = ({ - flexLayers: [ - { - children: [ - { - id: "1", - align: FlexLayerAlignment.Start, - }, - { - id: "2", - align: FlexLayerAlignment.Start, - }, - { - id: "3", - align: FlexLayerAlignment.Center, - }, - ], - }, - { - children: [ - { - id: "4", - align: FlexLayerAlignment.Start, - }, - ], - }, - { - children: [ - { - id: "5", - align: FlexLayerAlignment.Center, - }, - { - id: "6", - align: FlexLayerAlignment.End, - }, - ], - }, - ], - } as any) as FlattenedWidgetProps; - - const selectedWidgets = ["2", "3", "6"]; - - const selectedFlexLayers = [ - { - children: [ - { - id: "2", - align: FlexLayerAlignment.Start, - }, - { - id: "3", - align: FlexLayerAlignment.Center, - }, - ], - }, - { - children: [ - { - id: "6", - align: FlexLayerAlignment.End, - }, - ], - }, - ]; - - expect( - getFlexLayersForSelectedWidgets(selectedWidgets, parentCanvas), - ).toEqual(selectedFlexLayers); - }); - - it("should test getNewFlexLayers", () => { - const flexLayers = [ - { - children: [ - { - id: "1", - align: FlexLayerAlignment.Start, - }, - { - id: "2", - align: FlexLayerAlignment.Start, - }, - { - id: "3", - align: FlexLayerAlignment.Center, - }, - ], - }, - { - children: [ - { - id: "4", - align: FlexLayerAlignment.Start, - }, - ], - }, - { - children: [ - { - id: "5", - align: FlexLayerAlignment.Center, - }, - { - id: "6", - align: FlexLayerAlignment.End, - }, - ], - }, - ]; - - const widgetIdMap = { - "1": "11", - "2": "22", - "3": "33", - "4": "44", - "5": "55", - "6": "66", - }; - - const newFlexLayers = [ - { - children: [ - { - id: "11", - align: FlexLayerAlignment.Start, - }, - { - id: "22", - align: FlexLayerAlignment.Start, - }, - { - id: "33", - align: FlexLayerAlignment.Center, - }, - ], - }, - { - children: [ - { - id: "44", - align: FlexLayerAlignment.Start, - }, - ], - }, - { - children: [ - { - id: "55", - align: FlexLayerAlignment.Center, - }, - { - id: "66", - align: FlexLayerAlignment.End, - }, - ], - }, - ]; - - expect(getNewFlexLayers(flexLayers, widgetIdMap)).toEqual(newFlexLayers); - }); }); describe("getValueFromTree - ", () => { diff --git a/app/client/src/sagas/WidgetOperationUtils.ts b/app/client/src/sagas/WidgetOperationUtils.ts index 2fcc55d2d3cf..e7bc3332b326 100644 --- a/app/client/src/sagas/WidgetOperationUtils.ts +++ b/app/client/src/sagas/WidgetOperationUtils.ts @@ -55,7 +55,6 @@ import { DataTreeWidget } from "entities/DataTree/dataTreeFactory"; import { isWidget } from "@appsmith/workers/Evaluation/evaluationUtils"; import { CANVAS_DEFAULT_MIN_HEIGHT_PX } from "constants/AppConstants"; import { MetaState } from "reducers/entityReducers/metaReducer"; -import { FlexLayer } from "utils/autoLayout/autoLayoutTypes"; export interface CopiedWidgetGroup { widgetId: string; @@ -1799,72 +1798,3 @@ const updateListWidgetBindings = ( return widgets; }; - -/** - * This method preserves the flexLayers of the parent canvas, - * but only for the selected widgets - * @param selectedWidgets - * @param parentCanvas - * @returns - */ -export function getFlexLayersForSelectedWidgets( - selectedWidgets: string[], - parentCanvas: FlattenedWidgetProps | undefined, -): FlexLayer[] { - if ( - !parentCanvas || - !parentCanvas.flexLayers || - parentCanvas.flexLayers.length <= 0 - ) - return []; - - const currFlexLayers: FlexLayer[] = parentCanvas.flexLayers; - - const selectedFlexLayers: FlexLayer[] = []; - - for (const flexLayer of currFlexLayers) { - const layerChildren = []; - - for (const layerChild of flexLayer.children) { - if (selectedWidgets.indexOf(layerChild.id) > -1) { - layerChildren.push(layerChild); - } - } - - if (layerChildren.length > 0) { - selectedFlexLayers.push({ children: layerChildren }); - } - } - - return selectedFlexLayers; -} - -/** - * This method helps in Converting the widgetId inside flexLayers - * to the new corresponding widgetIds in widgetIdMap - * @param flexLayers - * @param widgetIdMap - * @returns - */ -export function getNewFlexLayers( - flexLayers: FlexLayer[], - widgetIdMap: Record, -) { - const newFlexLayers: FlexLayer[] = []; - - for (const flexLayer of flexLayers) { - const newChildren = []; - - for (const layerChild of flexLayer.children) { - if (widgetIdMap[layerChild.id]) { - newChildren.push({ - id: widgetIdMap[layerChild.id], - align: layerChild.align, - }); - } - } - newFlexLayers.push({ children: newChildren }); - } - - return newFlexLayers; -} diff --git a/app/client/src/utils/autoLayout/AutoLayoutUtils.ts b/app/client/src/utils/autoLayout/AutoLayoutUtils.ts index 057f2a949ab9..fbcd00ac1111 100644 --- a/app/client/src/utils/autoLayout/AutoLayoutUtils.ts +++ b/app/client/src/utils/autoLayout/AutoLayoutUtils.ts @@ -4,7 +4,10 @@ import { GridDefaults, MAIN_CONTAINER_WIDGET_ID, } from "constants/WidgetConstants"; -import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; +import { + CanvasWidgetsReduxState, + FlattenedWidgetProps, +} from "reducers/entityReducers/canvasWidgetsReducer"; import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; import { defaultAutoLayoutWidgets, @@ -428,3 +431,72 @@ export function getAlignmentColumnInfo( [FlexLayerAlignment.End]: end, }; } + +/** + * This method preserves the flexLayers of the parent canvas, + * but only for the selected widgets + * @param selectedWidgets + * @param parentCanvas + * @returns + */ +export function getFlexLayersForSelectedWidgets( + selectedWidgets: string[], + parentCanvas: FlattenedWidgetProps | undefined, +): FlexLayer[] { + if ( + !parentCanvas || + !parentCanvas.flexLayers || + parentCanvas.flexLayers.length <= 0 + ) + return []; + + const currFlexLayers: FlexLayer[] = parentCanvas.flexLayers; + + const selectedFlexLayers: FlexLayer[] = []; + + for (const flexLayer of currFlexLayers) { + const layerChildren = []; + + for (const layerChild of flexLayer.children) { + if (selectedWidgets.indexOf(layerChild.id) > -1) { + layerChildren.push(layerChild); + } + } + + if (layerChildren.length > 0) { + selectedFlexLayers.push({ children: layerChildren }); + } + } + + return selectedFlexLayers; +} + +/** + * This method helps in Converting the widgetId inside flexLayers + * to the new corresponding widgetIds in widgetIdMap + * @param flexLayers + * @param widgetIdMap + * @returns + */ +export function getNewFlexLayers( + flexLayers: FlexLayer[], + widgetIdMap: Record, +) { + const newFlexLayers: FlexLayer[] = []; + + for (const flexLayer of flexLayers) { + const newChildren = []; + + for (const layerChild of flexLayer.children) { + if (widgetIdMap[layerChild.id]) { + newChildren.push({ + id: widgetIdMap[layerChild.id], + align: layerChild.align, + }); + } + } + newFlexLayers.push({ children: newChildren }); + } + + return newFlexLayers; +} diff --git a/app/client/src/utils/autoLayout/autoLayoutUtils.test.ts b/app/client/src/utils/autoLayout/autoLayoutUtils.test.ts index e2e05c85f10e..1ae8b244b8ac 100644 --- a/app/client/src/utils/autoLayout/autoLayoutUtils.test.ts +++ b/app/client/src/utils/autoLayout/autoLayoutUtils.test.ts @@ -1,11 +1,17 @@ import { FlexLayer, LayerChild } from "./autoLayoutTypes"; -import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; import { + CanvasWidgetsReduxState, + FlattenedWidgetProps, +} from "reducers/entityReducers/canvasWidgetsReducer"; +import { + getFlexLayersForSelectedWidgets, getLayerIndexOfWidget, + getNewFlexLayers, pasteWidgetInFlexLayers, updateFlexLayersOnDelete, } from "./AutoLayoutUtils"; import { data } from "./testData"; +import { FlexLayerAlignment } from "./constants"; describe("test AutoLayoutUtils methods", () => { describe("test updateFlexLayersOnDelete method", () => { @@ -147,4 +153,167 @@ describe("test AutoLayoutUtils methods", () => { ); }); }); + + it("should test getFlexLayersForSelectedWidgets", () => { + const parentCanvas = ({ + flexLayers: [ + { + children: [ + { + id: "1", + align: FlexLayerAlignment.Start, + }, + { + id: "2", + align: FlexLayerAlignment.Start, + }, + { + id: "3", + align: FlexLayerAlignment.Center, + }, + ], + }, + { + children: [ + { + id: "4", + align: FlexLayerAlignment.Start, + }, + ], + }, + { + children: [ + { + id: "5", + align: FlexLayerAlignment.Center, + }, + { + id: "6", + align: FlexLayerAlignment.End, + }, + ], + }, + ], + } as any) as FlattenedWidgetProps; + + const selectedWidgets = ["2", "3", "6"]; + + const selectedFlexLayers = [ + { + children: [ + { + id: "2", + align: FlexLayerAlignment.Start, + }, + { + id: "3", + align: FlexLayerAlignment.Center, + }, + ], + }, + { + children: [ + { + id: "6", + align: FlexLayerAlignment.End, + }, + ], + }, + ]; + + expect( + getFlexLayersForSelectedWidgets(selectedWidgets, parentCanvas), + ).toEqual(selectedFlexLayers); + }); + + it("should test getNewFlexLayers", () => { + const flexLayers = [ + { + children: [ + { + id: "1", + align: FlexLayerAlignment.Start, + }, + { + id: "2", + align: FlexLayerAlignment.Start, + }, + { + id: "3", + align: FlexLayerAlignment.Center, + }, + ], + }, + { + children: [ + { + id: "4", + align: FlexLayerAlignment.Start, + }, + ], + }, + { + children: [ + { + id: "5", + align: FlexLayerAlignment.Center, + }, + { + id: "6", + align: FlexLayerAlignment.End, + }, + ], + }, + ]; + + const widgetIdMap = { + "1": "11", + "2": "22", + "3": "33", + "4": "44", + "5": "55", + "6": "66", + }; + + const newFlexLayers = [ + { + children: [ + { + id: "11", + align: FlexLayerAlignment.Start, + }, + { + id: "22", + align: FlexLayerAlignment.Start, + }, + { + id: "33", + align: FlexLayerAlignment.Center, + }, + ], + }, + { + children: [ + { + id: "44", + align: FlexLayerAlignment.Start, + }, + ], + }, + { + children: [ + { + id: "55", + align: FlexLayerAlignment.Center, + }, + { + id: "66", + align: FlexLayerAlignment.End, + }, + ], + }, + ]; + + expect(getNewFlexLayers(flexLayers, widgetIdMap)).toEqual(newFlexLayers); + }); }); From 15579c2a939389c8d1d83a5e2d2cfab69211ca91 Mon Sep 17 00:00:00 2001 From: rahulramesha Date: Thu, 23 Feb 2023 19:03:37 +0530 Subject: [PATCH 556/708] fix minor gap at the bottom of the canvas #20693 --- app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx index 6aff50604007..519090af3857 100644 --- a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx +++ b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx @@ -109,8 +109,8 @@ function CanvasContainer() { // calculating exact height to not allow scroll at this component, // calculating total height minus margin on top, top bar and bottom bar - const heightWithTopMargin = `calc(100vh - 2.25rem - ${theme.smallHeaderHeight} - ${theme.bottomBarHeight})`; - const resizerTop = `calc(2.25rem + ${theme.smallHeaderHeight})`; + const heightWithTopMargin = `calc(100vh - 2rem - ${theme.smallHeaderHeight} - ${theme.bottomBarHeight})`; + const resizerTop = `calc(2rem + ${theme.smallHeaderHeight})`; return ( <> Date: Thu, 23 Feb 2023 19:04:53 +0530 Subject: [PATCH 557/708] remove unused code --- app/client/src/utils/hooks/useTrace.tsx | 17 ----------------- app/client/src/widgets/constants.ts | 1 - 2 files changed, 18 deletions(-) delete mode 100644 app/client/src/utils/hooks/useTrace.tsx diff --git a/app/client/src/utils/hooks/useTrace.tsx b/app/client/src/utils/hooks/useTrace.tsx deleted file mode 100644 index 3b3267f7173d..000000000000 --- a/app/client/src/utils/hooks/useTrace.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { useEffect, useRef } from "react"; - -export function useTraceUpdate(props: any, name?: string) { - const prev = useRef(props); - useEffect(() => { - const changedProps = Object.entries(props).reduce((ps: any, [k, v]) => { - if (prev.current[k] !== v) { - ps[k] = [prev.current[k], v]; - } - return ps; - }, {}); - if (Object.keys(changedProps).length > 0) { - console.log("#### Changed props:", props.widgetId, name, changedProps); - } - prev.current = props; - }); -} diff --git a/app/client/src/widgets/constants.ts b/app/client/src/widgets/constants.ts index c8c522c88e89..d4751077f228 100644 --- a/app/client/src/widgets/constants.ts +++ b/app/client/src/widgets/constants.ts @@ -295,7 +295,6 @@ export const dateFormatOptions = [ }, ]; -export const DRAG_MARGIN = 4; export type ThemeProp = { theme: Theme; }; From a91dcb1aa181579fa07987ca2e25057b128e5c0e Mon Sep 17 00:00:00 2001 From: Preet Date: Fri, 24 Feb 2023 07:42:58 +0530 Subject: [PATCH 558/708] use flattenedWidgetProps type --- .../src/utils/autoLayout/highlightUtils.ts | 10 ++++++---- app/client/src/utils/autoLayout/positionUtils.ts | 16 +++++++--------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/app/client/src/utils/autoLayout/highlightUtils.ts b/app/client/src/utils/autoLayout/highlightUtils.ts index 29e8a3c8b3b3..fcc8fafebb6e 100644 --- a/app/client/src/utils/autoLayout/highlightUtils.ts +++ b/app/client/src/utils/autoLayout/highlightUtils.ts @@ -5,7 +5,10 @@ import { GridDefaults, MAIN_CONTAINER_WIDGET_ID, } from "constants/WidgetConstants"; -import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; +import { + CanvasWidgetsReduxState, + FlattenedWidgetProps, +} from "reducers/entityReducers/canvasWidgetsReducer"; import { getLeftColumn, getRightColumn, @@ -18,7 +21,6 @@ import { getAlignmentSizeInfo, getTotalRowsOfAllChildren, getWrappedAlignmentInfo, - Widget, } from "./positionUtils"; import { DropZone, @@ -269,7 +271,7 @@ export function generateVerticalHighlights(data: { * - If the alignment has no children, then add an initial highlight to mark the start of the alignment. */ export function generateHighlightsForAlignment(data: { - arr: Widget[]; + arr: FlattenedWidgetProps[]; childCount: number; layerIndex: number; alignment: FlexLayerAlignment; @@ -318,7 +320,7 @@ export function generateHighlightsForAlignment(data: { } if (!avoidInitialHighlight) { - const lastChild: Widget | null = + const lastChild: FlattenedWidgetProps | null = arr && arr.length ? arr[arr.length - 1] : null; res.push({ isNewLayer: false, diff --git a/app/client/src/utils/autoLayout/positionUtils.ts b/app/client/src/utils/autoLayout/positionUtils.ts index b4ce3c30df04..49a576c01ac7 100644 --- a/app/client/src/utils/autoLayout/positionUtils.ts +++ b/app/client/src/utils/autoLayout/positionUtils.ts @@ -3,13 +3,15 @@ import { GridDefaults, MAIN_CONTAINER_WIDGET_ID, } from "constants/WidgetConstants"; -import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; +import { + CanvasWidgetsReduxState, + FlattenedWidgetProps, +} from "reducers/entityReducers/canvasWidgetsReducer"; import { FlexLayerAlignment, Positioning, ResponsiveBehavior, } from "utils/autoLayout/constants"; -import { WidgetProps } from "widgets/BaseWidget"; import { getBottomRow, getTopRow, @@ -19,14 +21,10 @@ import { setDimensions, } from "./flexWidgetUtils"; -export type Widget = WidgetProps & { - children?: string[] | undefined; -}; - export interface AlignmentInfo { alignment: FlexLayerAlignment; columns: number; - children: Widget[]; + children: FlattenedWidgetProps[]; } export interface Row extends AlignmentInfo { @@ -539,7 +537,7 @@ export function getWrappedRows( height: 0, }; const space = GridDefaults.DEFAULT_GRID_COLUMNS; - const temp: Widget[] = []; + const temp: FlattenedWidgetProps[] = []; let columns = 0, index = 0, maxHeight = 0; @@ -575,7 +573,7 @@ export function getWrappedRows( function getHeightOfFixedCanvas( widgets: CanvasWidgetsReduxState, - parent: Widget, + parent: FlattenedWidgetProps, isMobile: boolean, ): number { if (!parent.children || !parent.children.length) From f35ba2edb9dae2dec3e98e87ecd4ad890b9dcc19 Mon Sep 17 00:00:00 2001 From: Preet Date: Fri, 24 Feb 2023 08:25:27 +0530 Subject: [PATCH 559/708] clean up --- app/client/src/constants/WidgetConstants.tsx | 5 ----- .../src/utils/hooks/useDynamicAppLayout.tsx | 21 ++++++++----------- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/app/client/src/constants/WidgetConstants.tsx b/app/client/src/constants/WidgetConstants.tsx index fd7993f2db5f..ef3ab68e84a8 100644 --- a/app/client/src/constants/WidgetConstants.tsx +++ b/app/client/src/constants/WidgetConstants.tsx @@ -99,11 +99,6 @@ export const MAIN_CONTAINER_WIDGET_NAME = "MainContainer"; export const MODAL_PORTAL_CLASSNAME = "bp3-modal-widget"; export const MODAL_PORTAL_OVERLAY_CLASSNAME = "bp3-overlay-zindex"; export const CANVAS_SELECTOR = "canvas"; -export const widgetTypeClassname = (widgetType: string): string => - `t--widget-${widgetType - .split("_") - .join("") - .toLowerCase()}`; export const DEFAULT_CENTER = { lat: -34.397, lng: 150.644 }; diff --git a/app/client/src/utils/hooks/useDynamicAppLayout.tsx b/app/client/src/utils/hooks/useDynamicAppLayout.tsx index 1f682a5f75c0..63c552bbb4b5 100644 --- a/app/client/src/utils/hooks/useDynamicAppLayout.tsx +++ b/app/client/src/utils/hooks/useDynamicAppLayout.tsx @@ -264,18 +264,15 @@ export const useDynamicAppLayout = () => { ]); useEffect(() => { - function relayoutAtBreakpoint() { - dispatch( - updateLayoutForMobileBreakpointAction( - MAIN_CONTAINER_WIDGET_ID, - appPositioningType === AppPositioningTypes.AUTO - ? mainCanvasProps?.isMobile - : false, - calculateCanvasWidth(), - ), - ); - } - relayoutAtBreakpoint(); + dispatch( + updateLayoutForMobileBreakpointAction( + MAIN_CONTAINER_WIDGET_ID, + appPositioningType === AppPositioningTypes.AUTO + ? mainCanvasProps?.isMobile + : false, + calculateCanvasWidth(), + ), + ); }, [mainCanvasProps?.isMobile, appPositioningType]); return isCanvasInitialized; From 2606bb22407fdc20c72f50c80a1a0213ed35d8b9 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Fri, 24 Feb 2023 10:56:07 +0530 Subject: [PATCH 560/708] removing widget name popper based changes. --- .../WidgetNameComponent/SettingsControl.tsx | 39 ++-- .../WidgetNameComponent/index.tsx | 177 +++--------------- app/client/src/widgets/BaseWidget.tsx | 66 +++---- .../widgets/ModalWidget/component/index.tsx | 47 ++--- .../src/widgets/ModalWidget/widget/index.tsx | 50 +++-- 5 files changed, 134 insertions(+), 245 deletions(-) diff --git a/app/client/src/components/editorComponents/WidgetNameComponent/SettingsControl.tsx b/app/client/src/components/editorComponents/WidgetNameComponent/SettingsControl.tsx index 9f821a2d3bd0..5d52d7ab7447 100644 --- a/app/client/src/components/editorComponents/WidgetNameComponent/SettingsControl.tsx +++ b/app/client/src/components/editorComponents/WidgetNameComponent/SettingsControl.tsx @@ -2,7 +2,7 @@ import { Classes, Tooltip } from "@blueprintjs/core"; import { Colors } from "constants/Colors"; import { Icon, IconSize } from "design-system-old"; import { ControlIcons } from "icons/ControlIcons"; -import { CSSProperties, default as React } from "react"; +import React, { CSSProperties } from "react"; import { useSelector } from "react-redux"; import { snipingModeSelector } from "selectors/editorSelectors"; import styled from "styled-components"; @@ -20,17 +20,13 @@ const StyledTooltip = styled(Tooltip)<{ height: 100%; } `; -const WidgetNameBoundary = 1; -const BORDER_RADIUS = 4; -const SettingsWrapper = styled.div<{ widgetWidth: number }>` +const SettingsWrapper = styled.div` justify-self: flex-end; height: 100%; - padding: 0 5px; - margin-left: 0px; + padding: 0 10px; display: flex; justify-content: space-between; align-items: center; - max-width: ${(props) => props.widgetWidth - BORDER_RADIUS / 2}px; & { pre { margin: 0 5px 0 0; @@ -39,16 +35,12 @@ const SettingsWrapper = styled.div<{ widgetWidth: number }>` line-height: ${(props) => props.theme.fontSizes[3] - 1}px; } } - border-top-left-radius: ${BORDER_RADIUS}px; - border-top-right-radius: ${BORDER_RADIUS}px; - border: ${WidgetNameBoundary}px solid ${Colors.GREY_1}; - border-bottom: none; + border-radius: 2px; `; const WidgetName = styled.span` - width: inherit; - overflow-x: hidden; - text-overflow: ellipsis; + margin-right: ${(props) => props.theme.spaces[1] + 1}px; + margin-left: ${(props) => props.theme.spaces[3]}px; white-space: nowrap; `; @@ -68,10 +60,10 @@ type SettingsControlProps = { activity: Activities; name: string; errorCount: number; - widgetWidth: number; }; const BindDataIcon = ControlIcons.BIND_DATA_CONTROL; +const SettingsIcon = ControlIcons.SETTINGS_CONTROL; const getStyles = ( activity: Activities, @@ -99,7 +91,7 @@ const getStyles = ( case Activities.HOVERING: return { background: Colors.WATUSI, - color: Colors.WHITE, + color: Colors.BLACK_PEARL, }; case Activities.SELECTED: return { @@ -111,6 +103,19 @@ const getStyles = ( export function SettingsControl(props: SettingsControlProps) { const isSnipingMode = useSelector(snipingModeSelector); + const settingsIcon = ( + + ); const errorIcon = ( {!!props.errorCount && !isSnipingMode && ( <> @@ -148,6 +152,7 @@ export function SettingsControl(props: SettingsControlProps) { {isSnipingMode ? `Bind to ${props.name}` : props.name} + {!isSnipingMode && settingsIcon} ); diff --git a/app/client/src/components/editorComponents/WidgetNameComponent/index.tsx b/app/client/src/components/editorComponents/WidgetNameComponent/index.tsx index 33807e5f9dac..33185e6280ef 100644 --- a/app/client/src/components/editorComponents/WidgetNameComponent/index.tsx +++ b/app/client/src/components/editorComponents/WidgetNameComponent/index.tsx @@ -1,49 +1,37 @@ import { AppState } from "@appsmith/reducers"; -import { Popover2 } from "@blueprintjs/popover2"; import { bindDataToWidget } from "actions/propertyPaneActions"; -import { Layers } from "constants/Layers"; import { WidgetType } from "constants/WidgetConstants"; import React from "react"; import { useDispatch, useSelector } from "react-redux"; -import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; -import { RESIZE_BORDER_BUFFER } from "resizable/resizenreflow"; import { SelectionRequestType } from "sagas/WidgetSelectUtils"; import { hideErrors } from "selectors/debuggerSelectors"; import { - getCurrentAppPositioningType, previewModeSelector, snipingModeSelector, } from "selectors/editorSelectors"; import { getIsPropertyPaneVisible } from "selectors/propertyPaneSelectors"; import { getIsTableFilterPaneVisible } from "selectors/tableFilterSelectors"; -import { - isCurrentWidgetFocused, - isWidgetSelected, -} from "selectors/widgetSelectors"; import styled from "styled-components"; import AnalyticsUtil from "utils/AnalyticsUtil"; -import { - useShowTableFilterPane, - useWidgetDragResize, -} from "utils/hooks/dragResizeHooks"; +import { useShowTableFilterPane } from "utils/hooks/dragResizeHooks"; import { useWidgetSelection } from "utils/hooks/useWidgetSelection"; import PerformanceTracker, { PerformanceTransactionName, } from "utils/PerformanceTracker"; import WidgetFactory from "utils/WidgetFactory"; -import { canDrag } from "../DraggableComponent"; import SettingsControl, { Activities } from "./SettingsControl"; const WidgetTypes = WidgetFactory.widgetTypes; -const PositionStyle = styled.div<{ - isSnipingMode: boolean; -}>` +const PositionStyle = styled.div<{ topRow: number; isSnipingMode: boolean }>` + position: absolute; + top: ${(props) => + props.topRow > 2 ? `${-1 * props.theme.spaces[10]}px` : "calc(100%)"}; height: ${(props) => props.theme.spaces[10]}px; - ${(props) => (props.isSnipingMode ? "left: -7px" : "left: 0px")}; + ${(props) => (props.isSnipingMode ? "left: -7px" : "right: 0")}; display: flex; + padding: 0 4px; cursor: pointer; - z-index: ${Layers.widgetName}; `; const ControlGroup = styled.div` @@ -66,9 +54,6 @@ type WidgetNameComponentProps = { showControls?: boolean; topRow: number; errorCount: number; - isFlexChild: boolean; - widgetProps: any; - children: any; }; export function WidgetNameComponent(props: WidgetNameComponentProps) { @@ -82,8 +67,6 @@ export function WidgetNameComponent(props: WidgetNameComponentProps) { const selectedWidget = useSelector( (state: AppState) => state.ui.widgetDragResize.lastSelectedWidget, ); - const isAutoLayout = - useSelector(getCurrentAppPositioningType) === AppPositioningTypes.AUTO; const selectedWidgets = useSelector( (state: AppState) => state.ui.widgetDragResize.selectedWidgets, ); @@ -147,11 +130,8 @@ export function WidgetNameComponent(props: WidgetNameComponentProps) { selectedWidgets && selectedWidgets.length > 1 && selectedWidgets.includes(props.widgetId); - // True when any widget is dragging or resizing, including this one - const isResizingOrDragging = !!isResizing || !!isDragging; const shouldShowWidgetName = () => { return ( - !isResizingOrDragging && !isPreviewMode && !isMultiSelectedWidget && (isSnipingMode @@ -180,135 +160,24 @@ export function WidgetNameComponent(props: WidgetNameComponentProps) { propertyPaneWidgetId === props.widgetId ) currentActivity = Activities.ACTIVE; - const targetNode: any = document.getElementById( - `${isAutoLayout ? "auto_" : ""}${props.widgetId}`, - ); - - // This state tells us to disable dragging, - // This is usually true when widgets themselves implement drag/drop - // This flag resolves conflicting drag/drop triggers. - const isDraggingDisabled: boolean = useSelector( - (state: AppState) => state.ui.widgetDragResize.isDraggingDisabled, - ); - - const allowDrag = canDrag( - isResizingOrDragging, - isDraggingDisabled, - props.widgetProps, - isSnipingMode, - isPreviewMode, - ); - const isSelected = useSelector(isWidgetSelected(props.widgetId)); - // This state tels us which widget is focused - // The value is the widgetId of the focused widget. - const isFocused = useSelector(isCurrentWidgetFocused(props.widgetId)); - const { setDraggingState } = useWidgetDragResize(); - const onDragStart = (e: any) => { - e.preventDefault(); - e.stopPropagation(); - // allowDrag check is added as react jest test simulation is not respecting default behaviour - // of draggable=false and triggering onDragStart. allowDrag condition check is purely for the test cases. - if (allowDrag && targetNode && !(e.metaKey || e.ctrlKey)) { - if (!isFocused) return; - - if (!isSelected) { - selectWidget(SelectionRequestType.One, [props.widgetId]); - } - const widgetHeight = - props.widgetProps.bottomRow - props.widgetProps.topRow; - const widgetWidth = - props.widgetProps.rightColumn - props.widgetProps.leftColumn; - const bounds = targetNode.getBoundingClientRect(); - const startPoints = { - top: Math.min( - Math.max( - (e.clientY - bounds.top) / props.widgetProps.parentRowSpace, - 0, - ), - widgetHeight - 1, - ), - left: Math.min( - Math.max( - (e.clientX - bounds.left) / props.widgetProps.parentColumnSpace, - 0, - ), - widgetWidth - 1, - ), - }; - showTableFilterPane(); - setDraggingState({ - isDragging: true, - dragGroupActualParent: props.widgetProps.parentId || "", - draggingGroupCenter: { widgetId: props.widgetProps.widgetId }, - startPoints, - draggedOn: props.widgetProps.parentId, - }); - } - }; - // bottom offset is RESIZE_BORDER_BUFFER - 1 because bottom border is none for the widget name - const popperOffset: any = [-RESIZE_BORDER_BUFFER, RESIZE_BORDER_BUFFER - 1]; - const widgetWidth = - (props.widgetProps.rightColumn - props.widgetProps.leftColumn) * - props.widgetProps.parentColumnSpace; - return ( - - - - - - ) : ( -
- ) - } - enforceFocus={false} - hoverCloseDelay={0} - isOpen={showWidgetName} - minimal - modifiers={{ - offset: { - enabled: true, - options: { - offset: popperOffset, - }, - }, - flip: { - enabled: false, - }, - computeStyles: { - options: { - roundOffsets: false, - }, - }, - }} - placement="top-start" - popoverClassName="widget-name-popper" - portalContainer={document.getElementById("widgets-editor") || undefined} - targetTagName="div" - usePortal + return showWidgetName ? ( + - {props.children} - - ); + + + + + ) : null; } export default WidgetNameComponent; diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index a9ef65015358..26dda168d479 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -7,14 +7,13 @@ import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants"; import { BatchPropertyUpdatePayload } from "actions/controlActions"; import AutoHeightContainerWrapper from "components/autoHeight/AutoHeightContainerWrapper"; import AutoHeightOverlayContainer from "components/autoHeightOverlay"; -import { - FlexVerticalAlignment, - LayoutDirection, - ResponsiveBehavior, -} from "utils/autoLayout/constants"; import FlexComponent from "components/designSystems/appsmith/autoLayout/FlexComponent"; import PositionedContainer from "components/designSystems/appsmith/PositionedContainer"; import DraggableComponent from "components/editorComponents/DraggableComponent"; +import { + EditorContext, + EditorContextType, +} from "components/editorComponents/EditorContextProvider"; import ErrorBoundary from "components/editorComponents/ErrorBoundry"; import ResizableComponent from "components/editorComponents/ResizableComponent"; import SnipeableComponent from "components/editorComponents/SnipeableComponent"; @@ -31,26 +30,33 @@ import { WIDGET_PADDING, } from "constants/WidgetConstants"; import { ENTITY_TYPE } from "entities/AppsmithConsole"; +import { Stylesheet } from "entities/AppTheming"; import { DataTreeWidget } from "entities/DataTree/dataTreeFactory"; -import React, { Component, Context, ReactNode, RefObject } from "react"; import { get, memoize } from "lodash"; +import React, { Component, Context, ReactNode, RefObject } from "react"; +import { + ModifyMetaWidgetPayload, + UpdateMetaWidgetPropertyPayload, +} from "reducers/entityReducers/metaWidgetsReducer"; import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; +import { SelectionRequestType } from "sagas/WidgetSelectUtils"; import shallowequal from "shallowequal"; import { CSSProperties } from "styled-components"; import AnalyticsUtil from "utils/AnalyticsUtil"; import AppsmithConsole from "utils/AppsmithConsole"; import { - EditorContext, - EditorContextType, -} from "components/editorComponents/EditorContextProvider"; -import { DerivedPropertiesMap } from "utils/WidgetFactory"; + FlexVerticalAlignment, + LayoutDirection, + ResponsiveBehavior, +} from "utils/autoLayout/constants"; import { DataTreeEvaluationProps, EvaluationError, EVAL_ERROR_PATH, WidgetDynamicPathListProps, } from "utils/DynamicBindingUtils"; -import { CanvasWidgetStructure } from "./constants"; +import { DerivedPropertiesMap } from "utils/WidgetFactory"; +import { CanvasWidgetStructure, FlattenedWidgetProps } from "./constants"; import Skeleton from "./Skeleton"; import { getWidgetMaxAutoHeight, @@ -58,13 +64,6 @@ import { isAutoHeightEnabledForWidget, shouldUpdateWidgetHeightAutomatically, } from "./WidgetUtils"; -import { Stylesheet } from "entities/AppTheming"; -import { FlattenedWidgetProps } from "./constants"; -import { - ModifyMetaWidgetPayload, - UpdateMetaWidgetPropertyPayload, -} from "reducers/entityReducers/metaWidgetsReducer"; -import { SelectionRequestType } from "sagas/WidgetSelectUtils"; /*** * BaseWidget @@ -428,22 +427,23 @@ abstract class BaseWidget< * @param showControls */ showWidgetName(content: ReactNode, showControls = false) { - return !this.props.disablePropertyPane ? ( - + return ( + <> + {!this.props.disablePropertyPane && ( + + )} {content} - - ) : ( - content + ); } diff --git a/app/client/src/widgets/ModalWidget/component/index.tsx b/app/client/src/widgets/ModalWidget/component/index.tsx index 084c724c9628..0573430fa38f 100644 --- a/app/client/src/widgets/ModalWidget/component/index.tsx +++ b/app/client/src/widgets/ModalWidget/component/index.tsx @@ -77,9 +77,6 @@ const Container = styled.div<{ left: ${(props) => props.left}px; bottom: ${(props) => props.bottom}px; right: ${(props) => props.right}px; - .bp3-popover2-target.bp3-popover2-open { - height: 100%; - } } } } @@ -141,6 +138,7 @@ export type ModalComponentProps = { isDynamicHeightEnabled: boolean; background?: string; borderRadius?: string; + settingsComponent?: ReactNode; }; /* eslint-disable react/display-name */ @@ -232,29 +230,9 @@ export default function ModalComponent(props: ModalComponentProps) { return !props.isDynamicHeightEnabled && enableResize; }, [props.isDynamicHeightEnabled, enableResize]); - const getModalContent = () => { - return ( - - - {props.children} - - - ); - }; - const getResizableContent = () => { //id for Content is required for Copy Paste inside the modal - return enableResize ? ( + return ( - {getModalContent()} + {props.settingsComponent} + + + {props.children} + + - ) : ( - getModalContent() ); }; diff --git a/app/client/src/widgets/ModalWidget/widget/index.tsx b/app/client/src/widgets/ModalWidget/widget/index.tsx index bf5ef595b60b..450889b64e00 100644 --- a/app/client/src/widgets/ModalWidget/widget/index.tsx +++ b/app/client/src/widgets/ModalWidget/widget/index.tsx @@ -5,13 +5,15 @@ import { connect } from "react-redux"; import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants"; import { AppState } from "@appsmith/reducers"; import { UIElementSize } from "components/editorComponents/ResizableUtils"; +import WidgetNameComponent from "components/editorComponents/WidgetNameComponent"; import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; import { RenderMode, WIDGET_PADDING } from "constants/WidgetConstants"; import { ValidationTypes } from "constants/WidgetValidation"; import { Stylesheet } from "entities/AppTheming"; +import { get } from "lodash"; import { SelectionRequestType } from "sagas/WidgetSelectUtils"; import { getCanvasWidth, snipingModeSelector } from "selectors/editorSelectors"; -import { Alignment, Positioning, Spacing } from "utils/autoLayout/constants"; +import { EVAL_ERROR_PATH } from "utils/DynamicBindingUtils"; import { generateClassName } from "utils/generators"; import { ClickContentToOpenPropPane } from "utils/hooks/useClickToSelectWidget"; import WidgetFactory from "utils/WidgetFactory"; @@ -54,7 +56,6 @@ export class ModalWidget extends BaseWidget { isBindProperty: false, isTriggerProperty: false, }, - // { ...generatePositioningConfig(Positioning.Fixed) }, ], }, { @@ -142,9 +143,6 @@ export class ModalWidget extends BaseWidget { childData.rightColumn = this.getModalWidth(this.props.width) + WIDGET_PADDING * 2; - childData.positioning = this.props.positioning; - childData.alignment = this.props.alignment; - childData.spacing = this.props.spacing; return WidgetFactory.createWidget(childData, this.props.renderMode); }; @@ -215,9 +213,34 @@ export class ModalWidget extends BaseWidget { makeModalComponent(content: ReactNode, isEditMode: boolean) { const artBoard = document.getElementById("art-board"); const portalContainer = isEditMode && artBoard ? artBoard : undefined; - const { isPreviewMode, isSnipingMode } = this.props; - - const isResizeEnabled = isEditMode && !isSnipingMode && !isPreviewMode; + const { + focusedWidget, + isDragging, + isSnipingMode, + selectedWidget, + selectedWidgets, + widgetId, + } = this.props; + + const isWidgetFocused = + focusedWidget === widgetId || + selectedWidget === widgetId || + selectedWidgets.includes(widgetId); + + const isResizeEnabled = + !isDragging && isWidgetFocused && isEditMode && !isSnipingMode; + + const settingsComponent = isEditMode ? ( + + ) : null; return ( { portalContainer={portalContainer} resizeModal={this.onModalResize} scrollContents={!!this.props.shouldScrollContents} + settingsComponent={settingsComponent} widgetId={this.props.widgetId} widgetName={this.props.widgetName} width={this.getModalWidth(this.props.width)} @@ -250,8 +274,6 @@ export class ModalWidget extends BaseWidget { getCanvasView() { let children = this.getChildren(); children = this.makeModalSelectable(children); - children = this.showWidgetName(children, true); - return this.makeModalComponent(children, true); } @@ -282,9 +304,6 @@ export interface ModalWidgetProps extends WidgetProps { backgroundColor: string; borderRadius: string; mainCanvasWidth: number; - positioning?: Positioning; - alignment: Alignment; - spacing: Spacing; } const mapDispatchToProps = (dispatch: any) => ({ @@ -309,8 +328,11 @@ const mapStateToProps = (state: AppState) => { const props = { mainCanvasWidth: getCanvasWidth(state), isSnipingMode: snipingModeSelector(state), + selectedWidget: state.ui.widgetDragResize.lastSelectedWidget, + selectedWidgets: state.ui.widgetDragResize.selectedWidgets, + focusedWidget: state.ui.widgetDragResize.focusedWidget, + isDragging: state.ui.widgetDragResize.isDragging, isResizing: state.ui.widgetDragResize.isResizing, - isPreviewMode: state.ui.editor.isPreviewMode, }; return props; }; From 7232468c8147952d5d6068d1489adae67e3b4926 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Fri, 24 Feb 2023 18:07:59 +0530 Subject: [PATCH 561/708] fixing resize based css. --- .../src/resizable/autolayoutresize/index.tsx | 9 ++++----- app/client/src/resizable/common.tsx | 15 +++++++-------- app/client/src/resizable/resizenreflow/index.tsx | 9 ++++----- 3 files changed, 15 insertions(+), 18 deletions(-) diff --git a/app/client/src/resizable/autolayoutresize/index.tsx b/app/client/src/resizable/autolayoutresize/index.tsx index 70d4914a37b1..a3d768094592 100644 --- a/app/client/src/resizable/autolayoutresize/index.tsx +++ b/app/client/src/resizable/autolayoutresize/index.tsx @@ -567,15 +567,14 @@ export function ReflowResizable(props: ResizableProps) { /> ); }); - const bufferForBoundary = props.showResizeBoundary ? RESIZE_BORDER_BUFFER : 0; const widgetWidth = (reflowedPosition?.width === undefined ? newDimensions.width - : reflowedPosition.width - 2 * WIDGET_PADDING) + bufferForBoundary; + : reflowedPosition.width - 2 * WIDGET_PADDING) + RESIZE_BORDER_BUFFER; const widgetHeight = (reflowedPosition?.height === undefined ? newDimensions.height - : reflowedPosition.height - 2 * WIDGET_PADDING) + bufferForBoundary; + : reflowedPosition.height - 2 * WIDGET_PADDING) + RESIZE_BORDER_BUFFER; return ( {(_props) => ( diff --git a/app/client/src/resizable/common.tsx b/app/client/src/resizable/common.tsx index 1eb238a7ed52..e6f7f5d16a4f 100644 --- a/app/client/src/resizable/common.tsx +++ b/app/client/src/resizable/common.tsx @@ -30,20 +30,19 @@ export const ResizeWrapper = styled(animated.div)<{ pointer-events: ${(props) => !props.$prevents && "none"}; } } + border-radius: 0px 4px 4px 4px; + border: ${resizeBorder}px solid; + padding: ${resizeBorderPadding}px; + outline: ${resizeOutline}px solid !important; + outline-offset: 1px; ${(props) => { if (props.showBoundaries) { return ` box-shadow: 0px 0px 0px ${resizeBoxShadow}px ${ props.isHovered ? Colors.WATUSI : "#f86a2b" }; - border-radius: 0px 4px 4px 4px; - border: ${resizeBorder}px solid ${Colors.GREY_1}; - padding: ${resizeBorderPadding}px; - outline: ${resizeOutline}px solid ${Colors.GREY_1} !important; - outline-offset: 1px;`; - } else { - return ` - border: 0px solid transparent; + outline-color: ${Colors.GREY_1} !important; + border-color: ${Colors.GREY_1}; `; } }}} diff --git a/app/client/src/resizable/resizenreflow/index.tsx b/app/client/src/resizable/resizenreflow/index.tsx index 8efee632d623..6524d49f4505 100644 --- a/app/client/src/resizable/resizenreflow/index.tsx +++ b/app/client/src/resizable/resizenreflow/index.tsx @@ -384,15 +384,14 @@ export function ReflowResizable(props: ResizableProps) { ); }); - const bufferForBoundary = props.showResizeBoundary ? RESIZE_BORDER_BUFFER : 0; const widgetWidth = (reflowedPosition?.width === undefined ? newDimensions.width - : reflowedPosition.width - 2 * WIDGET_PADDING) + bufferForBoundary; + : reflowedPosition.width - 2 * WIDGET_PADDING) + RESIZE_BORDER_BUFFER; const widgetHeight = (reflowedPosition?.height === undefined ? newDimensions.height - : reflowedPosition.height - 2 * WIDGET_PADDING) + bufferForBoundary; + : reflowedPosition.height - 2 * WIDGET_PADDING) + RESIZE_BORDER_BUFFER; return ( {(_props) => ( From 6b138a682f87231cfe5990b0e7534995644b05e9 Mon Sep 17 00:00:00 2001 From: Aswath K Date: Mon, 27 Feb 2023 10:55:13 +0530 Subject: [PATCH 562/708] min width for Divider widget --- app/client/src/widgets/DividerWidget/index.ts | 12 ++++++++++++ .../src/widgets/DividerWidget/widget/index.tsx | 2 ++ 2 files changed, 14 insertions(+) diff --git a/app/client/src/widgets/DividerWidget/index.ts b/app/client/src/widgets/DividerWidget/index.ts index 58eb9941edee..a6bdfb4f5ee3 100644 --- a/app/client/src/widgets/DividerWidget/index.ts +++ b/app/client/src/widgets/DividerWidget/index.ts @@ -33,6 +33,18 @@ export const CONFIG = { contentConfig: Widget.getPropertyPaneContentConfig(), styleConfig: Widget.getPropertyPaneStyleConfig(), }, + autoLayout: { + widgetSize: [ + { + viewportMinWidth: 0, + configuration: () => { + return { + minWidth: "280px", + }; + }, + }, + ], + }, }; export default Widget; diff --git a/app/client/src/widgets/DividerWidget/widget/index.tsx b/app/client/src/widgets/DividerWidget/widget/index.tsx index f5ecce497403..14bc77dec2d8 100644 --- a/app/client/src/widgets/DividerWidget/widget/index.tsx +++ b/app/client/src/widgets/DividerWidget/widget/index.tsx @@ -5,6 +5,7 @@ import DividerComponent from "../component"; import { ValidationTypes } from "constants/WidgetValidation"; import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; +import { isAutoLayout } from "selectors/mainCanvasSelectors"; class DividerWidget extends BaseWidget { static getPropertyPaneContentConfig() { @@ -60,6 +61,7 @@ class DividerWidget extends BaseWidget { value: "vertical", }, ], + hidden: isAutoLayout, isJSConvertible: true, isBindProperty: true, isTriggerProperty: false, From 0a79faaaf10eda313b61edf9b72cf6387b0ba96d Mon Sep 17 00:00:00 2001 From: Arsalan Yaldram Date: Mon, 27 Feb 2023 12:40:31 +0530 Subject: [PATCH 563/708] feat: 2d widget min dimensions (#20909) --- app/client/src/widgets/AudioRecorderWidget/index.ts | 13 +++++++++++++ app/client/src/widgets/ButtonGroupWidget/index.ts | 12 ++++++++++++ app/client/src/widgets/CameraWidget/index.ts | 13 +++++++++++++ app/client/src/widgets/ChartWidget/index.ts | 13 +++++++++++++ app/client/src/widgets/CodeScannerWidget/index.ts | 13 +++++++++++++ .../src/widgets/DocumentViewerWidget/index.ts | 12 ++++++++++++ app/client/src/widgets/FormWidget/index.ts | 13 +++++++++++++ app/client/src/widgets/IframeWidget/index.ts | 12 ++++++++++++ app/client/src/widgets/ImageWidget/index.ts | 12 ++++++++++++ app/client/src/widgets/JSONFormWidget/index.ts | 13 +++++++++++++ app/client/src/widgets/ListWidget/index.ts | 13 +++++++++++++ app/client/src/widgets/ListWidgetV2/index.ts | 13 +++++++++++++ app/client/src/widgets/ProgressWidget/index.ts | 13 +++++++++++++ .../src/widgets/RichTextEditorWidget/index.ts | 12 ++++++++++++ app/client/src/widgets/StatboxWidget/index.ts | 12 ++++++++++++ app/client/src/widgets/TableWidgetV2/index.ts | 12 ++++++++++++ app/client/src/widgets/TabsWidget/index.ts | 12 ++++++++++++ app/client/src/widgets/TextWidget/index.ts | 13 +++++++++++++ app/client/src/widgets/VideoWidget/index.ts | 12 ++++++++++++ 19 files changed, 238 insertions(+) diff --git a/app/client/src/widgets/AudioRecorderWidget/index.ts b/app/client/src/widgets/AudioRecorderWidget/index.ts index f3d8186efaca..d3d8e22b6c61 100644 --- a/app/client/src/widgets/AudioRecorderWidget/index.ts +++ b/app/client/src/widgets/AudioRecorderWidget/index.ts @@ -30,6 +30,19 @@ export const CONFIG = { styleConfig: Widget.getPropertyPaneStyleConfig(), stylesheetConfig: Widget.getStylesheetConfig(), }, + autoLayout: { + widgetSize: [ + { + viewportMinWidth: 0, + configuration: () => { + return { + minWidth: "280px", + minHeight: "70px", + }; + }, + }, + ], + }, }; export default Widget; diff --git a/app/client/src/widgets/ButtonGroupWidget/index.ts b/app/client/src/widgets/ButtonGroupWidget/index.ts index 0ddbd0dddb6a..b8ba7ca37e46 100644 --- a/app/client/src/widgets/ButtonGroupWidget/index.ts +++ b/app/client/src/widgets/ButtonGroupWidget/index.ts @@ -151,6 +151,18 @@ export const CONFIG = { styleConfig: Widget.getPropertyPaneStyleConfig(), stylesheetConfig: Widget.getStylesheetConfig(), }, + autoLayout: { + widgetSize: [ + { + viewportMinWidth: 0, + configuration: () => { + return { + minWidth: "120px", + }; + }, + }, + ], + }, }; export default Widget; diff --git a/app/client/src/widgets/CameraWidget/index.ts b/app/client/src/widgets/CameraWidget/index.ts index 6c4db078bf71..9b261dc98495 100644 --- a/app/client/src/widgets/CameraWidget/index.ts +++ b/app/client/src/widgets/CameraWidget/index.ts @@ -30,6 +30,19 @@ export const CONFIG = { styleConfig: Widget.getPropertyPaneStyleConfig(), stylesheetConfig: Widget.getStylesheetConfig(), }, + autoLayout: { + widgetSize: [ + { + viewportMinWidth: 0, + configuration: () => { + return { + minWidth: "280px", + maxWidth: "300px", + }; + }, + }, + ], + }, }; export default Widget; diff --git a/app/client/src/widgets/ChartWidget/index.ts b/app/client/src/widgets/ChartWidget/index.ts index af52c0e3063a..e9c487c7b3ad 100644 --- a/app/client/src/widgets/ChartWidget/index.ts +++ b/app/client/src/widgets/ChartWidget/index.ts @@ -108,6 +108,19 @@ export const CONFIG = { styleConfig: Widget.getPropertyPaneStyleConfig(), stylesheetConfig: Widget.getStylesheetConfig(), }, + autoLayout: { + widgetSize: [ + { + viewportMinWidth: 0, + configuration: () => { + return { + minWidth: "280px", + maxWidth: "300px", + }; + }, + }, + ], + }, }; export default Widget; diff --git a/app/client/src/widgets/CodeScannerWidget/index.ts b/app/client/src/widgets/CodeScannerWidget/index.ts index 880826d9cbf1..f584cd0bf847 100644 --- a/app/client/src/widgets/CodeScannerWidget/index.ts +++ b/app/client/src/widgets/CodeScannerWidget/index.ts @@ -35,6 +35,19 @@ export const CONFIG = { contentConfig: Widget.getPropertyPaneContentConfig(), stylesheetConfig: Widget.getStylesheetConfig(), }, + autoLayout: { + widgetSize: [ + { + viewportMinWidth: 0, + configuration: () => { + return { + minWidth: "280px", + maxWidth: "300px", + }; + }, + }, + ], + }, }; export default Widget; diff --git a/app/client/src/widgets/DocumentViewerWidget/index.ts b/app/client/src/widgets/DocumentViewerWidget/index.ts index 7d04810f1944..78867ac66950 100644 --- a/app/client/src/widgets/DocumentViewerWidget/index.ts +++ b/app/client/src/widgets/DocumentViewerWidget/index.ts @@ -24,6 +24,18 @@ export const CONFIG = { config: Widget.getPropertyPaneConfig(), contentConfig: Widget.getPropertyPaneContentConfig(), }, + autoLayout: { + widgetSize: [ + { + viewportMinWidth: 0, + configuration: () => { + return { + minWidth: "280px", + }; + }, + }, + ], + }, }; export default Widget; diff --git a/app/client/src/widgets/FormWidget/index.ts b/app/client/src/widgets/FormWidget/index.ts index fa382e0aeaaa..c1e67aa5c2aa 100644 --- a/app/client/src/widgets/FormWidget/index.ts +++ b/app/client/src/widgets/FormWidget/index.ts @@ -124,6 +124,19 @@ export const CONFIG = { styleConfig: Widget.getPropertyPaneStyleConfig(), stylesheetConfig: Widget.getStylesheetConfig(), }, + autoLayout: { + widgetSize: [ + { + viewportMinWidth: 0, + configuration: () => { + return { + minWidth: "280px", + minHeight: "300px", + }; + }, + }, + ], + }, }; export default Widget; diff --git a/app/client/src/widgets/IframeWidget/index.ts b/app/client/src/widgets/IframeWidget/index.ts index b529390a9048..f1b2d32104ba 100644 --- a/app/client/src/widgets/IframeWidget/index.ts +++ b/app/client/src/widgets/IframeWidget/index.ts @@ -26,6 +26,18 @@ export const CONFIG = { styleConfig: Widget.getPropertyPaneStyleConfig(), stylesheetConfig: Widget.getStylesheetConfig(), }, + autoLayout: { + widgetSize: [ + { + viewportMinWidth: 0, + configuration: () => { + return { + minWidth: "280px", + }; + }, + }, + ], + }, }; export default Widget; diff --git a/app/client/src/widgets/ImageWidget/index.ts b/app/client/src/widgets/ImageWidget/index.ts index b50f7c298855..eab729cfe72a 100644 --- a/app/client/src/widgets/ImageWidget/index.ts +++ b/app/client/src/widgets/ImageWidget/index.ts @@ -28,6 +28,18 @@ export const CONFIG = { styleConfig: Widget.getPropertyPaneStyleConfig(), stylesheetConfig: Widget.getStylesheetConfig(), }, + autoLayout: { + widgetSize: [ + { + viewportMinWidth: 0, + configuration: () => { + return { + minWidth: "280px", + }; + }, + }, + ], + }, }; export default Widget; diff --git a/app/client/src/widgets/JSONFormWidget/index.ts b/app/client/src/widgets/JSONFormWidget/index.ts index 7e29d0b8d2ec..4b8ce17b1809 100644 --- a/app/client/src/widgets/JSONFormWidget/index.ts +++ b/app/client/src/widgets/JSONFormWidget/index.ts @@ -95,6 +95,19 @@ export const CONFIG = { styleConfig: Widget.getPropertyPaneStyleConfig(), stylesheetConfig: Widget.getStylesheetConfig(), }, + autoLayout: { + widgetSize: [ + { + viewportMinWidth: 0, + configuration: () => { + return { + minWidth: "280px", + minHeight: "300px", + }; + }, + }, + ], + }, }; export default Widget; diff --git a/app/client/src/widgets/ListWidget/index.ts b/app/client/src/widgets/ListWidget/index.ts index 8b00f563ab42..a43aa9cd491d 100644 --- a/app/client/src/widgets/ListWidget/index.ts +++ b/app/client/src/widgets/ListWidget/index.ts @@ -421,6 +421,19 @@ export const CONFIG = { styleConfig: Widget.getPropertyPaneStyleConfig(), stylesheetConfig: Widget.getStylesheetConfig(), }, + autoLayout: { + widgetSize: [ + { + viewportMinWidth: 0, + configuration: () => { + return { + minWidth: "280px", + maxWidth: "300px", + }; + }, + }, + ], + }, }; export default Widget; diff --git a/app/client/src/widgets/ListWidgetV2/index.ts b/app/client/src/widgets/ListWidgetV2/index.ts index 9c21bbf16663..0f183c711ca2 100644 --- a/app/client/src/widgets/ListWidgetV2/index.ts +++ b/app/client/src/widgets/ListWidgetV2/index.ts @@ -344,6 +344,19 @@ export const CONFIG = { styleConfig: Widget.getPropertyPaneStyleConfig(), stylesheetConfig: Widget.getStylesheetConfig(), }, + autoLayout: { + widgetSize: [ + { + viewportMinWidth: 0, + configuration: () => { + return { + minWidth: "280px", + maxWidth: "300px", + }; + }, + }, + ], + }, }; export default Widget; diff --git a/app/client/src/widgets/ProgressWidget/index.ts b/app/client/src/widgets/ProgressWidget/index.ts index a2b81184f1ee..afbba1456871 100644 --- a/app/client/src/widgets/ProgressWidget/index.ts +++ b/app/client/src/widgets/ProgressWidget/index.ts @@ -33,6 +33,19 @@ export const CONFIG = { styleConfig: Widget.getPropertyPaneStyleConfig(), stylesheetConfig: Widget.getStylesheetConfig(), }, + autoLayout: { + widgetSize: [ + { + viewportMinWidth: 0, + configuration: () => { + return { + minWidth: "160px", + minHeight: "70px", + }; + }, + }, + ], + }, }; export default Widget; diff --git a/app/client/src/widgets/RichTextEditorWidget/index.ts b/app/client/src/widgets/RichTextEditorWidget/index.ts index 705ec97e304c..282ceec6b0b6 100644 --- a/app/client/src/widgets/RichTextEditorWidget/index.ts +++ b/app/client/src/widgets/RichTextEditorWidget/index.ts @@ -47,6 +47,18 @@ export const CONFIG = { styleConfig: Widget.getPropertyPaneStyleConfig(), stylesheetConfig: Widget.getStylesheetConfig(), }, + autoLayout: { + widgetSize: [ + { + viewportMinWidth: 0, + configuration: () => { + return { + minWidth: "280px", + }; + }, + }, + ], + }, }; export default Widget; diff --git a/app/client/src/widgets/StatboxWidget/index.ts b/app/client/src/widgets/StatboxWidget/index.ts index 3bd08eb219c1..8984495b3978 100644 --- a/app/client/src/widgets/StatboxWidget/index.ts +++ b/app/client/src/widgets/StatboxWidget/index.ts @@ -135,6 +135,18 @@ export const CONFIG = { styleConfig: Widget.getPropertyPaneStyleConfig(), stylesheetConfig: Widget.getStylesheetConfig(), }, + autoLayout: { + widgetSize: [ + { + viewportMinWidth: 0, + configuration: () => { + return { + minWidth: "280px", + }; + }, + }, + ], + }, }; export default Widget; diff --git a/app/client/src/widgets/TableWidgetV2/index.ts b/app/client/src/widgets/TableWidgetV2/index.ts index 0a5a029e92ef..d90d56298958 100644 --- a/app/client/src/widgets/TableWidgetV2/index.ts +++ b/app/client/src/widgets/TableWidgetV2/index.ts @@ -249,6 +249,18 @@ export const CONFIG = { stylesheetConfig: Widget.getStylesheetConfig(), loadingProperties: Widget.getLoadingProperties(), }, + autoLayout: { + widgetSize: [ + { + viewportMinWidth: 0, + configuration: () => { + return { + minWidth: "280px", + }; + }, + }, + ], + }, }; export default Widget; diff --git a/app/client/src/widgets/TabsWidget/index.ts b/app/client/src/widgets/TabsWidget/index.ts index 0724789892f3..ed93155109a1 100644 --- a/app/client/src/widgets/TabsWidget/index.ts +++ b/app/client/src/widgets/TabsWidget/index.ts @@ -146,6 +146,18 @@ export const CONFIG = { styleConfig: Widget.getPropertyPaneStyleConfig(), stylesheetConfig: Widget.getStylesheetConfig(), }, + autoLayout: { + widgetSize: [ + { + viewportMinWidth: 0, + configuration: () => { + return { + minWidth: "280px", + }; + }, + }, + ], + }, }; export default Widget; diff --git a/app/client/src/widgets/TextWidget/index.ts b/app/client/src/widgets/TextWidget/index.ts index c8e308d90cda..6fd7bb9e89f3 100644 --- a/app/client/src/widgets/TextWidget/index.ts +++ b/app/client/src/widgets/TextWidget/index.ts @@ -41,6 +41,19 @@ export const CONFIG = { styleConfig: Widget.getPropertyPaneStyleConfig(), stylesheetConfig: Widget.getStylesheetConfig(), }, + autoLayout: { + widgetSize: [ + { + viewportMinWidth: 0, + configuration: () => { + return { + minWidth: "120px", + minHeight: "70px", + }; + }, + }, + ], + }, }; export default Widget; diff --git a/app/client/src/widgets/VideoWidget/index.ts b/app/client/src/widgets/VideoWidget/index.ts index 902eb4a271f7..02158f6ee464 100644 --- a/app/client/src/widgets/VideoWidget/index.ts +++ b/app/client/src/widgets/VideoWidget/index.ts @@ -26,6 +26,18 @@ export const CONFIG = { styleConfig: Widget.getPropertyPaneStyleConfig(), stylesheetConfig: Widget.getStylesheetConfig(), }, + autoLayout: { + widgetSize: [ + { + viewportMinWidth: 0, + configuration: () => { + return { + minWidth: "280px", + }; + }, + }, + ], + }, }; export default Widget; From faca83a1e2a3a774b889195f0364eb69a25fd394 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Mon, 27 Feb 2023 15:32:25 +0530 Subject: [PATCH 564/708] transparent resize borders. --- app/client/src/resizable/common.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/client/src/resizable/common.tsx b/app/client/src/resizable/common.tsx index e6f7f5d16a4f..001c24853bb3 100644 --- a/app/client/src/resizable/common.tsx +++ b/app/client/src/resizable/common.tsx @@ -44,6 +44,11 @@ export const ResizeWrapper = styled(animated.div)<{ outline-color: ${Colors.GREY_1} !important; border-color: ${Colors.GREY_1}; `; + } else { + return ` + outline-color: transparent !important; + border-color: transparent; + `; } }}} `; From 9a94f2d4de4e2fd9e4ce021d52d40076339cee2c Mon Sep 17 00:00:00 2001 From: Aswath K Date: Tue, 28 Feb 2023 07:06:09 +0530 Subject: [PATCH 565/708] use flattenedWidgetProps type --- app/client/src/utils/autoLayout/AutoLayoutUtils.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/app/client/src/utils/autoLayout/AutoLayoutUtils.ts b/app/client/src/utils/autoLayout/AutoLayoutUtils.ts index 10d0aad58ba3..5dc17c6bb741 100644 --- a/app/client/src/utils/autoLayout/AutoLayoutUtils.ts +++ b/app/client/src/utils/autoLayout/AutoLayoutUtils.ts @@ -6,7 +6,10 @@ import { MAIN_CONTAINER_WIDGET_ID, WIDGET_PADDING, } from "constants/WidgetConstants"; -import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; +import { + CanvasWidgetsReduxState, + FlattenedWidgetProps, +} from "reducers/entityReducers/canvasWidgetsReducer"; import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; import { defaultAutoLayoutWidgets, @@ -17,7 +20,6 @@ import { import { updateWidgetPositions } from "utils/autoLayout/positionUtils"; import { getWidgetWidth } from "./flexWidgetUtils"; import { AlignmentColumnInfo } from "./autoLayoutTypes"; -import { Widget } from "./positionUtils"; function getCanvas(widgets: CanvasWidgetsReduxState, containerId: string) { const container = widgets[containerId]; @@ -521,7 +523,7 @@ export function getAlignmentColumnInfo( } export function getCanvasDimensions( - canvas: Widget, + canvas: FlattenedWidgetProps, widgets: CanvasWidgetsReduxState, mainCanvasWidth: number, isMobile: boolean, @@ -539,7 +541,7 @@ export function getCanvasDimensions( } function getCanvasWidth( - canvas: Widget, + canvas: FlattenedWidgetProps, widgets: CanvasWidgetsReduxState, mainCanvasWidth: number, isMobile: boolean, @@ -563,7 +565,7 @@ function getCanvasWidth( return totalWidth - padding; } -function getPadding(canvas: Widget): number { +function getPadding(canvas: FlattenedWidgetProps): number { let padding = 0; if ( canvas.widgetId === MAIN_CONTAINER_WIDGET_ID || From aa19bff0f63b17910270b3a129dff605641b0faf Mon Sep 17 00:00:00 2001 From: Aswath K Date: Tue, 28 Feb 2023 07:26:11 +0530 Subject: [PATCH 566/708] Cleanup --- app/client/src/actions/autoLayoutActions.ts | 7 --- .../src/ce/constants/ReduxActionConstants.tsx | 1 - .../AutoLayoutDimensionObeserver.tsx | 12 ---- .../appsmith/autoLayout/FlexComponent.tsx | 31 +--------- .../src/sagas/AutoLayoutUpdateSagas.tsx | 26 -------- .../src/utils/autoLayout/AutoLayoutUtils.ts | 60 ------------------- .../src/utils/autoLayout/flexWidgetUtils.ts | 19 ------ .../src/utils/autoLayout/positionUtils.ts | 7 ++- app/client/src/widgets/BaseWidget.tsx | 7 --- 9 files changed, 7 insertions(+), 163 deletions(-) diff --git a/app/client/src/actions/autoLayoutActions.ts b/app/client/src/actions/autoLayoutActions.ts index 96e0e1cd903c..3aaa09c8bd03 100644 --- a/app/client/src/actions/autoLayoutActions.ts +++ b/app/client/src/actions/autoLayoutActions.ts @@ -13,13 +13,6 @@ export const updateLayoutForMobileBreakpointAction = ( }, }); -export const widgetViolatedMinDimensionsAction = (parentId: string) => ({ - type: ReduxActionTypes.WIDGET_VIOLATED_MIN_DIMENSIONS, - payload: { - parentId, - }, -}); - export function updateWidgetDimensionAction( widgetId: string, width: number, diff --git a/app/client/src/ce/constants/ReduxActionConstants.tsx b/app/client/src/ce/constants/ReduxActionConstants.tsx index 9795699bc2c0..807d1152914c 100644 --- a/app/client/src/ce/constants/ReduxActionConstants.tsx +++ b/app/client/src/ce/constants/ReduxActionConstants.tsx @@ -760,7 +760,6 @@ export const ReduxActionTypes = { AUTOLAYOUT_REORDER_WIDGETS: "AUTOLAYOUT_REORDER_WIDGETS", AUTOLAYOUT_ADD_NEW_WIDGETS: "AUTOLAYOUT_ADD_NEW_WIDGETS", RECALCULATE_COLUMNS: "RECALCULATE_COLUMNS", - WIDGET_VIOLATED_MIN_DIMENSIONS: "WIDGET_VIOLATED_MIN_DIMENSIONS", UPDATE_WIDGET_DIMENSIONS: "UPDATE_WIDGET_DIMENSIONS", PROCESS_AUTO_LAYOUT_DIMENSION_UPDATES: "PROCESS_AUTO_LAYOUT_DIMENSION_UPDATES", diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutDimensionObeserver.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutDimensionObeserver.tsx index a09120a0b421..c4cf9a606dec 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutDimensionObeserver.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutDimensionObeserver.tsx @@ -12,12 +12,9 @@ const SimpleContainer = styled.div` interface AutoLayoutDimensionObserverProps { onDimensionUpdate: (width: number, height: number) => void; - // widgetProps: WidgetProps; width: number; height: number; isFillWidget: boolean; - // TODO(aswathkk): Remove this since this is being only used for debuggind - widgetName: string; } export default function AutoLayoutDimensionObserver( @@ -50,15 +47,6 @@ export default function AutoLayoutDimensionObserver( props.height - 2 * FLEXBOX_PADDING - currentDimension.height, ); if (widthDiff > 2 || heightDiff > 2) { - // console.log( - // "#### dimensionUpdateObserver", - // props.widgetName, - // "dWidth", - // widthDiff, - // "dHeight", - // heightDiff, - // currentDimension, - // ); onDimensionUpdate(currentDimension.width, currentDimension.height); } }, [ diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx index a7f2f28c642d..7358e6deb54a 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx @@ -1,14 +1,8 @@ -import React, { - CSSProperties, - ReactNode, - useCallback, - useEffect, - useMemo, -} from "react"; +import React, { CSSProperties, ReactNode, useCallback, useMemo } from "react"; import styled from "styled-components"; import { WidgetType, WIDGET_PADDING } from "constants/WidgetConstants"; -import { useDispatch, useSelector } from "react-redux"; +import { useSelector } from "react-redux"; import { snipingModeSelector } from "selectors/editorSelectors"; import { getIsResizing } from "selectors/widgetSelectors"; import { @@ -19,8 +13,6 @@ import { import { useClickToSelectWidget } from "utils/hooks/useClickToSelectWidget"; import { usePositionedContainerZIndex } from "utils/hooks/usePositionedContainerZIndex"; import { checkIsDropTarget } from "../PositionedContainer"; -import { widgetViolatedMinDimensionsAction } from "actions/autoLayoutActions"; -import { debounce } from "lodash"; import { widgetTypeClassname } from "widgets/WidgetUtils"; export type AutoLayoutProps = { @@ -29,7 +21,6 @@ export type AutoLayoutProps = { componentWidth: number; direction?: LayoutDirection; focused?: boolean; - minWidth?: number; parentId?: string; responsiveBehavior?: ResponsiveBehavior; selected?: boolean; @@ -47,7 +38,6 @@ const FlexWidget = styled.div` export function FlexComponent(props: AutoLayoutProps) { const isSnipingMode = useSelector(snipingModeSelector); - const dispatch = useDispatch(); const clickToSelectWidget = useClickToSelectWidget(props.widgetId); const onClickFn = useCallback( @@ -81,23 +71,6 @@ export function FlexComponent(props: AutoLayoutProps) { const isResizing = useSelector(getIsResizing); - const widgetReachedMinWidth = useCallback( - debounce((parentId) => { - dispatch(widgetViolatedMinDimensionsAction(parentId)); - }, 50), - [], - ); - - useEffect(() => { - if ( - props.minWidth && - props.componentWidth < props.minWidth && - props.parentId - ) { - widgetReachedMinWidth(props.parentId); - } - }, [props.componentWidth]); - const flexComponentStyle: CSSProperties = useMemo(() => { return { display: "flex", diff --git a/app/client/src/sagas/AutoLayoutUpdateSagas.tsx b/app/client/src/sagas/AutoLayoutUpdateSagas.tsx index 147fa815822f..f98dfeafcac4 100644 --- a/app/client/src/sagas/AutoLayoutUpdateSagas.tsx +++ b/app/client/src/sagas/AutoLayoutUpdateSagas.tsx @@ -53,28 +53,6 @@ export function* updateLayoutForMobileCheckpoint( } } -// const processedParentIds = new Map(); - -// function* widgetViolatedMinDimensionsSaga( -// action: ReduxAction<{ parentId: string }>, -// ) { -// const isMobile: boolean = yield select(getIsMobile); -// const allWidgets: CanvasWidgetsReduxState = yield select(getWidgets); -// const mainCanvasWidth: number = yield select(getCanvasWidth); - -// const parentId = action.payload.parentId; -// if (processedParentIds.has(parentId)) return; -// processedParentIds.set(parentId, true); -// setTimeout(() => processedParentIds.delete(parentId), 1000); -// const updatedWidgets = updateWidgetPositions( -// allWidgets, -// parentId, -// isMobile, -// mainCanvasWidth, -// ); -// yield put(updateAndSaveLayout(updatedWidgets)); -// } - let autoLayoutWidgetDimensionUpdateBatch: Record< string, { width: number; height: number } @@ -266,10 +244,6 @@ export default function* layoutUpdateSagas() { ReduxActionTypes.RECALCULATE_COLUMNS, updateLayoutForMobileCheckpoint, ), - // takeLatest( - // ReduxActionTypes.WIDGET_VIOLATED_MIN_DIMENSIONS, - // widgetViolatedMinDimensionsSaga, - // ), takeLatest( ReduxActionTypes.UPDATE_WIDGET_DIMENSIONS, updateWidgetDimensionsSaga, diff --git a/app/client/src/utils/autoLayout/AutoLayoutUtils.ts b/app/client/src/utils/autoLayout/AutoLayoutUtils.ts index 5dc17c6bb741..50d77654c584 100644 --- a/app/client/src/utils/autoLayout/AutoLayoutUtils.ts +++ b/app/client/src/utils/autoLayout/AutoLayoutUtils.ts @@ -174,66 +174,6 @@ export function updateFillChildStatus( ); } -// export function alterLayoutForMobile( -// allWidgets: CanvasWidgetsReduxState, -// parentId: string, -// canvasWidth: number, -// mainCanvasWidth: number, -// ): CanvasWidgetsReduxState { -// let widgets = { ...allWidgets }; -// const parent = widgets[parentId]; -// const children = parent.children; - -// if (!isStack(allWidgets, parent)) { -// return widgets; -// } -// if (!children || !children.length) return widgets; - -// for (const child of children) { -// const widget = { ...widgets[child] }; -// const minWidth: number | undefined = getMinPixelWidth( -// widget, -// mainCanvasWidth, -// ); - -// if (widget.responsiveBehavior === ResponsiveBehavior.Fill) { -// widget.mobileRightColumn = 64; -// widget.mobileLeftColumn = 0; -// } else if ( -// widget.responsiveBehavior === ResponsiveBehavior.Hug && -// minWidth -// ) { -// const { leftColumn, rightColumn } = widget; -// const columnSpace = (canvasWidth - FLEXBOX_PADDING * 2) / 64; -// if (columnSpace * (rightColumn - leftColumn) < minWidth) { -// /** -// * Set a proper width for the widget => left column = 0; -// * Actual positioning of the widget will be updated by updateWidgetPositions function. -// */ -// widget.mobileLeftColumn = 0; -// widget.mobileRightColumn = Math.min(minWidth / columnSpace, 64); -// } -// } else { -// widget.mobileLeftColumn = widget.leftColumn; -// widget.mobileRightColumn = widget.rightColumn; -// } - -// widget.mobileTopRow = widget.topRow; -// widget.mobileBottomRow = widget.bottomRow; -// if (widget.mobileRightColumn !== undefined) -// widgets = alterLayoutForMobile( -// widgets, -// child, -// (canvasWidth * widget.mobileRightColumn) / 64, -// mainCanvasWidth, -// ); -// widgets[child] = widget; -// widgets = updateWidgetPositions(widgets, child, true, mainCanvasWidth); -// } -// widgets = updateWidgetPositions(widgets, parentId, true, mainCanvasWidth); -// return widgets; -// } - export function alterLayoutForMobile( allWidgets: CanvasWidgetsReduxState, parentId: string, diff --git a/app/client/src/utils/autoLayout/flexWidgetUtils.ts b/app/client/src/utils/autoLayout/flexWidgetUtils.ts index 380566a76bb1..ed34bc9f3c54 100644 --- a/app/client/src/utils/autoLayout/flexWidgetUtils.ts +++ b/app/client/src/utils/autoLayout/flexWidgetUtils.ts @@ -161,25 +161,6 @@ export function getCurrentSizeConfig( return sizes[index - 1]; } -/** - * Return the minimum pixel width of a widget based on the widget type and the canvas width. - * minSize can be configured in columns (number) or pixels (string). - * Return an appropriate pixel width based on the minSize type. - */ -export function getMinPixelWidth( - widget: any, - canvasWidth: number, -): number | undefined { - if (!widget) return; - const minSize = getMinMaxSize(widget, canvasWidth); - if (!minSize) return; - const arr: string[] = - typeof minSize.minWidth === "string" ? minSize.minWidth.split("px") : []; - if (arr.length) return parseInt(arr[0]); - if (typeof minSize.minWidth === "number") - return minSize.minWidth * widget.parentColumnSpace; -} - function getPxValue(val: string | number, factor: number): number | undefined { const arr: string[] = typeof val === "string" ? val.split("px") : []; if (arr.length) return parseInt(arr[0]); diff --git a/app/client/src/utils/autoLayout/positionUtils.ts b/app/client/src/utils/autoLayout/positionUtils.ts index ca22ab9ff311..0b7b2be7bbb6 100644 --- a/app/client/src/utils/autoLayout/positionUtils.ts +++ b/app/client/src/utils/autoLayout/positionUtils.ts @@ -14,9 +14,9 @@ import { } from "utils/autoLayout/constants"; import { getBottomRow, - getMinPixelWidth, getTopRow, getWidgetHeight, + getWidgetMinMaxDimensionsInPixel, getWidgetRows, getWidgetWidth, setDimensions, @@ -226,7 +226,10 @@ export function placeWidgetsWithoutWrap( each.columns, ); for (const widget of each.children) { - const minWidth = getMinPixelWidth(widget, mainCanvasWidth); + const { minWidth } = getWidgetMinMaxDimensionsInPixel( + widget, + mainCanvasWidth, + ); const height = getWidgetHeight(widget, isMobile); let width = widget.responsiveBehavior === ResponsiveBehavior.Fill diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index 771fadc21663..965b9ff83416 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -65,7 +65,6 @@ import { isAutoHeightEnabledForWidget, shouldUpdateWidgetHeightAutomatically, } from "./WidgetUtils"; -import { getMinPixelWidth } from "utils/autoLayout/flexWidgetUtils"; import AutoLayoutDimensionObserver from "components/designSystems/appsmith/autoLayout/AutoLayoutDimensionObeserver"; import { WIDGET_WITH_DYNAMIC_HEIGHT, @@ -560,10 +559,6 @@ abstract class BaseWidget< makeFlex(content: ReactNode) { const { componentHeight, componentWidth } = this.getComponentDimensions(); - const minWidth = getMinPixelWidth( - this.props, - this.props?.mainCanvasWidth || 0, - ); return ( {content} From 36d66c5e81bd1c1636b5a437f52b7d57a3eb4d75 Mon Sep 17 00:00:00 2001 From: Arsalan Yaldram Date: Tue, 28 Feb 2023 07:41:31 +0530 Subject: [PATCH 567/708] poc: widget min-width & min-height constraints (#20489) --- .../src/resizable/resizenreflow/index.tsx | 34 ++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/app/client/src/resizable/resizenreflow/index.tsx b/app/client/src/resizable/resizenreflow/index.tsx index c0dfeb9baa74..868e12d519a0 100644 --- a/app/client/src/resizable/resizenreflow/index.tsx +++ b/app/client/src/resizable/resizenreflow/index.tsx @@ -24,6 +24,7 @@ import { getContainerOccupiedSpacesSelectorWhileResizing, getCurrentAppPositioningType, } from "selectors/editorSelectors"; +import { getCanvasWidth } from "selectors/editorSelectors"; import { getReflowSelector } from "selectors/widgetReflowSelectors"; import styled, { StyledComponent } from "styled-components"; import { @@ -34,6 +35,7 @@ import { LayoutDirection, ResponsiveBehavior, } from "utils/autoLayout/constants"; +import { getWidgetMinMaxDimensionsInPixel } from "utils/autoLayout/flexWidgetUtils"; import { getNearestParentCanvas } from "utils/generators"; import { useReflow } from "utils/hooks/useReflow"; import PerformanceTracker, { @@ -277,7 +279,6 @@ export function ReflowResizable(props: ResizableProps) { [props.originalPositions], props.parentId || "", props.gridProps, - !isAutoLayout, ); useEffect(() => { @@ -296,10 +297,13 @@ export function ReflowResizable(props: ResizableProps) { direction: ReflowDirection.UNSET, }); const allWidgets = useSelector(getWidgets); + const mainCanvasWidth = useSelector(getCanvasWidth); + const dispatch = useDispatch(); const triggerAutoLayoutBasedReflow = (resizedPositions: OccupiedSpace) => { const { widgetId } = props; const widget = allWidgets[widgetId]; + if (!widget || !widget.parentId) return; const parent = allWidgets[widget.parentId]; if (!parent) return; @@ -424,9 +428,22 @@ export function ReflowResizable(props: ResizableProps) { const handles = []; const widget = allWidgets[props.widgetId]; + + const { + minHeight: widgetMinHeight, + minWidth: widgetMinWidth, + } = getWidgetMinMaxDimensionsInPixel(widget, mainCanvasWidth); + if (!(isAutoLayout && widget.leftColumn === 0) && props.handles.left) { handles.push({ dragCallback: (x: number) => { + if ( + isAutoLayout && + widgetMinWidth && + props.componentWidth - x < widgetMinWidth && + x > 0 + ) + return; setNewDimensions({ width: props.componentWidth - x, height: newDimensions.height, @@ -468,6 +485,13 @@ export function ReflowResizable(props: ResizableProps) { ) { handles.push({ dragCallback: (x: number) => { + if ( + isAutoLayout && + widgetMinWidth && + props.componentWidth + x < widgetMinWidth && + x < 0 + ) + return; setNewDimensions({ width: props.componentWidth + x, height: newDimensions.height, @@ -485,6 +509,13 @@ export function ReflowResizable(props: ResizableProps) { if (props.handles.bottom) { handles.push({ dragCallback: (x: number, y: number) => { + if ( + isAutoLayout && + widgetMinHeight && + props.componentHeight + y < widgetMinHeight && + y < 0 + ) + return; setNewDimensions({ width: newDimensions.width, height: props.componentHeight + y, @@ -596,6 +627,7 @@ export function ReflowResizable(props: ResizableProps) { props.isFlexChild, props.responsiveBehavior, ); + return ( Date: Tue, 28 Feb 2023 08:46:47 +0530 Subject: [PATCH 568/708] remove unused code --- .../src/components/editorComponents/DraggableComponent.tsx | 7 ------- 1 file changed, 7 deletions(-) diff --git a/app/client/src/components/editorComponents/DraggableComponent.tsx b/app/client/src/components/editorComponents/DraggableComponent.tsx index 2759d540a867..dd621afa6719 100644 --- a/app/client/src/components/editorComponents/DraggableComponent.tsx +++ b/app/client/src/components/editorComponents/DraggableComponent.tsx @@ -185,13 +185,6 @@ function DraggableComponent(props: DraggableComponentProps) { } }; - const widgetBoundaries = props.isFlexChild ? null : ( - - ); - return ( Date: Tue, 28 Feb 2023 09:34:30 +0530 Subject: [PATCH 569/708] add layer children check and enable scroll in container --- app/client/src/resizable/autolayoutresize/index.tsx | 1 + app/client/src/widgets/ContainerWidget/widget/index.tsx | 6 +----- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/app/client/src/resizable/autolayoutresize/index.tsx b/app/client/src/resizable/autolayoutresize/index.tsx index a3d768094592..656c2b5120e9 100644 --- a/app/client/src/resizable/autolayoutresize/index.tsx +++ b/app/client/src/resizable/autolayoutresize/index.tsx @@ -127,6 +127,7 @@ export function ReflowResizable(props: ResizableProps) { }, [props, allWidgets]); const hasFillChild = !!layer && + layer?.children?.length && layer.children.some((each: any) => { const widget = allWidgets[each.id]; return widget && widget.responsiveBehavior === ResponsiveBehavior.Fill; diff --git a/app/client/src/widgets/ContainerWidget/widget/index.tsx b/app/client/src/widgets/ContainerWidget/widget/index.tsx index e0ee58c41b7b..47c8998e2631 100644 --- a/app/client/src/widgets/ContainerWidget/widget/index.tsx +++ b/app/client/src/widgets/ContainerWidget/widget/index.tsx @@ -226,11 +226,7 @@ export class ContainerWidget extends BaseWidget< }; renderAsContainerComponent(props: ContainerWidgetProps) { - //ToDo: Ashok Need a better way of doing this. - const useAutoLayout = this.props.positioning - ? this.props.positioning !== Positioning.Fixed - : false; - const shouldScroll = props.shouldScrollContents && !useAutoLayout; + const shouldScroll = props.shouldScrollContents; return ( Date: Tue, 28 Feb 2023 11:53:41 +0530 Subject: [PATCH 570/708] update positions of parent and siblings --- .../CanvasSagas/AutoLayoutDraggingSagas.ts | 9 ++- .../src/utils/autoLayout/AutoLayoutUtils.ts | 13 ++- .../src/utils/autoLayout/autoLayoutTypes.ts | 2 +- .../src/utils/autoLayout/positionUtils.ts | 81 ++++++++++++++++--- 4 files changed, 85 insertions(+), 20 deletions(-) diff --git a/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts b/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts index 7d7eabc7e89e..bba630130b85 100644 --- a/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts +++ b/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts @@ -22,7 +22,7 @@ import { updateRelationships, } from "utils/autoLayout/autoLayoutDraggingUtils"; import { HighlightInfo, FlexLayer } from "utils/autoLayout/autoLayoutTypes"; -import { updateWidgetPositions } from "utils/autoLayout/positionUtils"; +import { updatePositionsOfParentAndSiblings } from "utils/autoLayout/positionUtils"; import { getCanvasWidth } from "selectors/editorSelectors"; function* addWidgetAndReorderSaga( @@ -123,9 +123,9 @@ function* reorderAutolayoutChildren(params: { allWidgets: CanvasWidgetsReduxState; alignment: FlexLayerAlignment; direction: LayoutDirection; - layerIndex?: number; + layerIndex: number; rowIndex: number; - isMobile?: boolean; + isMobile: boolean; }) { const { alignment, @@ -216,9 +216,10 @@ function* reorderAutolayoutChildren(params: { bottomRow: parentWidget.topRow + height, }; } - const widgetsAfterPositionUpdate = updateWidgetPositions( + const widgetsAfterPositionUpdate = updatePositionsOfParentAndSiblings( updatedWidgets, parentId, + layerIndex, isMobile, mainCanvasWidth, ); diff --git a/app/client/src/utils/autoLayout/AutoLayoutUtils.ts b/app/client/src/utils/autoLayout/AutoLayoutUtils.ts index 50d77654c584..9567f6fb47fb 100644 --- a/app/client/src/utils/autoLayout/AutoLayoutUtils.ts +++ b/app/client/src/utils/autoLayout/AutoLayoutUtils.ts @@ -17,7 +17,10 @@ import { Positioning, ResponsiveBehavior, } from "utils/autoLayout/constants"; -import { updateWidgetPositions } from "utils/autoLayout/positionUtils"; +import { + updatePositionsOfParentAndSiblings, + updateWidgetPositions, +} from "utils/autoLayout/positionUtils"; import { getWidgetWidth } from "./flexWidgetUtils"; import { AlignmentColumnInfo } from "./autoLayoutTypes"; @@ -138,7 +141,13 @@ export function updateFlexLayersOnDelete( }; widgets[parentId] = parent; - return updateWidgetPositions(widgets, parentId, isMobile, mainCanvasWidth); + return updatePositionsOfParentAndSiblings( + widgets, + parentId, + layerIndex, + isMobile, + mainCanvasWidth, + ); } export function updateFillChildStatus( diff --git a/app/client/src/utils/autoLayout/autoLayoutTypes.ts b/app/client/src/utils/autoLayout/autoLayoutTypes.ts index 5ad87212487f..75014dfc72d2 100644 --- a/app/client/src/utils/autoLayout/autoLayoutTypes.ts +++ b/app/client/src/utils/autoLayout/autoLayoutTypes.ts @@ -27,7 +27,7 @@ export interface DropZone { export interface HighlightInfo { isNewLayer: boolean; // determines if a new layer / child has been added directly to the container. index: number; // index of the child in props.children. - layerIndex?: number; // index of layer in props.flexLayers. + layerIndex: number; // index of layer in props.flexLayers. rowIndex: number; // index of highlight within a horizontal layer. alignment: FlexLayerAlignment; // alignment of the child in the layer. posX: number; // x position of the highlight. diff --git a/app/client/src/utils/autoLayout/positionUtils.ts b/app/client/src/utils/autoLayout/positionUtils.ts index 0b7b2be7bbb6..e13dae77868b 100644 --- a/app/client/src/utils/autoLayout/positionUtils.ts +++ b/app/client/src/utils/autoLayout/positionUtils.ts @@ -1,4 +1,4 @@ -import { FlexLayer } from "./autoLayoutTypes"; +import { FlexLayer, LayerChild } from "./autoLayoutTypes"; import { GridDefaults, MAIN_CONTAINER_WIDGET_ID, @@ -258,18 +258,18 @@ export function placeWidgetsWithoutWrap( } // Trigger a position update for the widgets inside container widgets - for (const each of arr) { - for (const widget of each.children) { - if (widget.type === "CONTAINER_WIDGET" && widget.children?.length) { - widgets = updateWidgetPositions( - widgets, - widget.children[0], - isMobile, - mainCanvasWidth, - ); - } - } - } + // for (const each of arr) { + // for (const widget of each.children) { + // if (widget.type === "CONTAINER_WIDGET" && widget.children?.length) { + // widgets = updateWidgetPositions( + // widgets, + // widget.children[0], + // isMobile, + // mainCanvasWidth, + // ); + // } + // } + // } return { height: maxHeight, widgets }; } @@ -661,3 +661,58 @@ export function getTotalRowsOfAllChildren( } return bottom - top; } + +/** + * Update sizes and positions of all the canvas containing widgets in the affected flex layer and its parent canvas. + * Sibling canvases in flex layers are updated to recheck the minSize situations within them. + * @param allWidgets | CanvasWidgetsReduxState: all widgets. + * @param parentId | string: parent id. + * @param layerIndex | number: layer index of the affected flex layer. + * @param isMobile | boolean: is mobile viewport. + * @param mainCanvasWidth | number: width of the main canvas. + * @returns CanvasWidgetsReduxState + */ +export function updatePositionsOfParentAndSiblings( + allWidgets: CanvasWidgetsReduxState, + parentId: string, + layerIndex: number, + isMobile: boolean, + mainCanvasWidth: number, +): CanvasWidgetsReduxState { + let widgets = { ...allWidgets }; + const parent = widgets[parentId]; + if (!parent) return widgets; + const { children, flexLayers } = parent; + if (!children || !children?.length || !flexLayers || !flexLayers?.length) + return widgets; + // Extract all widgets to be updated. => parent canvas + all other canvas containing widgets in the same flex layer. + let widgetsToBeParsed: string[] = [parentId]; + if ( + layerIndex > -1 && + layerIndex < flexLayers.length && + flexLayers[layerIndex]?.children?.length + ) { + flexLayers[layerIndex]?.children.forEach((child: LayerChild) => { + const widget = widgets[child.id]; + if (!widget || !widget.children || !widget.children?.length) return; + // Due to canvas / cell splitting, a widget can contain multiple canvases. + const canvases: string[] = widget.children?.filter( + (id: string) => widgets[id] && widgets[id].type === "CANVAS_WIDGET", + ); + if (canvases.length) { + widgetsToBeParsed = [...widgetsToBeParsed, ...canvases]; + } + }); + } + // Update positions of all the widgets. + for (const widgetId of widgetsToBeParsed) { + widgets = updateWidgetPositions( + widgets, + widgetId, + isMobile, + mainCanvasWidth, + ); + } + + return widgets; +} From 0679443609c3618e166a04d91ee75c58791d6c5c Mon Sep 17 00:00:00 2001 From: Preet Date: Tue, 28 Feb 2023 12:45:10 +0530 Subject: [PATCH 571/708] add new update position method for resize and paste --- app/client/src/sagas/WidgetOperationSagas.tsx | 6 ++++-- app/client/src/utils/autoLayout/AutoLayoutUtils.ts | 11 +++++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/app/client/src/sagas/WidgetOperationSagas.tsx b/app/client/src/sagas/WidgetOperationSagas.tsx index 17724dc12d76..515916a59ff0 100644 --- a/app/client/src/sagas/WidgetOperationSagas.tsx +++ b/app/client/src/sagas/WidgetOperationSagas.tsx @@ -99,7 +99,7 @@ import { builderURL } from "RouteBuilder"; import { getIsMobile } from "selectors/mainCanvasSelectors"; import { getSelectedWidgets } from "selectors/ui"; import { getReflow } from "selectors/widgetReflowSelectors"; -import { updateWidgetPositions } from "utils/autoLayout/positionUtils"; +import { updatePositionsOfParentAndSiblings } from "utils/autoLayout/positionUtils"; import { flashElementsById } from "utils/helpers"; import history from "utils/history"; import { @@ -109,6 +109,7 @@ import { import WidgetFactory from "utils/WidgetFactory"; import { addChildToPastedFlexLayers, + getLayerIndexOfWidget, isStack, pasteWidgetInFlexLayers, } from "../utils/autoLayout/AutoLayoutUtils"; @@ -209,9 +210,10 @@ export function* resizeSaga(resizeAction: ReduxAction) { const isMobile: boolean = yield select(getIsMobile); let updatedWidgetsAfterResizing = movedWidgets; if (appPositioningType === AppPositioningTypes.AUTO) { - updatedWidgetsAfterResizing = updateWidgetPositions( + updatedWidgetsAfterResizing = updatePositionsOfParentAndSiblings( movedWidgets, parentId, + getLayerIndexOfWidget(widgets[parentId]?.flexLayers, widgetId), isMobile, mainCanvasWidth, ); diff --git a/app/client/src/utils/autoLayout/AutoLayoutUtils.ts b/app/client/src/utils/autoLayout/AutoLayoutUtils.ts index 9567f6fb47fb..283530f2b520 100644 --- a/app/client/src/utils/autoLayout/AutoLayoutUtils.ts +++ b/app/client/src/utils/autoLayout/AutoLayoutUtils.ts @@ -263,6 +263,7 @@ export function pasteWidgetInFlexLayers( let widgets = { ...allWidgets }; const parent = widgets[parentId]; let flexLayers: FlexLayer[] = parent.flexLayers || []; + let flexLayerIndex = -1; /** * If the new parent is not the same as the original parent, * then add a new flex layer. @@ -289,7 +290,7 @@ export function pasteWidgetInFlexLayers( */ let rowIndex = -1, alignment = FlexLayerAlignment.Start; - const flexLayerIndex = flexLayers.findIndex((layer: FlexLayer) => { + flexLayerIndex = flexLayers.findIndex((layer: FlexLayer) => { const temp = layer.children.findIndex( (child: LayerChild) => child.id === originalWidgetId, ); @@ -322,7 +323,13 @@ export function pasteWidgetInFlexLayers( flexLayers, }, }; - return updateWidgetPositions(widgets, parentId, isMobile, mainCanvasWidth); + return updatePositionsOfParentAndSiblings( + widgets, + parentId, + flexLayerIndex, + isMobile, + mainCanvasWidth, + ); } /** From 96e92796492da07e9bce876948aa05c57bb9becb Mon Sep 17 00:00:00 2001 From: Aswath K Date: Tue, 28 Feb 2023 15:11:09 +0530 Subject: [PATCH 572/708] fix: failed jest test --- app/client/src/widgets/DropdownWidget/widget/index.test.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/client/src/widgets/DropdownWidget/widget/index.test.tsx b/app/client/src/widgets/DropdownWidget/widget/index.test.tsx index 86ac16eaed4e..9b310ef6dc1e 100644 --- a/app/client/src/widgets/DropdownWidget/widget/index.test.tsx +++ b/app/client/src/widgets/DropdownWidget/widget/index.test.tsx @@ -44,6 +44,9 @@ describe("", () => { autoHeightUI: { isAutoHeightWithLimitsChanging: false, }, + mainCanvas: { + width: 1159, + }, }, entities: { canvasWidgets: {}, app: { mode: "canvas" } }, }; From eb541385c02a2881410c278b00f2e1e8de3340d8 Mon Sep 17 00:00:00 2001 From: Aswath K Date: Tue, 28 Feb 2023 17:44:59 +0530 Subject: [PATCH 573/708] fix: jest test --- app/client/src/widgets/DividerWidget/widget/index.test.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/client/src/widgets/DividerWidget/widget/index.test.tsx b/app/client/src/widgets/DividerWidget/widget/index.test.tsx index 4e4908025738..957682cb82dc 100644 --- a/app/client/src/widgets/DividerWidget/widget/index.test.tsx +++ b/app/client/src/widgets/DividerWidget/widget/index.test.tsx @@ -40,6 +40,9 @@ describe("", () => { autoHeightUI: { isAutoHeightWithLimitsChanging: false, }, + mainCanvas: { + width: 1159, + }, }, entities: { canvasWidgets: {}, app: { mode: "canvas" } }, }; From d0a726b33dde82382f67ae4a98fdcef2c14f37a5 Mon Sep 17 00:00:00 2001 From: Aswath K Date: Tue, 28 Feb 2023 19:25:23 +0530 Subject: [PATCH 574/708] Adds min dimension for ContainerWidget --- app/client/src/widgets/ContainerWidget/index.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/app/client/src/widgets/ContainerWidget/index.ts b/app/client/src/widgets/ContainerWidget/index.ts index 1d599feedb12..1ce8d6167a48 100644 --- a/app/client/src/widgets/ContainerWidget/index.ts +++ b/app/client/src/widgets/ContainerWidget/index.ts @@ -60,6 +60,19 @@ export const CONFIG = { responsiveBehavior: getDefaultResponsiveBehavior(Widget.getWidgetType()), minWidth: FILL_WIDGET_MIN_WIDTH, }, + autoLayout: { + widgetSize: [ + { + viewportMinWidth: 0, + configuration: () => { + return { + minWidth: "280px", + minHeight: "50px", + }; + }, + }, + ], + }, properties: { derived: Widget.getDerivedPropertiesMap(), default: Widget.getDefaultPropertiesMap(), From 3d411db6ef13c2323442d0e0c41613854f17425e Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 1 Mar 2023 11:45:14 +0530 Subject: [PATCH 575/708] address code review comments 1 --- .../hooks/useAutoLayoutHighlights.ts | 42 ++------ .../CanvasArenas/hooks/useCanvasDragging.ts | 11 ++- .../src/selectors/autoLayoutSelectors.tsx | 11 --- .../src/utils/autoLayout/AutoLayoutUtils.ts | 98 ++----------------- .../autoLayout/autoLayoutDraggingUtils.ts | 2 +- .../autoLayout/highlightSelectionUtils.ts | 2 +- .../ContainerWidget/component/index.tsx | 11 +-- .../widgets/ContainerWidget/widget/index.tsx | 4 - 8 files changed, 28 insertions(+), 153 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts index deb3625483ef..80ae257f5457 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useAutoLayoutHighlights.ts @@ -98,10 +98,14 @@ export const useAutoLayoutHighlights = ({ * @param e | MouseMoveEvent * @returns HighlightInfo | undefined */ - const highlightDropPosition = ( - e: any, + const getDropPosition = ( snapColumnSpace: number, - ): HighlightInfo | undefined => { + e?: any, + val?: Point, + mouseUp = false, + ) => { + if (mouseUp && lastActiveHighlight) return lastActiveHighlight; + if (!highlights || !highlights.length) highlights = deriveHighlightsFromLayers( allWidgets, @@ -114,7 +118,8 @@ export const useAutoLayoutHighlights = ({ const highlight: HighlightInfo | undefined = getHighlightPayload( highlights, - e, + e || null, + val, ); if (!highlight) return; @@ -122,36 +127,9 @@ export const useAutoLayoutHighlights = ({ return highlight; }; - const getDropInfo = ( - val: Point, - snapColumnSpace: number, - ): HighlightInfo | undefined => { - if (lastActiveHighlight) return lastActiveHighlight; - - if (!highlights || !highlights.length) - highlights = deriveHighlightsFromLayers( - allWidgets, - canvasId, - snapColumnSpace, - blocksToDraw.map((block) => block?.widgetId), - isFillWidget, - isMobile, - ); - - const payload: HighlightInfo | undefined = getHighlightPayload( - highlights, - null, - val, - ); - if (!payload) return; - lastActiveHighlight = payload; - return payload; - }; - return { calculateHighlights, cleanUpTempStyles, - getDropInfo, - highlightDropPosition, + getDropPosition, }; }; diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index 04763f0e5531..b9a5be952fe1 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -111,8 +111,7 @@ export const useCanvasDragging = ( const { calculateHighlights, cleanUpTempStyles, - getDropInfo, - highlightDropPosition, + getDropPosition, } = useAutoLayoutHighlights({ blocksToDraw, canvasId: widgetId, @@ -220,12 +219,14 @@ export const useCanvasDragging = ( const onMouseUp = () => { if (isDragging && canvasIsDragging) { if (useAutoLayout) { - const dropInfo: HighlightInfo | undefined = getDropInfo( + const dropInfo: HighlightInfo | undefined = getDropPosition( + snapColumnSpace, + null, { x: currentRectanglesToDraw[0].top, y: currentRectanglesToDraw[0].left, }, - snapColumnSpace, + true, ); if (dropInfo !== undefined) updateChildrenPositions(dropInfo, currentRectanglesToDraw); @@ -436,7 +437,7 @@ export const useCanvasDragging = ( if (useAutoLayout && isCurrentDraggedCanvas) { setTimeout(() => { - selectedHighlight = highlightDropPosition(e, snapColumnSpace); + selectedHighlight = getDropPosition(snapColumnSpace, e); }, 50); } } diff --git a/app/client/src/selectors/autoLayoutSelectors.tsx b/app/client/src/selectors/autoLayoutSelectors.tsx index 1fb668fe9299..9e23f3c5cce3 100644 --- a/app/client/src/selectors/autoLayoutSelectors.tsx +++ b/app/client/src/selectors/autoLayoutSelectors.tsx @@ -19,17 +19,6 @@ export const getFlexLayers = (parentId: string) => { }); }; -export const getSiblingCount = (widgetId: string, parentId: string) => { - return createSelector(getFlexLayers(parentId), (flexLayers): number => { - if (!flexLayers) return -1; - const selectedLayer = flexLayers?.find((layer: FlexLayer) => - layer.children?.some((child: LayerChild) => child.id === widgetId), - ); - if (!selectedLayer) return -1; - return selectedLayer.children?.length; - }); -}; - export const getLayerIndex = (widgetId: string, parentId: string) => { return createSelector( getFlexLayers(parentId), diff --git a/app/client/src/utils/autoLayout/AutoLayoutUtils.ts b/app/client/src/utils/autoLayout/AutoLayoutUtils.ts index 057f2a949ab9..8e71f4c061a4 100644 --- a/app/client/src/utils/autoLayout/AutoLayoutUtils.ts +++ b/app/client/src/utils/autoLayout/AutoLayoutUtils.ts @@ -16,64 +16,6 @@ import { updateWidgetPositions } from "utils/autoLayout/positionUtils"; import { AlignmentColumnInfo } from "./autoLayoutTypes"; import { getWidgetWidth } from "./flexWidgetUtils"; -function getCanvas(widgets: CanvasWidgetsReduxState, containerId: string) { - const container = widgets[containerId]; - if (!container) return; - let canvas; - // True for MainContainer - if (container.type === "CANVAS_WIDGET") canvas = container; - else { - const canvasId = container.children ? container.children[0] : ""; - canvas = widgets[canvasId]; - } - if (!canvas) return; - return canvas; -} - -export function removeChildLayers( - allWidgets: CanvasWidgetsReduxState, - containerId: string, -): CanvasWidgetsReduxState { - const widgets = { ...allWidgets }; - let canvas = getCanvas(widgets, containerId); - if (!canvas) return widgets; - canvas = { ...canvas, flexLayers: [] }; - widgets[canvas.widgetId] = canvas; - return widgets; -} - -export function* wrapChildren( - allWidgets: CanvasWidgetsReduxState, - containerId: string, - isMobile?: boolean, -) { - const widgets = { ...allWidgets }; - let canvas = getCanvas(widgets, containerId); - if (!canvas) return widgets; - - const children = canvas.children || []; - if (!children.length) return widgets; - - const flexLayers: FlexLayer[] = []; - - for (const each of children) { - const child = widgets[each]; - if (!child) continue; - flexLayers.push({ - children: [{ id: child.widgetId, align: FlexLayerAlignment.Start }], - }); - } - canvas = { ...canvas, flexLayers }; - widgets[canvas.widgetId] = canvas; - // update size - const updatedWidgets = updateWidgetPositions( - widgets, - canvas.widgetId, - isMobile, - ); - return updatedWidgets; -} - export function updateFlexLayersOnDelete( allWidgets: CanvasWidgetsReduxState, widgetId: string, @@ -133,33 +75,6 @@ export function updateFlexLayersOnDelete( return updateWidgetPositions(widgets, parentId, isMobile); } -export function updateFillChildStatus( - allWidgets: CanvasWidgetsReduxState, - widgetId: string, - fill: boolean, - isMobile: boolean, -) { - let widgets = { ...allWidgets }; - const widget = widgets[widgetId]; - if (!widget || !widget.parentId) return widgets; - const canvas = getCanvas(widgets, widget.parentId); - if (!canvas) return widgets; - - const flexLayers: FlexLayer[] = canvas.flexLayers || []; - if (!flexLayers.length) return widgets; - widgets = { - ...widgets, - [widgetId]: { - ...widget, - ResponsiveBehavior: fill - ? ResponsiveBehavior.Fill - : ResponsiveBehavior.Hug, - }, - }; - - return updateWidgetPositions(widgets, canvas.widgetId, isMobile); -} - export function alterLayoutForMobile( allWidgets: CanvasWidgetsReduxState, parentId: string, @@ -177,17 +92,21 @@ export function alterLayoutForMobile( for (const child of children) { const widget = { ...widgets[child] }; if (widget.responsiveBehavior === ResponsiveBehavior.Fill) { - widget.mobileRightColumn = 64; + widget.mobileRightColumn = GridDefaults.DEFAULT_GRID_COLUMNS; widget.mobileLeftColumn = 0; } else if ( widget.responsiveBehavior === ResponsiveBehavior.Hug && widget.minWidth ) { const { minWidth, rightColumn } = widget; - const columnSpace = (canvasWidth - FLEXBOX_PADDING * 2) / 64; + const columnSpace = + (canvasWidth - FLEXBOX_PADDING * 2) / GridDefaults.DEFAULT_GRID_COLUMNS; if (columnSpace * rightColumn < minWidth) { widget.mobileLeftColumn = 0; - widget.mobileRightColumn = Math.min(minWidth / columnSpace, 64); + widget.mobileRightColumn = Math.min( + minWidth / columnSpace, + GridDefaults.DEFAULT_GRID_COLUMNS, + ); } } widget.mobileTopRow = widget.topRow; @@ -195,7 +114,8 @@ export function alterLayoutForMobile( widgets = alterLayoutForMobile( widgets, child, - (canvasWidth * (widget.mobileRightColumn || 1)) / 64, + (canvasWidth * (widget.mobileRightColumn || 1)) / + GridDefaults.DEFAULT_GRID_COLUMNS, ); widgets[child] = widget; widgets = updateWidgetPositions(widgets, child, true); diff --git a/app/client/src/utils/autoLayout/autoLayoutDraggingUtils.ts b/app/client/src/utils/autoLayout/autoLayoutDraggingUtils.ts index 7b87680fc5ff..374c0f01dcf0 100644 --- a/app/client/src/utils/autoLayout/autoLayoutDraggingUtils.ts +++ b/app/client/src/utils/autoLayout/autoLayoutDraggingUtils.ts @@ -85,7 +85,7 @@ export function updateRelationships( if (prevParentId !== undefined) { prevParents.push(prevParentId); const prevParent = Object.assign({}, widgets[prevParentId]); - if (prevParent && prevParent.children && isArray(prevParent.children)) { + if (isArray(prevParent.children)) { const updatedPrevParent = { ...prevParent, children: onlyUpdateFlexLayers diff --git a/app/client/src/utils/autoLayout/highlightSelectionUtils.ts b/app/client/src/utils/autoLayout/highlightSelectionUtils.ts index 126bf551248b..be8e44975192 100644 --- a/app/client/src/utils/autoLayout/highlightSelectionUtils.ts +++ b/app/client/src/utils/autoLayout/highlightSelectionUtils.ts @@ -118,5 +118,5 @@ function calculateDistance(a: HighlightInfo, b: Point): number { distX = 0; } } - return Math.abs(Math.sqrt(distX * distX + distY * distY)); + return Math.hypot(distX, distY); } diff --git a/app/client/src/widgets/ContainerWidget/component/index.tsx b/app/client/src/widgets/ContainerWidget/component/index.tsx index 62028b9b12dd..c20be3ff957a 100644 --- a/app/client/src/widgets/ContainerWidget/component/index.tsx +++ b/app/client/src/widgets/ContainerWidget/component/index.tsx @@ -24,7 +24,7 @@ const StyledContainerComponent = styled.div< width: 100%; overflow: hidden; ${(props) => (!!props.dropDisabled ? `position: relative;` : ``)} - + ${(props) => props.shouldScrollContents && !props.$noScroll ? scrollCSS : ``} opacity: ${(props) => (props.resizeDisabled ? "0.8" : "1")}; @@ -41,15 +41,6 @@ const StyledContainerComponent = styled.div< z-index: ${(props) => (props.onClickCapture ? "2" : "1")}; cursor: ${(props) => (props.onClickCapture ? "pointer" : "inherit")}; } - - .auto-temp-no-display { - position: absolute; - left: -9999px; - } - - .no-display { - display: none; - } `; interface ContainerWrapperProps { diff --git a/app/client/src/widgets/ContainerWidget/widget/index.tsx b/app/client/src/widgets/ContainerWidget/widget/index.tsx index 65ecc4cf2a99..b90fb7c1408b 100644 --- a/app/client/src/widgets/ContainerWidget/widget/index.tsx +++ b/app/client/src/widgets/ContainerWidget/widget/index.tsx @@ -151,10 +151,6 @@ export class ContainerWidget extends BaseWidget< return {}; } - componentDidMount(): void { - super.componentDidMount(); - } - static getStylesheetConfig(): Stylesheet { return { borderRadius: "{{appsmith.theme.borderRadius.appBorderRadius}}", From 6620e47cb895c9e2d1cde05e801a36bf07abfab9 Mon Sep 17 00:00:00 2001 From: Aswath K Date: Wed, 1 Mar 2023 12:18:26 +0530 Subject: [PATCH 576/708] updates widget position without causing an eval --- .../src/sagas/AutoLayoutUpdateSagas.tsx | 118 +++++++----------- 1 file changed, 44 insertions(+), 74 deletions(-) diff --git a/app/client/src/sagas/AutoLayoutUpdateSagas.tsx b/app/client/src/sagas/AutoLayoutUpdateSagas.tsx index f98dfeafcac4..a3023226c5fd 100644 --- a/app/client/src/sagas/AutoLayoutUpdateSagas.tsx +++ b/app/client/src/sagas/AutoLayoutUpdateSagas.tsx @@ -18,8 +18,9 @@ import { getIsMobile } from "selectors/mainCanvasSelectors"; import { getCanvasWidth as getMainCanvasWidth } from "selectors/editorSelectors"; import { getWidgetMinMaxDimensionsInPixel } from "utils/autoLayout/flexWidgetUtils"; import { getIsDraggingOrResizing } from "selectors/widgetSelectors"; -// import { diff } from "deep-diff"; -// import { updateMultipleWidgetPropertiesAction } from "actions/controlActions"; +import { updateMultipleWidgetPropertiesAction } from "actions/controlActions"; +import { isEmpty } from "lodash"; +import { mutation_setPropertiesToUpdate } from "./autoHeightSagas/helpers"; export function* updateLayoutForMobileCheckpoint( actionPayload: ReduxAction<{ @@ -113,20 +114,20 @@ function* updateWidgetDimensionsSaga( }); } +/** + * This saga is responsible for updating the bounding box of the widget + * when the widget component get resized internally. + * It also updates the position of other affected widgets as well. + */ function* processAutoLayoutDimensionUpdatesSaga() { const allWidgets: CanvasWidgetsReduxState = yield select(getWidgets); const mainCanvasWidth: number = yield select(getMainCanvasWidth); const isMobile: boolean = yield select(getIsMobile); let widgets = allWidgets; - // const widgetsOld = { ...widgets }; + const widgetsOld = { ...widgets }; const parentIds = new Set(); - // Initialise a list of changes so far. - // This contains a map of widgetIds with their new topRow and bottomRow - const changesSoFar: Record< - string, - { bottomRow?: number; rightColumn?: number } - > = {}; + // Iterate through the batch and update the new dimensions for (const widgetId in autoLayoutWidgetDimensionUpdateBatch) { const { height, width } = autoLayoutWidgetDimensionUpdateBatch[widgetId]; const widget = allWidgets[widgetId]; @@ -142,30 +143,6 @@ function* processAutoLayoutDimensionUpdatesSaga() { isMobile, ); - const newBottomRow = widget.topRow + height / widget.parentRowSpace; - const newRightColumn = widget.leftColumn + width / columnSpace; - - if ( - widget.bottomRow !== newBottomRow || - widget.rightColumn !== newRightColumn - ) { - // console.log( - // "#### change", - // widget.widgetName, - // { width, columnSpace, leftColumn: widget.leftColumn }, - // "rightColumn", - // widget.rightColumn, - // newRightColumn, - // "bottomRow", - // widget.bottomRow, - // newBottomRow, - // ); - changesSoFar[widgetId] = { - bottomRow: newBottomRow, - rightColumn: newRightColumn, - }; - } - widgets = { ...widgets, [widgetId]: { @@ -176,6 +153,7 @@ function* processAutoLayoutDimensionUpdatesSaga() { }; } + // Update the position of all the widgets for (const parentId of parentIds) { widgets = updateWidgetPositions( widgets, @@ -185,54 +163,46 @@ function* processAutoLayoutDimensionUpdatesSaga() { ); } - const widgetsToUpdate: any = []; - for (const changedWidgetId in changesSoFar) { - widgetsToUpdate[changedWidgetId] = [ - { - propertyPath: "topRow", - propertyValue: widgets[changedWidgetId].topRow, - }, - { - propertyPath: "bottomRow", - propertyValue: changesSoFar[changedWidgetId].bottomRow, - }, - { - propertyPath: "leftColumn", - propertyValue: widgets[changedWidgetId].leftColumn, - }, - { - propertyPath: "rightColumn", - propertyValue: changesSoFar[changedWidgetId].rightColumn, - }, - ]; - } - - // const d = diff(widgetsOld, widgets); - - // console.log( - // "#### process", - // { widgetsToUpdate, widgets, widgetsOld }, - // d?.map((x: any) => ({ - // path: `${widgets[x.path[0]]?.widgetName}.${x.path[1]}`, - // lhs: x?.lhs, - // rhs: x?.rhs, - // })), - // ); + let widgetsToUpdate: any = {}; /** - * TODO(aswathkk): Use updateMultipleWidgetPropertiesAction instead of updateAndSaveLayout - * But, using updateMultipleWidgetPropertiesAction is causing following issues - * 1. Highlights are getting broken - * 2. widget's posision are not properly getting updated + * Iterate over all widgets and check if any of their dimensions have changed + * If they have, add them to the list of widgets to update + * Note: We need to iterate through all widgets since changing dimension of one widget might affect the dimensions of other widgets */ + for (const widgetId of Object.keys(widgets)) { + const widget = widgets[widgetId]; + const oldWidget = widgetsOld[widgetId]; + const propertiesToUpdate: Record = {}; + + if (widget.topRow !== oldWidget.topRow) { + propertiesToUpdate["topRow"] = widget.topRow; + } + if (widget.bottomRow !== oldWidget.bottomRow) { + propertiesToUpdate["bottomRow"] = widget.bottomRow; + } + if (widget.leftColumn !== oldWidget.leftColumn) { + propertiesToUpdate["leftColumn"] = widget.leftColumn; + } + if (widget.rightColumn !== oldWidget.rightColumn) { + propertiesToUpdate["rightColumn"] = widget.rightColumn; + } + + if (isEmpty(propertiesToUpdate)) continue; + + widgetsToUpdate = mutation_setPropertiesToUpdate( + widgetsToUpdate, + widgetId, + propertiesToUpdate, + ); + } // 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)); - - // Save the layout - yield put(updateAndSaveLayout(widgets)); + if (!isEmpty(widgetsToUpdate)) { + yield put(updateMultipleWidgetPropertiesAction(widgetsToUpdate)); + } // clear the batch after processing autoLayoutWidgetDimensionUpdateBatch = {}; From 88ad524a4d5fcfc4efdff68232a35dff1f189fe5 Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 1 Mar 2023 12:38:13 +0530 Subject: [PATCH 577/708] address code review comments 2 --- .../appsmith/PositionedContainer.tsx | 9 +--- .../appsmith/autoLayout/AutoLayoutLayer.tsx | 40 ++++-------------- .../appsmith/autoLayout/FlexBoxComponent.tsx | 30 +++++++++----- .../appsmith/autoLayout/FlexComponent.tsx | 2 +- .../Editor/WidgetsEditor/CanvasContainer.tsx | 5 --- .../src/sagas/AutoLayoutUpdateSagas.tsx | 4 +- .../CanvasSagas/AutoLayoutDraggingSagas.ts | 41 +++++++++++++++---- app/client/src/sagas/WidgetSelectUtils.ts | 2 +- app/client/src/selectors/editorSelectors.tsx | 2 +- app/client/src/utils/WidgetFactoryHelpers.ts | 9 +++- 10 files changed, 73 insertions(+), 71 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/PositionedContainer.tsx b/app/client/src/components/designSystems/appsmith/PositionedContainer.tsx index b1642c33d512..86a5010afd67 100644 --- a/app/client/src/components/designSystems/appsmith/PositionedContainer.tsx +++ b/app/client/src/components/designSystems/appsmith/PositionedContainer.tsx @@ -13,8 +13,6 @@ import { useClickToSelectWidget } from "utils/hooks/useClickToSelectWidget"; import { usePositionedContainerZIndex } from "utils/hooks/usePositionedContainerZIndex"; import { useSelector } from "react-redux"; import { snipingModeSelector } from "selectors/editorSelectors"; -import WidgetFactory from "utils/WidgetFactory"; -import { memoize } from "lodash"; import { getIsReflowEffectedSelector, getReflowSelector, @@ -22,6 +20,7 @@ import { import { POSITIONED_WIDGET } from "constants/componentClassNameConstants"; import equal from "fast-deep-equal"; import { widgetTypeClassname } from "widgets/WidgetUtils"; +import { checkIsDropTarget } from "utils/WidgetFactoryHelpers"; const PositionedWidget = styled.div<{ zIndexOnHover: number; @@ -51,12 +50,6 @@ export type PositionedContainerProps = { widgetName: string; }; -export const checkIsDropTarget = memoize(function isDropTarget( - type: WidgetType, -) { - return !!WidgetFactory.widgetConfigMap.get(type)?.isCanvas; -}); - export function PositionedContainer( props: PositionedContainerProps, ref: Ref, diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx index 5bc9371d5ba2..959d20087a31 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx @@ -1,7 +1,7 @@ import React, { ReactNode } from "react"; import styled from "styled-components"; -import { FlexDirection, LayoutDirection } from "utils/autoLayout/constants"; +import { LayoutDirection } from "utils/autoLayout/constants"; /** * 1. Given a direction if should employ flex in perpendicular direction. @@ -24,11 +24,10 @@ export interface AutoLayoutLayerProps { } const LayoutLayerContainer = styled.div<{ - flexDirection: FlexDirection; wrap?: boolean; }>` display: flex; - flex-direction: ${({ flexDirection }) => flexDirection || FlexDirection.Row}; + flex-direction: row; justify-content: flex-start; align-items: flex-start; flex-wrap: ${({ wrap }) => (wrap ? "wrap" : "nowrap")}; @@ -37,12 +36,11 @@ const LayoutLayerContainer = styled.div<{ `; const SubWrapper = styled.div<{ - flexDirection: FlexDirection; wrap?: boolean; }>` flex: ${({ wrap }) => `1 1 ${wrap ? "100" : "33.3333"}%`}; display: flex; - flex-direction: ${({ flexDirection }) => flexDirection || "row"}; + flex-direction: row; align-items: flex-start; align-self: stretch; flex-wrap: ${({ wrap }) => (wrap ? "wrap" : "nowrap")}; @@ -60,47 +58,23 @@ const CenterWrapper = styled(SubWrapper)` justify-content: center; `; -function getFlexDirection(direction: LayoutDirection): FlexDirection { - return direction === LayoutDirection.Horizontal - ? FlexDirection.Row - : FlexDirection.Column; -} - -function getInverseDirection(direction: LayoutDirection): LayoutDirection { - return direction === LayoutDirection.Horizontal - ? LayoutDirection.Vertical - : LayoutDirection.Horizontal; -} - function AutoLayoutLayer(props: AutoLayoutLayerProps) { - const flexDirection = getFlexDirection(getInverseDirection(props.direction)); - return ( - + {props.start} - + {props.center} - + {props.end} ); } -export default React.memo(AutoLayoutLayer); +export default AutoLayoutLayer; diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx index c3d81ebea8cb..3da2ba5b01fe 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx @@ -9,7 +9,7 @@ import { APP_MODE } from "entities/App"; import { useSelector } from "react-redux"; import { getAppMode } from "selectors/entitiesSelector"; import AutoLayoutLayer from "./AutoLayoutLayer"; -import { FLEXBOX_PADDING } from "constants/WidgetConstants"; +import { FLEXBOX_PADDING, GridDefaults } from "constants/WidgetConstants"; import { AlignmentColumnInfo, FlexBoxAlignmentColumnInfo, @@ -32,7 +32,7 @@ export const DEFAULT_HIGHLIGHT_SIZE = 4; function FlexBoxComponent(props: FlexBoxProps) { const direction: LayoutDirection = props.direction || LayoutDirection.Horizontal; - const appMode = useSelector(getAppMode); + const appMode: APP_MODE | undefined = useSelector(getAppMode); const leaveSpaceForWidgetName = appMode === APP_MODE.EDIT; const isMobile: boolean = props.isMobile || false; const alignmentColumnInfo: FlexBoxAlignmentColumnInfo = useSelector( @@ -79,16 +79,21 @@ function FlexBoxComponent(props: FlexBoxProps) { const start = [], center = [], end = []; + let startColumns = 0, + centerColumns = 0, + endColumns = 0; const columnInfo: AlignmentColumnInfo = alignmentColumnInfo[index]; - const startColumns = columnInfo ? columnInfo[FlexLayerAlignment.Start] : 0, - centerColumns = columnInfo ? columnInfo[FlexLayerAlignment.Center] : 0, - endColumns = columnInfo ? columnInfo[FlexLayerAlignment.End] : 0; + if (columnInfo) { + startColumns = columnInfo[FlexLayerAlignment.Start]; + centerColumns = columnInfo[FlexLayerAlignment.Center]; + endColumns = columnInfo[FlexLayerAlignment.End]; + } for (const child of children) { const widget = map[child.id]; - if (child.align === "end") { + if (child.align === FlexLayerAlignment.End) { end.push(widget); - } else if (child.align === "center") { + } else if (child.align === FlexLayerAlignment.Center) { center.push(widget); } else { start.push(widget); @@ -105,10 +110,13 @@ function FlexBoxComponent(props: FlexBoxProps) { key={index} start={start} widgetId={props.widgetId} - wrapCenter={centerColumns > 64} - wrapEnd={endColumns > 64} - wrapLayer={startColumns + centerColumns + endColumns > 64} - wrapStart={startColumns > 64} + wrapCenter={centerColumns > GridDefaults.DEFAULT_GRID_COLUMNS} + wrapEnd={endColumns > GridDefaults.DEFAULT_GRID_COLUMNS} + wrapLayer={ + startColumns + centerColumns + endColumns > + GridDefaults.DEFAULT_GRID_COLUMNS + } + wrapStart={startColumns > GridDefaults.DEFAULT_GRID_COLUMNS} /> ); } diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx index 3133dadc3f50..448bfc015f78 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx @@ -12,8 +12,8 @@ import { } from "utils/autoLayout/constants"; import { useClickToSelectWidget } from "utils/hooks/useClickToSelectWidget"; import { usePositionedContainerZIndex } from "utils/hooks/usePositionedContainerZIndex"; -import { checkIsDropTarget } from "../PositionedContainer"; import { widgetTypeClassname } from "widgets/WidgetUtils"; +import { checkIsDropTarget } from "utils/WidgetFactoryHelpers"; export type AutoLayoutProps = { children: ReactNode; diff --git a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx index 32cba3accab7..49ce826224db 100644 --- a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx +++ b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx @@ -34,8 +34,6 @@ import { useDynamicAppLayout, } from "utils/hooks/useDynamicAppLayout"; import useGoogleFont from "utils/hooks/useGoogleFont"; -// import { noop } from "utils/AppsmithUtils"; -// import useHorizontalResize from "utils/hooks/useHorizontalResize"; import { layoutConfigurations } from "constants/WidgetConstants"; import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; import Canvas from "../Canvas"; @@ -284,8 +282,5 @@ function CanvasContainer() { ); } -CanvasContainer.whyDidYouRender = { - logOnDifferentValues: true, -}; export default CanvasContainer; diff --git a/app/client/src/sagas/AutoLayoutUpdateSagas.tsx b/app/client/src/sagas/AutoLayoutUpdateSagas.tsx index 9fa2e06f83d7..ca4a3f0846da 100644 --- a/app/client/src/sagas/AutoLayoutUpdateSagas.tsx +++ b/app/client/src/sagas/AutoLayoutUpdateSagas.tsx @@ -10,7 +10,7 @@ import { all, put, select, takeLatest } from "redux-saga/effects"; import { alterLayoutForDesktop, alterLayoutForMobile, -} from "../utils/autoLayout/AutoLayoutUtils"; +} from "utils/autoLayout/AutoLayoutUtils"; import { getWidgets } from "./selectors"; export function* updateLayoutForMobileCheckpoint( @@ -29,7 +29,7 @@ export function* updateLayoutForMobileCheckpoint( : alterLayoutForDesktop(allWidgets, parentId); yield put(updateAndSaveLayout(updatedWidgets)); log.debug( - "updating layout for mobile viewport took", + "Auto Layout : updating layout for mobile viewport took", performance.now() - start, "ms", ); diff --git a/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts b/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts index 7f7c28e9d816..b9b56b5f247e 100644 --- a/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts +++ b/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts @@ -1,13 +1,17 @@ import { updateAndSaveLayout, WidgetAddChild } from "actions/pageActions"; import { ReduxAction, + ReduxActionErrorTypes, ReduxActionTypes, } from "ce/constants/ReduxActionConstants"; import { FlexLayerAlignment, LayoutDirection, } from "utils/autoLayout/constants"; -import { GridDefaults } from "constants/WidgetConstants"; +import { + GridDefaults, + MAIN_CONTAINER_WIDGET_ID, +} from "constants/WidgetConstants"; import log from "loglevel"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; import { all, call, put, select, takeLatest } from "redux-saga/effects"; @@ -62,9 +66,19 @@ function* addWidgetAndReorderSaga( ); yield put(updateAndSaveLayout(updatedWidgetsOnMove)); - log.debug("reorder computations took", performance.now() - start, "ms"); - } catch (e) { - // console.error(e); + log.debug( + "Auto Layout : add new widget took", + performance.now() - start, + "ms", + ); + } catch (error) { + yield put({ + type: ReduxActionErrorTypes.WIDGET_OPERATION_ERROR, + payload: { + action: ReduxActionTypes.AUTOLAYOUT_ADD_NEW_WIDGETS, + error, + }, + }); } } @@ -108,9 +122,19 @@ function* autoLayoutReorderSaga( ); yield put(updateAndSaveLayout(updatedWidgets)); - log.debug("reorder computations took", performance.now() - start, "ms"); - } catch (e) { - // console.error(e); + log.debug( + "Auto Layout : reorder computations took", + performance.now() - start, + "ms", + ); + } catch (error) { + yield put({ + type: ReduxActionErrorTypes.WIDGET_OPERATION_ERROR, + payload: { + action: ReduxActionTypes.AUTOLAYOUT_REORDER_WIDGETS, + error, + }, + }); } } @@ -203,7 +227,8 @@ function* reorderAutolayoutChildren(params: { ...newItems.slice(pos), ], }; - const parentWidget = allWidgets[allWidgets[parentId].parentId || "0"]; + const parentWidget = + allWidgets[allWidgets[parentId].parentId || MAIN_CONTAINER_WIDGET_ID]; const isAutoLayoutContainerCanvas = parentWidget.type === "CONTAINER_WIDGET"; if (isAutoLayoutContainerCanvas) { const height = diff --git a/app/client/src/sagas/WidgetSelectUtils.ts b/app/client/src/sagas/WidgetSelectUtils.ts index d874e02c1ec9..e6fbe555217a 100644 --- a/app/client/src/sagas/WidgetSelectUtils.ts +++ b/app/client/src/sagas/WidgetSelectUtils.ts @@ -4,7 +4,6 @@ import { ReduxActionErrorTypes, ReduxActionTypes, } from "ce/constants/ReduxActionConstants"; -import { checkIsDropTarget } from "components/designSystems/appsmith/PositionedContainer"; import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; import { Toaster, Variant } from "design-system-old"; import { uniq } from "lodash"; @@ -21,6 +20,7 @@ import { import { getWidgetChildrenIds } from "sagas/WidgetOperationUtils"; import { getLastSelectedWidget, getSelectedWidgets } from "selectors/ui"; import WidgetFactory from "utils/WidgetFactory"; +import { checkIsDropTarget } from "utils/WidgetFactoryHelpers"; /** * Selection types that are possible for widget select diff --git a/app/client/src/selectors/editorSelectors.tsx b/app/client/src/selectors/editorSelectors.tsx index 23f35d1cf94c..eb53721dc71a 100644 --- a/app/client/src/selectors/editorSelectors.tsx +++ b/app/client/src/selectors/editorSelectors.tsx @@ -40,11 +40,11 @@ import { createLoadingWidget, } from "utils/widgetRenderUtils"; import { ContainerWidgetProps } from "widgets/ContainerWidget/widget"; -import { checkIsDropTarget } from "components/designSystems/appsmith/PositionedContainer"; import { LOCAL_STORAGE_KEYS } from "utils/localStorage"; import { CanvasWidgetStructure } from "widgets/constants"; import { denormalize } from "utils/canvasStructureHelpers"; import { isAutoHeightEnabledForWidget } from "widgets/WidgetUtils"; +import { checkIsDropTarget } from "utils/WidgetFactoryHelpers"; const getIsDraggingOrResizing = (state: AppState) => state.ui.widgetDragResize.isResizing || state.ui.widgetDragResize.isDragging; diff --git a/app/client/src/utils/WidgetFactoryHelpers.ts b/app/client/src/utils/WidgetFactoryHelpers.ts index 79a188cee2f7..e0970ac10e90 100644 --- a/app/client/src/utils/WidgetFactoryHelpers.ts +++ b/app/client/src/utils/WidgetFactoryHelpers.ts @@ -4,9 +4,10 @@ import { PropertyPaneSectionConfig, } from "constants/PropertyControlConstants"; import { ValidationTypes } from "constants/WidgetValidation"; +import { memoize } from "lodash"; import log from "loglevel"; import { generateReactKey } from "./generators"; -import { WidgetType } from "./WidgetFactory"; +import WidgetFactory, { WidgetType } from "./WidgetFactory"; import { PropertyPaneConfigTemplates, RegisteredWidgetFeatures, @@ -272,3 +273,9 @@ export function convertFunctionsToString(config: PropertyPaneConfig[]) { return sectionOrControlConfig; }); } + +export const checkIsDropTarget = memoize(function isDropTarget( + type: WidgetType, +) { + return !!WidgetFactory.widgetConfigMap.get(type)?.isCanvas; +}); From 6435c5d0ce8ce3031bb072c28679bdc9af452239 Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 1 Mar 2023 16:03:43 +0530 Subject: [PATCH 578/708] undo resize handles ui --- .../ResizeStyledComponents.autolayout.tsx | 188 ++++++++++++++++++ .../ResizeStyledComponents.tsx | 74 ++----- app/client/src/resizable/resize/index.tsx | 4 +- .../src/resizable/resizenreflow/index.tsx | 173 ++++++++-------- 4 files changed, 303 insertions(+), 136 deletions(-) create mode 100644 app/client/src/components/editorComponents/ResizeStyledComponents.autolayout.tsx diff --git a/app/client/src/components/editorComponents/ResizeStyledComponents.autolayout.tsx b/app/client/src/components/editorComponents/ResizeStyledComponents.autolayout.tsx new file mode 100644 index 000000000000..6f95de4ec2b3 --- /dev/null +++ b/app/client/src/components/editorComponents/ResizeStyledComponents.autolayout.tsx @@ -0,0 +1,188 @@ +import { Colors } from "constants/Colors"; +import { invisible } from "constants/DefaultTheme"; +import { WIDGET_PADDING } from "constants/WidgetConstants"; +import styled, { css } from "styled-components"; + +const EDGE_RESIZE_HANDLE_WIDTH = 12; +const CORNER_RESIZE_HANDLE_WIDTH = 10; + +export const VisibilityContainer = styled.div<{ + visible: boolean; + padding: number; + reduceOpacity: boolean; +}>` + ${(props) => (!props.visible ? invisible : "")} + height: 100%; + width: 100%; + ${({ reduceOpacity }) => + reduceOpacity && + css` + opacity: 0.25; + `} +`; + +const VerticalResizeIndicators = css<{ + showLightBorder: boolean; + isHovered: boolean; +}>` + &::after { + position: absolute; + content: ""; + width: 7px; + height: 16px; + border-radius: 50%/16%; + background: ${Colors.GREY_1}; + top: calc(50% - 8px); + left: calc(50% - 2.5px); + border: ${(props) => { + return `1px solid ${props.isHovered ? Colors.WATUSI : "#F86A2B"}`; + }}; + outline: 1px solid ${Colors.GREY_1}; + } + &:hover::after { + background: #f86a2b; + } +`; + +const HorizontalResizeIndicators = css<{ + showLightBorder: boolean; + isHovered: boolean; +}>` + &::after { + position: absolute; + content: ""; + width: 16px; + height: 7px; + border-radius: 16%/50%; + border: ${(props) => { + return `1px solid ${props.isHovered ? Colors.WATUSI : "#F86A2B"}`; + }}; + background: ${Colors.GREY_1}; + top: calc(50% - 2.5px); + left: calc(50% - 8px); + outline: 1px solid ${Colors.GREY_1}; + } + &:hover::after { + background: #f86a2b; + } +`; + +export const EdgeHandleStyles = css<{ + showAsBorder: boolean; + showLightBorder: boolean; + disableDot: boolean; + isHovered: boolean; +}>` + position: absolute; + width: ${EDGE_RESIZE_HANDLE_WIDTH}px; + height: ${EDGE_RESIZE_HANDLE_WIDTH}px; + &::before { + position: absolute; + background: "transparent"; + content: ""; + } +`; + +export const VerticalHandleStyles = css<{ + showAsBorder: boolean; + showLightBorder: boolean; + disableDot: boolean; + isHovered: boolean; +}>` + ${EdgeHandleStyles} + ${(props) => + props.showAsBorder || props.disableDot ? "" : VerticalResizeIndicators} + top:${~(WIDGET_PADDING - 1) + 1}px; + height: calc(100% + ${2 * WIDGET_PADDING - 1}px); + ${(props) => + props.showAsBorder || props.disableDot ? "" : "cursor: col-resize;"} + &:before { + left: 50%; + bottom: 0px; + top: 0; + width: 1px; + } +`; + +export const HorizontalHandleStyles = css<{ + showAsBorder: boolean; + showLightBorder: boolean; + disableDot: boolean; + isHovered: boolean; +}>` + ${EdgeHandleStyles} + ${(props) => + props.showAsBorder || props.disableDot ? "" : HorizontalResizeIndicators} + left: ${~WIDGET_PADDING + 1}px; + width: calc(100% + ${2 * WIDGET_PADDING}px); + ${(props) => + props.showAsBorder || props.disableDot ? "" : "cursor: row-resize;"} + &:before { + top: 50%; + right: 0px; + left: 0px; + height: 1px; + } +`; + +export const LeftHandleStyles = styled.div` + ${VerticalHandleStyles} + left: ${-EDGE_RESIZE_HANDLE_WIDTH / 2 - WIDGET_PADDING + 1}px; +`; + +export const RightHandleStyles = styled.div` + ${VerticalHandleStyles}; + right: ${-EDGE_RESIZE_HANDLE_WIDTH / 2 - WIDGET_PADDING + 3}px; + height: calc(100% + ${2 * WIDGET_PADDING}px); +`; + +export const TopHandleStyles = styled.div` + ${HorizontalHandleStyles}; + top: ${-EDGE_RESIZE_HANDLE_WIDTH / 2 - WIDGET_PADDING + 1}px; +`; + +export const BottomHandleStyles = styled.div` + ${HorizontalHandleStyles}; + bottom: ${-EDGE_RESIZE_HANDLE_WIDTH / 2 - WIDGET_PADDING + 3}px; +`; + +export const CornerHandleStyles = css` + position: absolute; + z-index: 3; + width: ${CORNER_RESIZE_HANDLE_WIDTH}px; + height: ${CORNER_RESIZE_HANDLE_WIDTH}px; +`; + +export const BottomRightHandleStyles = styled.div<{ + showAsBorder: boolean; +}>` + ${CornerHandleStyles}; + bottom: -${CORNER_RESIZE_HANDLE_WIDTH / 2}px; + right: -${CORNER_RESIZE_HANDLE_WIDTH / 2}px; + ${(props) => (!props.showAsBorder ? "cursor: se-resize;" : "")} +`; + +export const BottomLeftHandleStyles = styled.div<{ + showAsBorder: boolean; +}>` + ${CornerHandleStyles}; + left: -${CORNER_RESIZE_HANDLE_WIDTH / 2}px; + bottom: -${CORNER_RESIZE_HANDLE_WIDTH / 2}px; + ${(props) => (!props.showAsBorder ? "cursor: sw-resize;" : "")} +`; +export const TopLeftHandleStyles = styled.div<{ + showAsBorder: boolean; +}>` + ${CornerHandleStyles}; + left: -${CORNER_RESIZE_HANDLE_WIDTH / 2}px; + top: -${CORNER_RESIZE_HANDLE_WIDTH / 2}px; + ${(props) => (!props.showAsBorder ? "cursor: nw-resize;" : "")} +`; +export const TopRightHandleStyles = styled.div<{ + showAsBorder: boolean; +}>` + ${CornerHandleStyles}; + right: -${CORNER_RESIZE_HANDLE_WIDTH / 2}px; + top: -${CORNER_RESIZE_HANDLE_WIDTH / 2}px; + ${(props) => (!props.showAsBorder ? "cursor: ne-resize;" : "")} +`; diff --git a/app/client/src/components/editorComponents/ResizeStyledComponents.tsx b/app/client/src/components/editorComponents/ResizeStyledComponents.tsx index 6f95de4ec2b3..ceb3361323bd 100644 --- a/app/client/src/components/editorComponents/ResizeStyledComponents.tsx +++ b/app/client/src/components/editorComponents/ResizeStyledComponents.tsx @@ -1,5 +1,4 @@ -import { Colors } from "constants/Colors"; -import { invisible } from "constants/DefaultTheme"; +import { invisible, theme } from "constants/DefaultTheme"; import { WIDGET_PADDING } from "constants/WidgetConstants"; import styled, { css } from "styled-components"; @@ -21,49 +20,21 @@ export const VisibilityContainer = styled.div<{ `} `; -const VerticalResizeIndicators = css<{ +const ResizeIndicatorStyle = css<{ showLightBorder: boolean; - isHovered: boolean; }>` &::after { position: absolute; content: ""; - width: 7px; - height: 16px; - border-radius: 50%/16%; - background: ${Colors.GREY_1}; - top: calc(50% - 8px); - left: calc(50% - 2.5px); - border: ${(props) => { - return `1px solid ${props.isHovered ? Colors.WATUSI : "#F86A2B"}`; - }}; - outline: 1px solid ${Colors.GREY_1}; - } - &:hover::after { - background: #f86a2b; - } -`; - -const HorizontalResizeIndicators = css<{ - showLightBorder: boolean; - isHovered: boolean; -}>` - &::after { - position: absolute; - content: ""; - width: 16px; - height: 7px; - border-radius: 16%/50%; - border: ${(props) => { - return `1px solid ${props.isHovered ? Colors.WATUSI : "#F86A2B"}`; - }}; - background: ${Colors.GREY_1}; - top: calc(50% - 2.5px); - left: calc(50% - 8px); - outline: 1px solid ${Colors.GREY_1}; - } - &:hover::after { - background: #f86a2b; + width: 6px; + height: 6px; + border-radius: 50%; + background: ${(props) => + props.showLightBorder + ? theme.colors.widgetLightBorder + : theme.colors.widgetBorder}; + top: calc(50% - 2px); + left: calc(50% - 2px); } `; @@ -71,27 +42,29 @@ export const EdgeHandleStyles = css<{ showAsBorder: boolean; showLightBorder: boolean; disableDot: boolean; - isHovered: boolean; }>` position: absolute; width: ${EDGE_RESIZE_HANDLE_WIDTH}px; height: ${EDGE_RESIZE_HANDLE_WIDTH}px; &::before { position: absolute; - background: "transparent"; + background: ${(props) => { + if (props.showLightBorder) return theme.colors.widgetLightBorder; + if (props.showAsBorder) return theme.colors.widgetMultiSelectBorder; + return theme.colors.widgetBorder; + }}; content: ""; } + ${(props) => + props.showAsBorder || props.disableDot ? "" : ResizeIndicatorStyle} `; export const VerticalHandleStyles = css<{ showAsBorder: boolean; showLightBorder: boolean; disableDot: boolean; - isHovered: boolean; }>` ${EdgeHandleStyles} - ${(props) => - props.showAsBorder || props.disableDot ? "" : VerticalResizeIndicators} top:${~(WIDGET_PADDING - 1) + 1}px; height: calc(100% + ${2 * WIDGET_PADDING - 1}px); ${(props) => @@ -108,11 +81,8 @@ export const HorizontalHandleStyles = css<{ showAsBorder: boolean; showLightBorder: boolean; disableDot: boolean; - isHovered: boolean; }>` ${EdgeHandleStyles} - ${(props) => - props.showAsBorder || props.disableDot ? "" : HorizontalResizeIndicators} left: ${~WIDGET_PADDING + 1}px; width: calc(100% + ${2 * WIDGET_PADDING}px); ${(props) => @@ -127,23 +97,23 @@ export const HorizontalHandleStyles = css<{ export const LeftHandleStyles = styled.div` ${VerticalHandleStyles} - left: ${-EDGE_RESIZE_HANDLE_WIDTH / 2 - WIDGET_PADDING + 1}px; + left: ${-EDGE_RESIZE_HANDLE_WIDTH / 2 - WIDGET_PADDING}px; `; export const RightHandleStyles = styled.div` ${VerticalHandleStyles}; - right: ${-EDGE_RESIZE_HANDLE_WIDTH / 2 - WIDGET_PADDING + 3}px; + right: ${-EDGE_RESIZE_HANDLE_WIDTH / 2 - WIDGET_PADDING + 1}px; height: calc(100% + ${2 * WIDGET_PADDING}px); `; export const TopHandleStyles = styled.div` ${HorizontalHandleStyles}; - top: ${-EDGE_RESIZE_HANDLE_WIDTH / 2 - WIDGET_PADDING + 1}px; + top: ${-EDGE_RESIZE_HANDLE_WIDTH / 2 - WIDGET_PADDING}px; `; export const BottomHandleStyles = styled.div` ${HorizontalHandleStyles}; - bottom: ${-EDGE_RESIZE_HANDLE_WIDTH / 2 - WIDGET_PADDING + 3}px; + bottom: ${-EDGE_RESIZE_HANDLE_WIDTH / 2 - WIDGET_PADDING}px; `; export const CornerHandleStyles = css` diff --git a/app/client/src/resizable/resize/index.tsx b/app/client/src/resizable/resize/index.tsx index 0937906fc98c..38038932eab0 100644 --- a/app/client/src/resizable/resize/index.tsx +++ b/app/client/src/resizable/resize/index.tsx @@ -337,8 +337,8 @@ export const Resizable = function Resizable(props: ResizableProps) { {props.children} diff --git a/app/client/src/resizable/resizenreflow/index.tsx b/app/client/src/resizable/resizenreflow/index.tsx index c0dfeb9baa74..94f90adce078 100644 --- a/app/client/src/resizable/resizenreflow/index.tsx +++ b/app/client/src/resizable/resizenreflow/index.tsx @@ -1,7 +1,6 @@ -import { reflowMoveAction, stopReflowAction } from "actions/reflowActions"; +import { stopReflowAction } from "actions/reflowActions"; import { isHandleResizeAllowed } from "components/editorComponents/ResizableUtils"; import { OccupiedSpace } from "constants/CanvasEditorConstants"; -import { Colors } from "constants/Colors"; import { GridDefaults, WidgetHeightLimits, @@ -17,7 +16,6 @@ import { MovementLimitMap, ReflowDirection, ReflowedSpace, - ReflowedSpaceMap, } from "reflow/reflowTypes"; import { getWidgets } from "sagas/selectors"; import { @@ -26,10 +24,6 @@ import { } from "selectors/editorSelectors"; import { getReflowSelector } from "selectors/widgetReflowSelectors"; import styled, { StyledComponent } from "styled-components"; -import { - getFillWidgetLengthForLayer, - getLayerIndexOfWidget, -} from "utils/autoLayout/AutoLayoutUtils"; import { LayoutDirection, ResponsiveBehavior, @@ -40,42 +34,55 @@ import PerformanceTracker, { PerformanceTransactionName, } from "utils/PerformanceTracker"; import { isDropZoneOccupied } from "utils/WidgetPropsUtils"; -const resizeBorderPadding = 1; -const resizeBorder = 1; -const resizeBoxShadow = 1; -const resizeOutline = 1; - -export const RESIZE_BORDER_BUFFER = - resizeBorderPadding + resizeBorder + resizeBoxShadow + resizeOutline; -export const ResizeWrapper = styled(animated.div)<{ - $prevents: boolean; - isHovered: boolean; - showBoundaries: boolean; -}>` +// TODO: Preet / Ashok: The following section has been commented out to facilitate the first merge to release. +// The commented out code will be used in the next iteration for the public release. + +// const resizeBorderPadding = 1; +// const resizeBorder = 1; +// const resizeBoxShadow = 1; +// const resizeOutline = 1; + +// export const RESIZE_BORDER_BUFFER = +// resizeBorderPadding + resizeBorder + resizeBoxShadow + resizeOutline; + +// export const ResizeWrapper = styled(animated.div)<{ +// $prevents: boolean; +// isHovered: boolean; +// showBoundaries: boolean; +// }>` +// display: block; +// & { +// * { +// pointer-events: ${(props) => !props.$prevents && "none"}; +// } +// } +// ${(props) => { +// if (props.showBoundaries) { +// return ` +// box-shadow: 0px 0px 0px ${resizeBoxShadow}px ${ +// props.isHovered ? Colors.WATUSI : "#f86a2b" +// }; +// border-radius: 0px 4px 4px 4px; +// border: ${resizeBorder}px solid ${Colors.GREY_1}; +// padding: ${resizeBorderPadding}px; +// outline: ${resizeOutline}px solid ${Colors.GREY_1} !important; +// outline-offset: 1px;`; +// } else { +// return ` +// border: 0px solid transparent; +// `; +// } +// }}} +// `; + +export const ResizeWrapper = styled(animated.div)<{ $prevents: boolean }>` display: block; & { * { pointer-events: ${(props) => !props.$prevents && "none"}; } } - ${(props) => { - if (props.showBoundaries) { - return ` - box-shadow: 0px 0px 0px ${resizeBoxShadow}px ${ - props.isHovered ? Colors.WATUSI : "#f86a2b" - }; - border-radius: 0px 4px 4px 4px; - border: ${resizeBorder}px solid ${Colors.GREY_1}; - padding: ${resizeBorderPadding}px; - outline: ${resizeOutline}px solid ${Colors.GREY_1} !important; - outline-offset: 1px;`; - } else { - return ` - border: 0px solid transparent; - `; - } - }}} `; const getSnappedValues = ( @@ -297,46 +304,46 @@ export function ReflowResizable(props: ResizableProps) { }); const allWidgets = useSelector(getWidgets); const dispatch = useDispatch(); - const triggerAutoLayoutBasedReflow = (resizedPositions: OccupiedSpace) => { - const { widgetId } = props; - const widget = allWidgets[widgetId]; - if (!widget || !widget.parentId) return; - const parent = allWidgets[widget.parentId]; - if (!parent) return; - const flexLayers = parent.flexLayers; - const layerIndex = getLayerIndexOfWidget(flexLayers, widgetId); - if (layerIndex === -1) return; - const layer = flexLayers[layerIndex]; - const widgets = { - ...allWidgets, - [props.widgetId]: { - ...allWidgets[props.widgetId], - leftColumn: resizedPositions.left, - rightColumn: resizedPositions.right, - topRow: resizedPositions.top, - bottomRow: resizedPositions.bottom, - }, - }; - const fillWidgetsLength = getFillWidgetLengthForLayer(layer, widgets); - if (fillWidgetsLength) { - let correctedMovementMap: ReflowedSpaceMap = {}; - for (const child of layer.children) { - const childWidget = allWidgets[child.id]; - if ( - childWidget && - childWidget.responsiveBehavior === ResponsiveBehavior.Fill - ) { - correctedMovementMap = { - ...correctedMovementMap, - [child.id]: { - width: fillWidgetsLength * widget.parentColumnSpace, - }, - }; - } - } - dispatch(reflowMoveAction(correctedMovementMap)); - } - }; + // const triggerAutoLayoutBasedReflow = (resizedPositions: OccupiedSpace) => { + // const { widgetId } = props; + // const widget = allWidgets[widgetId]; + // if (!widget || !widget.parentId) return; + // const parent = allWidgets[widget.parentId]; + // if (!parent) return; + // const flexLayers = parent.flexLayers; + // const layerIndex = getLayerIndexOfWidget(flexLayers, widgetId); + // if (layerIndex === -1) return; + // const layer = flexLayers[layerIndex]; + // const widgets = { + // ...allWidgets, + // [props.widgetId]: { + // ...allWidgets[props.widgetId], + // leftColumn: resizedPositions.left, + // rightColumn: resizedPositions.right, + // topRow: resizedPositions.top, + // bottomRow: resizedPositions.bottom, + // }, + // }; + // const fillWidgetsLength = getFillWidgetLengthForLayer(layer, widgets); + // if (fillWidgetsLength) { + // let correctedMovementMap: ReflowedSpaceMap = {}; + // for (const child of layer.children) { + // const childWidget = allWidgets[child.id]; + // if ( + // childWidget && + // childWidget.responsiveBehavior === ResponsiveBehavior.Fill + // ) { + // correctedMovementMap = { + // ...correctedMovementMap, + // [child.id]: { + // width: fillWidgetsLength * widget.parentColumnSpace, + // }, + // }; + // } + // } + // dispatch(reflowMoveAction(correctedMovementMap)); + // } + // }; const setNewDimensions = (rect: DimensionProps) => { const { direction, height, width, x, y } = rect; @@ -400,9 +407,9 @@ export function ReflowResizable(props: ResizableProps) { if (bottomMostRow) { props.updateBottomRow(bottomMostRow); } - if (isAutoLayout && resizedPositions) { - triggerAutoLayoutBasedReflow(resizedPositions); - } + // if (isAutoLayout && resizedPositions) { + // triggerAutoLayoutBasedReflow(resizedPositions); + // } return newRect; }); @@ -622,7 +629,9 @@ export function ReflowResizable(props: ResizableProps) { /> ); }); - const bufferForBoundary = props.showResizeBoundary ? RESIZE_BORDER_BUFFER : 0; + // TODO: Uncomment this code after first release. + // const bufferForBoundary = props.showResizeBoundary ? RESIZE_BORDER_BUFFER : 0; + const bufferForBoundary = 0; const widgetWidth = (reflowedPosition?.width === undefined ? newDimensions.width @@ -677,9 +686,9 @@ export function ReflowResizable(props: ResizableProps) { $prevents={pointerEvents} className={props.className} id={`resize-${props.widgetId}`} - isHovered={props.isHovered} + // isHovered={props.isHovered} ref={resizableRef} - showBoundaries={props.showResizeBoundary} + // showBoundaries={props.showResizeBoundary} style={_props} > {props.children} From 2c38633dd676ad5fb3fb5676bf30929429487144 Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 1 Mar 2023 17:08:52 +0530 Subject: [PATCH 579/708] fix list widget v2 --- app/client/src/resizable/resizenreflow/index.tsx | 12 ++++++++---- app/client/src/widgets/ListWidgetV2/index.ts | 6 +++++- app/client/src/widgets/ListWidgetV2/widget/index.tsx | 2 ++ 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/app/client/src/resizable/resizenreflow/index.tsx b/app/client/src/resizable/resizenreflow/index.tsx index 94f90adce078..0403fc0ec37a 100644 --- a/app/client/src/resizable/resizenreflow/index.tsx +++ b/app/client/src/resizable/resizenreflow/index.tsx @@ -236,8 +236,9 @@ type ResizableProps = { export function ReflowResizable(props: ResizableProps) { const resizableRef = useRef(null); const [isResizing, setResizing] = useState(false); - const isAutoLayout = - useSelector(getCurrentAppPositioningType) === AppPositioningTypes.AUTO; + // const isAutoLayout = + // useSelector(getCurrentAppPositioningType) === AppPositioningTypes.AUTO; + const isAutoLayout = props.isFlexChild; const occupiedSpacesBySiblingWidgets = useSelector( getContainerOccupiedSpacesSelectorWhileResizing(props.parentId), ); @@ -431,7 +432,10 @@ export function ReflowResizable(props: ResizableProps) { const handles = []; const widget = allWidgets[props.widgetId]; - if (!(isAutoLayout && widget.leftColumn === 0) && props.handles.left) { + if ( + !(isAutoLayout && widget && widget?.leftColumn === 0) && + props.handles.left + ) { handles.push({ dragCallback: (x: number) => { setNewDimensions({ @@ -468,7 +472,7 @@ export function ReflowResizable(props: ResizableProps) { if ( !( isAutoLayout && - widget.leftColumn !== 0 && + widget?.leftColumn !== 0 && widget.rightColumn === GridDefaults.DEFAULT_GRID_COLUMNS ) && props.handles.right diff --git a/app/client/src/widgets/ListWidgetV2/index.ts b/app/client/src/widgets/ListWidgetV2/index.ts index 9c21bbf16663..40afe42a01b2 100644 --- a/app/client/src/widgets/ListWidgetV2/index.ts +++ b/app/client/src/widgets/ListWidgetV2/index.ts @@ -12,6 +12,7 @@ import { getNumberOfChildListWidget, getNumberOfParentListWidget, } from "./widget/helper"; +import { Positioning, ResponsiveBehavior } from "utils/autoLayout/constants"; const DEFAULT_LIST_DATA = [ { @@ -49,6 +50,8 @@ export const CONFIG = { columns: 24, animateLoading: true, gridType: "vertical", + positioning: Positioning.Fixed, + responsiveBehavior: ResponsiveBehavior.Fill, dynamicBindingPathList: [ { key: "currentItemsView", @@ -121,7 +124,7 @@ export const CONFIG = { isDeletable: false, disallowCopy: true, noContainerOffset: true, - + positioning: Positioning.Fixed, disabledWidgetFeatures: [ RegisteredWidgetFeatures.DYNAMIC_HEIGHT, ], @@ -139,6 +142,7 @@ export const CONFIG = { detachFromLayout: true, children: [], version: 1, + useAutoLayout: false, blueprint: { view: [ { diff --git a/app/client/src/widgets/ListWidgetV2/widget/index.tsx b/app/client/src/widgets/ListWidgetV2/widget/index.tsx index 5749619176c4..b4bbc838b852 100644 --- a/app/client/src/widgets/ListWidgetV2/widget/index.tsx +++ b/app/client/src/widgets/ListWidgetV2/widget/index.tsx @@ -835,7 +835,9 @@ class ListWidget extends BaseWidget< child.parentColumnSpace = parentColumnSpace; child.rightColumn = componentWidth; child.canExtend = true; + child.positioning = this.props.positioning; child.children = child.children?.map((container, viewIndex) => { + container.positioning = this.props.positioning; const rowIndex = viewIndex + startIndex; const focused = this.props.renderMode === RenderModes.CANVAS && rowIndex === 0; From 3dff787712e25f64279f18ee17ca04a2d723fdaf Mon Sep 17 00:00:00 2001 From: Aswath K Date: Wed, 1 Mar 2023 17:28:48 +0530 Subject: [PATCH 580/708] Adds default columns/rows config specific for AutoLayout --- app/client/src/pages/Editor/WidgetCard.tsx | 15 +++++++++++++++ app/client/src/selectors/editorSelectors.tsx | 5 +++++ app/client/src/widgets/BaseWidget.tsx | 2 ++ app/client/src/widgets/ButtonWidget/index.ts | 4 ++++ .../src/widgets/CurrencyInputWidget/index.ts | 3 +++ .../src/widgets/FilePickerWidgetV2/index.ts | 4 ++++ app/client/src/widgets/IconButtonWidget/index.ts | 4 ++++ app/client/src/widgets/InputWidgetV2/index.ts | 3 +++ app/client/src/widgets/MenuButtonWidget/index.ts | 4 ++++ app/client/src/widgets/PhoneInputWidget/index.ts | 3 +++ app/client/src/widgets/constants.ts | 1 + 11 files changed, 48 insertions(+) diff --git a/app/client/src/pages/Editor/WidgetCard.tsx b/app/client/src/pages/Editor/WidgetCard.tsx index 635ce813f1c2..c6e5400555da 100644 --- a/app/client/src/pages/Editor/WidgetCard.tsx +++ b/app/client/src/pages/Editor/WidgetCard.tsx @@ -7,6 +7,9 @@ import { generateReactKey } from "utils/generators"; import { Colors } from "constants/Colors"; import { useWidgetSelection } from "utils/hooks/useWidgetSelection"; import { IconWrapper } from "constants/IconConstants"; +import { useSelector } from "react-redux"; +import { getIsAutoLayout } from "selectors/editorSelectors"; +import WidgetFactory from "utils/WidgetFactory"; type CardProps = { details: WidgetCardProps; @@ -70,6 +73,7 @@ export const IconLabel = styled.h5` function WidgetCard(props: CardProps) { const { setDraggingNewWidget } = useWidgetDragResize(); const { deselectAll } = useWidgetSelection(); + const isAutoLayout = useSelector(getIsAutoLayout); const onDragStart = (e: any) => { e.preventDefault(); @@ -78,9 +82,20 @@ function WidgetCard(props: CardProps) { widgetType: props.details.type, widgetName: props.details.displayName, }); + let rows = props.details.rows; + let columns = props.details.columns; + const autoLayoutConfig = WidgetFactory.getWidgetAutoLayoutConfig( + props.details.type, + ); + if (isAutoLayout && autoLayoutConfig) { + rows = autoLayoutConfig?.defaults?.rows ?? rows; + columns = autoLayoutConfig?.defaults?.columns ?? columns; + } setDraggingNewWidget && setDraggingNewWidget(true, { ...props.details, + columns, + rows, widgetId: generateReactKey(), }); deselectAll(); diff --git a/app/client/src/selectors/editorSelectors.tsx b/app/client/src/selectors/editorSelectors.tsx index 23f35d1cf94c..bbd23d414a7f 100644 --- a/app/client/src/selectors/editorSelectors.tsx +++ b/app/client/src/selectors/editorSelectors.tsx @@ -261,6 +261,11 @@ export const getCurrentAppPositioningType = createSelector( }, ); +export const getIsAutoLayout = createSelector( + getCurrentAppPositioningType, + (positioningType) => positioningType === AppPositioningTypes.AUTO, +); + export const getCurrentApplicationLayout = createSelector( getAppLayout, getCurrentAppPositioningType, diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index 965b9ff83416..d921dab3ac5e 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -847,6 +847,8 @@ export interface WidgetProps } export interface WidgetCardProps { + rows: number; + columns: number; type: WidgetType; key?: string; displayName: string; diff --git a/app/client/src/widgets/ButtonWidget/index.ts b/app/client/src/widgets/ButtonWidget/index.ts index 929437d9c3de..0d07c98e32e6 100644 --- a/app/client/src/widgets/ButtonWidget/index.ts +++ b/app/client/src/widgets/ButtonWidget/index.ts @@ -42,6 +42,10 @@ export const CONFIG = { stylesheetConfig: Widget.getStylesheetConfig(), }, autoLayout: { + defaults: { + rows: 4, + columns: 6.632, + }, widgetSize: [ { viewportMinWidth: 0, diff --git a/app/client/src/widgets/CurrencyInputWidget/index.ts b/app/client/src/widgets/CurrencyInputWidget/index.ts index ced03ace7331..833c17f0b6c9 100644 --- a/app/client/src/widgets/CurrencyInputWidget/index.ts +++ b/app/client/src/widgets/CurrencyInputWidget/index.ts @@ -43,6 +43,9 @@ export const CONFIG = { stylesheetConfig: Widget.getStylesheetConfig(), }, autoLayout: { + defaults: { + rows: 6.6, + }, widgetSize: [ { viewportMinWidth: 0, diff --git a/app/client/src/widgets/FilePickerWidgetV2/index.ts b/app/client/src/widgets/FilePickerWidgetV2/index.ts index 63e3fa8b3f79..ec5c2ba47376 100644 --- a/app/client/src/widgets/FilePickerWidgetV2/index.ts +++ b/app/client/src/widgets/FilePickerWidgetV2/index.ts @@ -40,6 +40,10 @@ export const CONFIG = { stylesheetConfig: Widget.getStylesheetConfig(), }, autoLayout: { + defaults: { + rows: 4, + columns: 6.632, + }, widgetSize: [ { viewportMinWidth: 0, diff --git a/app/client/src/widgets/IconButtonWidget/index.ts b/app/client/src/widgets/IconButtonWidget/index.ts index f43e2bea2bf4..940086853291 100644 --- a/app/client/src/widgets/IconButtonWidget/index.ts +++ b/app/client/src/widgets/IconButtonWidget/index.ts @@ -31,6 +31,10 @@ export const CONFIG = { stylesheetConfig: Widget.getStylesheetConfig(), }, autoLayout: { + defaults: { + rows: 4, + columns: 2.21, + }, widgetSize: [ { viewportMinWidth: 0, diff --git a/app/client/src/widgets/InputWidgetV2/index.ts b/app/client/src/widgets/InputWidgetV2/index.ts index a0ecca638011..db00109a3c8c 100644 --- a/app/client/src/widgets/InputWidgetV2/index.ts +++ b/app/client/src/widgets/InputWidgetV2/index.ts @@ -40,6 +40,9 @@ export const CONFIG = { stylesheetConfig: Widget.getStylesheetConfig(), }, autoLayout: { + defaults: { + rows: 6.6, + }, widgetSize: [ { viewportMinWidth: 0, diff --git a/app/client/src/widgets/MenuButtonWidget/index.ts b/app/client/src/widgets/MenuButtonWidget/index.ts index 95afcf673e31..898eaabfe380 100644 --- a/app/client/src/widgets/MenuButtonWidget/index.ts +++ b/app/client/src/widgets/MenuButtonWidget/index.ts @@ -57,6 +57,10 @@ export const CONFIG = { stylesheetConfig: Widget.getStylesheetConfig(), }, autoLayout: { + defaults: { + rows: 4, + columns: 6.632, + }, widgetSize: [ { viewportMinWidth: 0, diff --git a/app/client/src/widgets/PhoneInputWidget/index.ts b/app/client/src/widgets/PhoneInputWidget/index.ts index 690971ae3287..77dd91c9b926 100644 --- a/app/client/src/widgets/PhoneInputWidget/index.ts +++ b/app/client/src/widgets/PhoneInputWidget/index.ts @@ -42,6 +42,9 @@ export const CONFIG = { stylesheetConfig: Widget.getStylesheetConfig(), }, autoLayout: { + defaults: { + rows: 6.6, + }, widgetSize: [ { viewportMinWidth: 0, diff --git a/app/client/src/widgets/constants.ts b/app/client/src/widgets/constants.ts index 76fb0572a793..2870b0af380c 100644 --- a/app/client/src/widgets/constants.ts +++ b/app/client/src/widgets/constants.ts @@ -22,6 +22,7 @@ export type WidgetSizeConfig = { export type AutoLayoutConfig = { widgetSize: Array; + defaults?: Partial & WidgetConfigProps; }; export interface WidgetConfiguration { From 044bff76210e756f481eaa35ee7d0020b64f8eef Mon Sep 17 00:00:00 2001 From: Aswath K Date: Wed, 1 Mar 2023 17:48:27 +0530 Subject: [PATCH 581/708] fix: Type issue --- app/client/src/widgets/constants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/client/src/widgets/constants.ts b/app/client/src/widgets/constants.ts index 2870b0af380c..91cc43e515c6 100644 --- a/app/client/src/widgets/constants.ts +++ b/app/client/src/widgets/constants.ts @@ -22,7 +22,7 @@ export type WidgetSizeConfig = { export type AutoLayoutConfig = { widgetSize: Array; - defaults?: Partial & WidgetConfigProps; + defaults?: Partial; }; export interface WidgetConfiguration { From 4753244db3dce92ed23de939cd3f95f47a3dd435 Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 1 Mar 2023 18:40:47 +0530 Subject: [PATCH 582/708] fix listwidgetv2 --- app/client/src/resizable/autolayoutresize/index.tsx | 8 ++++++-- app/client/src/widgets/ListWidgetV2/index.ts | 5 ++++- app/client/src/widgets/ListWidgetV2/widget/index.tsx | 1 + 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/app/client/src/resizable/autolayoutresize/index.tsx b/app/client/src/resizable/autolayoutresize/index.tsx index 656c2b5120e9..771f9b95960f 100644 --- a/app/client/src/resizable/autolayoutresize/index.tsx +++ b/app/client/src/resizable/autolayoutresize/index.tsx @@ -33,7 +33,10 @@ import { getFillWidgetLengthForLayer, getLayerIndexOfWidget, } from "utils/autoLayout/AutoLayoutUtils"; -import { ResponsiveBehavior } from "utils/autoLayout/constants"; +import { + FlexLayerAlignment, + ResponsiveBehavior, +} from "utils/autoLayout/constants"; import { useReflow } from "utils/hooks/useReflow"; import PerformanceTracker, { PerformanceTransactionName, @@ -112,7 +115,8 @@ export function ReflowResizable(props: ResizableProps) { reflectPosition: true, }); const allWidgets = useSelector(getWidgets); - const widgetAlignment = allWidgets[props.widgetId].alignment || "start"; + const widgetAlignment = + allWidgets[props.widgetId]?.alignment || FlexLayerAlignment.Start; const dispatch = useDispatch(); const layer = useMemo(() => { const { widgetId } = props; diff --git a/app/client/src/widgets/ListWidgetV2/index.ts b/app/client/src/widgets/ListWidgetV2/index.ts index 9c21bbf16663..ad5049871c40 100644 --- a/app/client/src/widgets/ListWidgetV2/index.ts +++ b/app/client/src/widgets/ListWidgetV2/index.ts @@ -12,6 +12,7 @@ import { getNumberOfChildListWidget, getNumberOfParentListWidget, } from "./widget/helper"; +import { Positioning } from "utils/autoLayout/constants"; const DEFAULT_LIST_DATA = [ { @@ -49,6 +50,7 @@ export const CONFIG = { columns: 24, animateLoading: true, gridType: "vertical", + positioning: Positioning.Fixed, dynamicBindingPathList: [ { key: "currentItemsView", @@ -121,7 +123,7 @@ export const CONFIG = { isDeletable: false, disallowCopy: true, noContainerOffset: true, - + positioning: Positioning.Fixed, disabledWidgetFeatures: [ RegisteredWidgetFeatures.DYNAMIC_HEIGHT, ], @@ -139,6 +141,7 @@ export const CONFIG = { detachFromLayout: true, children: [], version: 1, + useAutoLayout: false, blueprint: { view: [ { diff --git a/app/client/src/widgets/ListWidgetV2/widget/index.tsx b/app/client/src/widgets/ListWidgetV2/widget/index.tsx index 7898643d8419..73bb6853e765 100644 --- a/app/client/src/widgets/ListWidgetV2/widget/index.tsx +++ b/app/client/src/widgets/ListWidgetV2/widget/index.tsx @@ -826,6 +826,7 @@ class ListWidget extends BaseWidget< child.parentColumnSpace = parentColumnSpace; child.rightColumn = componentWidth; child.canExtend = true; + child.positioning = this.props.positioning; child.children = child.children?.map((container, viewIndex) => { const rowIndex = viewIndex + startIndex; const focused = From 381a7bad857e2635edc7ac4cd61c098b0336f9b2 Mon Sep 17 00:00:00 2001 From: Aswath K Date: Wed, 1 Mar 2023 19:45:57 +0530 Subject: [PATCH 583/708] cleanup --- app/client/src/selectors/mainCanvasSelectors.tsx | 10 ---------- app/client/src/utils/autoLayout/flexWidgetUtils.ts | 7 +++++++ app/client/src/utils/autoLayout/positionUtils.ts | 14 -------------- .../src/widgets/BaseInputWidget/widget/index.tsx | 2 +- app/client/src/widgets/BaseWidget.tsx | 1 - .../widget/propertyConfig/contentConfig.ts | 2 +- .../widget/propertyConfig/styleConfig.ts | 2 +- .../widgets/CheckboxGroupWidget/widget/index.tsx | 2 +- .../src/widgets/CheckboxWidget/widget/index.tsx | 2 +- .../src/widgets/DatePickerWidget2/widget/index.tsx | 2 +- .../src/widgets/DividerWidget/widget/index.tsx | 2 +- .../widgets/MultiSelectTreeWidget/widget/index.tsx | 2 +- .../widgets/MultiSelectWidgetV2/widget/index.tsx | 2 +- .../widget/propertyConfig/contentConfig.ts | 2 +- .../widget/propertyConfig/styleConfig.ts | 2 +- .../src/widgets/RadioGroupWidget/widget/index.tsx | 2 +- .../widget/propertyConfig/contentConfig.ts | 2 +- .../widget/propertyConfig/styleConfig.ts | 2 +- app/client/src/widgets/RateWidget/widget/index.tsx | 2 +- .../src/widgets/SelectWidget/widget/index.tsx | 2 +- .../SingleSelectTreeWidget/widget/index.tsx | 2 +- .../src/widgets/SwitchGroupWidget/widget/index.tsx | 2 +- .../src/widgets/SwitchWidget/widget/index.tsx | 2 +- app/client/src/widgets/withWidgetProps.tsx | 3 --- 24 files changed, 26 insertions(+), 47 deletions(-) diff --git a/app/client/src/selectors/mainCanvasSelectors.tsx b/app/client/src/selectors/mainCanvasSelectors.tsx index 156f72b968c7..e79e6c436731 100644 --- a/app/client/src/selectors/mainCanvasSelectors.tsx +++ b/app/client/src/selectors/mainCanvasSelectors.tsx @@ -1,17 +1,7 @@ import { AppState } from "@appsmith/reducers"; -import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; -import store from "store"; export const getIsCanvasInitialized = (state: AppState) => { return state.ui.mainCanvas.initialized; }; export const getIsMobile = (state: AppState) => state.ui.mainCanvas.isMobile; - -export const getUseAutoLayout = (state: AppState) => - state.entities.canvasWidgets[MAIN_CONTAINER_WIDGET_ID]?.useAutoLayout; - -export function isAutoLayout() { - const appState = store.getState(); - return !!getUseAutoLayout(appState); -} diff --git a/app/client/src/utils/autoLayout/flexWidgetUtils.ts b/app/client/src/utils/autoLayout/flexWidgetUtils.ts index ed34bc9f3c54..037265e3cbb5 100644 --- a/app/client/src/utils/autoLayout/flexWidgetUtils.ts +++ b/app/client/src/utils/autoLayout/flexWidgetUtils.ts @@ -1,3 +1,5 @@ +import { getIsAutoLayout } from "selectors/editorSelectors"; +import store from "store"; import WidgetFactory from "utils/WidgetFactory"; import { WidgetSizeConfig } from "widgets/constants"; @@ -209,3 +211,8 @@ export function getWidgetMinMaxDimensionsInPixel( return returnValue; } + +export function isAutoLayout() { + const appState = store.getState(); + return !!getIsAutoLayout(appState); +} diff --git a/app/client/src/utils/autoLayout/positionUtils.ts b/app/client/src/utils/autoLayout/positionUtils.ts index e13dae77868b..895a34550428 100644 --- a/app/client/src/utils/autoLayout/positionUtils.ts +++ b/app/client/src/utils/autoLayout/positionUtils.ts @@ -257,20 +257,6 @@ export function placeWidgetsWithoutWrap( } } - // Trigger a position update for the widgets inside container widgets - // for (const each of arr) { - // for (const widget of each.children) { - // if (widget.type === "CONTAINER_WIDGET" && widget.children?.length) { - // widgets = updateWidgetPositions( - // widgets, - // widget.children[0], - // isMobile, - // mainCanvasWidth, - // ); - // } - // } - // } - return { height: maxHeight, widgets }; } diff --git a/app/client/src/widgets/BaseInputWidget/widget/index.tsx b/app/client/src/widgets/BaseInputWidget/widget/index.tsx index 239ade8badca..1d238bfecc0c 100644 --- a/app/client/src/widgets/BaseInputWidget/widget/index.tsx +++ b/app/client/src/widgets/BaseInputWidget/widget/index.tsx @@ -8,7 +8,7 @@ import { import { WidgetType } from "constants/WidgetConstants"; import { ValidationTypes } from "constants/WidgetValidation"; import React from "react"; -import { isAutoLayout } from "selectors/mainCanvasSelectors"; +import { isAutoLayout } from "utils/autoLayout/flexWidgetUtils"; import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index d921dab3ac5e..4ad1cb97a33d 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -763,7 +763,6 @@ export interface WidgetBaseProps { renderMode: RenderMode; version: number; childWidgets?: DataTreeWidget[]; - mainCanvasWidth?: number; flattenedChildCanvasWidgets?: Record; metaWidgetChildrenStructure?: CanvasWidgetStructure[]; referencedWidgetId?: string; diff --git a/app/client/src/widgets/CategorySliderWidget/widget/propertyConfig/contentConfig.ts b/app/client/src/widgets/CategorySliderWidget/widget/propertyConfig/contentConfig.ts index 850d3014ccba..94d03949d2e9 100644 --- a/app/client/src/widgets/CategorySliderWidget/widget/propertyConfig/contentConfig.ts +++ b/app/client/src/widgets/CategorySliderWidget/widget/propertyConfig/contentConfig.ts @@ -2,8 +2,8 @@ import { Alignment } from "@blueprintjs/core"; import { LabelPosition } from "components/constants"; import { ValidationTypes } from "constants/WidgetValidation"; import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; -import { isAutoLayout } from "selectors/mainCanvasSelectors"; import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService"; +import { isAutoLayout } from "utils/autoLayout/flexWidgetUtils"; import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import { CategorySliderWidgetProps } from ".."; import { diff --git a/app/client/src/widgets/CategorySliderWidget/widget/propertyConfig/styleConfig.ts b/app/client/src/widgets/CategorySliderWidget/widget/propertyConfig/styleConfig.ts index 8410e4f26475..06cbeaddbb77 100644 --- a/app/client/src/widgets/CategorySliderWidget/widget/propertyConfig/styleConfig.ts +++ b/app/client/src/widgets/CategorySliderWidget/widget/propertyConfig/styleConfig.ts @@ -1,5 +1,5 @@ import { ValidationTypes } from "constants/WidgetValidation"; -import { isAutoLayout } from "selectors/mainCanvasSelectors"; +import { isAutoLayout } from "utils/autoLayout/flexWidgetUtils"; export default [ { diff --git a/app/client/src/widgets/CheckboxGroupWidget/widget/index.tsx b/app/client/src/widgets/CheckboxGroupWidget/widget/index.tsx index bb412faef4ad..9603add14118 100644 --- a/app/client/src/widgets/CheckboxGroupWidget/widget/index.tsx +++ b/app/client/src/widgets/CheckboxGroupWidget/widget/index.tsx @@ -22,7 +22,7 @@ import CheckboxGroupComponent from "../component"; import { OptionProps, SelectAllState, SelectAllStates } from "../constants"; import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; -import { isAutoLayout } from "selectors/mainCanvasSelectors"; +import { isAutoLayout } from "utils/autoLayout/flexWidgetUtils"; export function defaultSelectedValuesValidation( value: unknown, diff --git a/app/client/src/widgets/CheckboxWidget/widget/index.tsx b/app/client/src/widgets/CheckboxWidget/widget/index.tsx index 061cb50f9f39..a63dbc073a8a 100644 --- a/app/client/src/widgets/CheckboxWidget/widget/index.tsx +++ b/app/client/src/widgets/CheckboxWidget/widget/index.tsx @@ -4,7 +4,7 @@ import { WidgetType } from "constants/WidgetConstants"; import { ValidationTypes } from "constants/WidgetValidation"; import { Stylesheet } from "entities/AppTheming"; import React from "react"; -import { isAutoLayout } from "selectors/mainCanvasSelectors"; +import { isAutoLayout } from "utils/autoLayout/flexWidgetUtils"; import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; import { AlignWidgetTypes } from "widgets/constants"; diff --git a/app/client/src/widgets/DatePickerWidget2/widget/index.tsx b/app/client/src/widgets/DatePickerWidget2/widget/index.tsx index 2dc842ed234d..bada2e7056d6 100644 --- a/app/client/src/widgets/DatePickerWidget2/widget/index.tsx +++ b/app/client/src/widgets/DatePickerWidget2/widget/index.tsx @@ -17,7 +17,7 @@ import { isAutoHeightEnabledForWidget } from "widgets/WidgetUtils"; import { DatePickerType, TimePrecision } from "../constants"; import { DateFormatOptions } from "./constants"; import derivedProperties from "./parseDerivedProperties"; -import { isAutoLayout } from "selectors/mainCanvasSelectors"; +import { isAutoLayout } from "utils/autoLayout/flexWidgetUtils"; function allowedRange(value: any) { const allowedValues = [0, 1, 2, 3, 4, 5, 6]; diff --git a/app/client/src/widgets/DividerWidget/widget/index.tsx b/app/client/src/widgets/DividerWidget/widget/index.tsx index 14bc77dec2d8..56744c6a57f3 100644 --- a/app/client/src/widgets/DividerWidget/widget/index.tsx +++ b/app/client/src/widgets/DividerWidget/widget/index.tsx @@ -5,7 +5,7 @@ import DividerComponent from "../component"; import { ValidationTypes } from "constants/WidgetValidation"; import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; -import { isAutoLayout } from "selectors/mainCanvasSelectors"; +import { isAutoLayout } from "utils/autoLayout/flexWidgetUtils"; class DividerWidget extends BaseWidget { static getPropertyPaneContentConfig() { diff --git a/app/client/src/widgets/MultiSelectTreeWidget/widget/index.tsx b/app/client/src/widgets/MultiSelectTreeWidget/widget/index.tsx index 3faffe0b7f96..b2e20a4519cb 100644 --- a/app/client/src/widgets/MultiSelectTreeWidget/widget/index.tsx +++ b/app/client/src/widgets/MultiSelectTreeWidget/widget/index.tsx @@ -13,8 +13,8 @@ import { isArray, xor } from "lodash"; import { DefaultValueType } from "rc-tree-select/lib/interface"; import { CheckedStrategy } from "rc-tree-select/lib/utils/strategyUtil"; import React, { ReactNode } from "react"; -import { isAutoLayout } from "selectors/mainCanvasSelectors"; import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService"; +import { isAutoLayout } from "utils/autoLayout/flexWidgetUtils"; import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; import { GRID_DENSITY_MIGRATION_V1, MinimumPopupRows } from "widgets/constants"; diff --git a/app/client/src/widgets/MultiSelectWidgetV2/widget/index.tsx b/app/client/src/widgets/MultiSelectWidgetV2/widget/index.tsx index 3c88c96b4ae0..c5b4542267ee 100644 --- a/app/client/src/widgets/MultiSelectWidgetV2/widget/index.tsx +++ b/app/client/src/widgets/MultiSelectWidgetV2/widget/index.tsx @@ -13,8 +13,8 @@ import equal from "fast-deep-equal/es6"; import { isArray, isFinite, isString, LoDashStatic, xorWith } from "lodash"; import { DraftValueType, LabelInValueType } from "rc-select/lib/Select"; import React from "react"; -import { isAutoLayout } from "selectors/mainCanvasSelectors"; import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService"; +import { isAutoLayout } from "utils/autoLayout/flexWidgetUtils"; import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; import { GRID_DENSITY_MIGRATION_V1, MinimumPopupRows } from "widgets/constants"; diff --git a/app/client/src/widgets/NumberSliderWidget/widget/propertyConfig/contentConfig.ts b/app/client/src/widgets/NumberSliderWidget/widget/propertyConfig/contentConfig.ts index b5b92eb6e065..483fb227f8bc 100644 --- a/app/client/src/widgets/NumberSliderWidget/widget/propertyConfig/contentConfig.ts +++ b/app/client/src/widgets/NumberSliderWidget/widget/propertyConfig/contentConfig.ts @@ -2,8 +2,8 @@ import { Alignment } from "@blueprintjs/core"; import { LabelPosition } from "components/constants"; import { ValidationTypes } from "constants/WidgetValidation"; -import { isAutoLayout } from "selectors/mainCanvasSelectors"; import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService"; +import { isAutoLayout } from "utils/autoLayout/flexWidgetUtils"; import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import { NumberSliderWidgetProps } from ".."; import { diff --git a/app/client/src/widgets/NumberSliderWidget/widget/propertyConfig/styleConfig.ts b/app/client/src/widgets/NumberSliderWidget/widget/propertyConfig/styleConfig.ts index 8410e4f26475..06cbeaddbb77 100644 --- a/app/client/src/widgets/NumberSliderWidget/widget/propertyConfig/styleConfig.ts +++ b/app/client/src/widgets/NumberSliderWidget/widget/propertyConfig/styleConfig.ts @@ -1,5 +1,5 @@ import { ValidationTypes } from "constants/WidgetValidation"; -import { isAutoLayout } from "selectors/mainCanvasSelectors"; +import { isAutoLayout } from "utils/autoLayout/flexWidgetUtils"; export default [ { diff --git a/app/client/src/widgets/RadioGroupWidget/widget/index.tsx b/app/client/src/widgets/RadioGroupWidget/widget/index.tsx index c08e0918b08b..bdfbdad74607 100644 --- a/app/client/src/widgets/RadioGroupWidget/widget/index.tsx +++ b/app/client/src/widgets/RadioGroupWidget/widget/index.tsx @@ -18,7 +18,7 @@ import { isAutoHeightEnabledForWidget } from "widgets/WidgetUtils"; import BaseWidget, { WidgetProps, WidgetState } from "../../BaseWidget"; import RadioGroupComponent from "../component"; import { RadioOption } from "../constants"; -import { isAutoLayout } from "selectors/mainCanvasSelectors"; +import { isAutoLayout } from "utils/autoLayout/flexWidgetUtils"; /** * Validation rules: diff --git a/app/client/src/widgets/RangeSliderWidget/widget/propertyConfig/contentConfig.ts b/app/client/src/widgets/RangeSliderWidget/widget/propertyConfig/contentConfig.ts index 348fbcedc528..8b0b2e2b80b4 100644 --- a/app/client/src/widgets/RangeSliderWidget/widget/propertyConfig/contentConfig.ts +++ b/app/client/src/widgets/RangeSliderWidget/widget/propertyConfig/contentConfig.ts @@ -1,8 +1,8 @@ import { Alignment } from "@blueprintjs/core"; import { LabelPosition } from "components/constants"; import { ValidationTypes } from "constants/WidgetValidation"; -import { isAutoLayout } from "selectors/mainCanvasSelectors"; import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService"; +import { isAutoLayout } from "utils/autoLayout/flexWidgetUtils"; import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import { RangeSliderWidgetProps } from ".."; import { diff --git a/app/client/src/widgets/RangeSliderWidget/widget/propertyConfig/styleConfig.ts b/app/client/src/widgets/RangeSliderWidget/widget/propertyConfig/styleConfig.ts index 2990ec5c0467..0c2cde7212ba 100644 --- a/app/client/src/widgets/RangeSliderWidget/widget/propertyConfig/styleConfig.ts +++ b/app/client/src/widgets/RangeSliderWidget/widget/propertyConfig/styleConfig.ts @@ -1,5 +1,5 @@ import { ValidationTypes } from "constants/WidgetValidation"; -import { isAutoLayout } from "selectors/mainCanvasSelectors"; +import { isAutoLayout } from "utils/autoLayout/flexWidgetUtils"; export default [ { diff --git a/app/client/src/widgets/RateWidget/widget/index.tsx b/app/client/src/widgets/RateWidget/widget/index.tsx index fb41e0680daf..22bf986f4aa2 100644 --- a/app/client/src/widgets/RateWidget/widget/index.tsx +++ b/app/client/src/widgets/RateWidget/widget/index.tsx @@ -10,7 +10,7 @@ import { Stylesheet } from "entities/AppTheming"; import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService"; import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; -import { isAutoLayout } from "selectors/mainCanvasSelectors"; +import { isAutoLayout } from "utils/autoLayout/flexWidgetUtils"; function validateDefaultRate(value: unknown, props: any, _: any) { try { diff --git a/app/client/src/widgets/SelectWidget/widget/index.tsx b/app/client/src/widgets/SelectWidget/widget/index.tsx index 63911d238407..12a771577c74 100644 --- a/app/client/src/widgets/SelectWidget/widget/index.tsx +++ b/app/client/src/widgets/SelectWidget/widget/index.tsx @@ -18,8 +18,8 @@ import { LoDashStatic, } from "lodash"; import React from "react"; -import { isAutoLayout } from "selectors/mainCanvasSelectors"; import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService"; +import { isAutoLayout } from "utils/autoLayout/flexWidgetUtils"; import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import { GRID_DENSITY_MIGRATION_V1, MinimumPopupRows } from "widgets/constants"; import { isAutoHeightEnabledForWidget } from "widgets/WidgetUtils"; diff --git a/app/client/src/widgets/SingleSelectTreeWidget/widget/index.tsx b/app/client/src/widgets/SingleSelectTreeWidget/widget/index.tsx index 455aacab6bc8..347b81e00822 100644 --- a/app/client/src/widgets/SingleSelectTreeWidget/widget/index.tsx +++ b/app/client/src/widgets/SingleSelectTreeWidget/widget/index.tsx @@ -12,8 +12,8 @@ import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; import { isArray } from "lodash"; import { DefaultValueType } from "rc-tree-select/lib/interface"; import React, { ReactNode } from "react"; -import { isAutoLayout } from "selectors/mainCanvasSelectors"; import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService"; +import { isAutoLayout } from "utils/autoLayout/flexWidgetUtils"; import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; import { GRID_DENSITY_MIGRATION_V1, MinimumPopupRows } from "widgets/constants"; diff --git a/app/client/src/widgets/SwitchGroupWidget/widget/index.tsx b/app/client/src/widgets/SwitchGroupWidget/widget/index.tsx index a8f78df5603e..e85cfda675d8 100644 --- a/app/client/src/widgets/SwitchGroupWidget/widget/index.tsx +++ b/app/client/src/widgets/SwitchGroupWidget/widget/index.tsx @@ -6,7 +6,6 @@ import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; import { isString, xor } from "lodash"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; -import { isAutoLayout } from "selectors/mainCanvasSelectors"; import { LabelPosition } from "components/constants"; import { TextSize } from "constants/WidgetConstants"; import { Stylesheet } from "entities/AppTheming"; @@ -14,6 +13,7 @@ import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import { GRID_DENSITY_MIGRATION_V1 } from "widgets/constants"; import { isAutoHeightEnabledForWidget } from "widgets/WidgetUtils"; import SwitchGroupComponent, { OptionProps } from "../component"; +import { isAutoLayout } from "utils/autoLayout/flexWidgetUtils"; class SwitchGroupWidget extends BaseWidget< SwitchGroupWidgetProps, diff --git a/app/client/src/widgets/SwitchWidget/widget/index.tsx b/app/client/src/widgets/SwitchWidget/widget/index.tsx index 0a0fe64f5493..ba4eaf7f8361 100644 --- a/app/client/src/widgets/SwitchWidget/widget/index.tsx +++ b/app/client/src/widgets/SwitchWidget/widget/index.tsx @@ -13,7 +13,7 @@ import { AlignWidgetTypes } from "widgets/constants"; import { Stylesheet } from "entities/AppTheming"; import { isAutoHeightEnabledForWidget } from "widgets/WidgetUtils"; -import { isAutoLayout } from "selectors/mainCanvasSelectors"; +import { isAutoLayout } from "utils/autoLayout/flexWidgetUtils"; class SwitchWidget extends BaseWidget { static getPropertyPaneContentConfig() { diff --git a/app/client/src/widgets/withWidgetProps.tsx b/app/client/src/widgets/withWidgetProps.tsx index 330feb8ea06e..1aa69109017f 100644 --- a/app/client/src/widgets/withWidgetProps.tsx +++ b/app/client/src/widgets/withWidgetProps.tsx @@ -17,7 +17,6 @@ import { } from "selectors/dataTreeSelectors"; import { computeMainContainerWidget, - getCanvasWidth, getChildWidgets, getCurrentAppPositioningType, getMainCanvasProps, @@ -84,7 +83,6 @@ function withWidgetProps(WrappedWidget: typeof BaseWidget) { ); const isMobile = useSelector(getIsMobile); const appPositioningType = useSelector(getCurrentAppPositioningType); - const mainCanvasWidth = useSelector(getCanvasWidth); const dispatch = useDispatch(); @@ -145,7 +143,6 @@ function withWidgetProps(WrappedWidget: typeof BaseWidget) { widgetProps.isMobile = !!isMobile; widgetProps.appPositioningType = appPositioningType; - widgetProps.mainCanvasWidth = mainCanvasWidth; /** * MODAL_WIDGET by default is to be hidden unless the isVisible property is found. From 0b16dc4defc9a45f871e0b79d1eb72456e7b9ef8 Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 2 Mar 2023 08:13:47 +0530 Subject: [PATCH 584/708] fix list widget --- app/client/src/resizable/autolayoutresize/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/client/src/resizable/autolayoutresize/index.tsx b/app/client/src/resizable/autolayoutresize/index.tsx index 771f9b95960f..2a460056fdba 100644 --- a/app/client/src/resizable/autolayoutresize/index.tsx +++ b/app/client/src/resizable/autolayoutresize/index.tsx @@ -267,7 +267,7 @@ export function ReflowResizable(props: ResizableProps) { bottom: widget.bottomRow, id: widget.widgetId, }; - if (!(isAutoLayout && widget.leftColumn === 0) && props.handles.left) { + if (!(isAutoLayout && widget?.leftColumn === 0) && props.handles.left) { handles.push({ dragCallback: (x: number) => { const updatedPositions = { ...resizedPositions }; From 85da46fe493c0c52638aeb7223901ec7c8c65208 Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 2 Mar 2023 08:41:04 +0530 Subject: [PATCH 585/708] remove unused imports --- app/client/src/resizable/resizenreflow/index.tsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/app/client/src/resizable/resizenreflow/index.tsx b/app/client/src/resizable/resizenreflow/index.tsx index 0403fc0ec37a..b538e92f4ac2 100644 --- a/app/client/src/resizable/resizenreflow/index.tsx +++ b/app/client/src/resizable/resizenreflow/index.tsx @@ -10,7 +10,6 @@ import React, { ReactNode, useEffect, useRef, useState } from "react"; import { useDispatch, useSelector } from "react-redux"; import { animated, Spring } from "react-spring"; import { useDrag } from "react-use-gesture"; -import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; import { GridProps, MovementLimitMap, @@ -18,10 +17,7 @@ import { ReflowedSpace, } from "reflow/reflowTypes"; import { getWidgets } from "sagas/selectors"; -import { - getContainerOccupiedSpacesSelectorWhileResizing, - getCurrentAppPositioningType, -} from "selectors/editorSelectors"; +import { getContainerOccupiedSpacesSelectorWhileResizing } from "selectors/editorSelectors"; import { getReflowSelector } from "selectors/widgetReflowSelectors"; import styled, { StyledComponent } from "styled-components"; import { From cb7e5f364c33c888a550036619c05a70b6caa0f0 Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 2 Mar 2023 09:26:04 +0530 Subject: [PATCH 586/708] temp: revert resizable to release version --- .../src/resizable/resize/index.autolayout.tsx | 352 ++++++++++++++++++ app/client/src/resizable/resize/index.tsx | 54 +-- .../widgets/ModalWidget/component/index.tsx | 3 +- 3 files changed, 385 insertions(+), 24 deletions(-) create mode 100644 app/client/src/resizable/resize/index.autolayout.tsx diff --git a/app/client/src/resizable/resize/index.autolayout.tsx b/app/client/src/resizable/resize/index.autolayout.tsx new file mode 100644 index 000000000000..38038932eab0 --- /dev/null +++ b/app/client/src/resizable/resize/index.autolayout.tsx @@ -0,0 +1,352 @@ +import { isHandleResizeAllowed } from "components/editorComponents/ResizableUtils"; +import React, { ReactNode, useEffect, useState } from "react"; +import { Spring } from "react-spring"; +import { useDrag } from "react-use-gesture"; +import { ReflowDirection } from "reflow/reflowTypes"; +import { ResizeWrapper } from "resizable/resizenreflow"; +import { StyledComponent } from "styled-components"; +import PerformanceTracker, { + PerformanceTransactionName, +} from "utils/PerformanceTracker"; + +const getSnappedValues = ( + x: number, + y: number, + snapGrid: { x: number; y: number }, +) => { + return { + x: Math.round(x / snapGrid.x) * snapGrid.x, + y: Math.round(y / snapGrid.y) * snapGrid.y, + }; +}; + +type ResizableHandleProps = { + allowResize: boolean; + showLightBorder?: boolean; + isHovered: boolean; + disableDot: boolean; + dragCallback: (x: number, y: number) => void; + component: StyledComponent<"div", Record>; + onStart: () => void; + onStop: () => void; + snapGrid: { + x: number; + y: number; + }; +}; + +function ResizableHandle(props: ResizableHandleProps) { + const bind = useDrag( + ({ first, last, dragging, movement: [mx, my], memo }) => { + if (!props.allowResize) { + return; + } + const snapped = getSnappedValues(mx, my, props.snapGrid); + if (dragging && memo && (snapped.x !== memo.x || snapped.y !== memo.y)) { + props.dragCallback(snapped.x, snapped.y); + } + if (first) { + props.onStart(); + } + if (last) { + props.onStop(); + } + return snapped; + }, + ); + const propsToPass = { + ...bind(), + showAsBorder: !props.allowResize, + disableDot: props.disableDot, + isHovered: props.isHovered, + }; + + return ; +} + +type ResizableProps = { + allowResize: boolean; + handles: { + left?: StyledComponent<"div", Record>; + top?: StyledComponent<"div", Record>; + bottom?: StyledComponent<"div", Record>; + right?: StyledComponent<"div", Record>; + bottomRight?: StyledComponent<"div", Record>; + topLeft?: StyledComponent<"div", Record>; + topRight?: StyledComponent<"div", Record>; + bottomLeft?: StyledComponent<"div", Record>; + }; + componentWidth: number; + componentHeight: number; + children: ReactNode; + onStart: () => void; + onStop: ( + size: { width: number; height: number }, + position: { x: number; y: number }, + ) => void; + snapGrid: { x: number; y: number }; + enableVerticalResize: boolean; + enableHorizontalResize: boolean; + isColliding: ( + size: { width: number; height: number }, + position: { x: number; y: number }, + ) => boolean; + className?: string; + resizeDualSides?: boolean; + widgetId: string; + showLightBorder?: boolean; + zWidgetType?: string; +}; + +export const Resizable = function Resizable(props: ResizableProps) { + // Performance tracking start + const sentryPerfTags = props.zWidgetType + ? [{ name: "widget_type", value: props.zWidgetType }] + : []; + PerformanceTracker.startTracking( + PerformanceTransactionName.SHOW_RESIZE_HANDLES, + { widgetId: props.widgetId }, + true, + sentryPerfTags, + ); + + useEffect(() => { + PerformanceTracker.stopTracking( + PerformanceTransactionName.SHOW_RESIZE_HANDLES, + ); + }); + //end + const [pointerEvents, togglePointerEvents] = useState(true); + const [newDimensions, set] = useState({ + width: props.componentWidth, + height: props.componentHeight, + x: 0, + y: 0, + reset: false, + }); + + const { resizeDualSides } = props; + const multiplier = resizeDualSides ? 2 : 1; + + const setNewDimensions = (rect: { + width: number; + height: number; + x: number; + y: number; + }) => { + const { height, width, x, y } = rect; + const shouldUpdateHeight = + props.componentHeight !== height && props.enableVerticalResize; + if (!shouldUpdateHeight) rect.height = props.componentHeight; + + const shouldUpdateWidth = + props.componentWidth !== width && props.enableHorizontalResize; + if (!shouldUpdateWidth) rect.width = props.componentWidth; + + const isColliding = props.isColliding({ width, height }, { x, y }); + if (!isColliding) { + set({ ...rect, reset: false }); + } + }; + + useEffect(() => { + set({ + width: props.componentWidth, + height: props.componentHeight, + x: 0, + y: 0, + reset: true, + }); + }, [props.componentHeight, props.componentWidth]); + + const handles = []; + + if (props.handles.left) { + handles.push({ + dragCallback: (x: number) => { + setNewDimensions({ + width: props.componentWidth - multiplier * x, + height: newDimensions.height, + x: resizeDualSides ? newDimensions.x : x, + y: newDimensions.y, + }); + }, + component: props.handles.left, + handleDirection: ReflowDirection.LEFT, + }); + } + + if (props.handles.top) { + handles.push({ + dragCallback: (x: number, y: number) => { + setNewDimensions({ + width: newDimensions.width, + height: props.componentHeight - multiplier * y, + y: resizeDualSides ? newDimensions.y : y, + x: newDimensions.x, + }); + }, + component: props.handles.top, + handleDirection: ReflowDirection.TOP, + }); + } + + if (props.handles.right) { + handles.push({ + dragCallback: (x: number) => { + setNewDimensions({ + width: props.componentWidth + multiplier * x, + height: newDimensions.height, + x: newDimensions.x, + y: newDimensions.y, + }); + }, + component: props.handles.right, + handleDirection: ReflowDirection.RIGHT, + }); + } + + if (props.handles.bottom) { + handles.push({ + dragCallback: (x: number, y: number) => { + setNewDimensions({ + width: newDimensions.width, + height: props.componentHeight + multiplier * y, + x: newDimensions.x, + y: newDimensions.y, + }); + }, + component: props.handles.bottom, + handleDirection: ReflowDirection.BOTTOM, + }); + } + + if (props.handles.topLeft) { + handles.push({ + dragCallback: (x: number, y: number) => { + setNewDimensions({ + width: props.componentWidth - x, + height: props.componentHeight - y, + x: x, + y: y, + }); + }, + component: props.handles.topLeft, + }); + } + + if (props.handles.topRight) { + handles.push({ + dragCallback: (x: number, y: number) => { + setNewDimensions({ + width: props.componentWidth + x, + height: props.componentHeight - y, + x: newDimensions.x, + y: y, + }); + }, + component: props.handles.topRight, + }); + } + + if (props.handles.bottomRight) { + handles.push({ + dragCallback: (x: number, y: number) => { + setNewDimensions({ + width: props.componentWidth + x, + height: props.componentHeight + y, + x: newDimensions.x, + y: newDimensions.y, + }); + }, + component: props.handles.bottomRight, + }); + } + + if (props.handles.bottomLeft) { + handles.push({ + dragCallback: (x: number, y: number) => { + setNewDimensions({ + width: props.componentWidth - x, + height: props.componentHeight + y, + x, + y: newDimensions.y, + }); + }, + component: props.handles.bottomLeft, + }); + } + const onResizeStop = () => { + togglePointerEvents(true); + props.onStop( + { + width: newDimensions.width, + height: newDimensions.height, + }, + { + x: newDimensions.x, + y: newDimensions.y, + }, + ); + }; + const showResizeBoundary = props.enableHorizontalResize; + const renderHandles = handles.map((handle, index) => { + const disableDot = + !showResizeBoundary || + !isHandleResizeAllowed( + props.enableHorizontalResize, + props.enableVerticalResize, + handle.handleDirection, + ); + return ( + { + togglePointerEvents(false); + props.onStart(); + }} + onStop={onResizeStop} + snapGrid={props.snapGrid} + /> + ); + }); + + return ( + + {(_props) => ( + + {props.children} + {props.enableHorizontalResize && renderHandles} + + )} + + ); +}; + +export default Resizable; diff --git a/app/client/src/resizable/resize/index.tsx b/app/client/src/resizable/resize/index.tsx index 38038932eab0..94f20f71fdee 100644 --- a/app/client/src/resizable/resize/index.tsx +++ b/app/client/src/resizable/resize/index.tsx @@ -1,13 +1,21 @@ -import { isHandleResizeAllowed } from "components/editorComponents/ResizableUtils"; -import React, { ReactNode, useEffect, useState } from "react"; -import { Spring } from "react-spring"; +import React, { ReactNode, useState, useEffect, forwardRef, Ref } from "react"; +import styled, { StyledComponent } from "styled-components"; import { useDrag } from "react-use-gesture"; -import { ReflowDirection } from "reflow/reflowTypes"; -import { ResizeWrapper } from "resizable/resizenreflow"; -import { StyledComponent } from "styled-components"; +import { Spring, animated } from "react-spring"; import PerformanceTracker, { PerformanceTransactionName, } from "utils/PerformanceTracker"; +import { ReflowDirection } from "reflow/reflowTypes"; +import { isHandleResizeAllowed } from "components/editorComponents/ResizableUtils"; + +const ResizeWrapper = styled(animated.div)<{ $prevents: boolean }>` + display: block; + & { + * { + pointer-events: ${(props) => !props.$prevents && "none"}; + } + } +`; const getSnappedValues = ( x: number, @@ -23,7 +31,6 @@ const getSnappedValues = ( type ResizableHandleProps = { allowResize: boolean; showLightBorder?: boolean; - isHovered: boolean; disableDot: boolean; dragCallback: (x: number, y: number) => void; component: StyledComponent<"div", Record>; @@ -57,8 +64,8 @@ function ResizableHandle(props: ResizableHandleProps) { const propsToPass = { ...bind(), showAsBorder: !props.allowResize, + showLightBorder: props.showLightBorder, disableDot: props.disableDot, - isHovered: props.isHovered, }; return ; @@ -93,19 +100,23 @@ type ResizableProps = { ) => boolean; className?: string; resizeDualSides?: boolean; - widgetId: string; showLightBorder?: boolean; + widgetId: string; zWidgetType?: string; + zWidgetId?: string; }; -export const Resizable = function Resizable(props: ResizableProps) { +export const Resizable = forwardRef(function Resizable( + props: ResizableProps, + ref: Ref, +) { // Performance tracking start const sentryPerfTags = props.zWidgetType ? [{ name: "widget_type", value: props.zWidgetType }] : []; PerformanceTracker.startTracking( PerformanceTransactionName.SHOW_RESIZE_HANDLES, - { widgetId: props.widgetId }, + { widgetId: props.zWidgetId }, true, sentryPerfTags, ); @@ -289,27 +300,25 @@ export const Resizable = function Resizable(props: ResizableProps) { }, ); }; - const showResizeBoundary = props.enableHorizontalResize; + const renderHandles = handles.map((handle, index) => { - const disableDot = - !showResizeBoundary || - !isHandleResizeAllowed( - props.enableHorizontalResize, - props.enableVerticalResize, - handle.handleDirection, - ); + const disableDot = !isHandleResizeAllowed( + props.enableHorizontalResize, + props.enableVerticalResize, + handle.handleDirection, + ); return ( { togglePointerEvents(false); props.onStart(); }} onStop={onResizeStop} + showLightBorder={props.showLightBorder} snapGrid={props.snapGrid} /> ); @@ -337,8 +346,7 @@ export const Resizable = function Resizable(props: ResizableProps) { {props.children} @@ -347,6 +355,6 @@ export const Resizable = function Resizable(props: ResizableProps) { )} ); -}; +}); export default Resizable; diff --git a/app/client/src/widgets/ModalWidget/component/index.tsx b/app/client/src/widgets/ModalWidget/component/index.tsx index 0573430fa38f..723a33a399bd 100644 --- a/app/client/src/widgets/ModalWidget/component/index.tsx +++ b/app/client/src/widgets/ModalWidget/component/index.tsx @@ -149,7 +149,7 @@ export default function ModalComponent(props: ModalComponentProps) { const { enableResize = false } = props; const [modalPosition, setModalPosition] = useState("fixed"); - + const resizeRef = React.useRef(null); const { setIsResizing } = useWidgetDragResize(); const isResizing = useSelector( (state: AppState) => state.ui.widgetDragResize.isResizing, @@ -243,6 +243,7 @@ export default function ModalComponent(props: ModalComponentProps) { isColliding={() => false} onStart={onResizeStart} onStop={onResizeStop} + ref={resizeRef} resizeDualSides showLightBorder snapGrid={{ x: 1, y: 1 }} From 69a29e576c0b96968fc79ac9ca1d95f92a2d24e5 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Thu, 2 Mar 2023 12:12:26 +0530 Subject: [PATCH 587/708] remove unnecessary code. --- app/client/src/pages/Editor/Popper.tsx | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/app/client/src/pages/Editor/Popper.tsx b/app/client/src/pages/Editor/Popper.tsx index 5981f7fe8e30..8995daa5fccd 100644 --- a/app/client/src/pages/Editor/Popper.tsx +++ b/app/client/src/pages/Editor/Popper.tsx @@ -177,7 +177,6 @@ export default (props: PopperProps) => { elementRef.style.top = initPositon.top + "px"; elementRef.style.left = initPositon.left + "px"; } - _popper.scheduleUpdate(); }, modifiers: { flip: { @@ -201,18 +200,6 @@ export default (props: PopperProps) => { }, }, ); - if (props.targetNode) { - const config = { attributes: true }; - const callback = (mutationList: any) => { - for (const mutation of mutationList) { - if (["attributes", "childList"].includes(mutation.type)) { - _popper.scheduleUpdate(); - } - } - }; - const observer = new MutationObserver(callback); - observer.observe(props.targetNode, config); - } if (isDraggable) { disablePopperEvents && _popper.disableEventListeners(); draggableElement( From 79af3e891c93551696d1c164b8182a47918e2a30 Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 2 Mar 2023 15:31:09 +0530 Subject: [PATCH 588/708] temp: remove appPositioningType control --- .../pages/Editor/AppPositionTypeControl.tsx | 58 +++++++++---------- .../widgets/ModalWidget/component/index.tsx | 6 +- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/app/client/src/pages/Editor/AppPositionTypeControl.tsx b/app/client/src/pages/Editor/AppPositionTypeControl.tsx index 8aa07b5f006b..4dcad7142eff 100644 --- a/app/client/src/pages/Editor/AppPositionTypeControl.tsx +++ b/app/client/src/pages/Editor/AppPositionTypeControl.tsx @@ -65,16 +65,16 @@ export function AppPositionTypeControl() { ); }, [selectedOption]); - const [focusedIndex, setFocusedIndex] = React.useState(selectedIndex); + // const [focusedIndex, setFocusedIndex] = React.useState(selectedIndex); - useEffect(() => { - if (!isAutoLayoutActive) { - /** - * if feature flag is disabled, set the layout to fixed. - */ - updateAppPositioningLayout(AppsmithLayoutTypes[0]); - } - }, [isAutoLayoutActive]); + // useEffect(() => { + // if (!isAutoLayoutActive) { + // /** + // * if feature flag is disabled, set the layout to fixed. + // */ + // updateAppPositioningLayout(AppsmithLayoutTypes[0]); + // } + // }, [isAutoLayoutActive]); const updateAppPositioningLayout = ( layoutOption: ApplicationPositionTypeConfigOption, @@ -99,28 +99,28 @@ export function AppPositionTypeControl() { ); }; - const handleKeyDown = (event: React.KeyboardEvent, index: number) => { - if (!buttonRefs.length) return; + // const handleKeyDown = (event: React.KeyboardEvent, index: number) => { + // if (!buttonRefs.length) return; - switch (event.key) { - case "ArrowRight": - case "Right": - const rightIndex = index === buttonRefs.length - 1 ? 0 : index + 1; - buttonRefs[rightIndex]?.focus(); - setFocusedIndex(rightIndex); - break; - case "ArrowLeft": - case "Left": - const leftIndex = index === 0 ? buttonRefs.length - 1 : index - 1; - buttonRefs[leftIndex]?.focus(); - setFocusedIndex(leftIndex); - break; - } - }; + // switch (event.key) { + // case "ArrowRight": + // case "Right": + // const rightIndex = index === buttonRefs.length - 1 ? 0 : index + 1; + // buttonRefs[rightIndex]?.focus(); + // setFocusedIndex(rightIndex); + // break; + // case "ArrowLeft": + // case "Left": + // const leftIndex = index === 0 ? buttonRefs.length - 1 : index - 1; + // buttonRefs[leftIndex]?.focus(); + // setFocusedIndex(leftIndex); + // break; + // } + // }; return ( <> - {isAutoLayoutActive ? ( + {/* {isAutoLayoutActive ? ( <> App Positioning Type
@@ -151,7 +151,7 @@ export function AppPositionTypeControl() { updateAppPositioningLayout(layoutOption); setFocusedIndex(index); }} - onKeyDown={(event) => handleKeyDown(event, index)} + // onKeyDown={(event) => handleKeyDown(event, index)} ref={(input) => buttonRefs.push(input)} tabIndex={index === focusedIndex ? 0 : -1} > @@ -165,7 +165,7 @@ export function AppPositionTypeControl() {
- ) : null} + ) : null} */} {selectedOption === AppPositioningTypes.FIXED && ( <> Canvas Size diff --git a/app/client/src/widgets/ModalWidget/component/index.tsx b/app/client/src/widgets/ModalWidget/component/index.tsx index 723a33a399bd..6b914f409912 100644 --- a/app/client/src/widgets/ModalWidget/component/index.tsx +++ b/app/client/src/widgets/ModalWidget/component/index.tsx @@ -159,7 +159,7 @@ export default function ModalComponent(props: ModalComponentProps) { const isTableFilterPaneVisible = useSelector( (state: AppState) => state.ui.tableFilterPane.isVisible, ); - + const disabledResizeHandles = get(props, "disabledResizeHandles", []); const handles = useMemo(() => { const allHandles = { left: LeftHandleStyles, @@ -168,8 +168,8 @@ export default function ModalComponent(props: ModalComponentProps) { right: RightHandleStyles, }; - return omit(allHandles, get(props, "disabledResizeHandles", [])); - }, [props]); + return omit(allHandles, disabledResizeHandles); + }, [disabledResizeHandles]); useEffect(() => { setTimeout(() => { From 35805862bcb4774e424d64201861b5167a39e073 Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 2 Mar 2023 15:38:29 +0530 Subject: [PATCH 589/708] build fix --- .../pages/Editor/AppPositionTypeControl.tsx | 124 +++++++++--------- 1 file changed, 62 insertions(+), 62 deletions(-) diff --git a/app/client/src/pages/Editor/AppPositionTypeControl.tsx b/app/client/src/pages/Editor/AppPositionTypeControl.tsx index 4dcad7142eff..83f91b366c78 100644 --- a/app/client/src/pages/Editor/AppPositionTypeControl.tsx +++ b/app/client/src/pages/Editor/AppPositionTypeControl.tsx @@ -1,69 +1,69 @@ -import classNames from "classnames"; -import React, { useEffect, useMemo } from "react"; -import { useDispatch, useSelector } from "react-redux"; +// import classNames from "classnames"; +import React from "react"; +import { useSelector } from "react-redux"; import styled from "styled-components"; -import { batchUpdateMultipleWidgetProperties } from "actions/controlActions"; -import { ReactComponent as DesktopIcon } from "assets/icons/ads/app-icons/monitor-alt.svg"; -import { ReactComponent as MultiDeviceIcon } from "assets/icons/ads/app-icons/monitor-smartphone-alt.svg"; +// import { batchUpdateMultipleWidgetProperties } from "actions/controlActions"; +// import { ReactComponent as DesktopIcon } from "assets/icons/ads/app-icons/monitor-alt.svg"; +// import { ReactComponent as MultiDeviceIcon } from "assets/icons/ads/app-icons/monitor-smartphone-alt.svg"; import { Colors } from "constants/Colors"; -import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; -import { IconName, TooltipComponent } from "design-system-old"; +// import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; +// import { IconName } from "design-system-old"; import { AppPositioningTypeConfig, AppPositioningTypes, } from "reducers/entityReducers/pageListReducer"; import { getCurrentAppPositioningType, - isAutoLayoutEnabled, + // isAutoLayoutEnabled, } from "selectors/editorSelectors"; -import { LayoutDirection, Positioning } from "utils/autoLayout/constants"; +// import { LayoutDirection, Positioning } from "utils/autoLayout/constants"; import { MainContainerLayoutControl } from "./MainContainerLayoutControl"; -interface ApplicationPositionTypeConfigOption { - name: string; - type: AppPositioningTypes; - icon?: IconName; -} -const IconObj: any = { - fluid: , - desktop: , -}; +// interface ApplicationPositionTypeConfigOption { +// name: string; +// type: AppPositioningTypes; +// icon?: IconName; +// } +// const IconObj: any = { +// fluid: , +// desktop: , +// }; export const AppsmithDefaultPositionType: AppPositioningTypeConfig = { type: AppPositioningTypes.FIXED, }; -const AppsmithLayoutTypes: ApplicationPositionTypeConfigOption[] = [ - { - name: "Fixed Layout", - type: AppPositioningTypes.FIXED, - icon: "desktop", - }, - { - name: "Auto Layout", - type: AppPositioningTypes.AUTO, - icon: "fluid", - }, -]; +// const AppsmithLayoutTypes: ApplicationPositionTypeConfigOption[] = [ +// { +// name: "Fixed Layout", +// type: AppPositioningTypes.FIXED, +// icon: "desktop", +// }, +// { +// name: "Auto Layout", +// type: AppPositioningTypes.AUTO, +// icon: "fluid", +// }, +// ]; export const Title = styled.p` color: ${Colors.GRAY_800}; `; export function AppPositionTypeControl() { - const dispatch = useDispatch(); - const buttonRefs: Array = []; + // const dispatch = useDispatch(); + // const buttonRefs: Array = []; const selectedOption = useSelector(getCurrentAppPositioningType); - const isAutoLayoutActive = useSelector(isAutoLayoutEnabled); + // const isAutoLayoutActive = useSelector(isAutoLayoutEnabled); /** * return selected layout index. if there is no app * layout, use the default one ( fluid ) */ - const selectedIndex = useMemo(() => { - return AppsmithLayoutTypes.findIndex( - (each) => - each.type === (selectedOption || AppsmithDefaultPositionType.type), - ); - }, [selectedOption]); + // const selectedIndex = useMemo(() => { + // return AppsmithLayoutTypes.findIndex( + // (each) => + // each.type === (selectedOption || AppsmithDefaultPositionType.type), + // ); + // }, [selectedOption]); // const [focusedIndex, setFocusedIndex] = React.useState(selectedIndex); @@ -76,28 +76,28 @@ export function AppPositionTypeControl() { // } // }, [isAutoLayoutActive]); - const updateAppPositioningLayout = ( - layoutOption: ApplicationPositionTypeConfigOption, - ) => { - const selectedType = - layoutOption.type !== AppPositioningTypes.AUTO - ? Positioning.Fixed - : Positioning.Vertical; - dispatch( - batchUpdateMultipleWidgetProperties([ - { - widgetId: MAIN_CONTAINER_WIDGET_ID, - updates: { - modify: { - positioning: selectedType, - useAutoLayout: selectedType !== Positioning.Fixed, - direction: LayoutDirection.Vertical, - }, - }, - }, - ]), - ); - }; + // const updateAppPositioningLayout = ( + // layoutOption: ApplicationPositionTypeConfigOption, + // ) => { + // const selectedType = + // layoutOption.type !== AppPositioningTypes.AUTO + // ? Positioning.Fixed + // : Positioning.Vertical; + // dispatch( + // batchUpdateMultipleWidgetProperties([ + // { + // widgetId: MAIN_CONTAINER_WIDGET_ID, + // updates: { + // modify: { + // positioning: selectedType, + // useAutoLayout: selectedType !== Positioning.Fixed, + // direction: LayoutDirection.Vertical, + // }, + // }, + // }, + // ]), + // ); + // }; // const handleKeyDown = (event: React.KeyboardEvent, index: number) => { // if (!buttonRefs.length) return; From e95620c66f49c7df1ebfebff20dbd55d635a3d23 Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 2 Mar 2023 18:39:03 +0530 Subject: [PATCH 590/708] temp: remove canvas resize handle --- .../editorComponents/ResizableComponent.tsx | 6 +- .../Editor/WidgetsEditor/CanvasContainer.tsx | 250 +++++++++--------- .../src/resizable/resizenreflow/index.tsx | 1 + .../src/utils/hooks/useDynamicAppLayout.tsx | 101 +++---- 4 files changed, 182 insertions(+), 176 deletions(-) diff --git a/app/client/src/components/editorComponents/ResizableComponent.tsx b/app/client/src/components/editorComponents/ResizableComponent.tsx index 26d833aa5b69..7460d1d3382a 100644 --- a/app/client/src/components/editorComponents/ResizableComponent.tsx +++ b/app/client/src/components/editorComponents/ResizableComponent.tsx @@ -241,7 +241,11 @@ export const ResizableComponent = memo(function ResizableComponent( // Make sure that this tableFilterPane should close showTableFilterPane && showTableFilterPane(); // If resizing a fill widget "horizontally", then convert it to a hug widget. - if (props.responsiveBehavior === ResponsiveBehavior.Fill && affectsWidth) + if ( + props.isFlexChild && + props.responsiveBehavior === ResponsiveBehavior.Fill && + affectsWidth + ) dispatch( batchUpdateMultipleWidgetProperties([ { diff --git a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx index 49ce826224db..797c79a360cd 100644 --- a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx +++ b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx @@ -1,11 +1,11 @@ -import { ReactComponent as CanvasResizer } from "assets/icons/ads/app-icons/canvas-resizer.svg"; +// import { ReactComponent as CanvasResizer } from "assets/icons/ads/app-icons/canvas-resizer.svg"; import React, { ReactNode, useEffect } from "react"; import { useSelector } from "react-redux"; import { getCanvasWidth, - getCurrentApplicationLayout, - getCurrentAppPositioningType, + // getCurrentApplicationLayout, + // getCurrentAppPositioningType, getCurrentPageId, getIsFetchingPage, getViewModePageList, @@ -30,52 +30,52 @@ import { import { getCanvasWidgetsStructure } from "selectors/entitiesSelector"; import { getCurrentThemeDetails } from "selectors/themeSelectors"; import { - AUTOLAYOUT_RESIZER_WIDTH_BUFFER, + // AUTOLAYOUT_RESIZER_WIDTH_BUFFER, useDynamicAppLayout, } from "utils/hooks/useDynamicAppLayout"; import useGoogleFont from "utils/hooks/useGoogleFont"; -import { layoutConfigurations } from "constants/WidgetConstants"; -import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; +// import { layoutConfigurations } from "constants/WidgetConstants"; +// import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; import Canvas from "../Canvas"; -const AutoLayoutCanvasResizer = styled.div` - position: sticky; - cursor: col-resize; - width: 2px; - height: 100%; - display: flex; - background: #d9d9d9; - align-items: center; - justify-content: flex-start; - z-index: 2; - transition: width 300ms ease; - transition: background 300ms ease; - .canvas-resizer-icon { - border-left: 2px solid; - border-color: #d7d7d7; - transition: border 300ms ease; - margin-left: 2px; - & > svg { - fill: #d7d7d7; - transition: fill 300ms ease; - } - } - &:hover, - &:active { - width: 3px; - transition: width 300ms ease; - background: #ff9b4e; - transition: background 300ms ease; - .canvas-resizer-icon { - border-color: #ff9b4e; - transition: border 300ms ease; - & > svg { - fill: #ff9b4e; - transition: fill 300ms ease; - } - } - } -`; +// const AutoLayoutCanvasResizer = styled.div` +// position: sticky; +// cursor: col-resize; +// width: 2px; +// height: 100%; +// display: flex; +// background: #d9d9d9; +// align-items: center; +// justify-content: flex-start; +// z-index: 2; +// transition: width 300ms ease; +// transition: background 300ms ease; +// .canvas-resizer-icon { +// border-left: 2px solid; +// border-color: #d7d7d7; +// transition: border 300ms ease; +// margin-left: 2px; +// & > svg { +// fill: #d7d7d7; +// transition: fill 300ms ease; +// } +// } +// &:hover, +// &:active { +// width: 3px; +// transition: width 300ms ease; +// background: #ff9b4e; +// transition: background 300ms ease; +// .canvas-resizer-icon { +// border-color: #ff9b4e; +// transition: border 300ms ease; +// & > svg { +// fill: #ff9b4e; +// transition: fill 300ms ease; +// } +// } +// } +// `; const Container = styled.section<{ background: string; }>` @@ -140,91 +140,91 @@ function CanvasContainer() { /> ); } - const appPositioningType = useSelector(getCurrentAppPositioningType); - const appLayout = useSelector(getCurrentApplicationLayout); - useEffect(() => { - if (appPositioningType === AppPositioningTypes.AUTO) { - let buffer = 0; - const ele: any = document.getElementById("canvas-viewport"); - if (isPreviewMode) { - ele.style.width = "inherit"; - buffer = AUTOLAYOUT_RESIZER_WIDTH_BUFFER; - } else { - ele.style.width = "100%"; - } - if (appLayout?.type === "FLUID") { - const smallestWidth = layoutConfigurations.MOBILE.minWidth; - // Query the element - const ele: any = document.getElementById("canvas-viewport"); - let needsInitiation = true; - let initialWidth = ele.offsetWidth; - // The current position of mouse - let x = 0; - // let y = 0; + // const appPositioningType = useSelector(getCurrentAppPositioningType); + // const appLayout = useSelector(getCurrentApplicationLayout); + // useEffect(() => { + // if (appPositioningType === AppPositioningTypes.AUTO) { + // let buffer = 0; + // const ele: any = document.getElementById("canvas-viewport"); + // if (isPreviewMode) { + // ele.style.width = "inherit"; + // buffer = AUTOLAYOUT_RESIZER_WIDTH_BUFFER; + // } else { + // ele.style.width = "100%"; + // } + // if (appLayout?.type === "FLUID") { + // const smallestWidth = layoutConfigurations.MOBILE.minWidth; + // // Query the element + // const ele: any = document.getElementById("canvas-viewport"); + // let needsInitiation = true; + // let initialWidth = ele.offsetWidth; + // // The current position of mouse + // let x = 0; + // // let y = 0; - // The dimension of the element - let w = 0; - // let h = 0; - let events: any = []; + // // The dimension of the element + // let w = 0; + // // let h = 0; + // let events: any = []; - // Handle the mousedown event - // that's triggered when user drags the resizer - const mouseDownHandler = function(e: any) { - if (needsInitiation) { - initialWidth = ele.offsetWidth; - needsInitiation = false; - } - // Get the current mouse position - x = e.clientX; - // y = e.clientY; + // // Handle the mousedown event + // // that's triggered when user drags the resizer + // const mouseDownHandler = function(e: any) { + // if (needsInitiation) { + // initialWidth = ele.offsetWidth; + // needsInitiation = false; + // } + // // Get the current mouse position + // x = e.clientX; + // // y = e.clientY; - // Calculate the dimension of element - const styles = window.getComputedStyle(ele); - w = parseInt(styles.width, 10) + buffer; - // h = parseInt(styles.height, 10); - const mouseMove = (e: any) => mouseMoveHandler(e); - events.push(mouseMove); - // Attach the listeners to `document` - document.addEventListener("mousemove", mouseMove); - document.addEventListener("mouseup", mouseUpHandler); - // e.stopPropagation(); - }; + // // Calculate the dimension of element + // const styles = window.getComputedStyle(ele); + // w = parseInt(styles.width, 10) + buffer; + // // h = parseInt(styles.height, 10); + // const mouseMove = (e: any) => mouseMoveHandler(e); + // events.push(mouseMove); + // // Attach the listeners to `document` + // document.addEventListener("mousemove", mouseMove); + // document.addEventListener("mouseup", mouseUpHandler); + // // e.stopPropagation(); + // }; - const mouseMoveHandler = function(e: any) { - // How far the mouse has been moved - // const multiplier = rightHandle ? 2 : -2; - const multiplier = 2; - const dx = (e.clientX - x) * multiplier; - if (initialWidth >= w + dx && smallestWidth <= w + dx) { - // Adjust the dimension of element - ele.style.width = `${w + dx}px`; - } - if (initialWidth < w + dx) { - ele.style.width = `${initialWidth}px`; - } - if (smallestWidth > w + dx) { - ele.style.width = `${smallestWidth}px`; - } - // e.stopPropagation(); - }; + // const mouseMoveHandler = function(e: any) { + // // How far the mouse has been moved + // // const multiplier = rightHandle ? 2 : -2; + // const multiplier = 2; + // const dx = (e.clientX - x) * multiplier; + // if (initialWidth >= w + dx && smallestWidth <= w + dx) { + // // Adjust the dimension of element + // ele.style.width = `${w + dx}px`; + // } + // if (initialWidth < w + dx) { + // ele.style.width = `${initialWidth}px`; + // } + // if (smallestWidth > w + dx) { + // ele.style.width = `${smallestWidth}px`; + // } + // // e.stopPropagation(); + // }; - const mouseUpHandler = function(e: any) { - // Remove the handlers of `mousemove` and `mouseup` - mouseMoveHandler(e); - document.removeEventListener("mousemove", events[0] as any); - document.removeEventListener("mouseup", mouseUpHandler); - events = []; - }; - const rightResizer: any = ele.querySelectorAll(".resizer-right")[0]; - const rightMove = (e: any) => mouseDownHandler(e); - rightResizer.addEventListener("mousedown", rightMove); + // const mouseUpHandler = function(e: any) { + // // Remove the handlers of `mousemove` and `mouseup` + // mouseMoveHandler(e); + // document.removeEventListener("mousemove", events[0] as any); + // document.removeEventListener("mouseup", mouseUpHandler); + // events = []; + // }; + // const rightResizer: any = ele.querySelectorAll(".resizer-right")[0]; + // const rightMove = (e: any) => mouseDownHandler(e); + // rightResizer.addEventListener("mousedown", rightMove); - return () => { - rightResizer.removeEventListener("mousedown", rightMove); - }; - } - } - }, [appLayout, isPreviewMode, currentPageId, appPositioningType]); + // return () => { + // rightResizer.removeEventListener("mousedown", rightMove); + // }; + // } + // } + // }, [appLayout, isPreviewMode, currentPageId, appPositioningType]); // calculating exact height to not allow scroll at this component, // calculating total height minus margin on top, top bar and bottom bar @@ -259,7 +259,7 @@ function CanvasContainer() {
)} {node} - {appPositioningType === AppPositioningTypes.AUTO && ( + {/* {appPositioningType === AppPositioningTypes.AUTO && (
- )} + )} */} ); } diff --git a/app/client/src/resizable/resizenreflow/index.tsx b/app/client/src/resizable/resizenreflow/index.tsx index b538e92f4ac2..33f4d3f97a3f 100644 --- a/app/client/src/resizable/resizenreflow/index.tsx +++ b/app/client/src/resizable/resizenreflow/index.tsx @@ -609,6 +609,7 @@ export function ReflowResizable(props: ResizableProps) { allowResize={ props.allowResize && !( + isAutoLayout && props.responsiveBehavior === ResponsiveBehavior.Fill && handle?.affectsWidth ) diff --git a/app/client/src/utils/hooks/useDynamicAppLayout.tsx b/app/client/src/utils/hooks/useDynamicAppLayout.tsx index 63c552bbb4b5..77bfa8aa69e3 100644 --- a/app/client/src/utils/hooks/useDynamicAppLayout.tsx +++ b/app/client/src/utils/hooks/useDynamicAppLayout.tsx @@ -2,21 +2,21 @@ import { debounce, get } from "lodash"; import { useCallback, useEffect, useMemo } from "react"; import { useDispatch, useSelector } from "react-redux"; -import { updateLayoutForMobileBreakpointAction } from "actions/autoLayoutActions"; +// import { updateLayoutForMobileBreakpointAction } from "actions/autoLayoutActions"; import { updateCanvasLayoutAction } from "actions/editorActions"; import { APP_SETTINGS_PANE_WIDTH } from "constants/AppConstants"; import { DefaultLayoutType, layoutConfigurations, - MAIN_CONTAINER_WIDGET_ID, + // MAIN_CONTAINER_WIDGET_ID, } from "constants/WidgetConstants"; import { APP_MODE } from "entities/App"; import { SIDE_NAV_WIDTH } from "pages/common/SideNav"; -import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; +// import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; import { getIsAppSettingsPaneOpen } from "selectors/appSettingsPaneSelectors"; import { getCurrentApplicationLayout, - getCurrentAppPositioningType, + // getCurrentAppPositioningType, getCurrentPageId, getMainCanvasProps, previewModeSelector, @@ -56,7 +56,7 @@ export const useDynamicAppLayout = () => { const tabsPaneWidth = useSelector(getTabsPaneWidth); const isMultiPane = useSelector(isMultiPaneActive); const paneCount = useSelector(getPaneCount); - const appPositioningType = useSelector(getCurrentAppPositioningType); + // const appPositioningType = useSelector(getCurrentAppPositioningType); // /** // * calculates min height @@ -132,19 +132,19 @@ export const useDynamicAppLayout = () => { if (paneCount === 3) calculatedWidth -= propertyPaneWidth; } - const ele: any = document.getElementById("canvas-viewport"); - if ( - appMode === "EDIT" && - appLayout?.type === "FLUID" && - ele && - calculatedWidth > ele.clientWidth - ) { - calculatedWidth = ele.clientWidth; - } + // const ele: any = document.getElementById("canvas-viewport"); + // if ( + // appMode === "EDIT" && + // appLayout?.type === "FLUID" && + // ele && + // calculatedWidth > ele.clientWidth + // ) { + // calculatedWidth = ele.clientWidth; + // } - if (appPositioningType === AppPositioningTypes.AUTO && isPreviewMode) { - calculatedWidth -= AUTOLAYOUT_RESIZER_WIDTH_BUFFER; - } + // if (appPositioningType === AppPositioningTypes.AUTO && isPreviewMode) { + // calculatedWidth -= AUTOLAYOUT_RESIZER_WIDTH_BUFFER; + // } switch (true) { case maxWidth < 0: @@ -203,29 +203,29 @@ export const useDynamicAppLayout = () => { paneCount, ]); - const immediateDebouncedResize = useCallback(debounce(resizeToLayout), [ - mainCanvasProps, - screenWidth, - currentPageId, - appMode, - appLayout, - isPreviewMode, - ]); + // const immediateDebouncedResize = useCallback(debounce(resizeToLayout), [ + // mainCanvasProps, + // screenWidth, + // currentPageId, + // appMode, + // appLayout, + // isPreviewMode, + // ]); - const resizeObserver = new ResizeObserver(immediateDebouncedResize); - useEffect(() => { - const ele: any = document.getElementById("canvas-viewport"); - if (ele) { - if (appLayout?.type === "FLUID") { - resizeObserver.observe(ele); - } else { - resizeObserver.unobserve(ele); - } - } - return () => { - ele && resizeObserver.unobserve(ele); - }; - }, [appLayout, currentPageId, isPreviewMode]); + // const resizeObserver = new ResizeObserver(immediateDebouncedResize); + // useEffect(() => { + // const ele: any = document.getElementById("canvas-viewport"); + // if (ele) { + // if (appLayout?.type === "FLUID") { + // resizeObserver.observe(ele); + // } else { + // resizeObserver.unobserve(ele); + // } + // } + // return () => { + // ele && resizeObserver.unobserve(ele); + // }; + // }, [appLayout, currentPageId, isPreviewMode]); /** * when screen height is changed, update canvas layout @@ -261,19 +261,20 @@ export const useDynamicAppLayout = () => { isExplorerPinned, propertyPaneWidth, isAppSettingsPaneOpen, + currentPageId, //TODO: preet - remove this after first merge. ]); - useEffect(() => { - dispatch( - updateLayoutForMobileBreakpointAction( - MAIN_CONTAINER_WIDGET_ID, - appPositioningType === AppPositioningTypes.AUTO - ? mainCanvasProps?.isMobile - : false, - calculateCanvasWidth(), - ), - ); - }, [mainCanvasProps?.isMobile, appPositioningType]); + // useEffect(() => { + // dispatch( + // updateLayoutForMobileBreakpointAction( + // MAIN_CONTAINER_WIDGET_ID, + // appPositioningType === AppPositioningTypes.AUTO + // ? mainCanvasProps?.isMobile + // : false, + // calculateCanvasWidth(), + // ), + // ); + // }, [mainCanvasProps?.isMobile, appPositioningType]); return isCanvasInitialized; }; From 68e8a5627c25896b046a86790e0f6d428de8026c Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 2 Mar 2023 18:40:29 +0530 Subject: [PATCH 591/708] update useAutoLayout condition --- app/client/src/widgets/ContainerWidget/widget/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/client/src/widgets/ContainerWidget/widget/index.tsx b/app/client/src/widgets/ContainerWidget/widget/index.tsx index b90fb7c1408b..9812e5425b79 100644 --- a/app/client/src/widgets/ContainerWidget/widget/index.tsx +++ b/app/client/src/widgets/ContainerWidget/widget/index.tsx @@ -204,7 +204,7 @@ export class ContainerWidget extends BaseWidget< childWidget.positioning = childWidget?.positioning || this.props.positioning; childWidget.useAutoLayout = this.props.positioning - ? this.props.positioning !== Positioning.Fixed + ? this.props.positioning === Positioning.Vertical : false; return WidgetFactory.createWidget(childWidget, this.props.renderMode); From f740cc02b957e61a4b452fe999acdfa1848c4d54 Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 2 Mar 2023 21:47:00 +0530 Subject: [PATCH 592/708] revert widgetname component color --- app/client/src/constants/Colors.tsx | 3 ++- app/client/src/widgets/CanvasWidget.tsx | 3 --- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/app/client/src/constants/Colors.tsx b/app/client/src/constants/Colors.tsx index fd2bd5baa57d..7ef8eb4f0d94 100644 --- a/app/client/src/constants/Colors.tsx +++ b/app/client/src/constants/Colors.tsx @@ -75,7 +75,8 @@ export const Colors = { SOLID_MERCURY: "#E5E5E5", TROUT_DARK: "#535B62", ALABASTER: "#F9F8F8", - WATUSI: "#FF9B4E", + // WATUSI: "#FF9B4E", + WATUSI: "FFE0D2", GRAY: "#858282", GRAY2: "#939090", DOVE_GRAY2: "#716e6e", diff --git a/app/client/src/widgets/CanvasWidget.tsx b/app/client/src/widgets/CanvasWidget.tsx index 01cdc13620e0..731f6c95cf62 100644 --- a/app/client/src/widgets/CanvasWidget.tsx +++ b/app/client/src/widgets/CanvasWidget.tsx @@ -26,9 +26,6 @@ class CanvasWidget extends ContainerWidget { static getWidgetType() { return "CANVAS_WIDGET"; } - componentDidMount(): void { - super.componentDidMount(); - } getCanvasProps(): DSLWidget & { minHeight: number } { return { From 58c0db08639be225d31ce6892e4905525deab517 Mon Sep 17 00:00:00 2001 From: Preet Date: Thu, 2 Mar 2023 22:54:29 +0530 Subject: [PATCH 593/708] fix hover color --- app/client/src/constants/Colors.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/client/src/constants/Colors.tsx b/app/client/src/constants/Colors.tsx index 7ef8eb4f0d94..9096428d85be 100644 --- a/app/client/src/constants/Colors.tsx +++ b/app/client/src/constants/Colors.tsx @@ -76,7 +76,7 @@ export const Colors = { TROUT_DARK: "#535B62", ALABASTER: "#F9F8F8", // WATUSI: "#FF9B4E", - WATUSI: "FFE0D2", + WATUSI: "#FFE0D2", GRAY: "#858282", GRAY2: "#939090", DOVE_GRAY2: "#716e6e", From 7c31f61b219eb0ad86439ff60950732acc13c317 Mon Sep 17 00:00:00 2001 From: Preet Date: Fri, 3 Mar 2023 09:21:05 +0530 Subject: [PATCH 594/708] temp: cypress fixes --- .../Widgets/JSONForm/JSONForm_Footer_spec.js | 6 ++-- .../Widgets/Multiselect/MultiSelect3_spec.js | 2 +- .../Widgets/Widgets_Labels_spec.js | 36 ++++++++++++------- 3 files changed, 28 insertions(+), 16 deletions(-) diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/JSONForm/JSONForm_Footer_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/JSONForm/JSONForm_Footer_spec.js index c6bfa82588ab..11cc4a2e221e 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/JSONForm/JSONForm_Footer_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/JSONForm/JSONForm_Footer_spec.js @@ -46,7 +46,7 @@ describe("JSONForm Footer spec", () => { cy.get(".t--jsonform-footer").then(($footer) => { const gap = $footer.prop("offsetTop") - $body.prop("scrollHeight"); - expect(gap).equals(1); + expect(gap).equals(0); }); }); }); @@ -72,7 +72,7 @@ describe("JSONForm Footer spec", () => { $footer.prop("offsetHeight") - $form.prop("offsetHeight"); - expect(gap).equals(1); + expect(gap).equals(0); }); }); }); @@ -91,7 +91,7 @@ describe("JSONForm Footer spec", () => { $footer.prop("offsetHeight") - $form.prop("scrollHeight"); - expect(gap).equals(1); + expect(gap).equals(0); }); }); }); diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Multiselect/MultiSelect3_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Multiselect/MultiSelect3_spec.js index a3f120d392f0..5d36c29ceab8 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Multiselect/MultiSelect3_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Multiselect/MultiSelect3_spec.js @@ -28,7 +28,7 @@ describe("Dropdown Widget Functionality", function() { .find(widgetLocators.menuButton) .invoke("outerWidth") .then((width) => { - expect(parseInt(width)).to.equal(146); + expect(parseInt(width)).to.equal(147); }); cy.get(formWidgetsPage.menuButtonWidget) .find(widgetLocators.menuButton) diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Widgets_Labels_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Widgets_Labels_spec.js index 70642df2991f..a2738d4d040c 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Widgets_Labels_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Widgets_Labels_spec.js @@ -8,7 +8,8 @@ describe("Label feature", () => { it("CheckboxGroupWidget label properties: Text, Position, Alignment, Width", () => { const options = { widgetName: "checkboxgroupwidget", - parentColumnSpace: 11.90625, + // parentColumnSpace: 11.90625, + parentColumnSpace: 11.9375, containerSelector: "[data-testid='checkboxgroup-container']", isCompact: true, labelText: "Name", @@ -21,7 +22,8 @@ describe("Label feature", () => { it("CurrencyInputWidget label properties: Text, Position, Alignment, Width", () => { const options = { widgetName: "currencyinputwidget", - parentColumnSpace: 11.90625, + // parentColumnSpace: 11.90625, + parentColumnSpace: 11.9375, containerSelector: "[data-testid='input-container']", isCompact: true, labelText: "Name", @@ -34,7 +36,8 @@ describe("Label feature", () => { it("DatePickerWidget2 label properties: Text, Position, Alignment, Width", () => { const options = { widgetName: "datepickerwidget2", - parentColumnSpace: 11.90625, + // parentColumnSpace: 11.90625, + parentColumnSpace: 11.9375, containerSelector: "[data-testid='datepicker-container']", isCompact: true, labelText: "Name", @@ -47,7 +50,8 @@ describe("Label feature", () => { it("InputWidgetV2 label properties: Text, Position, Alignment, Width", () => { const options = { widgetName: "inputwidgetv2", - parentColumnSpace: 11.90625, + // parentColumnSpace: 11.90625, + parentColumnSpace: 11.9375, containerSelector: "[data-testid='input-container']", isCompact: true, labelText: "Name", @@ -60,7 +64,8 @@ describe("Label feature", () => { it("MultiSelectTreeWidget label properties: Text, Position, Alignment, Width", () => { const options = { widgetName: "multiselecttreewidget", - parentColumnSpace: 11.90625, + // parentColumnSpace: 11.90625, + parentColumnSpace: 11.9375, containerSelector: "[data-testid='multitreeselect-container']", isCompact: true, labelText: "Name", @@ -73,7 +78,8 @@ describe("Label feature", () => { it("MultiSelectWidgetV2 label properties: Text, Position, Alignment, Width", () => { const options = { widgetName: "multiselectwidgetv2", - parentColumnSpace: 11.90625, + // parentColumnSpace: 11.90625, + parentColumnSpace: 11.9375, containerSelector: "[data-testid='multiselect-container']", isCompact: true, labelText: "Name", @@ -86,7 +92,8 @@ describe("Label feature", () => { it("PhoneInputWidget label properties: Text, Position, Alignment, Width", () => { const options = { widgetName: "phoneinputwidget", - parentColumnSpace: 11.90625, + // parentColumnSpace: 11.90625, + parentColumnSpace: 11.9375, containerSelector: "[data-testid='input-container']", isCompact: true, labelText: "Name", @@ -99,7 +106,8 @@ describe("Label feature", () => { it("RadioGroupWidget label properties: Text, Position, Alignment, Width", () => { const options = { widgetName: "radiogroupwidget", - parentColumnSpace: 11.90625, + // parentColumnSpace: 11.90625, + parentColumnSpace: 11.9375, containerSelector: "[data-testid='radiogroup-container']", isCompact: true, labelText: "Name", @@ -112,7 +120,8 @@ describe("Label feature", () => { it("RichTextEditorWidget label properties: Text, Position, Alignment, Width", () => { const options = { widgetName: "richtexteditorwidget", - parentColumnSpace: 11.90625, + // parentColumnSpace: 11.90625, + parentColumnSpace: 11.9375, containerSelector: "[data-testid='rte-container']", isCompact: false, labelText: "Name", @@ -125,7 +134,8 @@ describe("Label feature", () => { it("SelectWidget label properties: Text, Position, Alignment, Width", () => { const options = { widgetName: "selectwidget", - parentColumnSpace: 11.90625, + // parentColumnSpace: 11.90625, + parentColumnSpace: 11.9375, containerSelector: "[data-testid='select-container']", isCompact: true, labelText: "Name", @@ -138,7 +148,8 @@ describe("Label feature", () => { it("SingleSelectTreeWidget label properties: Text, Position, Alignment, Width", () => { const options = { widgetName: "singleselecttreewidget", - parentColumnSpace: 11.90625, + // parentColumnSpace: 11.90625, + parentColumnSpace: 11.9375, containerSelector: "[data-testid='treeselect-container']", isCompact: true, labelText: "Name", @@ -151,7 +162,8 @@ describe("Label feature", () => { it("SwitchGroupWidget label properties: Text, Position, Alignment, Width", () => { const options = { widgetName: "switchgroupwidget", - parentColumnSpace: 11.90625, + // parentColumnSpace: 11.90625, + parentColumnSpace: 11.9375, containerSelector: "[data-testid='switchgroup-container']", isCompact: true, labelText: "Name", From 82a3d4bb7cd56514d74f768cfaf907a39309e94c Mon Sep 17 00:00:00 2001 From: Preet Date: Fri, 3 Mar 2023 09:51:10 +0530 Subject: [PATCH 595/708] temp: remove canvas positioning selector --- app/client/src/selectors/editorSelectors.tsx | 40 +++++++++++--------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/app/client/src/selectors/editorSelectors.tsx b/app/client/src/selectors/editorSelectors.tsx index eb53721dc71a..b46ca791ce16 100644 --- a/app/client/src/selectors/editorSelectors.tsx +++ b/app/client/src/selectors/editorSelectors.tsx @@ -14,7 +14,7 @@ import { WidgetCardProps, WidgetProps } from "widgets/BaseWidget"; import { Page } from "@appsmith/constants/ReduxActionConstants"; import { ApplicationVersion } from "actions/applicationActions"; -import { Positioning } from "utils/autoLayout/constants"; +// import { Positioning } from "utils/autoLayout/constants"; import { OccupiedSpace, WidgetSpace } from "constants/CanvasEditorConstants"; import { PLACEHOLDER_APP_SLUG, PLACEHOLDER_PAGE_SLUG } from "constants/routes"; import { @@ -236,29 +236,35 @@ const defaultLayout: AppLayoutConfig = { const getAppLayout = (state: AppState) => state.ui.applications.currentApplication?.appLayout || defaultLayout; -export const getMainCanvasPositioning = createSelector( - getWidgets, - (widgets) => { - return ( - widgets && - widgets[MAIN_CONTAINER_WIDGET_ID] && - widgets[MAIN_CONTAINER_WIDGET_ID].positioning - ); - }, -); +// export const getMainCanvasPositioning = createSelector( +// getWidgets, +// (widgets) => { +// return ( +// widgets && +// widgets[MAIN_CONTAINER_WIDGET_ID] && +// widgets[MAIN_CONTAINER_WIDGET_ID].positioning +// ); +// }, +// ); export const isAutoLayoutEnabled = (state: AppState): boolean => { return state.ui.users.featureFlag.data.AUTO_LAYOUT === true; }; +// export const getCurrentAppPositioningType = createSelector( +// () => AppPositioningTypes.FIXED, +// ); + export const getCurrentAppPositioningType = createSelector( - getMainCanvasPositioning, + // getMainCanvasPositioning, + // isAutoLayoutEnabled, + // (positioning: any, autoLayoutEnabled: boolean): AppPositioningTypes => { + // return positioning && positioning !== Positioning.Fixed && autoLayoutEnabled + // ? AppPositioningTypes.AUTO + // : AppPositioningTypes.FIXED; + // }, isAutoLayoutEnabled, - (positioning: any, autoLayoutEnabled: boolean): AppPositioningTypes => { - return positioning && positioning !== Positioning.Fixed && autoLayoutEnabled - ? AppPositioningTypes.AUTO - : AppPositioningTypes.FIXED; - }, + (): AppPositioningTypes => AppPositioningTypes.FIXED, ); export const getCurrentApplicationLayout = createSelector( From b3da788d0d0642950fddfe1902c618759a53faaf Mon Sep 17 00:00:00 2001 From: Preet Date: Fri, 3 Mar 2023 10:16:48 +0530 Subject: [PATCH 596/708] fix import order --- .../constants/PropertyControlConstants.tsx | 9 +- .../Editor/PropertyPane/PropertyControl.tsx | 59 +++---- .../hooks/useBlocksToBeDraggedOnCanvas.ts | 40 ++--- app/client/src/sagas/WidgetAdditionSagas.ts | 36 ++--- app/client/src/sagas/WidgetDeletionSagas.ts | 34 ++-- app/client/src/sagas/WidgetOperationSagas.tsx | 148 +++++++++--------- 6 files changed, 164 insertions(+), 162 deletions(-) diff --git a/app/client/src/constants/PropertyControlConstants.tsx b/app/client/src/constants/PropertyControlConstants.tsx index bf23d1c675d1..1ec66feb43a4 100644 --- a/app/client/src/constants/PropertyControlConstants.tsx +++ b/app/client/src/constants/PropertyControlConstants.tsx @@ -1,15 +1,14 @@ -import { ReduxActionType } from "@appsmith/constants/ReduxActionConstants"; -import { UpdateWidgetPropertyPayload } from "actions/controlActions"; -import { ReduxAction } from "ce/constants/ReduxActionConstants"; -import { CodeEditorExpected } from "components/editorComponents/CodeEditor"; import { getPropertyControlTypes } from "components/propertyControls"; import { ValidationResponse, ValidationTypes, } from "constants/WidgetValidation"; +import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; +import { CodeEditorExpected } from "components/editorComponents/CodeEditor"; +import { UpdateWidgetPropertyPayload } from "actions/controlActions"; import { AdditionalDynamicDataTree } from "utils/autocomplete/customTreeTypeDefCreator"; import { Stylesheet } from "entities/AppTheming"; -import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; +import { ReduxActionType } from "@appsmith/constants/ReduxActionConstants"; const ControlTypes = getPropertyControlTypes(); export type ControlType = typeof ControlTypes[keyof typeof ControlTypes]; diff --git a/app/client/src/pages/Editor/PropertyPane/PropertyControl.tsx b/app/client/src/pages/Editor/PropertyPane/PropertyControl.tsx index ce5dda21125b..f8ba0c4ad263 100644 --- a/app/client/src/pages/Editor/PropertyPane/PropertyControl.tsx +++ b/app/client/src/pages/Editor/PropertyPane/PropertyControl.tsx @@ -1,11 +1,17 @@ -import equal from "fast-deep-equal/es6"; +import React, { memo, useCallback, useEffect, useRef } from "react"; import _, { get, isFunction } from "lodash"; +import equal from "fast-deep-equal/es6"; import * as log from "loglevel"; -import React, { memo, useCallback, useEffect, useRef } from "react"; -import { JS_TOGGLE_DISABLED_MESSAGE } from "@appsmith/constants/messages"; -import { AppState } from "@appsmith/reducers"; -import { IPanelProps } from "@blueprintjs/core"; +import { + ControlPropertyLabelContainer, + ControlWrapper, +} from "components/propertyControls/StyledControls"; +import { JSToggleButton } from "design-system-old"; +import PropertyControlFactory from "utils/PropertyControlFactory"; +import PropertyHelpLabel from "pages/Editor/PropertyPane/PropertyHelpLabel"; +import { useDispatch, useSelector } from "react-redux"; +import AnalyticsUtil from "utils/AnalyticsUtil"; import { batchUpdateMultipleWidgetProperties, batchUpdateWidgetProperty, @@ -13,47 +19,42 @@ import { setWidgetDynamicProperty, UpdateWidgetPropertyPayload, } from "actions/controlActions"; -import { setFocusablePropertyPaneField } from "actions/propertyPaneActions"; -import { ReactComponent as ResetIcon } from "assets/icons/control/undo_2.svg"; -import { EditorTheme } from "components/editorComponents/CodeEditor/EditorConfig"; -import { ControlData } from "components/propertyControls/BaseControl"; -import { - ControlPropertyLabelContainer, - ControlWrapper, -} from "components/propertyControls/StyledControls"; import { PropertyHookUpdates, PropertyPaneControlConfig, } from "constants/PropertyControlConstants"; -import { JSToggleButton, TooltipComponent } from "design-system-old"; -import { ENTITY_TYPE } from "entities/AppsmithConsole"; -import LOG_TYPE from "entities/AppsmithConsole/logtype"; -import PropertyHelpLabel from "pages/Editor/PropertyPane/PropertyHelpLabel"; -import { useDispatch, useSelector } from "react-redux"; +import { IPanelProps } from "@blueprintjs/core"; +import PanelPropertiesEditor from "./PanelPropertiesEditor"; +import { + DynamicPath, + getEvalValuePath, + isDynamicValue, + THEME_BINDING_REGEX, +} from "utils/DynamicBindingUtils"; import { getShouldFocusPropertyPath, getWidgetPropsForPropertyName, WidgetProperties, } from "selectors/propertyPaneSelectors"; import { EnhancementFns } from "selectors/widgetEnhancementSelectors"; -import AnalyticsUtil from "utils/AnalyticsUtil"; +import { EditorTheme } from "components/editorComponents/CodeEditor/EditorConfig"; import AppsmithConsole from "utils/AppsmithConsole"; +import { ENTITY_TYPE } from "entities/AppsmithConsole"; +import LOG_TYPE from "entities/AppsmithConsole/logtype"; +import { getExpectedValue } from "utils/validation/common"; +import { ControlData } from "components/propertyControls/BaseControl"; +import { AppState } from "@appsmith/reducers"; import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService"; -import { - DynamicPath, - getEvalValuePath, - isDynamicValue, - THEME_BINDING_REGEX, -} from "utils/DynamicBindingUtils"; +import { TooltipComponent } from "design-system-old"; +import { ReactComponent as ResetIcon } from "assets/icons/control/undo_2.svg"; +import { JS_TOGGLE_DISABLED_MESSAGE } from "@appsmith/constants/messages"; import { getPropertyControlFocusElement, shouldFocusOnPropertyControl, } from "utils/editorContextUtils"; -import PropertyControlFactory from "utils/PropertyControlFactory"; -import { getExpectedValue } from "utils/validation/common"; -import WidgetFactory from "utils/WidgetFactory"; -import PanelPropertiesEditor from "./PanelPropertiesEditor"; import PropertyPaneHelperText from "./PropertyPaneHelperText"; +import { setFocusablePropertyPaneField } from "actions/propertyPaneActions"; +import WidgetFactory from "utils/WidgetFactory"; import { AdditionalDynamicDataTree } from "utils/autocomplete/customTreeTypeDefCreator"; type Props = PropertyPaneControlConfig & { diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts b/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts index 31b5b158d329..946a8f06a945 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts @@ -1,36 +1,36 @@ -import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants"; -import { AppState } from "@appsmith/reducers"; -import { stopReflowAction } from "actions/reflowActions"; -import { AlignItems, LayoutDirection } from "utils/autoLayout/constants"; -import { DropTargetContext } from "components/editorComponents/DropTargetComponent"; -import { EditorContext } from "components/editorComponents/EditorContextProvider"; -import { OccupiedSpace, WidgetSpace } from "constants/CanvasEditorConstants"; +import { useContext, useEffect, useRef } from "react"; import { CONTAINER_GRID_PADDING, GridDefaults, MAIN_CONTAINER_WIDGET_ID, } from "constants/WidgetConstants"; -import equal from "fast-deep-equal/es6"; -import { isEmpty } from "lodash"; -import { CanvasDraggingArenaProps } from "pages/common/CanvasArenas/CanvasDraggingArena"; -import { useContext, useEffect, useRef } from "react"; -import { useDispatch, useSelector } from "react-redux"; -import { DragDetails } from "reducers/uiReducers/dragResizeReducer"; -import { getDragDetails, getWidgetByID, getWidgets } from "sagas/selectors"; +import { AppState } from "@appsmith/reducers"; +import { getSelectedWidgets } from "selectors/ui"; import { getOccupiedSpacesWhileMoving } from "selectors/editorSelectors"; import { getTableFilterState } from "selectors/tableFilterSelectors"; -import { getSelectedWidgets } from "selectors/ui"; -import { getIsReflowing } from "selectors/widgetReflowSelectors"; -import AnalyticsUtil from "utils/AnalyticsUtil"; -import { snapToGrid } from "utils/helpers"; -import { useWidgetSelection } from "utils/hooks/useWidgetSelection"; +import { OccupiedSpace, WidgetSpace } from "constants/CanvasEditorConstants"; +import { getDragDetails, getWidgetByID, getWidgets } from "sagas/selectors"; import { getDropZoneOffsets, WidgetOperationParams, widgetOperationParams, } from "utils/WidgetPropsUtils"; -import { XYCord } from "./useRenderBlocksOnCanvas"; +import { DropTargetContext } from "components/editorComponents/DropTargetComponent"; +import { isEmpty } from "lodash"; +import equal from "fast-deep-equal/es6"; +import { CanvasDraggingArenaProps } from "pages/common/CanvasArenas/CanvasDraggingArena"; +import { useDispatch, useSelector } from "react-redux"; +import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants"; +import { EditorContext } from "components/editorComponents/EditorContextProvider"; +import { useWidgetSelection } from "utils/hooks/useWidgetSelection"; +import AnalyticsUtil from "utils/AnalyticsUtil"; +import { snapToGrid } from "utils/helpers"; +import { stopReflowAction } from "actions/reflowActions"; +import { DragDetails } from "reducers/uiReducers/dragResizeReducer"; +import { getIsReflowing } from "selectors/widgetReflowSelectors"; +import { XYCord } from "pages/common/CanvasArenas/hooks/useRenderBlocksOnCanvas"; import { SelectionRequestType } from "sagas/WidgetSelectUtils"; +import { AlignItems, LayoutDirection } from "utils/autoLayout/constants"; import { HighlightInfo } from "utils/autoLayout/autoLayoutTypes"; export interface WidgetDraggingUpdateParams extends WidgetDraggingBlock { diff --git a/app/client/src/sagas/WidgetAdditionSagas.ts b/app/client/src/sagas/WidgetAdditionSagas.ts index 1463612972b7..0a8de6cc6b6b 100644 --- a/app/client/src/sagas/WidgetAdditionSagas.ts +++ b/app/client/src/sagas/WidgetAdditionSagas.ts @@ -1,38 +1,22 @@ +import { updateAndSaveLayout, WidgetAddChild } from "actions/pageActions"; +import { Toaster } from "design-system-old"; import { ReduxAction, ReduxActionErrorTypes, ReduxActionTypes, WidgetReduxActionTypes, } from "@appsmith/constants/ReduxActionConstants"; -import { generateAutoHeightLayoutTreeAction } from "actions/autoHeightActions"; -import { updateAndSaveLayout, WidgetAddChild } from "actions/pageActions"; import { RenderModes } from "constants/WidgetConstants"; -import { Toaster } from "design-system-old"; import { ENTITY_TYPE } from "entities/AppsmithConsole"; -import { DataTree } from "entities/DataTree/dataTreeFactory"; -import produce from "immer"; -import { klona as clone } from "klona/full"; -import omit from "lodash/omit"; -import log from "loglevel"; import { CanvasWidgetsReduxState, FlattenedWidgetProps, } from "reducers/entityReducers/canvasWidgetsReducer"; import { WidgetBlueprint } from "reducers/entityReducers/widgetConfigReducer"; import { all, call, put, select, takeEvery } from "redux-saga/effects"; -import { getDataTree } from "selectors/dataTreeSelectors"; import AppsmithConsole from "utils/AppsmithConsole"; import { getNextEntityName } from "utils/AppsmithUtils"; -import { ResponsiveBehavior } from "utils/autoLayout/constants"; -import { generateReactKey } from "utils/generators"; -import WidgetFactory from "utils/WidgetFactory"; import { generateWidgetProps } from "utils/WidgetPropsUtils"; -import { WidgetProps } from "widgets/BaseWidget"; -import { - BlueprintOperationTypes, - GRID_DENSITY_MIGRATION_V1, -} from "widgets/constants"; -import { isStack } from "../utils/autoLayout/AutoLayoutUtils"; import { getWidget, getWidgets } from "./selectors"; import { buildWidgetBlueprint, @@ -40,7 +24,23 @@ import { executeWidgetBlueprintOperations, traverseTreeAndExecuteBlueprintChildOperations, } from "./WidgetBlueprintSagas"; +import log from "loglevel"; +import { getDataTree } from "selectors/dataTreeSelectors"; +import { generateReactKey } from "utils/generators"; +import { WidgetProps } from "widgets/BaseWidget"; +import WidgetFactory from "utils/WidgetFactory"; +import omit from "lodash/omit"; +import produce from "immer"; +import { + GRID_DENSITY_MIGRATION_V1, + BlueprintOperationTypes, +} from "widgets/constants"; import { getPropertiesToUpdate } from "./WidgetOperationSagas"; +import { klona as clone } from "klona/full"; +import { DataTree } from "entities/DataTree/dataTreeFactory"; +import { generateAutoHeightLayoutTreeAction } from "actions/autoHeightActions"; +import { ResponsiveBehavior } from "utils/autoLayout/constants"; +import { isStack } from "../utils/autoLayout/AutoLayoutUtils"; const WidgetTypes = WidgetFactory.widgetTypes; diff --git a/app/client/src/sagas/WidgetDeletionSagas.ts b/app/client/src/sagas/WidgetDeletionSagas.ts index 39fdc867361c..8418d4dc14f9 100644 --- a/app/client/src/sagas/WidgetDeletionSagas.ts +++ b/app/client/src/sagas/WidgetDeletionSagas.ts @@ -1,11 +1,3 @@ -import { - ReduxAction, - ReduxActionErrorTypes, - ReduxActionTypes, - WidgetReduxActionTypes, -} from "@appsmith/constants/ReduxActionConstants"; -import { generateAutoHeightLayoutTreeAction } from "actions/autoHeightActions"; -import { toggleShowDeviationDialog } from "actions/onboardingActions"; import { MultipleWidgetDeletePayload, updateAndSaveLayout, @@ -13,6 +5,12 @@ import { } from "actions/pageActions"; import { closePropertyPane, closeTableFilterPane } from "actions/widgetActions"; import { selectWidgetInitAction } from "actions/widgetSelectionActions"; +import { + ReduxAction, + ReduxActionErrorTypes, + ReduxActionTypes, + WidgetReduxActionTypes, +} from "@appsmith/constants/ReduxActionConstants"; import { ENTITY_TYPE } from "entities/AppsmithConsole"; import LOG_TYPE from "entities/AppsmithConsole/logtype"; import { flattenDeep, omit, orderBy } from "lodash"; @@ -21,25 +19,27 @@ import { FlattenedWidgetProps, } from "reducers/entityReducers/canvasWidgetsReducer"; import { all, call, put, select, takeEvery } from "redux-saga/effects"; -import { SelectionRequestType } from "sagas/WidgetSelectUtils"; -import { getIsMobile } from "selectors/mainCanvasSelectors"; -import { - inGuidedTour, - isExploringSelector, -} from "selectors/onboardingSelectors"; import { getSelectedWidgets } from "selectors/ui"; import AnalyticsUtil from "utils/AnalyticsUtil"; import AppsmithConsole from "utils/AppsmithConsole"; -import { showUndoRedoToast } from "utils/replayHelpers"; -import WidgetFactory from "utils/WidgetFactory"; import { WidgetProps } from "widgets/BaseWidget"; -import { updateFlexLayersOnDelete } from "../utils/autoLayout/AutoLayoutUtils"; import { getSelectedWidget, getWidget, getWidgets } from "./selectors"; import { getAllWidgetsInTree, updateListWidgetPropertiesOnChildDelete, WidgetsInTree, } from "./WidgetOperationUtils"; +import { showUndoRedoToast } from "utils/replayHelpers"; +import WidgetFactory from "utils/WidgetFactory"; +import { + inGuidedTour, + isExploringSelector, +} from "selectors/onboardingSelectors"; +import { toggleShowDeviationDialog } from "actions/onboardingActions"; +import { generateAutoHeightLayoutTreeAction } from "actions/autoHeightActions"; +import { SelectionRequestType } from "sagas/WidgetSelectUtils"; +import { getIsMobile } from "selectors/mainCanvasSelectors"; +import { updateFlexLayersOnDelete } from "../utils/autoLayout/AutoLayoutUtils"; const WidgetTypes = WidgetFactory.widgetTypes; diff --git a/app/client/src/sagas/WidgetOperationSagas.tsx b/app/client/src/sagas/WidgetOperationSagas.tsx index ac80bb6ba018..a6bd638e7174 100644 --- a/app/client/src/sagas/WidgetOperationSagas.tsx +++ b/app/client/src/sagas/WidgetOperationSagas.tsx @@ -5,27 +5,12 @@ import { ReduxActionTypes, WidgetReduxActionTypes, } from "@appsmith/constants/ReduxActionConstants"; -import { - batchUpdateWidgetProperty, - DeleteWidgetPropertyPayload, - SetWidgetDynamicPropertyPayload, - updateMultipleWidgetPropertiesAction, - UpdateWidgetPropertyPayload, - UpdateWidgetPropertyRequestPayload, -} from "actions/controlActions"; -import { resetWidgetMetaProperty } from "actions/metaActions"; import { updateAndSaveLayout, WidgetResize } from "actions/pageActions"; -import { - GridDefaults, - MAIN_CONTAINER_WIDGET_ID, - RenderModes, -} from "constants/WidgetConstants"; -import _, { cloneDeep, isString, set, uniq } from "lodash"; -import log from "loglevel"; import { CanvasWidgetsReduxState, FlattenedWidgetProps, } from "reducers/entityReducers/canvasWidgetsReducer"; +import { getWidget, getWidgets, getWidgetsMeta } from "./selectors"; import { actionChannel, all, @@ -38,13 +23,15 @@ import { takeLatest, takeLeading, } from "redux-saga/effects"; -import { - getContainerWidgetSpacesSelector, - getCurrentAppPositioningType, - getCurrentPageId, -} from "selectors/editorSelectors"; -import AnalyticsUtil from "utils/AnalyticsUtil"; import { convertToString } from "utils/AppsmithUtils"; +import { + batchUpdateWidgetProperty, + DeleteWidgetPropertyPayload, + SetWidgetDynamicPropertyPayload, + updateMultipleWidgetPropertiesAction, + UpdateWidgetPropertyPayload, + UpdateWidgetPropertyRequestPayload, +} from "actions/controlActions"; import { DynamicPath, getEntityDynamicBindingPathList, @@ -55,66 +42,45 @@ import { isPathADynamicBinding, isPathDynamicTrigger, } from "utils/DynamicBindingUtils"; -import { generateReactKey } from "utils/generators"; -import { getCopiedWidgets, saveCopiedWidgets } from "utils/storage"; import { WidgetProps } from "widgets/BaseWidget"; +import _, { cloneDeep, isString, set, uniq } from "lodash"; +import WidgetFactory from "utils/WidgetFactory"; +import { resetWidgetMetaProperty } from "actions/metaActions"; +import { + GridDefaults, + MAIN_CONTAINER_WIDGET_ID, + RenderModes, +} from "constants/WidgetConstants"; +import { getCopiedWidgets, saveCopiedWidgets } from "utils/storage"; +import { generateReactKey } from "utils/generators"; +import AnalyticsUtil from "utils/AnalyticsUtil"; +import log from "loglevel"; +import { + getCurrentPageId, + getCurrentAppPositioningType, + getContainerWidgetSpacesSelector, +} from "selectors/editorSelectors"; +import { selectWidgetInitAction } from "actions/widgetSelectionActions"; + +import { getDataTree } from "selectors/dataTreeSelectors"; +import { validateProperty } from "./EvaluationsSaga"; +import { Toaster, Variant } from "design-system-old"; +import { ColumnProperties } from "widgets/TableWidget/component/Constants"; +import { + getAllPathsFromPropertyConfig, + nextAvailableRowInContainer, +} from "entities/Widget/utils"; +import { getAllPaths } from "@appsmith/workers/Evaluation/evaluationUtils"; import { createMessage, - ERROR_WIDGET_COPY_NOT_ALLOWED, ERROR_WIDGET_COPY_NO_WIDGET_SELECTED, + ERROR_WIDGET_COPY_NOT_ALLOWED, ERROR_WIDGET_CUT_NO_WIDGET_SELECTED, WIDGET_COPY, WIDGET_CUT, ERROR_WIDGET_CUT_NOT_ALLOWED, } from "@appsmith/constants/messages"; -import { getAllPaths } from "@appsmith/workers/Evaluation/evaluationUtils"; -import { Toaster, Variant } from "design-system-old"; -import { - getAllPathsFromPropertyConfig, - nextAvailableRowInContainer, -} from "entities/Widget/utils"; -import { getDataTree } from "selectors/dataTreeSelectors"; -import { ColumnProperties } from "widgets/TableWidget/component/Constants"; -import { validateProperty } from "./EvaluationsSaga"; -import { generateAutoHeightLayoutTreeAction } from "actions/autoHeightActions"; -import { stopReflowAction } from "actions/reflowActions"; -import { selectWidgetInitAction } from "actions/widgetSelectionActions"; -import { WidgetSpace } from "constants/CanvasEditorConstants"; -import { getSlidingArenaName } from "constants/componentClassNameConstants"; -import { DataTree } from "entities/DataTree/dataTreeFactory"; -import { MetaState } from "reducers/entityReducers/metaReducer"; -import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; -import { widgetReflow } from "reducers/uiReducers/reflowReducer"; -import { reflow } from "reflow"; -import { - GridProps, - PrevReflowState, - ReflowDirection, - SpaceMap, -} from "reflow/reflowTypes"; -import { getBottomMostRow } from "reflow/reflowUtils"; -import { builderURL } from "RouteBuilder"; -import { getIsMobile } from "selectors/mainCanvasSelectors"; -import { getSelectedWidgets } from "selectors/ui"; -import { getReflow } from "selectors/widgetReflowSelectors"; -import { updateWidgetPositions } from "utils/autoLayout/positionUtils"; -import { flashElementsById } from "utils/helpers"; -import history from "utils/history"; -import { - collisionCheckPostReflow, - getBottomRowAfterReflow, -} from "utils/reflowHookUtils"; -import WidgetFactory from "utils/WidgetFactory"; -import { - addChildToPastedFlexLayers, - isStack, - pasteWidgetInFlexLayers, -} from "../utils/autoLayout/AutoLayoutUtils"; -import { getCanvasSizeAfterWidgetMove } from "./CanvasSagas/DraggingCanvasSagas"; -import { getWidget, getWidgets, getWidgetsMeta } from "./selectors"; -import widgetAdditionSagas from "./WidgetAdditionSagas"; -import widgetDeletionSagas from "./WidgetDeletionSagas"; import { changeIdsOfPastePositions, CopiedWidgetGroup, @@ -150,14 +116,50 @@ import { purgeOrphanedDynamicPaths, WIDGET_PASTE_PADDING, } from "./WidgetOperationUtils"; +import { getSelectedWidgets } from "selectors/ui"; import { widgetSelectionSagas } from "./WidgetSelectionSagas"; -import { SelectionRequestType } from "./WidgetSelectUtils"; +import { DataTree } from "entities/DataTree/dataTreeFactory"; +import { getCanvasSizeAfterWidgetMove } from "./CanvasSagas/DraggingCanvasSagas"; +import widgetAdditionSagas from "./WidgetAdditionSagas"; +import widgetDeletionSagas from "./WidgetDeletionSagas"; +import { getReflow } from "selectors/widgetReflowSelectors"; +import { widgetReflow } from "reducers/uiReducers/reflowReducer"; +import { stopReflowAction } from "actions/reflowActions"; +import { + collisionCheckPostReflow, + getBottomRowAfterReflow, +} from "utils/reflowHookUtils"; +import { + GridProps, + PrevReflowState, + ReflowDirection, + SpaceMap, +} from "reflow/reflowTypes"; +import { WidgetSpace } from "constants/CanvasEditorConstants"; +import { reflow } from "reflow"; +import { getBottomMostRow } from "reflow/reflowUtils"; +import { flashElementsById } from "utils/helpers"; +import { getSlidingArenaName } from "constants/componentClassNameConstants"; +import { builderURL } from "RouteBuilder"; +import history from "utils/history"; +import { generateAutoHeightLayoutTreeAction } from "actions/autoHeightActions"; import { executeWidgetBlueprintBeforeOperations, traverseTreeAndExecuteBlueprintChildOperations, } from "./WidgetBlueprintSagas"; +import { MetaState } from "reducers/entityReducers/metaReducer"; +import { SelectionRequestType } from "sagas/WidgetSelectUtils"; import { BlueprintOperationTypes } from "widgets/constants"; +import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; +import { getIsMobile } from "selectors/mainCanvasSelectors"; +import { updateWidgetPositions } from "utils/autoLayout/positionUtils"; +import { + addChildToPastedFlexLayers, + isStack, + pasteWidgetInFlexLayers, +} from "utils/autoLayout/AutoLayoutUtils"; + export function* resizeSaga(resizeAction: ReduxAction) { try { Toaster.clear(); From 02725e942cf058fd311486e0d2983637af3946b0 Mon Sep 17 00:00:00 2001 From: Preet Date: Fri, 3 Mar 2023 14:55:24 +0530 Subject: [PATCH 597/708] cypress fixes --- .../ClientSideTests/Widgets/Modal/Modal_focus_spec.js | 2 +- .../ClientSideTests/Widgets/Multiselect/MultiSelect3_spec.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Modal/Modal_focus_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Modal/Modal_focus_spec.js index 16902112a7a2..b9200c071d1e 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Modal/Modal_focus_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Modal/Modal_focus_spec.js @@ -70,7 +70,7 @@ describe("Modal focus", function() { cy.focused().should("have.value", someInputText); }); it("2. Should not focus on the input field if autofocus is disabled", () => { - cy.openPropertyPane("inputwidgetv2"); + cy.openPropertyPaneFromModal("inputwidgetv2"); // autofocus for input field is disabled cy.get(".t--property-control-autofocus") diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Multiselect/MultiSelect3_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Multiselect/MultiSelect3_spec.js index 5d36c29ceab8..9716b7a34865 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Multiselect/MultiSelect3_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Multiselect/MultiSelect3_spec.js @@ -40,7 +40,7 @@ describe("Dropdown Widget Functionality", function() { cy.get(".menu-button-popover") .invoke("outerWidth") .then((width) => { - expect(parseInt(width)).to.equal(146); + expect(parseInt(width)).to.equal(147); }); // MultiSelect From d551a2a303553baaf614527feaa4c742640def3c Mon Sep 17 00:00:00 2001 From: Preet Sidhu Date: Sat, 4 Mar 2023 14:10:49 +0530 Subject: [PATCH 598/708] rever first merge changes --- .../Widgets/JSONForm/JSONForm_Footer_spec.js | 6 +- .../Widgets/Multiselect/MultiSelect3_spec.js | 4 +- .../Widgets/Widgets_Labels_spec.js | 36 +- .../ResizeStyledComponents.autolayout.tsx | 188 ---------- .../ResizeStyledComponents.tsx | 74 ++-- app/client/src/constants/Colors.tsx | 3 +- .../pages/Editor/AppPositionTypeControl.tsx | 182 ++++----- .../Editor/WidgetsEditor/CanvasContainer.tsx | 252 ++++++------- .../src/resizable/resize/index.autolayout.tsx | 352 ------------------ app/client/src/resizable/resize/index.tsx | 54 ++- .../src/resizable/resizenreflow/index.tsx | 175 +++++---- app/client/src/selectors/editorSelectors.tsx | 40 +- .../src/utils/hooks/useDynamicAppLayout.tsx | 100 ++--- .../widgets/ModalWidget/component/index.tsx | 2 - 14 files changed, 462 insertions(+), 1006 deletions(-) delete mode 100644 app/client/src/components/editorComponents/ResizeStyledComponents.autolayout.tsx delete mode 100644 app/client/src/resizable/resize/index.autolayout.tsx diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/JSONForm/JSONForm_Footer_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/JSONForm/JSONForm_Footer_spec.js index 11cc4a2e221e..c6bfa82588ab 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/JSONForm/JSONForm_Footer_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/JSONForm/JSONForm_Footer_spec.js @@ -46,7 +46,7 @@ describe("JSONForm Footer spec", () => { cy.get(".t--jsonform-footer").then(($footer) => { const gap = $footer.prop("offsetTop") - $body.prop("scrollHeight"); - expect(gap).equals(0); + expect(gap).equals(1); }); }); }); @@ -72,7 +72,7 @@ describe("JSONForm Footer spec", () => { $footer.prop("offsetHeight") - $form.prop("offsetHeight"); - expect(gap).equals(0); + expect(gap).equals(1); }); }); }); @@ -91,7 +91,7 @@ describe("JSONForm Footer spec", () => { $footer.prop("offsetHeight") - $form.prop("scrollHeight"); - expect(gap).equals(0); + expect(gap).equals(1); }); }); }); diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Multiselect/MultiSelect3_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Multiselect/MultiSelect3_spec.js index 9716b7a34865..a3f120d392f0 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Multiselect/MultiSelect3_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Multiselect/MultiSelect3_spec.js @@ -28,7 +28,7 @@ describe("Dropdown Widget Functionality", function() { .find(widgetLocators.menuButton) .invoke("outerWidth") .then((width) => { - expect(parseInt(width)).to.equal(147); + expect(parseInt(width)).to.equal(146); }); cy.get(formWidgetsPage.menuButtonWidget) .find(widgetLocators.menuButton) @@ -40,7 +40,7 @@ describe("Dropdown Widget Functionality", function() { cy.get(".menu-button-popover") .invoke("outerWidth") .then((width) => { - expect(parseInt(width)).to.equal(147); + expect(parseInt(width)).to.equal(146); }); // MultiSelect diff --git a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Widgets_Labels_spec.js b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Widgets_Labels_spec.js index a2738d4d040c..70642df2991f 100644 --- a/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Widgets_Labels_spec.js +++ b/app/client/cypress/integration/Regression_TestSuite/ClientSideTests/Widgets/Widgets_Labels_spec.js @@ -8,8 +8,7 @@ describe("Label feature", () => { it("CheckboxGroupWidget label properties: Text, Position, Alignment, Width", () => { const options = { widgetName: "checkboxgroupwidget", - // parentColumnSpace: 11.90625, - parentColumnSpace: 11.9375, + parentColumnSpace: 11.90625, containerSelector: "[data-testid='checkboxgroup-container']", isCompact: true, labelText: "Name", @@ -22,8 +21,7 @@ describe("Label feature", () => { it("CurrencyInputWidget label properties: Text, Position, Alignment, Width", () => { const options = { widgetName: "currencyinputwidget", - // parentColumnSpace: 11.90625, - parentColumnSpace: 11.9375, + parentColumnSpace: 11.90625, containerSelector: "[data-testid='input-container']", isCompact: true, labelText: "Name", @@ -36,8 +34,7 @@ describe("Label feature", () => { it("DatePickerWidget2 label properties: Text, Position, Alignment, Width", () => { const options = { widgetName: "datepickerwidget2", - // parentColumnSpace: 11.90625, - parentColumnSpace: 11.9375, + parentColumnSpace: 11.90625, containerSelector: "[data-testid='datepicker-container']", isCompact: true, labelText: "Name", @@ -50,8 +47,7 @@ describe("Label feature", () => { it("InputWidgetV2 label properties: Text, Position, Alignment, Width", () => { const options = { widgetName: "inputwidgetv2", - // parentColumnSpace: 11.90625, - parentColumnSpace: 11.9375, + parentColumnSpace: 11.90625, containerSelector: "[data-testid='input-container']", isCompact: true, labelText: "Name", @@ -64,8 +60,7 @@ describe("Label feature", () => { it("MultiSelectTreeWidget label properties: Text, Position, Alignment, Width", () => { const options = { widgetName: "multiselecttreewidget", - // parentColumnSpace: 11.90625, - parentColumnSpace: 11.9375, + parentColumnSpace: 11.90625, containerSelector: "[data-testid='multitreeselect-container']", isCompact: true, labelText: "Name", @@ -78,8 +73,7 @@ describe("Label feature", () => { it("MultiSelectWidgetV2 label properties: Text, Position, Alignment, Width", () => { const options = { widgetName: "multiselectwidgetv2", - // parentColumnSpace: 11.90625, - parentColumnSpace: 11.9375, + parentColumnSpace: 11.90625, containerSelector: "[data-testid='multiselect-container']", isCompact: true, labelText: "Name", @@ -92,8 +86,7 @@ describe("Label feature", () => { it("PhoneInputWidget label properties: Text, Position, Alignment, Width", () => { const options = { widgetName: "phoneinputwidget", - // parentColumnSpace: 11.90625, - parentColumnSpace: 11.9375, + parentColumnSpace: 11.90625, containerSelector: "[data-testid='input-container']", isCompact: true, labelText: "Name", @@ -106,8 +99,7 @@ describe("Label feature", () => { it("RadioGroupWidget label properties: Text, Position, Alignment, Width", () => { const options = { widgetName: "radiogroupwidget", - // parentColumnSpace: 11.90625, - parentColumnSpace: 11.9375, + parentColumnSpace: 11.90625, containerSelector: "[data-testid='radiogroup-container']", isCompact: true, labelText: "Name", @@ -120,8 +112,7 @@ describe("Label feature", () => { it("RichTextEditorWidget label properties: Text, Position, Alignment, Width", () => { const options = { widgetName: "richtexteditorwidget", - // parentColumnSpace: 11.90625, - parentColumnSpace: 11.9375, + parentColumnSpace: 11.90625, containerSelector: "[data-testid='rte-container']", isCompact: false, labelText: "Name", @@ -134,8 +125,7 @@ describe("Label feature", () => { it("SelectWidget label properties: Text, Position, Alignment, Width", () => { const options = { widgetName: "selectwidget", - // parentColumnSpace: 11.90625, - parentColumnSpace: 11.9375, + parentColumnSpace: 11.90625, containerSelector: "[data-testid='select-container']", isCompact: true, labelText: "Name", @@ -148,8 +138,7 @@ describe("Label feature", () => { it("SingleSelectTreeWidget label properties: Text, Position, Alignment, Width", () => { const options = { widgetName: "singleselecttreewidget", - // parentColumnSpace: 11.90625, - parentColumnSpace: 11.9375, + parentColumnSpace: 11.90625, containerSelector: "[data-testid='treeselect-container']", isCompact: true, labelText: "Name", @@ -162,8 +151,7 @@ describe("Label feature", () => { it("SwitchGroupWidget label properties: Text, Position, Alignment, Width", () => { const options = { widgetName: "switchgroupwidget", - // parentColumnSpace: 11.90625, - parentColumnSpace: 11.9375, + parentColumnSpace: 11.90625, containerSelector: "[data-testid='switchgroup-container']", isCompact: true, labelText: "Name", diff --git a/app/client/src/components/editorComponents/ResizeStyledComponents.autolayout.tsx b/app/client/src/components/editorComponents/ResizeStyledComponents.autolayout.tsx deleted file mode 100644 index 6f95de4ec2b3..000000000000 --- a/app/client/src/components/editorComponents/ResizeStyledComponents.autolayout.tsx +++ /dev/null @@ -1,188 +0,0 @@ -import { Colors } from "constants/Colors"; -import { invisible } from "constants/DefaultTheme"; -import { WIDGET_PADDING } from "constants/WidgetConstants"; -import styled, { css } from "styled-components"; - -const EDGE_RESIZE_HANDLE_WIDTH = 12; -const CORNER_RESIZE_HANDLE_WIDTH = 10; - -export const VisibilityContainer = styled.div<{ - visible: boolean; - padding: number; - reduceOpacity: boolean; -}>` - ${(props) => (!props.visible ? invisible : "")} - height: 100%; - width: 100%; - ${({ reduceOpacity }) => - reduceOpacity && - css` - opacity: 0.25; - `} -`; - -const VerticalResizeIndicators = css<{ - showLightBorder: boolean; - isHovered: boolean; -}>` - &::after { - position: absolute; - content: ""; - width: 7px; - height: 16px; - border-radius: 50%/16%; - background: ${Colors.GREY_1}; - top: calc(50% - 8px); - left: calc(50% - 2.5px); - border: ${(props) => { - return `1px solid ${props.isHovered ? Colors.WATUSI : "#F86A2B"}`; - }}; - outline: 1px solid ${Colors.GREY_1}; - } - &:hover::after { - background: #f86a2b; - } -`; - -const HorizontalResizeIndicators = css<{ - showLightBorder: boolean; - isHovered: boolean; -}>` - &::after { - position: absolute; - content: ""; - width: 16px; - height: 7px; - border-radius: 16%/50%; - border: ${(props) => { - return `1px solid ${props.isHovered ? Colors.WATUSI : "#F86A2B"}`; - }}; - background: ${Colors.GREY_1}; - top: calc(50% - 2.5px); - left: calc(50% - 8px); - outline: 1px solid ${Colors.GREY_1}; - } - &:hover::after { - background: #f86a2b; - } -`; - -export const EdgeHandleStyles = css<{ - showAsBorder: boolean; - showLightBorder: boolean; - disableDot: boolean; - isHovered: boolean; -}>` - position: absolute; - width: ${EDGE_RESIZE_HANDLE_WIDTH}px; - height: ${EDGE_RESIZE_HANDLE_WIDTH}px; - &::before { - position: absolute; - background: "transparent"; - content: ""; - } -`; - -export const VerticalHandleStyles = css<{ - showAsBorder: boolean; - showLightBorder: boolean; - disableDot: boolean; - isHovered: boolean; -}>` - ${EdgeHandleStyles} - ${(props) => - props.showAsBorder || props.disableDot ? "" : VerticalResizeIndicators} - top:${~(WIDGET_PADDING - 1) + 1}px; - height: calc(100% + ${2 * WIDGET_PADDING - 1}px); - ${(props) => - props.showAsBorder || props.disableDot ? "" : "cursor: col-resize;"} - &:before { - left: 50%; - bottom: 0px; - top: 0; - width: 1px; - } -`; - -export const HorizontalHandleStyles = css<{ - showAsBorder: boolean; - showLightBorder: boolean; - disableDot: boolean; - isHovered: boolean; -}>` - ${EdgeHandleStyles} - ${(props) => - props.showAsBorder || props.disableDot ? "" : HorizontalResizeIndicators} - left: ${~WIDGET_PADDING + 1}px; - width: calc(100% + ${2 * WIDGET_PADDING}px); - ${(props) => - props.showAsBorder || props.disableDot ? "" : "cursor: row-resize;"} - &:before { - top: 50%; - right: 0px; - left: 0px; - height: 1px; - } -`; - -export const LeftHandleStyles = styled.div` - ${VerticalHandleStyles} - left: ${-EDGE_RESIZE_HANDLE_WIDTH / 2 - WIDGET_PADDING + 1}px; -`; - -export const RightHandleStyles = styled.div` - ${VerticalHandleStyles}; - right: ${-EDGE_RESIZE_HANDLE_WIDTH / 2 - WIDGET_PADDING + 3}px; - height: calc(100% + ${2 * WIDGET_PADDING}px); -`; - -export const TopHandleStyles = styled.div` - ${HorizontalHandleStyles}; - top: ${-EDGE_RESIZE_HANDLE_WIDTH / 2 - WIDGET_PADDING + 1}px; -`; - -export const BottomHandleStyles = styled.div` - ${HorizontalHandleStyles}; - bottom: ${-EDGE_RESIZE_HANDLE_WIDTH / 2 - WIDGET_PADDING + 3}px; -`; - -export const CornerHandleStyles = css` - position: absolute; - z-index: 3; - width: ${CORNER_RESIZE_HANDLE_WIDTH}px; - height: ${CORNER_RESIZE_HANDLE_WIDTH}px; -`; - -export const BottomRightHandleStyles = styled.div<{ - showAsBorder: boolean; -}>` - ${CornerHandleStyles}; - bottom: -${CORNER_RESIZE_HANDLE_WIDTH / 2}px; - right: -${CORNER_RESIZE_HANDLE_WIDTH / 2}px; - ${(props) => (!props.showAsBorder ? "cursor: se-resize;" : "")} -`; - -export const BottomLeftHandleStyles = styled.div<{ - showAsBorder: boolean; -}>` - ${CornerHandleStyles}; - left: -${CORNER_RESIZE_HANDLE_WIDTH / 2}px; - bottom: -${CORNER_RESIZE_HANDLE_WIDTH / 2}px; - ${(props) => (!props.showAsBorder ? "cursor: sw-resize;" : "")} -`; -export const TopLeftHandleStyles = styled.div<{ - showAsBorder: boolean; -}>` - ${CornerHandleStyles}; - left: -${CORNER_RESIZE_HANDLE_WIDTH / 2}px; - top: -${CORNER_RESIZE_HANDLE_WIDTH / 2}px; - ${(props) => (!props.showAsBorder ? "cursor: nw-resize;" : "")} -`; -export const TopRightHandleStyles = styled.div<{ - showAsBorder: boolean; -}>` - ${CornerHandleStyles}; - right: -${CORNER_RESIZE_HANDLE_WIDTH / 2}px; - top: -${CORNER_RESIZE_HANDLE_WIDTH / 2}px; - ${(props) => (!props.showAsBorder ? "cursor: ne-resize;" : "")} -`; diff --git a/app/client/src/components/editorComponents/ResizeStyledComponents.tsx b/app/client/src/components/editorComponents/ResizeStyledComponents.tsx index ceb3361323bd..6f95de4ec2b3 100644 --- a/app/client/src/components/editorComponents/ResizeStyledComponents.tsx +++ b/app/client/src/components/editorComponents/ResizeStyledComponents.tsx @@ -1,4 +1,5 @@ -import { invisible, theme } from "constants/DefaultTheme"; +import { Colors } from "constants/Colors"; +import { invisible } from "constants/DefaultTheme"; import { WIDGET_PADDING } from "constants/WidgetConstants"; import styled, { css } from "styled-components"; @@ -20,21 +21,49 @@ export const VisibilityContainer = styled.div<{ `} `; -const ResizeIndicatorStyle = css<{ +const VerticalResizeIndicators = css<{ showLightBorder: boolean; + isHovered: boolean; }>` &::after { position: absolute; content: ""; - width: 6px; - height: 6px; - border-radius: 50%; - background: ${(props) => - props.showLightBorder - ? theme.colors.widgetLightBorder - : theme.colors.widgetBorder}; - top: calc(50% - 2px); - left: calc(50% - 2px); + width: 7px; + height: 16px; + border-radius: 50%/16%; + background: ${Colors.GREY_1}; + top: calc(50% - 8px); + left: calc(50% - 2.5px); + border: ${(props) => { + return `1px solid ${props.isHovered ? Colors.WATUSI : "#F86A2B"}`; + }}; + outline: 1px solid ${Colors.GREY_1}; + } + &:hover::after { + background: #f86a2b; + } +`; + +const HorizontalResizeIndicators = css<{ + showLightBorder: boolean; + isHovered: boolean; +}>` + &::after { + position: absolute; + content: ""; + width: 16px; + height: 7px; + border-radius: 16%/50%; + border: ${(props) => { + return `1px solid ${props.isHovered ? Colors.WATUSI : "#F86A2B"}`; + }}; + background: ${Colors.GREY_1}; + top: calc(50% - 2.5px); + left: calc(50% - 8px); + outline: 1px solid ${Colors.GREY_1}; + } + &:hover::after { + background: #f86a2b; } `; @@ -42,29 +71,27 @@ export const EdgeHandleStyles = css<{ showAsBorder: boolean; showLightBorder: boolean; disableDot: boolean; + isHovered: boolean; }>` position: absolute; width: ${EDGE_RESIZE_HANDLE_WIDTH}px; height: ${EDGE_RESIZE_HANDLE_WIDTH}px; &::before { position: absolute; - background: ${(props) => { - if (props.showLightBorder) return theme.colors.widgetLightBorder; - if (props.showAsBorder) return theme.colors.widgetMultiSelectBorder; - return theme.colors.widgetBorder; - }}; + background: "transparent"; content: ""; } - ${(props) => - props.showAsBorder || props.disableDot ? "" : ResizeIndicatorStyle} `; export const VerticalHandleStyles = css<{ showAsBorder: boolean; showLightBorder: boolean; disableDot: boolean; + isHovered: boolean; }>` ${EdgeHandleStyles} + ${(props) => + props.showAsBorder || props.disableDot ? "" : VerticalResizeIndicators} top:${~(WIDGET_PADDING - 1) + 1}px; height: calc(100% + ${2 * WIDGET_PADDING - 1}px); ${(props) => @@ -81,8 +108,11 @@ export const HorizontalHandleStyles = css<{ showAsBorder: boolean; showLightBorder: boolean; disableDot: boolean; + isHovered: boolean; }>` ${EdgeHandleStyles} + ${(props) => + props.showAsBorder || props.disableDot ? "" : HorizontalResizeIndicators} left: ${~WIDGET_PADDING + 1}px; width: calc(100% + ${2 * WIDGET_PADDING}px); ${(props) => @@ -97,23 +127,23 @@ export const HorizontalHandleStyles = css<{ export const LeftHandleStyles = styled.div` ${VerticalHandleStyles} - left: ${-EDGE_RESIZE_HANDLE_WIDTH / 2 - WIDGET_PADDING}px; + left: ${-EDGE_RESIZE_HANDLE_WIDTH / 2 - WIDGET_PADDING + 1}px; `; export const RightHandleStyles = styled.div` ${VerticalHandleStyles}; - right: ${-EDGE_RESIZE_HANDLE_WIDTH / 2 - WIDGET_PADDING + 1}px; + right: ${-EDGE_RESIZE_HANDLE_WIDTH / 2 - WIDGET_PADDING + 3}px; height: calc(100% + ${2 * WIDGET_PADDING}px); `; export const TopHandleStyles = styled.div` ${HorizontalHandleStyles}; - top: ${-EDGE_RESIZE_HANDLE_WIDTH / 2 - WIDGET_PADDING}px; + top: ${-EDGE_RESIZE_HANDLE_WIDTH / 2 - WIDGET_PADDING + 1}px; `; export const BottomHandleStyles = styled.div` ${HorizontalHandleStyles}; - bottom: ${-EDGE_RESIZE_HANDLE_WIDTH / 2 - WIDGET_PADDING}px; + bottom: ${-EDGE_RESIZE_HANDLE_WIDTH / 2 - WIDGET_PADDING + 3}px; `; export const CornerHandleStyles = css` diff --git a/app/client/src/constants/Colors.tsx b/app/client/src/constants/Colors.tsx index 9096428d85be..fd2bd5baa57d 100644 --- a/app/client/src/constants/Colors.tsx +++ b/app/client/src/constants/Colors.tsx @@ -75,8 +75,7 @@ export const Colors = { SOLID_MERCURY: "#E5E5E5", TROUT_DARK: "#535B62", ALABASTER: "#F9F8F8", - // WATUSI: "#FF9B4E", - WATUSI: "#FFE0D2", + WATUSI: "#FF9B4E", GRAY: "#858282", GRAY2: "#939090", DOVE_GRAY2: "#716e6e", diff --git a/app/client/src/pages/Editor/AppPositionTypeControl.tsx b/app/client/src/pages/Editor/AppPositionTypeControl.tsx index 83f91b366c78..ce4c0b50ce38 100644 --- a/app/client/src/pages/Editor/AppPositionTypeControl.tsx +++ b/app/client/src/pages/Editor/AppPositionTypeControl.tsx @@ -1,126 +1,126 @@ -// import classNames from "classnames"; -import React from "react"; -import { useSelector } from "react-redux"; +import classNames from "classnames"; +import React, { useEffect, useMemo } from "react"; +import { useDispatch, useSelector } from "react-redux"; import styled from "styled-components"; -// import { batchUpdateMultipleWidgetProperties } from "actions/controlActions"; -// import { ReactComponent as DesktopIcon } from "assets/icons/ads/app-icons/monitor-alt.svg"; -// import { ReactComponent as MultiDeviceIcon } from "assets/icons/ads/app-icons/monitor-smartphone-alt.svg"; +import { batchUpdateMultipleWidgetProperties } from "actions/controlActions"; +import { ReactComponent as DesktopIcon } from "assets/icons/ads/app-icons/monitor-alt.svg"; +import { ReactComponent as MultiDeviceIcon } from "assets/icons/ads/app-icons/monitor-smartphone-alt.svg"; import { Colors } from "constants/Colors"; -// import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; -// import { IconName } from "design-system-old"; +import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; +import { IconName, TooltipComponent } from "design-system-old"; import { AppPositioningTypeConfig, AppPositioningTypes, } from "reducers/entityReducers/pageListReducer"; import { getCurrentAppPositioningType, - // isAutoLayoutEnabled, + isAutoLayoutEnabled, } from "selectors/editorSelectors"; -// import { LayoutDirection, Positioning } from "utils/autoLayout/constants"; +import { LayoutDirection, Positioning } from "utils/autoLayout/constants"; import { MainContainerLayoutControl } from "./MainContainerLayoutControl"; -// interface ApplicationPositionTypeConfigOption { -// name: string; -// type: AppPositioningTypes; -// icon?: IconName; -// } -// const IconObj: any = { -// fluid: , -// desktop: , -// }; +interface ApplicationPositionTypeConfigOption { + name: string; + type: AppPositioningTypes; + icon?: IconName; +} +const IconObj: any = { + fluid: , + desktop: , +}; export const AppsmithDefaultPositionType: AppPositioningTypeConfig = { type: AppPositioningTypes.FIXED, }; -// const AppsmithLayoutTypes: ApplicationPositionTypeConfigOption[] = [ -// { -// name: "Fixed Layout", -// type: AppPositioningTypes.FIXED, -// icon: "desktop", -// }, -// { -// name: "Auto Layout", -// type: AppPositioningTypes.AUTO, -// icon: "fluid", -// }, -// ]; +const AppsmithLayoutTypes: ApplicationPositionTypeConfigOption[] = [ + { + name: "Fixed Layout", + type: AppPositioningTypes.FIXED, + icon: "desktop", + }, + { + name: "Auto Layout", + type: AppPositioningTypes.AUTO, + icon: "fluid", + }, +]; export const Title = styled.p` color: ${Colors.GRAY_800}; `; export function AppPositionTypeControl() { - // const dispatch = useDispatch(); - // const buttonRefs: Array = []; + const dispatch = useDispatch(); + const buttonRefs: Array = []; const selectedOption = useSelector(getCurrentAppPositioningType); - // const isAutoLayoutActive = useSelector(isAutoLayoutEnabled); + const isAutoLayoutActive = useSelector(isAutoLayoutEnabled); /** * return selected layout index. if there is no app * layout, use the default one ( fluid ) */ - // const selectedIndex = useMemo(() => { - // return AppsmithLayoutTypes.findIndex( - // (each) => - // each.type === (selectedOption || AppsmithDefaultPositionType.type), - // ); - // }, [selectedOption]); + const selectedIndex = useMemo(() => { + return AppsmithLayoutTypes.findIndex( + (each) => + each.type === (selectedOption || AppsmithDefaultPositionType.type), + ); + }, [selectedOption]); - // const [focusedIndex, setFocusedIndex] = React.useState(selectedIndex); + const [focusedIndex, setFocusedIndex] = React.useState(selectedIndex); - // useEffect(() => { - // if (!isAutoLayoutActive) { - // /** - // * if feature flag is disabled, set the layout to fixed. - // */ - // updateAppPositioningLayout(AppsmithLayoutTypes[0]); - // } - // }, [isAutoLayoutActive]); + useEffect(() => { + if (!isAutoLayoutActive) { + /** + * if feature flag is disabled, set the layout to fixed. + */ + updateAppPositioningLayout(AppsmithLayoutTypes[0]); + } + }, [isAutoLayoutActive]); - // const updateAppPositioningLayout = ( - // layoutOption: ApplicationPositionTypeConfigOption, - // ) => { - // const selectedType = - // layoutOption.type !== AppPositioningTypes.AUTO - // ? Positioning.Fixed - // : Positioning.Vertical; - // dispatch( - // batchUpdateMultipleWidgetProperties([ - // { - // widgetId: MAIN_CONTAINER_WIDGET_ID, - // updates: { - // modify: { - // positioning: selectedType, - // useAutoLayout: selectedType !== Positioning.Fixed, - // direction: LayoutDirection.Vertical, - // }, - // }, - // }, - // ]), - // ); - // }; + const updateAppPositioningLayout = ( + layoutOption: ApplicationPositionTypeConfigOption, + ) => { + const selectedType = + layoutOption.type !== AppPositioningTypes.AUTO + ? Positioning.Fixed + : Positioning.Vertical; + dispatch( + batchUpdateMultipleWidgetProperties([ + { + widgetId: MAIN_CONTAINER_WIDGET_ID, + updates: { + modify: { + positioning: selectedType, + useAutoLayout: selectedType !== Positioning.Fixed, + direction: LayoutDirection.Vertical, + }, + }, + }, + ]), + ); + }; - // const handleKeyDown = (event: React.KeyboardEvent, index: number) => { - // if (!buttonRefs.length) return; + const handleKeyDown = (event: React.KeyboardEvent, index: number) => { + if (!buttonRefs.length) return; - // switch (event.key) { - // case "ArrowRight": - // case "Right": - // const rightIndex = index === buttonRefs.length - 1 ? 0 : index + 1; - // buttonRefs[rightIndex]?.focus(); - // setFocusedIndex(rightIndex); - // break; - // case "ArrowLeft": - // case "Left": - // const leftIndex = index === 0 ? buttonRefs.length - 1 : index - 1; - // buttonRefs[leftIndex]?.focus(); - // setFocusedIndex(leftIndex); - // break; - // } - // }; + switch (event.key) { + case "ArrowRight": + case "Right": + const rightIndex = index === buttonRefs.length - 1 ? 0 : index + 1; + buttonRefs[rightIndex]?.focus(); + setFocusedIndex(rightIndex); + break; + case "ArrowLeft": + case "Left": + const leftIndex = index === 0 ? buttonRefs.length - 1 : index - 1; + buttonRefs[leftIndex]?.focus(); + setFocusedIndex(leftIndex); + break; + } + }; return ( <> - {/* {isAutoLayoutActive ? ( + {isAutoLayoutActive ? ( <> App Positioning Type
@@ -151,7 +151,7 @@ export function AppPositionTypeControl() { updateAppPositioningLayout(layoutOption); setFocusedIndex(index); }} - // onKeyDown={(event) => handleKeyDown(event, index)} + onKeyDown={(event) => handleKeyDown(event, index)} //TODO: Ashok - This event listener isn't being removed. ref={(input) => buttonRefs.push(input)} tabIndex={index === focusedIndex ? 0 : -1} > @@ -165,7 +165,7 @@ export function AppPositionTypeControl() {
- ) : null} */} + ) : null} {selectedOption === AppPositioningTypes.FIXED && ( <> Canvas Size diff --git a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx index 797c79a360cd..27d341a449e8 100644 --- a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx +++ b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx @@ -1,11 +1,11 @@ -// import { ReactComponent as CanvasResizer } from "assets/icons/ads/app-icons/canvas-resizer.svg"; +import { ReactComponent as CanvasResizer } from "assets/icons/ads/app-icons/canvas-resizer.svg"; import React, { ReactNode, useEffect } from "react"; import { useSelector } from "react-redux"; import { getCanvasWidth, - // getCurrentApplicationLayout, - // getCurrentAppPositioningType, + getCurrentApplicationLayout, + getCurrentAppPositioningType, getCurrentPageId, getIsFetchingPage, getViewModePageList, @@ -30,52 +30,53 @@ import { import { getCanvasWidgetsStructure } from "selectors/entitiesSelector"; import { getCurrentThemeDetails } from "selectors/themeSelectors"; import { - // AUTOLAYOUT_RESIZER_WIDTH_BUFFER, + AUTOLAYOUT_RESIZER_WIDTH_BUFFER, useDynamicAppLayout, } from "utils/hooks/useDynamicAppLayout"; import useGoogleFont from "utils/hooks/useGoogleFont"; -// import { layoutConfigurations } from "constants/WidgetConstants"; -// import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; +import { layoutConfigurations } from "constants/WidgetConstants"; +import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; import Canvas from "../Canvas"; -// const AutoLayoutCanvasResizer = styled.div` -// position: sticky; -// cursor: col-resize; -// width: 2px; -// height: 100%; -// display: flex; -// background: #d9d9d9; -// align-items: center; -// justify-content: flex-start; -// z-index: 2; -// transition: width 300ms ease; -// transition: background 300ms ease; -// .canvas-resizer-icon { -// border-left: 2px solid; -// border-color: #d7d7d7; -// transition: border 300ms ease; -// margin-left: 2px; -// & > svg { -// fill: #d7d7d7; -// transition: fill 300ms ease; -// } -// } -// &:hover, -// &:active { -// width: 3px; -// transition: width 300ms ease; -// background: #ff9b4e; -// transition: background 300ms ease; -// .canvas-resizer-icon { -// border-color: #ff9b4e; -// transition: border 300ms ease; -// & > svg { -// fill: #ff9b4e; -// transition: fill 300ms ease; -// } -// } -// } -// `; +const AutoLayoutCanvasResizer = styled.div` + position: sticky; + cursor: col-resize; + width: 2px; + height: 100%; + display: flex; + background: #d9d9d9; + align-items: center; + justify-content: flex-start; + z-index: 2; + transition: width 300ms ease; + transition: background 300ms ease; + .canvas-resizer-icon { + border-left: 2px solid; + border-color: #d7d7d7; + transition: border 300ms ease; + margin-left: 2px; + & > svg { + fill: #d7d7d7; + transition: fill 300ms ease; + } + } + &:hover, + &:active { + width: 3px; + transition: width 300ms ease; + background: #ff9b4e; + transition: background 300ms ease; + .canvas-resizer-icon { + border-color: #ff9b4e; + transition: border 300ms ease; + & > svg { + fill: #ff9b4e; + transition: fill 300ms ease; + } + } + } +`; + const Container = styled.section<{ background: string; }>` @@ -140,91 +141,92 @@ function CanvasContainer() { /> ); } - // const appPositioningType = useSelector(getCurrentAppPositioningType); - // const appLayout = useSelector(getCurrentApplicationLayout); - // useEffect(() => { - // if (appPositioningType === AppPositioningTypes.AUTO) { - // let buffer = 0; - // const ele: any = document.getElementById("canvas-viewport"); - // if (isPreviewMode) { - // ele.style.width = "inherit"; - // buffer = AUTOLAYOUT_RESIZER_WIDTH_BUFFER; - // } else { - // ele.style.width = "100%"; - // } - // if (appLayout?.type === "FLUID") { - // const smallestWidth = layoutConfigurations.MOBILE.minWidth; - // // Query the element - // const ele: any = document.getElementById("canvas-viewport"); - // let needsInitiation = true; - // let initialWidth = ele.offsetWidth; - // // The current position of mouse - // let x = 0; - // // let y = 0; - // // The dimension of the element - // let w = 0; - // // let h = 0; - // let events: any = []; + const appPositioningType = useSelector(getCurrentAppPositioningType); + const appLayout = useSelector(getCurrentApplicationLayout); + useEffect(() => { + if (appPositioningType === AppPositioningTypes.AUTO) { + let buffer = 0; + const ele: any = document.getElementById("canvas-viewport"); + if (isPreviewMode) { + ele.style.width = "inherit"; + buffer = AUTOLAYOUT_RESIZER_WIDTH_BUFFER; + } else { + ele.style.width = "100%"; + } + if (appLayout?.type === "FLUID") { + const smallestWidth = layoutConfigurations.MOBILE.minWidth; + // Query the element + const ele: any = document.getElementById("canvas-viewport"); + let needsInitiation = true; + let initialWidth = ele.offsetWidth; + // The current position of mouse + let x = 0; + // let y = 0; - // // Handle the mousedown event - // // that's triggered when user drags the resizer - // const mouseDownHandler = function(e: any) { - // if (needsInitiation) { - // initialWidth = ele.offsetWidth; - // needsInitiation = false; - // } - // // Get the current mouse position - // x = e.clientX; - // // y = e.clientY; + // The dimension of the element + let w = 0; + // let h = 0; + let events: any = []; - // // Calculate the dimension of element - // const styles = window.getComputedStyle(ele); - // w = parseInt(styles.width, 10) + buffer; - // // h = parseInt(styles.height, 10); - // const mouseMove = (e: any) => mouseMoveHandler(e); - // events.push(mouseMove); - // // Attach the listeners to `document` - // document.addEventListener("mousemove", mouseMove); - // document.addEventListener("mouseup", mouseUpHandler); - // // e.stopPropagation(); - // }; + // Handle the mousedown event + // that's triggered when user drags the resizer + const mouseDownHandler = function(e: any) { + if (needsInitiation) { + initialWidth = ele.offsetWidth; + needsInitiation = false; + } + // Get the current mouse position + x = e.clientX; + // y = e.clientY; - // const mouseMoveHandler = function(e: any) { - // // How far the mouse has been moved - // // const multiplier = rightHandle ? 2 : -2; - // const multiplier = 2; - // const dx = (e.clientX - x) * multiplier; - // if (initialWidth >= w + dx && smallestWidth <= w + dx) { - // // Adjust the dimension of element - // ele.style.width = `${w + dx}px`; - // } - // if (initialWidth < w + dx) { - // ele.style.width = `${initialWidth}px`; - // } - // if (smallestWidth > w + dx) { - // ele.style.width = `${smallestWidth}px`; - // } - // // e.stopPropagation(); - // }; + // Calculate the dimension of element + const styles = window.getComputedStyle(ele); + w = parseInt(styles.width, 10) + buffer; + // h = parseInt(styles.height, 10); + const mouseMove = (e: any) => mouseMoveHandler(e); + events.push(mouseMove); + // Attach the listeners to `document` + document.addEventListener("mousemove", mouseMove); + document.addEventListener("mouseup", mouseUpHandler); + // e.stopPropagation(); + }; - // const mouseUpHandler = function(e: any) { - // // Remove the handlers of `mousemove` and `mouseup` - // mouseMoveHandler(e); - // document.removeEventListener("mousemove", events[0] as any); - // document.removeEventListener("mouseup", mouseUpHandler); - // events = []; - // }; - // const rightResizer: any = ele.querySelectorAll(".resizer-right")[0]; - // const rightMove = (e: any) => mouseDownHandler(e); - // rightResizer.addEventListener("mousedown", rightMove); + const mouseMoveHandler = function(e: any) { + // How far the mouse has been moved + // const multiplier = rightHandle ? 2 : -2; + const multiplier = 2; + const dx = (e.clientX - x) * multiplier; + if (initialWidth >= w + dx && smallestWidth <= w + dx) { + // Adjust the dimension of element + ele.style.width = `${w + dx}px`; + } + if (initialWidth < w + dx) { + ele.style.width = `${initialWidth}px`; + } + if (smallestWidth > w + dx) { + ele.style.width = `${smallestWidth}px`; + } + // e.stopPropagation(); + }; - // return () => { - // rightResizer.removeEventListener("mousedown", rightMove); - // }; - // } - // } - // }, [appLayout, isPreviewMode, currentPageId, appPositioningType]); + const mouseUpHandler = function(e: any) { + // Remove the handlers of `mousemove` and `mouseup` + mouseMoveHandler(e); + document.removeEventListener("mousemove", events[0] as any); + document.removeEventListener("mouseup", mouseUpHandler); + events = []; + }; + const rightResizer: any = ele.querySelectorAll(".resizer-right")[0]; + const rightMove = (e: any) => mouseDownHandler(e); + rightResizer.addEventListener("mousedown", rightMove); + + return () => { + rightResizer.removeEventListener("mousedown", rightMove); + }; + } + } + }, [appLayout, isPreviewMode, currentPageId, appPositioningType]); // calculating exact height to not allow scroll at this component, // calculating total height minus margin on top, top bar and bottom bar @@ -259,7 +261,7 @@ function CanvasContainer() { )} {node} - {/* {appPositioningType === AppPositioningTypes.AUTO && ( + {appPositioningType === AppPositioningTypes.AUTO && ( - )} */} + )} ); } diff --git a/app/client/src/resizable/resize/index.autolayout.tsx b/app/client/src/resizable/resize/index.autolayout.tsx deleted file mode 100644 index 38038932eab0..000000000000 --- a/app/client/src/resizable/resize/index.autolayout.tsx +++ /dev/null @@ -1,352 +0,0 @@ -import { isHandleResizeAllowed } from "components/editorComponents/ResizableUtils"; -import React, { ReactNode, useEffect, useState } from "react"; -import { Spring } from "react-spring"; -import { useDrag } from "react-use-gesture"; -import { ReflowDirection } from "reflow/reflowTypes"; -import { ResizeWrapper } from "resizable/resizenreflow"; -import { StyledComponent } from "styled-components"; -import PerformanceTracker, { - PerformanceTransactionName, -} from "utils/PerformanceTracker"; - -const getSnappedValues = ( - x: number, - y: number, - snapGrid: { x: number; y: number }, -) => { - return { - x: Math.round(x / snapGrid.x) * snapGrid.x, - y: Math.round(y / snapGrid.y) * snapGrid.y, - }; -}; - -type ResizableHandleProps = { - allowResize: boolean; - showLightBorder?: boolean; - isHovered: boolean; - disableDot: boolean; - dragCallback: (x: number, y: number) => void; - component: StyledComponent<"div", Record>; - onStart: () => void; - onStop: () => void; - snapGrid: { - x: number; - y: number; - }; -}; - -function ResizableHandle(props: ResizableHandleProps) { - const bind = useDrag( - ({ first, last, dragging, movement: [mx, my], memo }) => { - if (!props.allowResize) { - return; - } - const snapped = getSnappedValues(mx, my, props.snapGrid); - if (dragging && memo && (snapped.x !== memo.x || snapped.y !== memo.y)) { - props.dragCallback(snapped.x, snapped.y); - } - if (first) { - props.onStart(); - } - if (last) { - props.onStop(); - } - return snapped; - }, - ); - const propsToPass = { - ...bind(), - showAsBorder: !props.allowResize, - disableDot: props.disableDot, - isHovered: props.isHovered, - }; - - return ; -} - -type ResizableProps = { - allowResize: boolean; - handles: { - left?: StyledComponent<"div", Record>; - top?: StyledComponent<"div", Record>; - bottom?: StyledComponent<"div", Record>; - right?: StyledComponent<"div", Record>; - bottomRight?: StyledComponent<"div", Record>; - topLeft?: StyledComponent<"div", Record>; - topRight?: StyledComponent<"div", Record>; - bottomLeft?: StyledComponent<"div", Record>; - }; - componentWidth: number; - componentHeight: number; - children: ReactNode; - onStart: () => void; - onStop: ( - size: { width: number; height: number }, - position: { x: number; y: number }, - ) => void; - snapGrid: { x: number; y: number }; - enableVerticalResize: boolean; - enableHorizontalResize: boolean; - isColliding: ( - size: { width: number; height: number }, - position: { x: number; y: number }, - ) => boolean; - className?: string; - resizeDualSides?: boolean; - widgetId: string; - showLightBorder?: boolean; - zWidgetType?: string; -}; - -export const Resizable = function Resizable(props: ResizableProps) { - // Performance tracking start - const sentryPerfTags = props.zWidgetType - ? [{ name: "widget_type", value: props.zWidgetType }] - : []; - PerformanceTracker.startTracking( - PerformanceTransactionName.SHOW_RESIZE_HANDLES, - { widgetId: props.widgetId }, - true, - sentryPerfTags, - ); - - useEffect(() => { - PerformanceTracker.stopTracking( - PerformanceTransactionName.SHOW_RESIZE_HANDLES, - ); - }); - //end - const [pointerEvents, togglePointerEvents] = useState(true); - const [newDimensions, set] = useState({ - width: props.componentWidth, - height: props.componentHeight, - x: 0, - y: 0, - reset: false, - }); - - const { resizeDualSides } = props; - const multiplier = resizeDualSides ? 2 : 1; - - const setNewDimensions = (rect: { - width: number; - height: number; - x: number; - y: number; - }) => { - const { height, width, x, y } = rect; - const shouldUpdateHeight = - props.componentHeight !== height && props.enableVerticalResize; - if (!shouldUpdateHeight) rect.height = props.componentHeight; - - const shouldUpdateWidth = - props.componentWidth !== width && props.enableHorizontalResize; - if (!shouldUpdateWidth) rect.width = props.componentWidth; - - const isColliding = props.isColliding({ width, height }, { x, y }); - if (!isColliding) { - set({ ...rect, reset: false }); - } - }; - - useEffect(() => { - set({ - width: props.componentWidth, - height: props.componentHeight, - x: 0, - y: 0, - reset: true, - }); - }, [props.componentHeight, props.componentWidth]); - - const handles = []; - - if (props.handles.left) { - handles.push({ - dragCallback: (x: number) => { - setNewDimensions({ - width: props.componentWidth - multiplier * x, - height: newDimensions.height, - x: resizeDualSides ? newDimensions.x : x, - y: newDimensions.y, - }); - }, - component: props.handles.left, - handleDirection: ReflowDirection.LEFT, - }); - } - - if (props.handles.top) { - handles.push({ - dragCallback: (x: number, y: number) => { - setNewDimensions({ - width: newDimensions.width, - height: props.componentHeight - multiplier * y, - y: resizeDualSides ? newDimensions.y : y, - x: newDimensions.x, - }); - }, - component: props.handles.top, - handleDirection: ReflowDirection.TOP, - }); - } - - if (props.handles.right) { - handles.push({ - dragCallback: (x: number) => { - setNewDimensions({ - width: props.componentWidth + multiplier * x, - height: newDimensions.height, - x: newDimensions.x, - y: newDimensions.y, - }); - }, - component: props.handles.right, - handleDirection: ReflowDirection.RIGHT, - }); - } - - if (props.handles.bottom) { - handles.push({ - dragCallback: (x: number, y: number) => { - setNewDimensions({ - width: newDimensions.width, - height: props.componentHeight + multiplier * y, - x: newDimensions.x, - y: newDimensions.y, - }); - }, - component: props.handles.bottom, - handleDirection: ReflowDirection.BOTTOM, - }); - } - - if (props.handles.topLeft) { - handles.push({ - dragCallback: (x: number, y: number) => { - setNewDimensions({ - width: props.componentWidth - x, - height: props.componentHeight - y, - x: x, - y: y, - }); - }, - component: props.handles.topLeft, - }); - } - - if (props.handles.topRight) { - handles.push({ - dragCallback: (x: number, y: number) => { - setNewDimensions({ - width: props.componentWidth + x, - height: props.componentHeight - y, - x: newDimensions.x, - y: y, - }); - }, - component: props.handles.topRight, - }); - } - - if (props.handles.bottomRight) { - handles.push({ - dragCallback: (x: number, y: number) => { - setNewDimensions({ - width: props.componentWidth + x, - height: props.componentHeight + y, - x: newDimensions.x, - y: newDimensions.y, - }); - }, - component: props.handles.bottomRight, - }); - } - - if (props.handles.bottomLeft) { - handles.push({ - dragCallback: (x: number, y: number) => { - setNewDimensions({ - width: props.componentWidth - x, - height: props.componentHeight + y, - x, - y: newDimensions.y, - }); - }, - component: props.handles.bottomLeft, - }); - } - const onResizeStop = () => { - togglePointerEvents(true); - props.onStop( - { - width: newDimensions.width, - height: newDimensions.height, - }, - { - x: newDimensions.x, - y: newDimensions.y, - }, - ); - }; - const showResizeBoundary = props.enableHorizontalResize; - const renderHandles = handles.map((handle, index) => { - const disableDot = - !showResizeBoundary || - !isHandleResizeAllowed( - props.enableHorizontalResize, - props.enableVerticalResize, - handle.handleDirection, - ); - return ( - { - togglePointerEvents(false); - props.onStart(); - }} - onStop={onResizeStop} - snapGrid={props.snapGrid} - /> - ); - }); - - return ( - - {(_props) => ( - - {props.children} - {props.enableHorizontalResize && renderHandles} - - )} - - ); -}; - -export default Resizable; diff --git a/app/client/src/resizable/resize/index.tsx b/app/client/src/resizable/resize/index.tsx index 94f20f71fdee..0937906fc98c 100644 --- a/app/client/src/resizable/resize/index.tsx +++ b/app/client/src/resizable/resize/index.tsx @@ -1,21 +1,13 @@ -import React, { ReactNode, useState, useEffect, forwardRef, Ref } from "react"; -import styled, { StyledComponent } from "styled-components"; +import { isHandleResizeAllowed } from "components/editorComponents/ResizableUtils"; +import React, { ReactNode, useEffect, useState } from "react"; +import { Spring } from "react-spring"; import { useDrag } from "react-use-gesture"; -import { Spring, animated } from "react-spring"; +import { ReflowDirection } from "reflow/reflowTypes"; +import { ResizeWrapper } from "resizable/resizenreflow"; +import { StyledComponent } from "styled-components"; import PerformanceTracker, { PerformanceTransactionName, } from "utils/PerformanceTracker"; -import { ReflowDirection } from "reflow/reflowTypes"; -import { isHandleResizeAllowed } from "components/editorComponents/ResizableUtils"; - -const ResizeWrapper = styled(animated.div)<{ $prevents: boolean }>` - display: block; - & { - * { - pointer-events: ${(props) => !props.$prevents && "none"}; - } - } -`; const getSnappedValues = ( x: number, @@ -31,6 +23,7 @@ const getSnappedValues = ( type ResizableHandleProps = { allowResize: boolean; showLightBorder?: boolean; + isHovered: boolean; disableDot: boolean; dragCallback: (x: number, y: number) => void; component: StyledComponent<"div", Record>; @@ -64,8 +57,8 @@ function ResizableHandle(props: ResizableHandleProps) { const propsToPass = { ...bind(), showAsBorder: !props.allowResize, - showLightBorder: props.showLightBorder, disableDot: props.disableDot, + isHovered: props.isHovered, }; return ; @@ -100,23 +93,19 @@ type ResizableProps = { ) => boolean; className?: string; resizeDualSides?: boolean; - showLightBorder?: boolean; widgetId: string; + showLightBorder?: boolean; zWidgetType?: string; - zWidgetId?: string; }; -export const Resizable = forwardRef(function Resizable( - props: ResizableProps, - ref: Ref, -) { +export const Resizable = function Resizable(props: ResizableProps) { // Performance tracking start const sentryPerfTags = props.zWidgetType ? [{ name: "widget_type", value: props.zWidgetType }] : []; PerformanceTracker.startTracking( PerformanceTransactionName.SHOW_RESIZE_HANDLES, - { widgetId: props.zWidgetId }, + { widgetId: props.widgetId }, true, sentryPerfTags, ); @@ -300,25 +289,27 @@ export const Resizable = forwardRef(function Resizable( }, ); }; - + const showResizeBoundary = props.enableHorizontalResize; const renderHandles = handles.map((handle, index) => { - const disableDot = !isHandleResizeAllowed( - props.enableHorizontalResize, - props.enableVerticalResize, - handle.handleDirection, - ); + const disableDot = + !showResizeBoundary || + !isHandleResizeAllowed( + props.enableHorizontalResize, + props.enableVerticalResize, + handle.handleDirection, + ); return ( { togglePointerEvents(false); props.onStart(); }} onStop={onResizeStop} - showLightBorder={props.showLightBorder} snapGrid={props.snapGrid} /> ); @@ -346,7 +337,8 @@ export const Resizable = forwardRef(function Resizable( {props.children} @@ -355,6 +347,6 @@ export const Resizable = forwardRef(function Resizable( )} ); -}); +}; export default Resizable; diff --git a/app/client/src/resizable/resizenreflow/index.tsx b/app/client/src/resizable/resizenreflow/index.tsx index 33f4d3f97a3f..98ae6ea0db55 100644 --- a/app/client/src/resizable/resizenreflow/index.tsx +++ b/app/client/src/resizable/resizenreflow/index.tsx @@ -1,6 +1,7 @@ -import { stopReflowAction } from "actions/reflowActions"; +import { reflowMoveAction, stopReflowAction } from "actions/reflowActions"; import { isHandleResizeAllowed } from "components/editorComponents/ResizableUtils"; import { OccupiedSpace } from "constants/CanvasEditorConstants"; +import { Colors } from "constants/Colors"; import { GridDefaults, WidgetHeightLimits, @@ -15,11 +16,16 @@ import { MovementLimitMap, ReflowDirection, ReflowedSpace, + ReflowedSpaceMap, } from "reflow/reflowTypes"; import { getWidgets } from "sagas/selectors"; import { getContainerOccupiedSpacesSelectorWhileResizing } from "selectors/editorSelectors"; import { getReflowSelector } from "selectors/widgetReflowSelectors"; import styled, { StyledComponent } from "styled-components"; +import { + getFillWidgetLengthForLayer, + getLayerIndexOfWidget, +} from "utils/autoLayout/AutoLayoutUtils"; import { LayoutDirection, ResponsiveBehavior, @@ -31,54 +37,42 @@ import PerformanceTracker, { } from "utils/PerformanceTracker"; import { isDropZoneOccupied } from "utils/WidgetPropsUtils"; -// TODO: Preet / Ashok: The following section has been commented out to facilitate the first merge to release. -// The commented out code will be used in the next iteration for the public release. - -// const resizeBorderPadding = 1; -// const resizeBorder = 1; -// const resizeBoxShadow = 1; -// const resizeOutline = 1; - -// export const RESIZE_BORDER_BUFFER = -// resizeBorderPadding + resizeBorder + resizeBoxShadow + resizeOutline; - -// export const ResizeWrapper = styled(animated.div)<{ -// $prevents: boolean; -// isHovered: boolean; -// showBoundaries: boolean; -// }>` -// display: block; -// & { -// * { -// pointer-events: ${(props) => !props.$prevents && "none"}; -// } -// } -// ${(props) => { -// if (props.showBoundaries) { -// return ` -// box-shadow: 0px 0px 0px ${resizeBoxShadow}px ${ -// props.isHovered ? Colors.WATUSI : "#f86a2b" -// }; -// border-radius: 0px 4px 4px 4px; -// border: ${resizeBorder}px solid ${Colors.GREY_1}; -// padding: ${resizeBorderPadding}px; -// outline: ${resizeOutline}px solid ${Colors.GREY_1} !important; -// outline-offset: 1px;`; -// } else { -// return ` -// border: 0px solid transparent; -// `; -// } -// }}} -// `; - -export const ResizeWrapper = styled(animated.div)<{ $prevents: boolean }>` +const resizeBorderPadding = 1; +const resizeBorder = 1; +const resizeBoxShadow = 1; +const resizeOutline = 1; + +export const RESIZE_BORDER_BUFFER = + resizeBorderPadding + resizeBorder + resizeBoxShadow + resizeOutline; + +export const ResizeWrapper = styled(animated.div)<{ + $prevents: boolean; + isHovered: boolean; + showBoundaries: boolean; +}>` display: block; & { * { pointer-events: ${(props) => !props.$prevents && "none"}; } } + ${(props) => { + if (props.showBoundaries) { + return ` + box-shadow: 0px 0px 0px ${resizeBoxShadow}px ${ + props.isHovered ? Colors.WATUSI : "#f86a2b" + }; + border-radius: 0px 4px 4px 4px; + border: ${resizeBorder}px solid ${Colors.GREY_1}; + padding: ${resizeBorderPadding}px; + outline: ${resizeOutline}px solid ${Colors.GREY_1} !important; + outline-offset: 1px;`; + } else { + return ` + border: 0px solid transparent; + `; + } + }}} `; const getSnappedValues = ( @@ -301,46 +295,46 @@ export function ReflowResizable(props: ResizableProps) { }); const allWidgets = useSelector(getWidgets); const dispatch = useDispatch(); - // const triggerAutoLayoutBasedReflow = (resizedPositions: OccupiedSpace) => { - // const { widgetId } = props; - // const widget = allWidgets[widgetId]; - // if (!widget || !widget.parentId) return; - // const parent = allWidgets[widget.parentId]; - // if (!parent) return; - // const flexLayers = parent.flexLayers; - // const layerIndex = getLayerIndexOfWidget(flexLayers, widgetId); - // if (layerIndex === -1) return; - // const layer = flexLayers[layerIndex]; - // const widgets = { - // ...allWidgets, - // [props.widgetId]: { - // ...allWidgets[props.widgetId], - // leftColumn: resizedPositions.left, - // rightColumn: resizedPositions.right, - // topRow: resizedPositions.top, - // bottomRow: resizedPositions.bottom, - // }, - // }; - // const fillWidgetsLength = getFillWidgetLengthForLayer(layer, widgets); - // if (fillWidgetsLength) { - // let correctedMovementMap: ReflowedSpaceMap = {}; - // for (const child of layer.children) { - // const childWidget = allWidgets[child.id]; - // if ( - // childWidget && - // childWidget.responsiveBehavior === ResponsiveBehavior.Fill - // ) { - // correctedMovementMap = { - // ...correctedMovementMap, - // [child.id]: { - // width: fillWidgetsLength * widget.parentColumnSpace, - // }, - // }; - // } - // } - // dispatch(reflowMoveAction(correctedMovementMap)); - // } - // }; + const triggerAutoLayoutBasedReflow = (resizedPositions: OccupiedSpace) => { + const { widgetId } = props; + const widget = allWidgets[widgetId]; + if (!widget || !widget.parentId) return; + const parent = allWidgets[widget.parentId]; + if (!parent) return; + const flexLayers = parent.flexLayers; + const layerIndex = getLayerIndexOfWidget(flexLayers, widgetId); + if (layerIndex === -1) return; + const layer = flexLayers[layerIndex]; + const widgets = { + ...allWidgets, + [props.widgetId]: { + ...allWidgets[props.widgetId], + leftColumn: resizedPositions.left, + rightColumn: resizedPositions.right, + topRow: resizedPositions.top, + bottomRow: resizedPositions.bottom, + }, + }; + const fillWidgetsLength = getFillWidgetLengthForLayer(layer, widgets); + if (fillWidgetsLength) { + let correctedMovementMap: ReflowedSpaceMap = {}; + for (const child of layer.children) { + const childWidget = allWidgets[child.id]; + if ( + childWidget && + childWidget.responsiveBehavior === ResponsiveBehavior.Fill + ) { + correctedMovementMap = { + ...correctedMovementMap, + [child.id]: { + width: fillWidgetsLength * widget.parentColumnSpace, + }, + }; + } + } + dispatch(reflowMoveAction(correctedMovementMap)); + } + }; const setNewDimensions = (rect: DimensionProps) => { const { direction, height, width, x, y } = rect; @@ -404,9 +398,9 @@ export function ReflowResizable(props: ResizableProps) { if (bottomMostRow) { props.updateBottomRow(bottomMostRow); } - // if (isAutoLayout && resizedPositions) { - // triggerAutoLayoutBasedReflow(resizedPositions); - // } + if (isAutoLayout && resizedPositions) { + triggerAutoLayoutBasedReflow(resizedPositions); + } return newRect; }); @@ -630,9 +624,8 @@ export function ReflowResizable(props: ResizableProps) { /> ); }); - // TODO: Uncomment this code after first release. - // const bufferForBoundary = props.showResizeBoundary ? RESIZE_BORDER_BUFFER : 0; - const bufferForBoundary = 0; + + const bufferForBoundary = props.showResizeBoundary ? RESIZE_BORDER_BUFFER : 0; const widgetWidth = (reflowedPosition?.width === undefined ? newDimensions.width @@ -687,9 +680,9 @@ export function ReflowResizable(props: ResizableProps) { $prevents={pointerEvents} className={props.className} id={`resize-${props.widgetId}`} - // isHovered={props.isHovered} + isHovered={props.isHovered} ref={resizableRef} - // showBoundaries={props.showResizeBoundary} + showBoundaries={props.showResizeBoundary} style={_props} > {props.children} diff --git a/app/client/src/selectors/editorSelectors.tsx b/app/client/src/selectors/editorSelectors.tsx index b46ca791ce16..eb53721dc71a 100644 --- a/app/client/src/selectors/editorSelectors.tsx +++ b/app/client/src/selectors/editorSelectors.tsx @@ -14,7 +14,7 @@ import { WidgetCardProps, WidgetProps } from "widgets/BaseWidget"; import { Page } from "@appsmith/constants/ReduxActionConstants"; import { ApplicationVersion } from "actions/applicationActions"; -// import { Positioning } from "utils/autoLayout/constants"; +import { Positioning } from "utils/autoLayout/constants"; import { OccupiedSpace, WidgetSpace } from "constants/CanvasEditorConstants"; import { PLACEHOLDER_APP_SLUG, PLACEHOLDER_PAGE_SLUG } from "constants/routes"; import { @@ -236,35 +236,29 @@ const defaultLayout: AppLayoutConfig = { const getAppLayout = (state: AppState) => state.ui.applications.currentApplication?.appLayout || defaultLayout; -// export const getMainCanvasPositioning = createSelector( -// getWidgets, -// (widgets) => { -// return ( -// widgets && -// widgets[MAIN_CONTAINER_WIDGET_ID] && -// widgets[MAIN_CONTAINER_WIDGET_ID].positioning -// ); -// }, -// ); +export const getMainCanvasPositioning = createSelector( + getWidgets, + (widgets) => { + return ( + widgets && + widgets[MAIN_CONTAINER_WIDGET_ID] && + widgets[MAIN_CONTAINER_WIDGET_ID].positioning + ); + }, +); export const isAutoLayoutEnabled = (state: AppState): boolean => { return state.ui.users.featureFlag.data.AUTO_LAYOUT === true; }; -// export const getCurrentAppPositioningType = createSelector( -// () => AppPositioningTypes.FIXED, -// ); - export const getCurrentAppPositioningType = createSelector( - // getMainCanvasPositioning, - // isAutoLayoutEnabled, - // (positioning: any, autoLayoutEnabled: boolean): AppPositioningTypes => { - // return positioning && positioning !== Positioning.Fixed && autoLayoutEnabled - // ? AppPositioningTypes.AUTO - // : AppPositioningTypes.FIXED; - // }, + getMainCanvasPositioning, isAutoLayoutEnabled, - (): AppPositioningTypes => AppPositioningTypes.FIXED, + (positioning: any, autoLayoutEnabled: boolean): AppPositioningTypes => { + return positioning && positioning !== Positioning.Fixed && autoLayoutEnabled + ? AppPositioningTypes.AUTO + : AppPositioningTypes.FIXED; + }, ); export const getCurrentApplicationLayout = createSelector( diff --git a/app/client/src/utils/hooks/useDynamicAppLayout.tsx b/app/client/src/utils/hooks/useDynamicAppLayout.tsx index 77bfa8aa69e3..57313d969c07 100644 --- a/app/client/src/utils/hooks/useDynamicAppLayout.tsx +++ b/app/client/src/utils/hooks/useDynamicAppLayout.tsx @@ -2,21 +2,21 @@ import { debounce, get } from "lodash"; import { useCallback, useEffect, useMemo } from "react"; import { useDispatch, useSelector } from "react-redux"; -// import { updateLayoutForMobileBreakpointAction } from "actions/autoLayoutActions"; +import { updateLayoutForMobileBreakpointAction } from "actions/autoLayoutActions"; import { updateCanvasLayoutAction } from "actions/editorActions"; import { APP_SETTINGS_PANE_WIDTH } from "constants/AppConstants"; import { DefaultLayoutType, layoutConfigurations, - // MAIN_CONTAINER_WIDGET_ID, + MAIN_CONTAINER_WIDGET_ID, } from "constants/WidgetConstants"; import { APP_MODE } from "entities/App"; import { SIDE_NAV_WIDTH } from "pages/common/SideNav"; -// import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; +import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; import { getIsAppSettingsPaneOpen } from "selectors/appSettingsPaneSelectors"; import { getCurrentApplicationLayout, - // getCurrentAppPositioningType, + getCurrentAppPositioningType, getCurrentPageId, getMainCanvasProps, previewModeSelector, @@ -56,7 +56,7 @@ export const useDynamicAppLayout = () => { const tabsPaneWidth = useSelector(getTabsPaneWidth); const isMultiPane = useSelector(isMultiPaneActive); const paneCount = useSelector(getPaneCount); - // const appPositioningType = useSelector(getCurrentAppPositioningType); + const appPositioningType = useSelector(getCurrentAppPositioningType); // /** // * calculates min height @@ -132,19 +132,19 @@ export const useDynamicAppLayout = () => { if (paneCount === 3) calculatedWidth -= propertyPaneWidth; } - // const ele: any = document.getElementById("canvas-viewport"); - // if ( - // appMode === "EDIT" && - // appLayout?.type === "FLUID" && - // ele && - // calculatedWidth > ele.clientWidth - // ) { - // calculatedWidth = ele.clientWidth; - // } + const ele: any = document.getElementById("canvas-viewport"); + if ( + appMode === "EDIT" && + appLayout?.type === "FLUID" && + ele && + calculatedWidth > ele.clientWidth + ) { + calculatedWidth = ele.clientWidth; + } - // if (appPositioningType === AppPositioningTypes.AUTO && isPreviewMode) { - // calculatedWidth -= AUTOLAYOUT_RESIZER_WIDTH_BUFFER; - // } + if (appPositioningType === AppPositioningTypes.AUTO && isPreviewMode) { + calculatedWidth -= AUTOLAYOUT_RESIZER_WIDTH_BUFFER; + } switch (true) { case maxWidth < 0: @@ -203,29 +203,29 @@ export const useDynamicAppLayout = () => { paneCount, ]); - // const immediateDebouncedResize = useCallback(debounce(resizeToLayout), [ - // mainCanvasProps, - // screenWidth, - // currentPageId, - // appMode, - // appLayout, - // isPreviewMode, - // ]); + const immediateDebouncedResize = useCallback(debounce(resizeToLayout), [ + mainCanvasProps, + screenWidth, + currentPageId, + appMode, + appLayout, + isPreviewMode, + ]); - // const resizeObserver = new ResizeObserver(immediateDebouncedResize); - // useEffect(() => { - // const ele: any = document.getElementById("canvas-viewport"); - // if (ele) { - // if (appLayout?.type === "FLUID") { - // resizeObserver.observe(ele); - // } else { - // resizeObserver.unobserve(ele); - // } - // } - // return () => { - // ele && resizeObserver.unobserve(ele); - // }; - // }, [appLayout, currentPageId, isPreviewMode]); + const resizeObserver = new ResizeObserver(immediateDebouncedResize); + useEffect(() => { + const ele: any = document.getElementById("canvas-viewport"); + if (ele) { + if (appLayout?.type === "FLUID") { + resizeObserver.observe(ele); + } else { + resizeObserver.unobserve(ele); + } + } + return () => { + ele && resizeObserver.unobserve(ele); + }; + }, [appLayout, currentPageId, isPreviewMode]); /** * when screen height is changed, update canvas layout @@ -264,17 +264,17 @@ export const useDynamicAppLayout = () => { currentPageId, //TODO: preet - remove this after first merge. ]); - // useEffect(() => { - // dispatch( - // updateLayoutForMobileBreakpointAction( - // MAIN_CONTAINER_WIDGET_ID, - // appPositioningType === AppPositioningTypes.AUTO - // ? mainCanvasProps?.isMobile - // : false, - // calculateCanvasWidth(), - // ), - // ); - // }, [mainCanvasProps?.isMobile, appPositioningType]); + useEffect(() => { + dispatch( + updateLayoutForMobileBreakpointAction( + MAIN_CONTAINER_WIDGET_ID, + appPositioningType === AppPositioningTypes.AUTO + ? mainCanvasProps?.isMobile + : false, + calculateCanvasWidth(), + ), + ); + }, [mainCanvasProps?.isMobile, appPositioningType]); return isCanvasInitialized; }; diff --git a/app/client/src/widgets/ModalWidget/component/index.tsx b/app/client/src/widgets/ModalWidget/component/index.tsx index 6b914f409912..6d815de2633d 100644 --- a/app/client/src/widgets/ModalWidget/component/index.tsx +++ b/app/client/src/widgets/ModalWidget/component/index.tsx @@ -149,7 +149,6 @@ export default function ModalComponent(props: ModalComponentProps) { const { enableResize = false } = props; const [modalPosition, setModalPosition] = useState("fixed"); - const resizeRef = React.useRef(null); const { setIsResizing } = useWidgetDragResize(); const isResizing = useSelector( (state: AppState) => state.ui.widgetDragResize.isResizing, @@ -243,7 +242,6 @@ export default function ModalComponent(props: ModalComponentProps) { isColliding={() => false} onStart={onResizeStart} onStop={onResizeStop} - ref={resizeRef} resizeDualSides showLightBorder snapGrid={{ x: 1, y: 1 }} From 8793a9d60513fd21b997bdc62f1f45bd280e8319 Mon Sep 17 00:00:00 2001 From: Preet Sidhu Date: Sun, 5 Mar 2023 09:48:14 +0530 Subject: [PATCH 599/708] merge fix --- .../src/resizable/resize/index.autolayout.tsx | 352 ------------------ app/client/src/resizable/resize/index.tsx | 1 - 2 files changed, 353 deletions(-) delete mode 100644 app/client/src/resizable/resize/index.autolayout.tsx diff --git a/app/client/src/resizable/resize/index.autolayout.tsx b/app/client/src/resizable/resize/index.autolayout.tsx deleted file mode 100644 index 38038932eab0..000000000000 --- a/app/client/src/resizable/resize/index.autolayout.tsx +++ /dev/null @@ -1,352 +0,0 @@ -import { isHandleResizeAllowed } from "components/editorComponents/ResizableUtils"; -import React, { ReactNode, useEffect, useState } from "react"; -import { Spring } from "react-spring"; -import { useDrag } from "react-use-gesture"; -import { ReflowDirection } from "reflow/reflowTypes"; -import { ResizeWrapper } from "resizable/resizenreflow"; -import { StyledComponent } from "styled-components"; -import PerformanceTracker, { - PerformanceTransactionName, -} from "utils/PerformanceTracker"; - -const getSnappedValues = ( - x: number, - y: number, - snapGrid: { x: number; y: number }, -) => { - return { - x: Math.round(x / snapGrid.x) * snapGrid.x, - y: Math.round(y / snapGrid.y) * snapGrid.y, - }; -}; - -type ResizableHandleProps = { - allowResize: boolean; - showLightBorder?: boolean; - isHovered: boolean; - disableDot: boolean; - dragCallback: (x: number, y: number) => void; - component: StyledComponent<"div", Record>; - onStart: () => void; - onStop: () => void; - snapGrid: { - x: number; - y: number; - }; -}; - -function ResizableHandle(props: ResizableHandleProps) { - const bind = useDrag( - ({ first, last, dragging, movement: [mx, my], memo }) => { - if (!props.allowResize) { - return; - } - const snapped = getSnappedValues(mx, my, props.snapGrid); - if (dragging && memo && (snapped.x !== memo.x || snapped.y !== memo.y)) { - props.dragCallback(snapped.x, snapped.y); - } - if (first) { - props.onStart(); - } - if (last) { - props.onStop(); - } - return snapped; - }, - ); - const propsToPass = { - ...bind(), - showAsBorder: !props.allowResize, - disableDot: props.disableDot, - isHovered: props.isHovered, - }; - - return ; -} - -type ResizableProps = { - allowResize: boolean; - handles: { - left?: StyledComponent<"div", Record>; - top?: StyledComponent<"div", Record>; - bottom?: StyledComponent<"div", Record>; - right?: StyledComponent<"div", Record>; - bottomRight?: StyledComponent<"div", Record>; - topLeft?: StyledComponent<"div", Record>; - topRight?: StyledComponent<"div", Record>; - bottomLeft?: StyledComponent<"div", Record>; - }; - componentWidth: number; - componentHeight: number; - children: ReactNode; - onStart: () => void; - onStop: ( - size: { width: number; height: number }, - position: { x: number; y: number }, - ) => void; - snapGrid: { x: number; y: number }; - enableVerticalResize: boolean; - enableHorizontalResize: boolean; - isColliding: ( - size: { width: number; height: number }, - position: { x: number; y: number }, - ) => boolean; - className?: string; - resizeDualSides?: boolean; - widgetId: string; - showLightBorder?: boolean; - zWidgetType?: string; -}; - -export const Resizable = function Resizable(props: ResizableProps) { - // Performance tracking start - const sentryPerfTags = props.zWidgetType - ? [{ name: "widget_type", value: props.zWidgetType }] - : []; - PerformanceTracker.startTracking( - PerformanceTransactionName.SHOW_RESIZE_HANDLES, - { widgetId: props.widgetId }, - true, - sentryPerfTags, - ); - - useEffect(() => { - PerformanceTracker.stopTracking( - PerformanceTransactionName.SHOW_RESIZE_HANDLES, - ); - }); - //end - const [pointerEvents, togglePointerEvents] = useState(true); - const [newDimensions, set] = useState({ - width: props.componentWidth, - height: props.componentHeight, - x: 0, - y: 0, - reset: false, - }); - - const { resizeDualSides } = props; - const multiplier = resizeDualSides ? 2 : 1; - - const setNewDimensions = (rect: { - width: number; - height: number; - x: number; - y: number; - }) => { - const { height, width, x, y } = rect; - const shouldUpdateHeight = - props.componentHeight !== height && props.enableVerticalResize; - if (!shouldUpdateHeight) rect.height = props.componentHeight; - - const shouldUpdateWidth = - props.componentWidth !== width && props.enableHorizontalResize; - if (!shouldUpdateWidth) rect.width = props.componentWidth; - - const isColliding = props.isColliding({ width, height }, { x, y }); - if (!isColliding) { - set({ ...rect, reset: false }); - } - }; - - useEffect(() => { - set({ - width: props.componentWidth, - height: props.componentHeight, - x: 0, - y: 0, - reset: true, - }); - }, [props.componentHeight, props.componentWidth]); - - const handles = []; - - if (props.handles.left) { - handles.push({ - dragCallback: (x: number) => { - setNewDimensions({ - width: props.componentWidth - multiplier * x, - height: newDimensions.height, - x: resizeDualSides ? newDimensions.x : x, - y: newDimensions.y, - }); - }, - component: props.handles.left, - handleDirection: ReflowDirection.LEFT, - }); - } - - if (props.handles.top) { - handles.push({ - dragCallback: (x: number, y: number) => { - setNewDimensions({ - width: newDimensions.width, - height: props.componentHeight - multiplier * y, - y: resizeDualSides ? newDimensions.y : y, - x: newDimensions.x, - }); - }, - component: props.handles.top, - handleDirection: ReflowDirection.TOP, - }); - } - - if (props.handles.right) { - handles.push({ - dragCallback: (x: number) => { - setNewDimensions({ - width: props.componentWidth + multiplier * x, - height: newDimensions.height, - x: newDimensions.x, - y: newDimensions.y, - }); - }, - component: props.handles.right, - handleDirection: ReflowDirection.RIGHT, - }); - } - - if (props.handles.bottom) { - handles.push({ - dragCallback: (x: number, y: number) => { - setNewDimensions({ - width: newDimensions.width, - height: props.componentHeight + multiplier * y, - x: newDimensions.x, - y: newDimensions.y, - }); - }, - component: props.handles.bottom, - handleDirection: ReflowDirection.BOTTOM, - }); - } - - if (props.handles.topLeft) { - handles.push({ - dragCallback: (x: number, y: number) => { - setNewDimensions({ - width: props.componentWidth - x, - height: props.componentHeight - y, - x: x, - y: y, - }); - }, - component: props.handles.topLeft, - }); - } - - if (props.handles.topRight) { - handles.push({ - dragCallback: (x: number, y: number) => { - setNewDimensions({ - width: props.componentWidth + x, - height: props.componentHeight - y, - x: newDimensions.x, - y: y, - }); - }, - component: props.handles.topRight, - }); - } - - if (props.handles.bottomRight) { - handles.push({ - dragCallback: (x: number, y: number) => { - setNewDimensions({ - width: props.componentWidth + x, - height: props.componentHeight + y, - x: newDimensions.x, - y: newDimensions.y, - }); - }, - component: props.handles.bottomRight, - }); - } - - if (props.handles.bottomLeft) { - handles.push({ - dragCallback: (x: number, y: number) => { - setNewDimensions({ - width: props.componentWidth - x, - height: props.componentHeight + y, - x, - y: newDimensions.y, - }); - }, - component: props.handles.bottomLeft, - }); - } - const onResizeStop = () => { - togglePointerEvents(true); - props.onStop( - { - width: newDimensions.width, - height: newDimensions.height, - }, - { - x: newDimensions.x, - y: newDimensions.y, - }, - ); - }; - const showResizeBoundary = props.enableHorizontalResize; - const renderHandles = handles.map((handle, index) => { - const disableDot = - !showResizeBoundary || - !isHandleResizeAllowed( - props.enableHorizontalResize, - props.enableVerticalResize, - handle.handleDirection, - ); - return ( - { - togglePointerEvents(false); - props.onStart(); - }} - onStop={onResizeStop} - snapGrid={props.snapGrid} - /> - ); - }); - - return ( - - {(_props) => ( - - {props.children} - {props.enableHorizontalResize && renderHandles} - - )} - - ); -}; - -export default Resizable; diff --git a/app/client/src/resizable/resize/index.tsx b/app/client/src/resizable/resize/index.tsx index 490ce51e975d..0937906fc98c 100644 --- a/app/client/src/resizable/resize/index.tsx +++ b/app/client/src/resizable/resize/index.tsx @@ -95,7 +95,6 @@ type ResizableProps = { resizeDualSides?: boolean; widgetId: string; showLightBorder?: boolean; - widgetId: string; zWidgetType?: string; }; From c214b565a1016345bcb619b5110b584621e6bc1e Mon Sep 17 00:00:00 2001 From: Preet Sidhu Date: Sun, 5 Mar 2023 11:04:41 +0530 Subject: [PATCH 600/708] address code review comments 3 --- app/client/src/actions/autoLayoutActions.ts | 5 +++++ app/client/src/constants/WidgetConstants.tsx | 4 ++++ app/client/src/utils/WidgetPropsUtils.tsx | 4 ++-- app/client/src/utils/hooks/useDynamicAppLayout.tsx | 2 +- app/client/src/widgets/ContainerWidget/widget/index.tsx | 5 +---- 5 files changed, 13 insertions(+), 7 deletions(-) diff --git a/app/client/src/actions/autoLayoutActions.ts b/app/client/src/actions/autoLayoutActions.ts index 45c0ea55b541..467c3690b1ea 100644 --- a/app/client/src/actions/autoLayoutActions.ts +++ b/app/client/src/actions/autoLayoutActions.ts @@ -1,5 +1,10 @@ import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants"; +/** + * Calculate size and position changes owing to minSizes and flex wrap. + * This function is triggered the first time mobile viewport (480px) is encountered. + * It is also called when increasing viewport size from mobile to desktop. + */ export const updateLayoutForMobileBreakpointAction = ( parentId: string, isMobile: boolean, diff --git a/app/client/src/constants/WidgetConstants.tsx b/app/client/src/constants/WidgetConstants.tsx index ef3ab68e84a8..e894df5e3450 100644 --- a/app/client/src/constants/WidgetConstants.tsx +++ b/app/client/src/constants/WidgetConstants.tsx @@ -190,4 +190,8 @@ export const WIDGET_PROPS_TO_SKIP_FROM_EVAL = { bottomRowBeforeCollapse: false, }; +/** + * This is the padding that is applied to the flexbox container. + * It is also used to calculate widget positions and highlight placements. + */ export const FLEXBOX_PADDING = 4; diff --git a/app/client/src/utils/WidgetPropsUtils.tsx b/app/client/src/utils/WidgetPropsUtils.tsx index 9a60e61dc760..161a4a40cbd6 100644 --- a/app/client/src/utils/WidgetPropsUtils.tsx +++ b/app/client/src/utils/WidgetPropsUtils.tsx @@ -244,7 +244,7 @@ export const widgetOperationParams = ( topRow + widgetSizeUpdates.height / parentRowSpace, ), rightColumn: fullWidth - ? 64 + ? GridDefaults.DEFAULT_GRID_COLUMNS : Math.round( leftColumn + widgetSizeUpdates.width / parentColumnSpace, ), @@ -256,7 +256,7 @@ export const widgetOperationParams = ( // Therefore, this is an operation to add child to this container } const widgetDimensions = { - columns: fullWidth ? 64 : widget.columns, + columns: fullWidth ? GridDefaults.DEFAULT_GRID_COLUMNS : widget.columns, rows: widget.rows, }; diff --git a/app/client/src/utils/hooks/useDynamicAppLayout.tsx b/app/client/src/utils/hooks/useDynamicAppLayout.tsx index 57313d969c07..d49be6a7877b 100644 --- a/app/client/src/utils/hooks/useDynamicAppLayout.tsx +++ b/app/client/src/utils/hooks/useDynamicAppLayout.tsx @@ -134,7 +134,7 @@ export const useDynamicAppLayout = () => { const ele: any = document.getElementById("canvas-viewport"); if ( - appMode === "EDIT" && + appMode === APP_MODE.EDIT && appLayout?.type === "FLUID" && ele && calculatedWidth > ele.clientWidth diff --git a/app/client/src/widgets/ContainerWidget/widget/index.tsx b/app/client/src/widgets/ContainerWidget/widget/index.tsx index 9812e5425b79..8fa2d45363b5 100644 --- a/app/client/src/widgets/ContainerWidget/widget/index.tsx +++ b/app/client/src/widgets/ContainerWidget/widget/index.tsx @@ -223,13 +223,10 @@ export class ContainerWidget extends BaseWidget< }; renderAsContainerComponent(props: ContainerWidgetProps) { - const useAutoLayout = this.props.positioning - ? this.props.positioning === Positioning.Vertical - : false; const isAutoHeightEnabled: boolean = isAutoHeightEnabledForWidget(this.props) && !isAutoHeightEnabledForWidget(this.props, true) && - !useAutoLayout; + this.props.positioning !== Positioning.Vertical; return ( Date: Sun, 5 Mar 2023 19:56:11 +0530 Subject: [PATCH 601/708] remove LayoutDirection usage --- .../appsmith/autoLayout/AutoLayoutLayer.tsx | 10 +-- .../appsmith/autoLayout/FlexBoxComponent.tsx | 30 ++----- .../appsmith/autoLayout/FlexComponent.tsx | 2 - .../CanvasArenas/CanvasDraggingArena.tsx | 4 - .../hooks/useBlocksToBeDraggedOnCanvas.ts | 7 -- .../CanvasArenas/hooks/useCanvasDragging.ts | 2 - .../src/resizable/resizenreflow/index.tsx | 6 +- .../CanvasSagas/AutoLayoutDraggingSagas.ts | 84 ++++++++----------- app/client/src/sagas/WidgetAdditionSagas.ts | 4 +- app/client/src/utils/WidgetPropsUtils.tsx | 14 ++-- app/client/src/utils/autoLayout/testData.ts | 3 +- app/client/src/widgets/BaseWidget.tsx | 7 +- app/client/src/widgets/CanvasWidget.tsx | 17 +--- .../widgets/ContainerWidget/widget/index.tsx | 1 - .../src/widgets/TabsWidget/widget/index.tsx | 6 +- app/client/src/widgets/constants.ts | 7 +- 16 files changed, 66 insertions(+), 138 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx index 959d20087a31..a63f2cc8437f 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx @@ -1,19 +1,17 @@ import React, { ReactNode } from "react"; import styled from "styled-components"; -import { LayoutDirection } from "utils/autoLayout/constants"; - /** - * 1. Given a direction if should employ flex in perpendicular direction. - * 2. It should be able to render children within three nested wrappers for start, center and end alignment. - * 3. Only render start wrapper if a fill widget is present. + * 1. Each row in a FlexBoxComponent (Column) is wrapped in an AutoLayoutLayer. + * 2. It employs a row flex to layout three child row flexes called alignments (start, center and end). + * Together these allow to horizontally align widgets inside a Vertical Stack. + * 3. On mobile viewport, if an alignment requires > 64 columns, then it used flex wrap to neatly place the widgets into subsequent rows. */ export interface AutoLayoutLayerProps { start?: ReactNode; center?: ReactNode; end?: ReactNode; - direction: LayoutDirection; index: number; widgetId: string; isMobile?: boolean; diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx index 3da2ba5b01fe..90858af8fb34 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx @@ -1,10 +1,7 @@ import { isArray } from "lodash"; -import React, { CSSProperties, ReactNode, useMemo } from "react"; +import React, { CSSProperties, ReactNode, useCallback, useMemo } from "react"; -import { - FlexLayerAlignment, - LayoutDirection, -} from "utils/autoLayout/constants"; +import { FlexLayerAlignment } from "utils/autoLayout/constants"; import { APP_MODE } from "entities/App"; import { useSelector } from "react-redux"; import { getAppMode } from "selectors/entitiesSelector"; @@ -18,20 +15,17 @@ import { import { getColumnsForAllLayers } from "selectors/autoLayoutSelectors"; export interface FlexBoxProps { - direction?: LayoutDirection; stretchHeight: boolean; useAutoLayout: boolean; children?: ReactNode; widgetId: string; flexLayers: FlexLayer[]; - isMobile?: boolean; + isMobile: boolean; } export const DEFAULT_HIGHLIGHT_SIZE = 4; function FlexBoxComponent(props: FlexBoxProps) { - const direction: LayoutDirection = - props.direction || LayoutDirection.Horizontal; const appMode: APP_MODE | undefined = useSelector(getAppMode); const leaveSpaceForWidgetName = appMode === APP_MODE.EDIT; const isMobile: boolean = props.isMobile || false; @@ -39,12 +33,9 @@ function FlexBoxComponent(props: FlexBoxProps) { getColumnsForAllLayers(props.widgetId), ); - const renderChildren = () => { + const renderChildren = useCallback(() => { if (!props.children) return null; if (!props.useAutoLayout) return props.children; - if (direction === LayoutDirection.Horizontal) { - return props.children; - } /** * Wrap children of a Vertical Stack in a flex layer. @@ -59,7 +50,7 @@ function FlexBoxComponent(props: FlexBoxProps) { const layers: any[] = processLayers(map); return layers; - }; + }, [props]); function processLayers(map: { [key: string]: any }) { const layers = []; @@ -103,7 +94,6 @@ function FlexBoxComponent(props: FlexBoxProps) { return ( { return { display: !!props.useAutoLayout ? "flex" : "block", - flexDirection: - props.direction === LayoutDirection.Vertical ? "column" : "row", + flexDirection: "column", justifyContent: "flex-start", alignItems: "flex-start", flexWrap: "nowrap", @@ -136,12 +125,7 @@ function FlexBoxComponent(props: FlexBoxProps) { ? `${FLEXBOX_PADDING}px ${FLEXBOX_PADDING}px 22px ${FLEXBOX_PADDING}px` : "0px", }; - }, [ - props.useAutoLayout, - props.direction, - props.stretchHeight, - leaveSpaceForWidgetName, - ]); + }, [props.useAutoLayout, props.stretchHeight, leaveSpaceForWidgetName]); return (
diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx index 4a32aa14968c..bc0124784609 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx @@ -7,7 +7,6 @@ import { snipingModeSelector } from "selectors/editorSelectors"; import { getIsResizing } from "selectors/widgetSelectors"; import { FlexVerticalAlignment, - LayoutDirection, ResponsiveBehavior, } from "utils/autoLayout/constants"; import { useClickToSelectWidget } from "utils/hooks/useClickToSelectWidget"; @@ -19,7 +18,6 @@ export type AutoLayoutProps = { children: ReactNode; componentHeight: number; componentWidth: number; - direction?: LayoutDirection; focused?: boolean; minWidth?: number; parentId?: string; diff --git a/app/client/src/pages/common/CanvasArenas/CanvasDraggingArena.tsx b/app/client/src/pages/common/CanvasArenas/CanvasDraggingArena.tsx index fbede3186a81..162dfea100ac 100644 --- a/app/client/src/pages/common/CanvasArenas/CanvasDraggingArena.tsx +++ b/app/client/src/pages/common/CanvasArenas/CanvasDraggingArena.tsx @@ -1,7 +1,6 @@ import { theme } from "constants/DefaultTheme"; import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; import React, { useMemo } from "react"; -import { LayoutDirection } from "utils/autoLayout/constants"; import { getNearestParentCanvas } from "utils/generators"; import { useCanvasDragging } from "./hooks/useCanvasDragging"; import { StickyCanvasArena } from "./StickyCanvasArena"; @@ -17,7 +16,6 @@ export interface CanvasDraggingArenaProps { alignItems?: string; canExtend: boolean; detachFromLayout?: boolean; - direction?: LayoutDirection; dropDisabled?: boolean; noPad?: boolean; snapColumnSpace: number; @@ -32,7 +30,6 @@ export interface CanvasDraggingArenaProps { export function CanvasDraggingArena({ alignItems, canExtend, - direction, dropDisabled = false, noPad, parentId = "", @@ -52,7 +49,6 @@ export function CanvasDraggingArena({ const { showCanvas } = useCanvasDragging(slidingArenaRef, stickyCanvasRef, { alignItems, canExtend, - direction, dropDisabled, noPad, parentId, diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts b/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts index 946a8f06a945..dd2e2472f9ca 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts @@ -30,7 +30,6 @@ import { DragDetails } from "reducers/uiReducers/dragResizeReducer"; import { getIsReflowing } from "selectors/widgetReflowSelectors"; import { XYCord } from "pages/common/CanvasArenas/hooks/useRenderBlocksOnCanvas"; import { SelectionRequestType } from "sagas/WidgetSelectUtils"; -import { AlignItems, LayoutDirection } from "utils/autoLayout/constants"; import { HighlightInfo } from "utils/autoLayout/autoLayoutTypes"; export interface WidgetDraggingUpdateParams extends WidgetDraggingBlock { @@ -52,8 +51,6 @@ export type WidgetDraggingBlock = { }; export const useBlocksToBeDraggedOnCanvas = ({ - alignItems, - direction, noPad, snapColumnSpace, snapRows, @@ -231,7 +228,6 @@ export const useBlocksToBeDraggedOnCanvas = ({ dropPayload, movedWidgets: selectedWidgets, parentId: widgetId, - direction, }, }); }; @@ -251,8 +247,6 @@ export const useBlocksToBeDraggedOnCanvas = ({ width: each.width, height: each.height, }, - direction === LayoutDirection.Vertical && - alignItems === AlignItems.Stretch, ); return { ...each, @@ -278,7 +272,6 @@ export const useBlocksToBeDraggedOnCanvas = ({ parentId: newWidget.detachFromLayout ? MAIN_CONTAINER_WIDGET_ID : widgetId, - direction, }, }); }; diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index 15b023bd09ab..fcd10309c99f 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -46,7 +46,6 @@ export const useCanvasDragging = ( { alignItems, canExtend, - direction, dropDisabled, noPad, snapColumnSpace, @@ -85,7 +84,6 @@ export const useCanvasDragging = ( } = useBlocksToBeDraggedOnCanvas({ alignItems, canExtend, - direction, noPad, snapColumnSpace, snapRows, diff --git a/app/client/src/resizable/resizenreflow/index.tsx b/app/client/src/resizable/resizenreflow/index.tsx index 98ae6ea0db55..28a3592fd74b 100644 --- a/app/client/src/resizable/resizenreflow/index.tsx +++ b/app/client/src/resizable/resizenreflow/index.tsx @@ -26,10 +26,7 @@ import { getFillWidgetLengthForLayer, getLayerIndexOfWidget, } from "utils/autoLayout/AutoLayoutUtils"; -import { - LayoutDirection, - ResponsiveBehavior, -} from "utils/autoLayout/constants"; +import { ResponsiveBehavior } from "utils/autoLayout/constants"; import { getNearestParentCanvas } from "utils/generators"; import { useReflow } from "utils/hooks/useReflow"; import PerformanceTracker, { @@ -217,7 +214,6 @@ type ResizableProps = { isFlexChild?: boolean; isHovered: boolean; responsiveBehavior?: ResponsiveBehavior; - direction?: LayoutDirection; paddingOffset: number; isMobile: boolean; showResizeBoundary: boolean; diff --git a/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts b/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts index b9b56b5f247e..21ea602bbe32 100644 --- a/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts +++ b/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts @@ -4,10 +4,7 @@ import { ReduxActionErrorTypes, ReduxActionTypes, } from "ce/constants/ReduxActionConstants"; -import { - FlexLayerAlignment, - LayoutDirection, -} from "utils/autoLayout/constants"; +import { FlexLayerAlignment } from "utils/autoLayout/constants"; import { GridDefaults, MAIN_CONTAINER_WIDGET_ID, @@ -32,12 +29,11 @@ function* addWidgetAndReorderSaga( actionPayload: ReduxAction<{ newWidget: WidgetAddChild; parentId: string; - direction: LayoutDirection; dropPayload: HighlightInfo; }>, ) { const start = performance.now(); - const { direction, dropPayload, newWidget, parentId } = actionPayload.payload; + const { dropPayload, newWidget, parentId } = actionPayload.payload; const { alignment, index, isNewLayer, layerIndex, rowIndex } = dropPayload; const isMobile: boolean = yield select(getIsMobile); try { @@ -58,7 +54,6 @@ function* addWidgetAndReorderSaga( parentId, allWidgets: updatedWidgetsOnAddition, alignment, - direction, layerIndex, rowIndex, isMobile, @@ -86,18 +81,12 @@ function* autoLayoutReorderSaga( actionPayload: ReduxAction<{ movedWidgets: string[]; parentId: string; - direction: LayoutDirection; dropPayload: HighlightInfo; }>, ) { const start = performance.now(); - const { - direction, - dropPayload, - movedWidgets, - parentId, - } = actionPayload.payload; + const { dropPayload, movedWidgets, parentId } = actionPayload.payload; const { alignment, index, isNewLayer, layerIndex, rowIndex } = dropPayload; @@ -114,7 +103,6 @@ function* autoLayoutReorderSaga( parentId, allWidgets, alignment, - direction, layerIndex, rowIndex, isMobile, @@ -145,7 +133,6 @@ function* reorderAutolayoutChildren(params: { parentId: string; allWidgets: CanvasWidgetsReduxState; alignment: FlexLayerAlignment; - direction: LayoutDirection; layerIndex?: number; rowIndex: number; isMobile?: boolean; @@ -153,7 +140,6 @@ function* reorderAutolayoutChildren(params: { const { alignment, allWidgets, - direction, index, isMobile, isNewLayer, @@ -175,42 +161,40 @@ function* reorderAutolayoutChildren(params: { ); // Update flexLayers for a vertical stack. - if (direction === LayoutDirection.Vertical) { - const canvas = widgets[parentId]; - if (!canvas) return widgets; - const flexLayers = canvas.flexLayers || []; + const canvas = widgets[parentId]; + if (!canvas) return widgets; + const flexLayers = canvas.flexLayers || []; - // Remove moved widgets from the flex layers. - const filteredLayers = removeWidgetsFromCurrentLayers( - selectedWidgets, - flexLayers, - ); + // Remove moved widgets from the flex layers. + const filteredLayers = removeWidgetsFromCurrentLayers( + selectedWidgets, + flexLayers, + ); - // Create a temporary layer from moved widgets. - const newLayer: FlexLayer = createFlexLayer( - selectedWidgets, - widgets, - alignment, - ); + // Create a temporary layer from moved widgets. + const newLayer: FlexLayer = createFlexLayer( + selectedWidgets, + widgets, + alignment, + ); - // Add the new layer to the flex layers. - updatedWidgets = isNewLayer - ? addNewLayer( - newLayer, - updatedWidgets, - parentId, - filteredLayers, - layerIndex, - ) - : updateExistingLayer( - newLayer, - updatedWidgets, - parentId, - filteredLayers, - layerIndex, - rowIndex, - ); - } + // Add the new layer to the flex layers. + updatedWidgets = isNewLayer + ? addNewLayer( + newLayer, + updatedWidgets, + parentId, + filteredLayers, + layerIndex, + ) + : updateExistingLayer( + newLayer, + updatedWidgets, + parentId, + filteredLayers, + layerIndex, + rowIndex, + ); // update children of the parent canvas. const items = [...(widgets[parentId].children || [])]; diff --git a/app/client/src/sagas/WidgetAdditionSagas.ts b/app/client/src/sagas/WidgetAdditionSagas.ts index 0a8de6cc6b6b..0cf68a9509a4 100644 --- a/app/client/src/sagas/WidgetAdditionSagas.ts +++ b/app/client/src/sagas/WidgetAdditionSagas.ts @@ -41,6 +41,7 @@ import { DataTree } from "entities/DataTree/dataTreeFactory"; import { generateAutoHeightLayoutTreeAction } from "actions/autoHeightActions"; import { ResponsiveBehavior } from "utils/autoLayout/constants"; import { isStack } from "../utils/autoLayout/AutoLayoutUtils"; +import { getIsMobile } from "selectors/mainCanvasSelectors"; const WidgetTypes = WidgetFactory.widgetTypes; @@ -106,7 +107,7 @@ function* getChildWidgetProps( }); } } - + const isMobile: boolean = yield select(getIsMobile); const isAutoLayout = isStack(widgets, parent); if ( isAutoLayout && @@ -134,6 +135,7 @@ function* getChildWidgetProps( widgetName, widgetProps, restDefaultConfig.version, + isMobile, ); widget.widgetId = newWidgetId; diff --git a/app/client/src/utils/WidgetPropsUtils.tsx b/app/client/src/utils/WidgetPropsUtils.tsx index 161a4a40cbd6..36ff3b3d3c56 100644 --- a/app/client/src/utils/WidgetPropsUtils.tsx +++ b/app/client/src/utils/WidgetPropsUtils.tsx @@ -223,7 +223,6 @@ export const widgetOperationParams = ( width: number; height: number; }, - fullWidth = false, ): WidgetOperationParams => { const [leftColumn, topRow] = getDropZoneOffsets( parentColumnSpace, @@ -243,11 +242,9 @@ export const widgetOperationParams = ( bottomRow: Math.round( topRow + widgetSizeUpdates.height / parentRowSpace, ), - rightColumn: fullWidth - ? GridDefaults.DEFAULT_GRID_COLUMNS - : Math.round( - leftColumn + widgetSizeUpdates.width / parentColumnSpace, - ), + rightColumn: Math.round( + leftColumn + widgetSizeUpdates.width / parentColumnSpace, + ), parentId: widget.parentId, newParentId: parentWidgetId, }, @@ -256,7 +253,7 @@ export const widgetOperationParams = ( // Therefore, this is an operation to add child to this container } const widgetDimensions = { - columns: fullWidth ? GridDefaults.DEFAULT_GRID_COLUMNS : widget.columns, + columns: widget.columns, rows: widget.rows, }; @@ -307,6 +304,7 @@ export const generateWidgetProps = ( renderMode: RenderMode; } & Partial, version: number, + isMobile: boolean, ): DSLWidget => { if (parent) { const sizes = { @@ -330,6 +328,8 @@ export const generateWidgetProps = ( ...others, parentId: parent.widgetId, version, + isMobile, + positioning: parent.positioning, }; delete props.rows; delete props.columns; diff --git a/app/client/src/utils/autoLayout/testData.ts b/app/client/src/utils/autoLayout/testData.ts index 5eeb48e8ee42..b106404a4d56 100644 --- a/app/client/src/utils/autoLayout/testData.ts +++ b/app/client/src/utils/autoLayout/testData.ts @@ -1,5 +1,5 @@ import { RenderModes } from "constants/WidgetConstants"; -import { LayoutDirection, ResponsiveBehavior } from "./constants"; +import { ResponsiveBehavior } from "./constants"; export const data = { "0": { @@ -39,7 +39,6 @@ export const data = { ], }, ], - direction: LayoutDirection.Vertical, renderMode: RenderModes.CANVAS, isLoading: false, }, diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index 26dda168d479..50cae2c70610 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -46,7 +46,7 @@ import AnalyticsUtil from "utils/AnalyticsUtil"; import AppsmithConsole from "utils/AppsmithConsole"; import { FlexVerticalAlignment, - LayoutDirection, + Positioning, ResponsiveBehavior, } from "utils/autoLayout/constants"; import { @@ -557,7 +557,6 @@ abstract class BaseWidget< , ): JSX.Element { - const direction = this.getDirection(); const snapRows = getCanvasSnapRows(props.bottomRow, props.canExtend); return ( @@ -93,7 +92,6 @@ class CanvasWidget extends ContainerWidget { {...this.getSnapSpaces()} alignItems={props.alignItems} canExtend={props.canExtend} - direction={direction} dropDisabled={!!props.dropDisabled} noPad={this.props.noPad} parentId={props.parentId} @@ -113,17 +111,16 @@ class CanvasWidget extends ContainerWidget { )} {this.props.useAutoLayout - ? this.renderFlexCanvas(direction) + ? this.renderFlexCanvas() : this.renderFixedCanvas(props)} ); } - renderFlexCanvas(direction: LayoutDirection) { + renderFlexCanvas() { const stretchFlexBox = !this.props.children || !this.props.children?.length; return ( containerStyle?: ContainerStyle; shouldScrollContents?: boolean; noPad?: boolean; - positioning?: Positioning; } export default ContainerWidget; diff --git a/app/client/src/widgets/TabsWidget/widget/index.tsx b/app/client/src/widgets/TabsWidget/widget/index.tsx index 9daf2cc71e7f..39d8c29ba100 100644 --- a/app/client/src/widgets/TabsWidget/widget/index.tsx +++ b/app/client/src/widgets/TabsWidget/widget/index.tsx @@ -1,5 +1,5 @@ import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants"; -import { LayoutDirection, Positioning } from "utils/autoLayout/constants"; +import { Positioning } from "utils/autoLayout/constants"; import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; import { WIDGET_PADDING } from "constants/WidgetConstants"; import { @@ -377,10 +377,6 @@ class TabsWidget extends BaseWidget< : Positioning.Fixed; childWidgetData.positioning = positioning; childWidgetData.useAutoLayout = positioning !== Positioning.Fixed; - childWidgetData.direction = - positioning === Positioning.Vertical - ? LayoutDirection.Vertical - : LayoutDirection.Horizontal; childWidgetData.alignment = selectedTabProps?.alignment; childWidgetData.spacing = selectedTabProps?.spacing; diff --git a/app/client/src/widgets/constants.ts b/app/client/src/widgets/constants.ts index d4751077f228..f502e63a9da2 100644 --- a/app/client/src/widgets/constants.ts +++ b/app/client/src/widgets/constants.ts @@ -6,11 +6,7 @@ import { Stylesheet } from "entities/AppTheming"; import { omit } from "lodash"; import moment from "moment"; import { WidgetConfigProps } from "reducers/entityReducers/widgetConfigReducer"; -import { - LayoutDirection, - Positioning, - ResponsiveBehavior, -} from "utils/autoLayout/constants"; +import { Positioning, ResponsiveBehavior } from "utils/autoLayout/constants"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; import { WidgetFeatures } from "utils/WidgetFeatures"; import { WidgetProps } from "./BaseWidget"; @@ -64,7 +60,6 @@ export interface DSLWidget extends WidgetProps { interface LayoutProps { positioning?: Positioning; useAutoLayout?: boolean; - direction?: LayoutDirection; isFlexChild?: boolean; responsiveBehavior?: ResponsiveBehavior; } From f9df3c78208ec3b62834e9c690782eef9fd5d1f5 Mon Sep 17 00:00:00 2001 From: Preet Sidhu Date: Sun, 5 Mar 2023 19:56:11 +0530 Subject: [PATCH 602/708] Revert "remove LayoutDirection usage" This reverts commit 40952f923ed0740b2f83f420959c1dca048cce2e. --- .../appsmith/autoLayout/AutoLayoutLayer.tsx | 10 ++- .../appsmith/autoLayout/FlexBoxComponent.tsx | 30 +++++-- .../appsmith/autoLayout/FlexComponent.tsx | 2 + .../CanvasArenas/CanvasDraggingArena.tsx | 4 + .../hooks/useBlocksToBeDraggedOnCanvas.ts | 7 ++ .../CanvasArenas/hooks/useCanvasDragging.ts | 2 + .../src/resizable/resizenreflow/index.tsx | 6 +- .../CanvasSagas/AutoLayoutDraggingSagas.ts | 84 +++++++++++-------- app/client/src/sagas/WidgetAdditionSagas.ts | 4 +- app/client/src/utils/WidgetPropsUtils.tsx | 14 ++-- app/client/src/utils/autoLayout/testData.ts | 3 +- app/client/src/widgets/BaseWidget.tsx | 7 +- app/client/src/widgets/CanvasWidget.tsx | 17 +++- .../widgets/ContainerWidget/widget/index.tsx | 1 + .../src/widgets/TabsWidget/widget/index.tsx | 6 +- app/client/src/widgets/constants.ts | 7 +- 16 files changed, 138 insertions(+), 66 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx index a63f2cc8437f..959d20087a31 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx @@ -1,17 +1,19 @@ import React, { ReactNode } from "react"; import styled from "styled-components"; +import { LayoutDirection } from "utils/autoLayout/constants"; + /** - * 1. Each row in a FlexBoxComponent (Column) is wrapped in an AutoLayoutLayer. - * 2. It employs a row flex to layout three child row flexes called alignments (start, center and end). - * Together these allow to horizontally align widgets inside a Vertical Stack. - * 3. On mobile viewport, if an alignment requires > 64 columns, then it used flex wrap to neatly place the widgets into subsequent rows. + * 1. Given a direction if should employ flex in perpendicular direction. + * 2. It should be able to render children within three nested wrappers for start, center and end alignment. + * 3. Only render start wrapper if a fill widget is present. */ export interface AutoLayoutLayerProps { start?: ReactNode; center?: ReactNode; end?: ReactNode; + direction: LayoutDirection; index: number; widgetId: string; isMobile?: boolean; diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx index 90858af8fb34..3da2ba5b01fe 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx @@ -1,7 +1,10 @@ import { isArray } from "lodash"; -import React, { CSSProperties, ReactNode, useCallback, useMemo } from "react"; +import React, { CSSProperties, ReactNode, useMemo } from "react"; -import { FlexLayerAlignment } from "utils/autoLayout/constants"; +import { + FlexLayerAlignment, + LayoutDirection, +} from "utils/autoLayout/constants"; import { APP_MODE } from "entities/App"; import { useSelector } from "react-redux"; import { getAppMode } from "selectors/entitiesSelector"; @@ -15,17 +18,20 @@ import { import { getColumnsForAllLayers } from "selectors/autoLayoutSelectors"; export interface FlexBoxProps { + direction?: LayoutDirection; stretchHeight: boolean; useAutoLayout: boolean; children?: ReactNode; widgetId: string; flexLayers: FlexLayer[]; - isMobile: boolean; + isMobile?: boolean; } export const DEFAULT_HIGHLIGHT_SIZE = 4; function FlexBoxComponent(props: FlexBoxProps) { + const direction: LayoutDirection = + props.direction || LayoutDirection.Horizontal; const appMode: APP_MODE | undefined = useSelector(getAppMode); const leaveSpaceForWidgetName = appMode === APP_MODE.EDIT; const isMobile: boolean = props.isMobile || false; @@ -33,9 +39,12 @@ function FlexBoxComponent(props: FlexBoxProps) { getColumnsForAllLayers(props.widgetId), ); - const renderChildren = useCallback(() => { + const renderChildren = () => { if (!props.children) return null; if (!props.useAutoLayout) return props.children; + if (direction === LayoutDirection.Horizontal) { + return props.children; + } /** * Wrap children of a Vertical Stack in a flex layer. @@ -50,7 +59,7 @@ function FlexBoxComponent(props: FlexBoxProps) { const layers: any[] = processLayers(map); return layers; - }, [props]); + }; function processLayers(map: { [key: string]: any }) { const layers = []; @@ -94,6 +103,7 @@ function FlexBoxComponent(props: FlexBoxProps) { return ( { return { display: !!props.useAutoLayout ? "flex" : "block", - flexDirection: "column", + flexDirection: + props.direction === LayoutDirection.Vertical ? "column" : "row", justifyContent: "flex-start", alignItems: "flex-start", flexWrap: "nowrap", @@ -125,7 +136,12 @@ function FlexBoxComponent(props: FlexBoxProps) { ? `${FLEXBOX_PADDING}px ${FLEXBOX_PADDING}px 22px ${FLEXBOX_PADDING}px` : "0px", }; - }, [props.useAutoLayout, props.stretchHeight, leaveSpaceForWidgetName]); + }, [ + props.useAutoLayout, + props.direction, + props.stretchHeight, + leaveSpaceForWidgetName, + ]); return (
diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx index bc0124784609..4a32aa14968c 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx @@ -7,6 +7,7 @@ import { snipingModeSelector } from "selectors/editorSelectors"; import { getIsResizing } from "selectors/widgetSelectors"; import { FlexVerticalAlignment, + LayoutDirection, ResponsiveBehavior, } from "utils/autoLayout/constants"; import { useClickToSelectWidget } from "utils/hooks/useClickToSelectWidget"; @@ -18,6 +19,7 @@ export type AutoLayoutProps = { children: ReactNode; componentHeight: number; componentWidth: number; + direction?: LayoutDirection; focused?: boolean; minWidth?: number; parentId?: string; diff --git a/app/client/src/pages/common/CanvasArenas/CanvasDraggingArena.tsx b/app/client/src/pages/common/CanvasArenas/CanvasDraggingArena.tsx index 162dfea100ac..fbede3186a81 100644 --- a/app/client/src/pages/common/CanvasArenas/CanvasDraggingArena.tsx +++ b/app/client/src/pages/common/CanvasArenas/CanvasDraggingArena.tsx @@ -1,6 +1,7 @@ import { theme } from "constants/DefaultTheme"; import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; import React, { useMemo } from "react"; +import { LayoutDirection } from "utils/autoLayout/constants"; import { getNearestParentCanvas } from "utils/generators"; import { useCanvasDragging } from "./hooks/useCanvasDragging"; import { StickyCanvasArena } from "./StickyCanvasArena"; @@ -16,6 +17,7 @@ export interface CanvasDraggingArenaProps { alignItems?: string; canExtend: boolean; detachFromLayout?: boolean; + direction?: LayoutDirection; dropDisabled?: boolean; noPad?: boolean; snapColumnSpace: number; @@ -30,6 +32,7 @@ export interface CanvasDraggingArenaProps { export function CanvasDraggingArena({ alignItems, canExtend, + direction, dropDisabled = false, noPad, parentId = "", @@ -49,6 +52,7 @@ export function CanvasDraggingArena({ const { showCanvas } = useCanvasDragging(slidingArenaRef, stickyCanvasRef, { alignItems, canExtend, + direction, dropDisabled, noPad, parentId, diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts b/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts index dd2e2472f9ca..946a8f06a945 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas.ts @@ -30,6 +30,7 @@ import { DragDetails } from "reducers/uiReducers/dragResizeReducer"; import { getIsReflowing } from "selectors/widgetReflowSelectors"; import { XYCord } from "pages/common/CanvasArenas/hooks/useRenderBlocksOnCanvas"; import { SelectionRequestType } from "sagas/WidgetSelectUtils"; +import { AlignItems, LayoutDirection } from "utils/autoLayout/constants"; import { HighlightInfo } from "utils/autoLayout/autoLayoutTypes"; export interface WidgetDraggingUpdateParams extends WidgetDraggingBlock { @@ -51,6 +52,8 @@ export type WidgetDraggingBlock = { }; export const useBlocksToBeDraggedOnCanvas = ({ + alignItems, + direction, noPad, snapColumnSpace, snapRows, @@ -228,6 +231,7 @@ export const useBlocksToBeDraggedOnCanvas = ({ dropPayload, movedWidgets: selectedWidgets, parentId: widgetId, + direction, }, }); }; @@ -247,6 +251,8 @@ export const useBlocksToBeDraggedOnCanvas = ({ width: each.width, height: each.height, }, + direction === LayoutDirection.Vertical && + alignItems === AlignItems.Stretch, ); return { ...each, @@ -272,6 +278,7 @@ export const useBlocksToBeDraggedOnCanvas = ({ parentId: newWidget.detachFromLayout ? MAIN_CONTAINER_WIDGET_ID : widgetId, + direction, }, }); }; diff --git a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts index fcd10309c99f..15b023bd09ab 100644 --- a/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts +++ b/app/client/src/pages/common/CanvasArenas/hooks/useCanvasDragging.ts @@ -46,6 +46,7 @@ export const useCanvasDragging = ( { alignItems, canExtend, + direction, dropDisabled, noPad, snapColumnSpace, @@ -84,6 +85,7 @@ export const useCanvasDragging = ( } = useBlocksToBeDraggedOnCanvas({ alignItems, canExtend, + direction, noPad, snapColumnSpace, snapRows, diff --git a/app/client/src/resizable/resizenreflow/index.tsx b/app/client/src/resizable/resizenreflow/index.tsx index 28a3592fd74b..98ae6ea0db55 100644 --- a/app/client/src/resizable/resizenreflow/index.tsx +++ b/app/client/src/resizable/resizenreflow/index.tsx @@ -26,7 +26,10 @@ import { getFillWidgetLengthForLayer, getLayerIndexOfWidget, } from "utils/autoLayout/AutoLayoutUtils"; -import { ResponsiveBehavior } from "utils/autoLayout/constants"; +import { + LayoutDirection, + ResponsiveBehavior, +} from "utils/autoLayout/constants"; import { getNearestParentCanvas } from "utils/generators"; import { useReflow } from "utils/hooks/useReflow"; import PerformanceTracker, { @@ -214,6 +217,7 @@ type ResizableProps = { isFlexChild?: boolean; isHovered: boolean; responsiveBehavior?: ResponsiveBehavior; + direction?: LayoutDirection; paddingOffset: number; isMobile: boolean; showResizeBoundary: boolean; diff --git a/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts b/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts index 21ea602bbe32..b9b56b5f247e 100644 --- a/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts +++ b/app/client/src/sagas/CanvasSagas/AutoLayoutDraggingSagas.ts @@ -4,7 +4,10 @@ import { ReduxActionErrorTypes, ReduxActionTypes, } from "ce/constants/ReduxActionConstants"; -import { FlexLayerAlignment } from "utils/autoLayout/constants"; +import { + FlexLayerAlignment, + LayoutDirection, +} from "utils/autoLayout/constants"; import { GridDefaults, MAIN_CONTAINER_WIDGET_ID, @@ -29,11 +32,12 @@ function* addWidgetAndReorderSaga( actionPayload: ReduxAction<{ newWidget: WidgetAddChild; parentId: string; + direction: LayoutDirection; dropPayload: HighlightInfo; }>, ) { const start = performance.now(); - const { dropPayload, newWidget, parentId } = actionPayload.payload; + const { direction, dropPayload, newWidget, parentId } = actionPayload.payload; const { alignment, index, isNewLayer, layerIndex, rowIndex } = dropPayload; const isMobile: boolean = yield select(getIsMobile); try { @@ -54,6 +58,7 @@ function* addWidgetAndReorderSaga( parentId, allWidgets: updatedWidgetsOnAddition, alignment, + direction, layerIndex, rowIndex, isMobile, @@ -81,12 +86,18 @@ function* autoLayoutReorderSaga( actionPayload: ReduxAction<{ movedWidgets: string[]; parentId: string; + direction: LayoutDirection; dropPayload: HighlightInfo; }>, ) { const start = performance.now(); - const { dropPayload, movedWidgets, parentId } = actionPayload.payload; + const { + direction, + dropPayload, + movedWidgets, + parentId, + } = actionPayload.payload; const { alignment, index, isNewLayer, layerIndex, rowIndex } = dropPayload; @@ -103,6 +114,7 @@ function* autoLayoutReorderSaga( parentId, allWidgets, alignment, + direction, layerIndex, rowIndex, isMobile, @@ -133,6 +145,7 @@ function* reorderAutolayoutChildren(params: { parentId: string; allWidgets: CanvasWidgetsReduxState; alignment: FlexLayerAlignment; + direction: LayoutDirection; layerIndex?: number; rowIndex: number; isMobile?: boolean; @@ -140,6 +153,7 @@ function* reorderAutolayoutChildren(params: { const { alignment, allWidgets, + direction, index, isMobile, isNewLayer, @@ -161,40 +175,42 @@ function* reorderAutolayoutChildren(params: { ); // Update flexLayers for a vertical stack. - const canvas = widgets[parentId]; - if (!canvas) return widgets; - const flexLayers = canvas.flexLayers || []; + if (direction === LayoutDirection.Vertical) { + const canvas = widgets[parentId]; + if (!canvas) return widgets; + const flexLayers = canvas.flexLayers || []; - // Remove moved widgets from the flex layers. - const filteredLayers = removeWidgetsFromCurrentLayers( - selectedWidgets, - flexLayers, - ); + // Remove moved widgets from the flex layers. + const filteredLayers = removeWidgetsFromCurrentLayers( + selectedWidgets, + flexLayers, + ); - // Create a temporary layer from moved widgets. - const newLayer: FlexLayer = createFlexLayer( - selectedWidgets, - widgets, - alignment, - ); + // Create a temporary layer from moved widgets. + const newLayer: FlexLayer = createFlexLayer( + selectedWidgets, + widgets, + alignment, + ); - // Add the new layer to the flex layers. - updatedWidgets = isNewLayer - ? addNewLayer( - newLayer, - updatedWidgets, - parentId, - filteredLayers, - layerIndex, - ) - : updateExistingLayer( - newLayer, - updatedWidgets, - parentId, - filteredLayers, - layerIndex, - rowIndex, - ); + // Add the new layer to the flex layers. + updatedWidgets = isNewLayer + ? addNewLayer( + newLayer, + updatedWidgets, + parentId, + filteredLayers, + layerIndex, + ) + : updateExistingLayer( + newLayer, + updatedWidgets, + parentId, + filteredLayers, + layerIndex, + rowIndex, + ); + } // update children of the parent canvas. const items = [...(widgets[parentId].children || [])]; diff --git a/app/client/src/sagas/WidgetAdditionSagas.ts b/app/client/src/sagas/WidgetAdditionSagas.ts index 0cf68a9509a4..0a8de6cc6b6b 100644 --- a/app/client/src/sagas/WidgetAdditionSagas.ts +++ b/app/client/src/sagas/WidgetAdditionSagas.ts @@ -41,7 +41,6 @@ import { DataTree } from "entities/DataTree/dataTreeFactory"; import { generateAutoHeightLayoutTreeAction } from "actions/autoHeightActions"; import { ResponsiveBehavior } from "utils/autoLayout/constants"; import { isStack } from "../utils/autoLayout/AutoLayoutUtils"; -import { getIsMobile } from "selectors/mainCanvasSelectors"; const WidgetTypes = WidgetFactory.widgetTypes; @@ -107,7 +106,7 @@ function* getChildWidgetProps( }); } } - const isMobile: boolean = yield select(getIsMobile); + const isAutoLayout = isStack(widgets, parent); if ( isAutoLayout && @@ -135,7 +134,6 @@ function* getChildWidgetProps( widgetName, widgetProps, restDefaultConfig.version, - isMobile, ); widget.widgetId = newWidgetId; diff --git a/app/client/src/utils/WidgetPropsUtils.tsx b/app/client/src/utils/WidgetPropsUtils.tsx index 36ff3b3d3c56..161a4a40cbd6 100644 --- a/app/client/src/utils/WidgetPropsUtils.tsx +++ b/app/client/src/utils/WidgetPropsUtils.tsx @@ -223,6 +223,7 @@ export const widgetOperationParams = ( width: number; height: number; }, + fullWidth = false, ): WidgetOperationParams => { const [leftColumn, topRow] = getDropZoneOffsets( parentColumnSpace, @@ -242,9 +243,11 @@ export const widgetOperationParams = ( bottomRow: Math.round( topRow + widgetSizeUpdates.height / parentRowSpace, ), - rightColumn: Math.round( - leftColumn + widgetSizeUpdates.width / parentColumnSpace, - ), + rightColumn: fullWidth + ? GridDefaults.DEFAULT_GRID_COLUMNS + : Math.round( + leftColumn + widgetSizeUpdates.width / parentColumnSpace, + ), parentId: widget.parentId, newParentId: parentWidgetId, }, @@ -253,7 +256,7 @@ export const widgetOperationParams = ( // Therefore, this is an operation to add child to this container } const widgetDimensions = { - columns: widget.columns, + columns: fullWidth ? GridDefaults.DEFAULT_GRID_COLUMNS : widget.columns, rows: widget.rows, }; @@ -304,7 +307,6 @@ export const generateWidgetProps = ( renderMode: RenderMode; } & Partial, version: number, - isMobile: boolean, ): DSLWidget => { if (parent) { const sizes = { @@ -328,8 +330,6 @@ export const generateWidgetProps = ( ...others, parentId: parent.widgetId, version, - isMobile, - positioning: parent.positioning, }; delete props.rows; delete props.columns; diff --git a/app/client/src/utils/autoLayout/testData.ts b/app/client/src/utils/autoLayout/testData.ts index b106404a4d56..5eeb48e8ee42 100644 --- a/app/client/src/utils/autoLayout/testData.ts +++ b/app/client/src/utils/autoLayout/testData.ts @@ -1,5 +1,5 @@ import { RenderModes } from "constants/WidgetConstants"; -import { ResponsiveBehavior } from "./constants"; +import { LayoutDirection, ResponsiveBehavior } from "./constants"; export const data = { "0": { @@ -39,6 +39,7 @@ export const data = { ], }, ], + direction: LayoutDirection.Vertical, renderMode: RenderModes.CANVAS, isLoading: false, }, diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index 50cae2c70610..26dda168d479 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -46,7 +46,7 @@ import AnalyticsUtil from "utils/AnalyticsUtil"; import AppsmithConsole from "utils/AppsmithConsole"; import { FlexVerticalAlignment, - Positioning, + LayoutDirection, ResponsiveBehavior, } from "utils/autoLayout/constants"; import { @@ -557,6 +557,7 @@ abstract class BaseWidget< , ): JSX.Element { + const direction = this.getDirection(); const snapRows = getCanvasSnapRows(props.bottomRow, props.canExtend); return ( @@ -92,6 +93,7 @@ class CanvasWidget extends ContainerWidget { {...this.getSnapSpaces()} alignItems={props.alignItems} canExtend={props.canExtend} + direction={direction} dropDisabled={!!props.dropDisabled} noPad={this.props.noPad} parentId={props.parentId} @@ -111,16 +113,17 @@ class CanvasWidget extends ContainerWidget { )} {this.props.useAutoLayout - ? this.renderFlexCanvas() + ? this.renderFlexCanvas(direction) : this.renderFixedCanvas(props)} ); } - renderFlexCanvas() { + renderFlexCanvas(direction: LayoutDirection) { const stretchFlexBox = !this.props.children || !this.props.children?.length; return ( containerStyle?: ContainerStyle; shouldScrollContents?: boolean; noPad?: boolean; + positioning?: Positioning; } export default ContainerWidget; diff --git a/app/client/src/widgets/TabsWidget/widget/index.tsx b/app/client/src/widgets/TabsWidget/widget/index.tsx index 39d8c29ba100..9daf2cc71e7f 100644 --- a/app/client/src/widgets/TabsWidget/widget/index.tsx +++ b/app/client/src/widgets/TabsWidget/widget/index.tsx @@ -1,5 +1,5 @@ import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants"; -import { Positioning } from "utils/autoLayout/constants"; +import { LayoutDirection, Positioning } from "utils/autoLayout/constants"; import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; import { WIDGET_PADDING } from "constants/WidgetConstants"; import { @@ -377,6 +377,10 @@ class TabsWidget extends BaseWidget< : Positioning.Fixed; childWidgetData.positioning = positioning; childWidgetData.useAutoLayout = positioning !== Positioning.Fixed; + childWidgetData.direction = + positioning === Positioning.Vertical + ? LayoutDirection.Vertical + : LayoutDirection.Horizontal; childWidgetData.alignment = selectedTabProps?.alignment; childWidgetData.spacing = selectedTabProps?.spacing; diff --git a/app/client/src/widgets/constants.ts b/app/client/src/widgets/constants.ts index f502e63a9da2..d4751077f228 100644 --- a/app/client/src/widgets/constants.ts +++ b/app/client/src/widgets/constants.ts @@ -6,7 +6,11 @@ import { Stylesheet } from "entities/AppTheming"; import { omit } from "lodash"; import moment from "moment"; import { WidgetConfigProps } from "reducers/entityReducers/widgetConfigReducer"; -import { Positioning, ResponsiveBehavior } from "utils/autoLayout/constants"; +import { + LayoutDirection, + Positioning, + ResponsiveBehavior, +} from "utils/autoLayout/constants"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; import { WidgetFeatures } from "utils/WidgetFeatures"; import { WidgetProps } from "./BaseWidget"; @@ -60,6 +64,7 @@ export interface DSLWidget extends WidgetProps { interface LayoutProps { positioning?: Positioning; useAutoLayout?: boolean; + direction?: LayoutDirection; isFlexChild?: boolean; responsiveBehavior?: ResponsiveBehavior; } From 5dd0b9de3d7513ec6b825b190f75f192f9880bb2 Mon Sep 17 00:00:00 2001 From: Arsalan Date: Mon, 6 Mar 2023 18:24:52 +0530 Subject: [PATCH 603/708] fix: group widgets expanding on adding new options. --- .../appsmith/autoLayout/AutoLayoutDimensionObeserver.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutDimensionObeserver.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutDimensionObeserver.tsx index c4cf9a606dec..1e737e7b7196 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutDimensionObeserver.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutDimensionObeserver.tsx @@ -34,7 +34,6 @@ export default function AutoLayoutDimensionObserver( const height = entries[0].contentRect.height; if (width === 0 || height === 0) return; setCurrentDimension({ width, height }); - onDimensionUpdate(width, height); }), ); From 81deb30bb2391a093bc97e9fdc05ef35c7f02a8e Mon Sep 17 00:00:00 2001 From: Aswath K Date: Wed, 8 Mar 2023 10:30:59 +0530 Subject: [PATCH 604/708] fix: widget width calculation --- .../src/utils/autoLayout/AutoLayoutUtils.ts | 40 +- .../utils/autoLayout/autoLayoutUtils.test.ts | 80 +- .../src/utils/autoLayout/flexWidgetUtils.ts | 2 +- app/client/src/utils/autoLayout/testData.ts | 1084 +++++++++++++++++ 4 files changed, 1187 insertions(+), 19 deletions(-) diff --git a/app/client/src/utils/autoLayout/AutoLayoutUtils.ts b/app/client/src/utils/autoLayout/AutoLayoutUtils.ts index 93f49e6363bf..ff12fe029350 100644 --- a/app/client/src/utils/autoLayout/AutoLayoutUtils.ts +++ b/app/client/src/utils/autoLayout/AutoLayoutUtils.ts @@ -5,6 +5,7 @@ import { GridDefaults, MAIN_CONTAINER_WIDGET_ID, WIDGET_PADDING, + CONTAINER_GRID_PADDING, } from "constants/WidgetConstants"; import { CanvasWidgetsReduxState, @@ -417,36 +418,41 @@ function getCanvasWidth( if (!mainCanvasWidth) return 0; if (canvas.widgetId === MAIN_CONTAINER_WIDGET_ID) return mainCanvasWidth - getPadding(canvas); + + const stack = []; let widget = canvas; - let columns = 0; - let width = 1; - let padding = 0; while (widget.parentId) { - columns = getWidgetWidth(widget, isMobile); - padding += getPadding(widget); - width *= columns > 64 ? 1 : columns / GridDefaults.DEFAULT_GRID_COLUMNS; + stack.push(widget); widget = widgets[widget.parentId]; } - const totalWidth = width * mainCanvasWidth; - if (widget.widgetId === MAIN_CONTAINER_WIDGET_ID) - padding += getPadding(widget); - return totalWidth - padding; + stack.push(widget); + + let width = mainCanvasWidth; + while (stack.length) { + const widget = stack.pop(); + if (!widget) continue; + const columns = getWidgetWidth(widget, isMobile); + const padding = getPadding(widget); + const factor = + columns > GridDefaults.DEFAULT_GRID_COLUMNS + ? 1 + : columns / GridDefaults.DEFAULT_GRID_COLUMNS; + width = width * factor - padding; + } + + return width; } function getPadding(canvas: FlattenedWidgetProps): number { let padding = 0; - if ( - canvas.widgetId === MAIN_CONTAINER_WIDGET_ID || - canvas.type === "CONTAINER_WIDGET" - ) { - //For MainContainer and any Container Widget padding doesn't exist coz there is already container padding. + if (canvas.widgetId === MAIN_CONTAINER_WIDGET_ID) { padding = FLEXBOX_PADDING * 2; + } else if (canvas.type === "CONTAINER_WIDGET") { + padding = (CONTAINER_GRID_PADDING + FLEXBOX_PADDING) * 2; } if (canvas.noPad) { // Widgets like ListWidget choose to have no container padding so will only have widget padding padding = WIDGET_PADDING * 2; } - // Account for container border. - padding += canvas.type === "CONTAINER_WIDGET" ? 2 : 0; return padding; } diff --git a/app/client/src/utils/autoLayout/autoLayoutUtils.test.ts b/app/client/src/utils/autoLayout/autoLayoutUtils.test.ts index de556b342a22..be3ebb516209 100644 --- a/app/client/src/utils/autoLayout/autoLayoutUtils.test.ts +++ b/app/client/src/utils/autoLayout/autoLayoutUtils.test.ts @@ -1,11 +1,12 @@ import { FlexLayer, LayerChild } from "./autoLayoutTypes"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; import { + getCanvasDimensions, getLayerIndexOfWidget, pasteWidgetInFlexLayers, updateFlexLayersOnDelete, } from "./AutoLayoutUtils"; -import { data } from "./testData"; +import { data, dataForgetCanvasDimensions } from "./testData"; describe("test AutoLayoutUtils methods", () => { const mainCanvasWidth = 960; @@ -154,4 +155,81 @@ describe("test AutoLayoutUtils methods", () => { ); }); }); + + describe("test getCanvasDimensions method", () => { + /** + * +---------------------------------------------------------------------------------------+ + * | MainContainer | + * | +------------------------------------------------------------------------------------+| + * | | Container1 || + * | +------------------------------------------------------------------------------------+| + * | +------------+ +---------------+ +---------------+ +----------------+ +--------------+| + * | | Container2 | | | | | | | | || + * | +------------+ +---------------+ +---------------+ +----------------+ ---------------+| + * | +------------------------------------------------------------------------------------+| + * | | +------------+ +---------------+ +---------------+ +----------------+ +-----------+|| + * | | | Container3 | | | | | | | | ||| + * | | +------------+ +---------------+ +---------------+ +----------------+ ------------+|| + * | +------------------------------------------------------------------------------------+| + * +---------------------------------------------------------------------------------------+ + */ + const mainCanvasWidth = 1166; + const widgets = dataForgetCanvasDimensions; + const mainContainerPadding = 4 * 2; + const containerPadding = (4 + 6) * 2; + it("should return proper dimension for MainContainer", () => { + const button0parent = widgets["kv4o6eopdn"] + .parentId as keyof typeof widgets; + const { canvasWidth } = getCanvasDimensions( + widgets[button0parent] as any, + widgets as any, + mainCanvasWidth, + false, + ); + expect(canvasWidth).toEqual(mainCanvasWidth - mainContainerPadding); + }); + + it("should return proper dimension for Container1", () => { + const button1parent = widgets["phf8e237zg"] + .parentId as keyof typeof widgets; + const { canvasWidth } = getCanvasDimensions( + widgets[button1parent] as any, + widgets as any, + mainCanvasWidth, + false, + ); + expect(canvasWidth).toEqual( + mainCanvasWidth - mainContainerPadding - containerPadding, + ); + }); + + it("should return proper dimension for Container2", () => { + const button2parent = widgets["alvcydt4he"] + .parentId as keyof typeof widgets; + const { canvasWidth } = getCanvasDimensions( + widgets[button2parent] as any, + widgets as any, + mainCanvasWidth, + false, + ); + expect(canvasWidth).toEqual( + (mainCanvasWidth - mainContainerPadding) / 4 - containerPadding, + ); + }); + + it("should return proper dimension for Container3", () => { + const button3parent = widgets["cq25w8hz6n"] + .parentId as keyof typeof widgets; + const { canvasWidth } = getCanvasDimensions( + widgets[button3parent] as any, + widgets as any, + mainCanvasWidth, + false, + ); + expect(canvasWidth).toEqual( + (mainCanvasWidth - mainContainerPadding - containerPadding) / 4 - + containerPadding, + ); + }); + }); }); diff --git a/app/client/src/utils/autoLayout/flexWidgetUtils.ts b/app/client/src/utils/autoLayout/flexWidgetUtils.ts index 037265e3cbb5..2b7d5f032d4c 100644 --- a/app/client/src/utils/autoLayout/flexWidgetUtils.ts +++ b/app/client/src/utils/autoLayout/flexWidgetUtils.ts @@ -130,7 +130,7 @@ export function getWidgetRows(widget: any, isMobile: boolean): number { * @param canvasWidth | number : main canvas width. * @returns MinSize | undefined */ -export function getMinMaxSize( +function getMinMaxSize( widget: any, canvasWidth: number, ): MinMaxSize | undefined { diff --git a/app/client/src/utils/autoLayout/testData.ts b/app/client/src/utils/autoLayout/testData.ts index 5eeb48e8ee42..acd3453fe1ee 100644 --- a/app/client/src/utils/autoLayout/testData.ts +++ b/app/client/src/utils/autoLayout/testData.ts @@ -303,3 +303,1087 @@ export const data = { minDynamicHeight: 10, }, }; + +export const dataForgetCanvasDimensions = { + "0": { + widgetName: "MainContainer", + backgroundColor: "none", + rightColumn: 1224, + snapColumns: 64, + detachFromLayout: true, + widgetId: "0", + topRow: 0, + bottomRow: 380, + containerStyle: "none", + snapRows: 82, + parentRowSpace: 1, + type: "CANVAS_WIDGET", + canExtend: true, + version: 77, + minHeight: 840, + useAutoLayout: true, + parentColumnSpace: 1, + dynamicTriggerPathList: [], + dynamicBindingPathList: [], + leftColumn: 0, + children: [ + "kv4o6eopdn", + "l29m7e6zds", + "xrf1j3juvs", + "341mei61on", + "w1g8kwyww5", + "c3qord3aex", + "sy88tbxp13", + ], + positioning: "vertical", + direction: "Vertical", + flexLayers: [ + { + children: [ + { + id: "kv4o6eopdn", + align: "start", + }, + ], + }, + { + children: [ + { + id: "l29m7e6zds", + align: "start", + }, + ], + }, + { + children: [ + { + id: "xrf1j3juvs", + align: "start", + }, + { + id: "341mei61on", + align: "start", + }, + { + id: "w1g8kwyww5", + align: "start", + }, + { + id: "c3qord3aex", + align: "start", + }, + ], + }, + { + children: [ + { + id: "sy88tbxp13", + align: "start", + }, + ], + }, + ], + }, + kv4o6eopdn: { + resetFormOnClick: false, + boxShadow: "none", + widgetName: "Button0", + buttonColor: "{{appsmith.theme.colors.primaryColor}}", + displayName: "Button", + iconSVG: "/static/media/icon.cca026338f1c8eb6df8ba03d084c2fca.svg", + searchTags: ["click", "submit"], + topRow: 0, + bottomRow: 4, + parentRowSpace: 10, + type: "BUTTON_WIDGET", + hideCard: false, + animateLoading: true, + parentColumnSpace: 18.015625, + dynamicTriggerPathList: [], + leftColumn: 0, + dynamicBindingPathList: [ + { + key: "buttonColor", + }, + { + key: "borderRadius", + }, + ], + text: "Button 0", + isDisabled: false, + key: "pr2n39s4pf", + isDeprecated: false, + rightColumn: 6.632124352331606, + isDefaultClickDisabled: true, + widgetId: "kv4o6eopdn", + minWidth: 120, + isVisible: true, + recaptchaType: "V3", + version: 1, + parentId: "0", + renderMode: "CANVAS", + isLoading: false, + responsiveBehavior: "hug", + disabledWhenInvalid: false, + borderRadius: "{{appsmith.theme.borderRadius.appBorderRadius}}", + buttonVariant: "PRIMARY", + placement: "CENTER", + alignment: "start", + }, + phf8e237zg: { + resetFormOnClick: false, + boxShadow: "none", + widgetName: "Button1", + buttonColor: "{{appsmith.theme.colors.primaryColor}}", + displayName: "Button", + iconSVG: "/static/media/icon.cca026338f1c8eb6df8ba03d084c2fca.svg", + searchTags: ["click", "submit"], + topRow: 0, + bottomRow: 4, + parentRowSpace: 10, + type: "BUTTON_WIDGET", + hideCard: false, + animateLoading: true, + parentColumnSpace: 17.703125, + dynamicTriggerPathList: [], + leftColumn: 0, + dynamicBindingPathList: [ + { + key: "buttonColor", + }, + { + key: "borderRadius", + }, + ], + text: "Button 1", + isDisabled: false, + key: "pr2n39s4pf", + isDeprecated: false, + rightColumn: 6.748681898066784, + isDefaultClickDisabled: true, + widgetId: "phf8e237zg", + minWidth: 120, + isVisible: true, + recaptchaType: "V3", + version: 1, + parentId: "r8zx3uttvz", + renderMode: "CANVAS", + isLoading: false, + responsiveBehavior: "hug", + disabledWhenInvalid: false, + borderRadius: "{{appsmith.theme.borderRadius.appBorderRadius}}", + buttonVariant: "PRIMARY", + placement: "CENTER", + alignment: "start", + }, + r8zx3uttvz: { + widgetName: "Canvas1", + displayName: "Canvas", + topRow: 0, + bottomRow: 100, + parentRowSpace: 1, + type: "CANVAS_WIDGET", + canExtend: false, + hideCard: true, + minHeight: 100, + parentColumnSpace: 1, + leftColumn: 0, + dynamicBindingPathList: [], + children: ["phf8e237zg"], + key: "t3ruwylkhy", + isDeprecated: false, + rightColumn: 64, + detachFromLayout: true, + widgetId: "r8zx3uttvz", + containerStyle: "none", + minWidth: 450, + isVisible: true, + version: 1, + parentId: "l29m7e6zds", + renderMode: "CANVAS", + isLoading: false, + responsiveBehavior: "fill", + flexLayers: [ + { + children: [ + { + id: "phf8e237zg", + align: "start", + }, + ], + }, + ], + }, + l29m7e6zds: { + boxShadow: "{{appsmith.theme.boxShadow.appBoxShadow}}", + widgetName: "Container1", + borderColor: "#E0DEDE", + isCanvas: true, + displayName: "Container", + iconSVG: "/static/media/icon.1977dca3370505e2db3a8e44cfd54907.svg", + searchTags: ["div", "parent", "group"], + topRow: 4, + bottomRow: 14, + parentRowSpace: 10, + type: "CONTAINER_WIDGET", + hideCard: false, + shouldScrollContents: true, + animateLoading: true, + parentColumnSpace: 18.015625, + leftColumn: 0, + dynamicBindingPathList: [ + { + key: "borderRadius", + }, + { + key: "boxShadow", + }, + ], + children: ["r8zx3uttvz"], + borderWidth: "1", + key: "el0i1qkdxm", + backgroundColor: "#FFFFFF", + isDeprecated: false, + rightColumn: 64, + dynamicHeight: "AUTO_HEIGHT", + widgetId: "l29m7e6zds", + containerStyle: "card", + minWidth: 450, + isVisible: true, + version: 1, + parentId: "0", + renderMode: "CANVAS", + isLoading: false, + responsiveBehavior: "fill", + originalTopRow: 4, + borderRadius: "{{appsmith.theme.borderRadius.appBorderRadius}}", + maxDynamicHeight: 9000, + originalBottomRow: 15, + alignment: "start", + minDynamicHeight: 10, + }, + alvcydt4he: { + resetFormOnClick: false, + boxShadow: "none", + widgetName: "Button2", + buttonColor: "{{appsmith.theme.colors.primaryColor}}", + displayName: "Button", + iconSVG: "/static/media/icon.cca026338f1c8eb6df8ba03d084c2fca.svg", + searchTags: ["click", "submit"], + topRow: 0, + bottomRow: 4, + parentRowSpace: 10, + type: "BUTTON_WIDGET", + hideCard: false, + animateLoading: true, + parentColumnSpace: 17.703125, + dynamicTriggerPathList: [], + leftColumn: 0, + dynamicBindingPathList: [ + { + key: "buttonColor", + }, + { + key: "borderRadius", + }, + ], + text: "Button 2", + isDisabled: false, + key: "pr2n39s4pf", + isDeprecated: false, + rightColumn: 28.49721706864564, + isDefaultClickDisabled: true, + widgetId: "alvcydt4he", + minWidth: 120, + isVisible: true, + recaptchaType: "V3", + version: 1, + parentId: "r2f7mqcbhz", + renderMode: "CANVAS", + isLoading: false, + responsiveBehavior: "hug", + disabledWhenInvalid: false, + borderRadius: "{{appsmith.theme.borderRadius.appBorderRadius}}", + buttonVariant: "PRIMARY", + placement: "CENTER", + alignment: "start", + }, + r2f7mqcbhz: { + widgetName: "Canvas2", + displayName: "Canvas", + topRow: 0, + bottomRow: 100, + parentRowSpace: 1, + type: "CANVAS_WIDGET", + canExtend: false, + hideCard: true, + minHeight: 100, + parentColumnSpace: 1, + leftColumn: 0, + dynamicBindingPathList: [], + children: ["alvcydt4he"], + key: "t3ruwylkhy", + isDeprecated: false, + rightColumn: 64, + detachFromLayout: true, + widgetId: "r2f7mqcbhz", + containerStyle: "none", + minWidth: 450, + isVisible: true, + version: 1, + parentId: "xrf1j3juvs", + renderMode: "CANVAS", + isLoading: false, + responsiveBehavior: "fill", + flexLayers: [ + { + children: [ + { + id: "alvcydt4he", + align: "start", + }, + ], + }, + ], + }, + xrf1j3juvs: { + boxShadow: "{{appsmith.theme.boxShadow.appBoxShadow}}", + widgetName: "Container2", + borderColor: "#E0DEDE", + isCanvas: true, + displayName: "Container", + iconSVG: "/static/media/icon.1977dca3370505e2db3a8e44cfd54907.svg", + searchTags: ["div", "parent", "group"], + topRow: 14, + bottomRow: 24, + parentRowSpace: 10, + type: "CONTAINER_WIDGET", + hideCard: false, + shouldScrollContents: true, + animateLoading: true, + parentColumnSpace: 18.015625, + leftColumn: 0, + dynamicBindingPathList: [ + { + key: "borderRadius", + }, + { + key: "boxShadow", + }, + ], + children: ["r2f7mqcbhz"], + borderWidth: "1", + key: "el0i1qkdxm", + backgroundColor: "#FFFFFF", + isDeprecated: false, + rightColumn: 16, + dynamicHeight: "AUTO_HEIGHT", + widgetId: "xrf1j3juvs", + containerStyle: "card", + minWidth: 450, + isVisible: true, + version: 1, + parentId: "0", + renderMode: "CANVAS", + isLoading: false, + responsiveBehavior: "fill", + originalTopRow: 15, + borderRadius: "{{appsmith.theme.borderRadius.appBorderRadius}}", + maxDynamicHeight: 9000, + originalBottomRow: 26, + alignment: "start", + minDynamicHeight: 10, + }, + xjcm9uf1d8: { + widgetName: "Canvas10", + displayName: "Canvas", + topRow: 0, + bottomRow: 100, + parentRowSpace: 1, + type: "CANVAS_WIDGET", + canExtend: false, + hideCard: true, + minHeight: 100, + parentColumnSpace: 1, + leftColumn: 0, + dynamicBindingPathList: [], + children: [], + key: "fkqrygr3nj", + isDeprecated: false, + rightColumn: 64, + detachFromLayout: true, + widgetId: "xjcm9uf1d8", + containerStyle: "none", + minWidth: 450, + isVisible: true, + version: 1, + parentId: "341mei61on", + renderMode: "CANVAS", + isLoading: false, + responsiveBehavior: "fill", + flexLayers: [], + }, + "341mei61on": { + boxShadow: "{{appsmith.theme.boxShadow.appBoxShadow}}", + widgetName: "Container10", + borderColor: "#E0DEDE", + isCanvas: true, + displayName: "Container", + iconSVG: "/static/media/icon.1977dca3370505e2db3a8e44cfd54907.svg", + searchTags: ["div", "parent", "group"], + topRow: 14, + bottomRow: 24, + parentRowSpace: 10, + type: "CONTAINER_WIDGET", + hideCard: false, + shouldScrollContents: true, + animateLoading: true, + parentColumnSpace: 18.015625, + leftColumn: 16, + dynamicBindingPathList: [ + { + key: "borderRadius", + }, + { + key: "boxShadow", + }, + ], + children: ["xjcm9uf1d8"], + borderWidth: "1", + key: "4yx1z5m6mk", + backgroundColor: "#FFFFFF", + isDeprecated: false, + rightColumn: 32, + dynamicHeight: "AUTO_HEIGHT", + widgetId: "341mei61on", + containerStyle: "card", + minWidth: 450, + isVisible: true, + version: 1, + parentId: "0", + renderMode: "CANVAS", + isLoading: false, + responsiveBehavior: "fill", + originalTopRow: 15, + borderRadius: "{{appsmith.theme.borderRadius.appBorderRadius}}", + maxDynamicHeight: 9000, + originalBottomRow: 26, + alignment: "start", + minDynamicHeight: 10, + }, + kffoaq7o8d: { + widgetName: "Canvas7", + displayName: "Canvas", + topRow: 0, + bottomRow: 100, + parentRowSpace: 1, + type: "CANVAS_WIDGET", + canExtend: false, + hideCard: true, + minHeight: 100, + parentColumnSpace: 1, + leftColumn: 0, + dynamicBindingPathList: [], + children: [], + key: "t3ruwylkhy", + isDeprecated: false, + rightColumn: 64, + detachFromLayout: true, + widgetId: "kffoaq7o8d", + containerStyle: "none", + minWidth: 450, + isVisible: true, + version: 1, + parentId: "w1g8kwyww5", + renderMode: "CANVAS", + isLoading: false, + responsiveBehavior: "fill", + flexLayers: [], + }, + w1g8kwyww5: { + boxShadow: "{{appsmith.theme.boxShadow.appBoxShadow}}", + widgetName: "Container7", + borderColor: "#E0DEDE", + isCanvas: true, + displayName: "Container", + iconSVG: "/static/media/icon.1977dca3370505e2db3a8e44cfd54907.svg", + searchTags: ["div", "parent", "group"], + topRow: 14, + bottomRow: 24, + parentRowSpace: 10, + type: "CONTAINER_WIDGET", + hideCard: false, + shouldScrollContents: true, + animateLoading: true, + parentColumnSpace: 18.015625, + leftColumn: 32, + dynamicBindingPathList: [ + { + key: "borderRadius", + }, + { + key: "boxShadow", + }, + ], + children: ["kffoaq7o8d"], + borderWidth: "1", + key: "el0i1qkdxm", + backgroundColor: "#FFFFFF", + isDeprecated: false, + rightColumn: 48, + dynamicHeight: "AUTO_HEIGHT", + widgetId: "w1g8kwyww5", + containerStyle: "card", + minWidth: 450, + isVisible: true, + version: 1, + parentId: "0", + renderMode: "CANVAS", + isLoading: false, + responsiveBehavior: "fill", + originalTopRow: 15, + borderRadius: "{{appsmith.theme.borderRadius.appBorderRadius}}", + maxDynamicHeight: 9000, + originalBottomRow: 26, + alignment: "start", + minDynamicHeight: 10, + }, + eqa5amaxqe: { + widgetName: "Canvas6", + displayName: "Canvas", + topRow: 0, + bottomRow: 100, + parentRowSpace: 1, + type: "CANVAS_WIDGET", + canExtend: false, + hideCard: true, + minHeight: 100, + parentColumnSpace: 1, + leftColumn: 0, + dynamicBindingPathList: [], + children: [], + key: "t3ruwylkhy", + isDeprecated: false, + rightColumn: 64, + detachFromLayout: true, + widgetId: "eqa5amaxqe", + containerStyle: "none", + minWidth: 450, + isVisible: true, + version: 1, + parentId: "c3qord3aex", + renderMode: "CANVAS", + isLoading: false, + responsiveBehavior: "fill", + flexLayers: [], + }, + c3qord3aex: { + boxShadow: "{{appsmith.theme.boxShadow.appBoxShadow}}", + widgetName: "Container6", + borderColor: "#E0DEDE", + isCanvas: true, + displayName: "Container", + iconSVG: "/static/media/icon.1977dca3370505e2db3a8e44cfd54907.svg", + searchTags: ["div", "parent", "group"], + topRow: 14, + bottomRow: 24, + parentRowSpace: 10, + type: "CONTAINER_WIDGET", + hideCard: false, + shouldScrollContents: true, + animateLoading: true, + parentColumnSpace: 18.015625, + leftColumn: 48, + dynamicBindingPathList: [ + { + key: "borderRadius", + }, + { + key: "boxShadow", + }, + ], + children: ["eqa5amaxqe"], + borderWidth: "1", + key: "el0i1qkdxm", + backgroundColor: "#FFFFFF", + isDeprecated: false, + rightColumn: 64, + dynamicHeight: "AUTO_HEIGHT", + widgetId: "c3qord3aex", + containerStyle: "card", + minWidth: 450, + isVisible: true, + version: 1, + parentId: "0", + renderMode: "CANVAS", + isLoading: false, + responsiveBehavior: "fill", + originalTopRow: 15, + borderRadius: "{{appsmith.theme.borderRadius.appBorderRadius}}", + maxDynamicHeight: 9000, + originalBottomRow: 26, + alignment: "start", + minDynamicHeight: 10, + }, + cq25w8hz6n: { + resetFormOnClick: false, + boxShadow: "none", + widgetName: "Button3", + buttonColor: "{{appsmith.theme.colors.primaryColor}}", + displayName: "Button", + iconSVG: "/static/media/icon.cca026338f1c8eb6df8ba03d084c2fca.svg", + searchTags: ["click", "submit"], + topRow: 0, + bottomRow: 4, + parentRowSpace: 10, + type: "BUTTON_WIDGET", + hideCard: false, + animateLoading: true, + parentColumnSpace: 17.703125, + dynamicTriggerPathList: [], + leftColumn: 0, + dynamicBindingPathList: [ + { + key: "buttonColor", + }, + { + key: "borderRadius", + }, + ], + text: "Button 3", + isDisabled: false, + key: "pr2n39s4pf", + isDeprecated: false, + rightColumn: 29.035916824196597, + isDefaultClickDisabled: true, + widgetId: "cq25w8hz6n", + minWidth: 120, + isVisible: true, + recaptchaType: "V3", + version: 1, + parentId: "b91wojl19o", + renderMode: "CANVAS", + isLoading: false, + responsiveBehavior: "hug", + disabledWhenInvalid: false, + borderRadius: "{{appsmith.theme.borderRadius.appBorderRadius}}", + buttonVariant: "PRIMARY", + placement: "CENTER", + alignment: "start", + }, + b91wojl19o: { + widgetName: "Canvas3", + displayName: "Canvas", + topRow: 0, + bottomRow: 100, + parentRowSpace: 1, + type: "CANVAS_WIDGET", + canExtend: false, + hideCard: true, + minHeight: 100, + parentColumnSpace: 1, + leftColumn: 0, + dynamicBindingPathList: [], + children: ["cq25w8hz6n"], + key: "t3ruwylkhy", + isDeprecated: false, + rightColumn: 64, + detachFromLayout: true, + widgetId: "b91wojl19o", + containerStyle: "none", + minWidth: 450, + isVisible: true, + version: 1, + parentId: "hibb5668qy", + renderMode: "CANVAS", + isLoading: false, + responsiveBehavior: "fill", + flexLayers: [ + { + children: [ + { + id: "cq25w8hz6n", + align: "start", + }, + ], + }, + ], + }, + hibb5668qy: { + boxShadow: "{{appsmith.theme.boxShadow.appBoxShadow}}", + widgetName: "Container3", + borderColor: "#E0DEDE", + isCanvas: true, + displayName: "Container", + iconSVG: "/static/media/icon.1977dca3370505e2db3a8e44cfd54907.svg", + searchTags: ["div", "parent", "group"], + topRow: 0, + bottomRow: 10, + parentRowSpace: 10, + type: "CONTAINER_WIDGET", + hideCard: false, + shouldScrollContents: true, + animateLoading: true, + parentColumnSpace: 18.015625, + leftColumn: 0, + dynamicBindingPathList: [ + { + key: "borderRadius", + }, + { + key: "boxShadow", + }, + ], + children: ["b91wojl19o"], + borderWidth: "1", + key: "el0i1qkdxm", + backgroundColor: "#FFFFFF", + isDeprecated: false, + rightColumn: 16, + dynamicHeight: "AUTO_HEIGHT", + widgetId: "hibb5668qy", + containerStyle: "card", + minWidth: 450, + isVisible: true, + version: 1, + parentId: "xp6lxmot21", + renderMode: "CANVAS", + isLoading: false, + responsiveBehavior: "fill", + originalTopRow: 0, + borderRadius: "{{appsmith.theme.borderRadius.appBorderRadius}}", + maxDynamicHeight: 9000, + originalBottomRow: 11, + alignment: "start", + minDynamicHeight: 10, + }, + hatp88y71d: { + widgetName: "Canvas9", + displayName: "Canvas", + topRow: 0, + bottomRow: 100, + parentRowSpace: 1, + type: "CANVAS_WIDGET", + canExtend: false, + hideCard: true, + minHeight: 100, + parentColumnSpace: 1, + leftColumn: 0, + dynamicBindingPathList: [], + children: [], + key: "fkqrygr3nj", + isDeprecated: false, + rightColumn: 64, + detachFromLayout: true, + widgetId: "hatp88y71d", + containerStyle: "none", + minWidth: 450, + isVisible: true, + version: 1, + parentId: "vjdtark0mi", + renderMode: "CANVAS", + isLoading: false, + responsiveBehavior: "fill", + flexLayers: [], + }, + vjdtark0mi: { + boxShadow: "{{appsmith.theme.boxShadow.appBoxShadow}}", + widgetName: "Container9", + borderColor: "#E0DEDE", + isCanvas: true, + displayName: "Container", + iconSVG: "/static/media/icon.1977dca3370505e2db3a8e44cfd54907.svg", + searchTags: ["div", "parent", "group"], + topRow: 0, + bottomRow: 10, + parentRowSpace: 10, + type: "CONTAINER_WIDGET", + hideCard: false, + shouldScrollContents: true, + animateLoading: true, + parentColumnSpace: 17.703125, + leftColumn: 16, + dynamicBindingPathList: [ + { + key: "borderRadius", + }, + { + key: "boxShadow", + }, + ], + children: ["hatp88y71d"], + borderWidth: "1", + key: "4yx1z5m6mk", + backgroundColor: "#FFFFFF", + isDeprecated: false, + rightColumn: 32, + dynamicHeight: "AUTO_HEIGHT", + widgetId: "vjdtark0mi", + containerStyle: "card", + minWidth: 450, + isVisible: true, + version: 1, + parentId: "xp6lxmot21", + renderMode: "CANVAS", + isLoading: false, + responsiveBehavior: "fill", + originalTopRow: 0, + borderRadius: "{{appsmith.theme.borderRadius.appBorderRadius}}", + maxDynamicHeight: 9000, + originalBottomRow: 11, + alignment: "start", + minDynamicHeight: 10, + }, + o1fc1zt9kd: { + widgetName: "Canvas8", + displayName: "Canvas", + topRow: 0, + bottomRow: 100, + parentRowSpace: 1, + type: "CANVAS_WIDGET", + canExtend: false, + hideCard: true, + minHeight: 100, + parentColumnSpace: 1, + leftColumn: 0, + dynamicBindingPathList: [], + children: [], + key: "fkqrygr3nj", + isDeprecated: false, + rightColumn: 64, + detachFromLayout: true, + widgetId: "o1fc1zt9kd", + containerStyle: "none", + minWidth: 450, + isVisible: true, + version: 1, + parentId: "jl6pk4uoaz", + renderMode: "CANVAS", + isLoading: false, + responsiveBehavior: "fill", + flexLayers: [], + }, + jl6pk4uoaz: { + boxShadow: "{{appsmith.theme.boxShadow.appBoxShadow}}", + widgetName: "Container8", + borderColor: "#E0DEDE", + isCanvas: true, + displayName: "Container", + iconSVG: "/static/media/icon.1977dca3370505e2db3a8e44cfd54907.svg", + searchTags: ["div", "parent", "group"], + topRow: 0, + bottomRow: 10, + parentRowSpace: 10, + type: "CONTAINER_WIDGET", + hideCard: false, + shouldScrollContents: true, + animateLoading: true, + parentColumnSpace: 17.703125, + leftColumn: 32, + dynamicBindingPathList: [ + { + key: "borderRadius", + }, + { + key: "boxShadow", + }, + ], + children: ["o1fc1zt9kd"], + borderWidth: "1", + key: "4yx1z5m6mk", + backgroundColor: "#FFFFFF", + isDeprecated: false, + rightColumn: 48, + dynamicHeight: "AUTO_HEIGHT", + widgetId: "jl6pk4uoaz", + containerStyle: "card", + minWidth: 450, + isVisible: true, + version: 1, + parentId: "xp6lxmot21", + renderMode: "CANVAS", + isLoading: false, + responsiveBehavior: "fill", + originalTopRow: 0, + borderRadius: "{{appsmith.theme.borderRadius.appBorderRadius}}", + maxDynamicHeight: 9000, + originalBottomRow: 11, + alignment: "start", + minDynamicHeight: 10, + }, + uh83f0dzzi: { + widgetName: "Canvas5", + displayName: "Canvas", + topRow: 0, + bottomRow: 100, + parentRowSpace: 1, + type: "CANVAS_WIDGET", + canExtend: false, + hideCard: true, + minHeight: 100, + parentColumnSpace: 1, + leftColumn: 0, + dynamicBindingPathList: [], + children: [], + key: "t3ruwylkhy", + isDeprecated: false, + rightColumn: 64, + detachFromLayout: true, + widgetId: "uh83f0dzzi", + containerStyle: "none", + minWidth: 450, + isVisible: true, + version: 1, + parentId: "ji56m4kt8i", + renderMode: "CANVAS", + isLoading: false, + responsiveBehavior: "fill", + flexLayers: [], + }, + ji56m4kt8i: { + boxShadow: "{{appsmith.theme.boxShadow.appBoxShadow}}", + widgetName: "Container5", + borderColor: "#E0DEDE", + isCanvas: true, + displayName: "Container", + iconSVG: "/static/media/icon.1977dca3370505e2db3a8e44cfd54907.svg", + searchTags: ["div", "parent", "group"], + topRow: 0, + bottomRow: 10, + parentRowSpace: 10, + type: "CONTAINER_WIDGET", + hideCard: false, + shouldScrollContents: true, + animateLoading: true, + parentColumnSpace: 18.015625, + leftColumn: 48, + dynamicBindingPathList: [ + { + key: "borderRadius", + }, + { + key: "boxShadow", + }, + ], + children: ["uh83f0dzzi"], + borderWidth: "1", + key: "el0i1qkdxm", + backgroundColor: "#FFFFFF", + isDeprecated: false, + rightColumn: 64, + dynamicHeight: "AUTO_HEIGHT", + widgetId: "ji56m4kt8i", + containerStyle: "card", + minWidth: 450, + isVisible: true, + version: 1, + parentId: "xp6lxmot21", + renderMode: "CANVAS", + isLoading: false, + responsiveBehavior: "fill", + originalTopRow: 0, + borderRadius: "{{appsmith.theme.borderRadius.appBorderRadius}}", + maxDynamicHeight: 9000, + originalBottomRow: 11, + alignment: "start", + minDynamicHeight: 10, + }, + xp6lxmot21: { + widgetName: "Canvas4", + displayName: "Canvas", + topRow: 0, + bottomRow: 120, + parentRowSpace: 1, + type: "CANVAS_WIDGET", + canExtend: false, + hideCard: true, + minHeight: 100, + parentColumnSpace: 1, + leftColumn: 0, + dynamicBindingPathList: [], + children: ["hibb5668qy", "vjdtark0mi", "jl6pk4uoaz", "ji56m4kt8i"], + key: "t3ruwylkhy", + isDeprecated: false, + rightColumn: 64, + detachFromLayout: true, + widgetId: "xp6lxmot21", + containerStyle: "none", + minWidth: 450, + isVisible: true, + version: 1, + parentId: "sy88tbxp13", + renderMode: "CANVAS", + isLoading: false, + responsiveBehavior: "fill", + flexLayers: [ + { + children: [ + { + id: "hibb5668qy", + align: "start", + }, + { + id: "vjdtark0mi", + align: "start", + }, + { + id: "jl6pk4uoaz", + align: "start", + }, + { + id: "ji56m4kt8i", + align: "end", + }, + ], + }, + ], + }, + sy88tbxp13: { + boxShadow: "{{appsmith.theme.boxShadow.appBoxShadow}}", + widgetName: "Container4", + borderColor: "#E0DEDE", + isCanvas: true, + displayName: "Container", + iconSVG: "/static/media/icon.1977dca3370505e2db3a8e44cfd54907.svg", + searchTags: ["div", "parent", "group"], + topRow: 24, + bottomRow: 36, + parentRowSpace: 10, + type: "CONTAINER_WIDGET", + hideCard: false, + shouldScrollContents: true, + animateLoading: true, + parentColumnSpace: 18.015625, + leftColumn: 0, + dynamicBindingPathList: [ + { + key: "borderRadius", + }, + { + key: "boxShadow", + }, + ], + children: ["xp6lxmot21"], + borderWidth: "1", + key: "el0i1qkdxm", + backgroundColor: "#FFFFFF", + isDeprecated: false, + rightColumn: 64, + dynamicHeight: "AUTO_HEIGHT", + widgetId: "sy88tbxp13", + containerStyle: "card", + minWidth: 450, + isVisible: true, + version: 1, + parentId: "0", + renderMode: "CANVAS", + isLoading: false, + responsiveBehavior: "fill", + originalTopRow: 26, + borderRadius: "{{appsmith.theme.borderRadius.appBorderRadius}}", + maxDynamicHeight: 9000, + originalBottomRow: 39, + alignment: "start", + minDynamicHeight: 10, + }, +}; From a813dd954e1c2deaaf455fd108a69d2fb4fff40a Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Wed, 8 Mar 2023 12:07:59 +0530 Subject: [PATCH 605/708] review comments(Define widgetnamecomponent's height) --- .../designSystems/appsmith/autoLayout/FlexBoxComponent.tsx | 3 ++- .../editorComponents/WidgetNameComponent/index.tsx | 7 ++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx index 3da2ba5b01fe..e1e9d123b06d 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx @@ -16,6 +16,7 @@ import { FlexLayer, } from "utils/autoLayout/autoLayoutTypes"; import { getColumnsForAllLayers } from "selectors/autoLayoutSelectors"; +import { WidgetNameComponentHeight } from "components/editorComponents/WidgetNameComponent"; export interface FlexBoxProps { direction?: LayoutDirection; @@ -133,7 +134,7 @@ function FlexBoxComponent(props: FlexBoxProps) { height: props.stretchHeight ? "100%" : "auto", overflow: "hidden", padding: leaveSpaceForWidgetName - ? `${FLEXBOX_PADDING}px ${FLEXBOX_PADDING}px 22px ${FLEXBOX_PADDING}px` + ? `${FLEXBOX_PADDING}px ${FLEXBOX_PADDING}px ${WidgetNameComponentHeight}px ${FLEXBOX_PADDING}px` : "0px", }; }, [ diff --git a/app/client/src/components/editorComponents/WidgetNameComponent/index.tsx b/app/client/src/components/editorComponents/WidgetNameComponent/index.tsx index 33185e6280ef..279a27fcdec5 100644 --- a/app/client/src/components/editorComponents/WidgetNameComponent/index.tsx +++ b/app/client/src/components/editorComponents/WidgetNameComponent/index.tsx @@ -1,5 +1,6 @@ import { AppState } from "@appsmith/reducers"; import { bindDataToWidget } from "actions/propertyPaneActions"; +import { theme } from "constants/DefaultTheme"; import { WidgetType } from "constants/WidgetConstants"; import React from "react"; import { useDispatch, useSelector } from "react-redux"; @@ -22,12 +23,12 @@ import WidgetFactory from "utils/WidgetFactory"; import SettingsControl, { Activities } from "./SettingsControl"; const WidgetTypes = WidgetFactory.widgetTypes; - +export const WidgetNameComponentHeight = theme.spaces[10]; const PositionStyle = styled.div<{ topRow: number; isSnipingMode: boolean }>` position: absolute; top: ${(props) => - props.topRow > 2 ? `${-1 * props.theme.spaces[10]}px` : "calc(100%)"}; - height: ${(props) => props.theme.spaces[10]}px; + props.topRow > 2 ? `${-1 * WidgetNameComponentHeight}px` : "calc(100%)"}; + height: ${WidgetNameComponentHeight}px; ${(props) => (props.isSnipingMode ? "left: -7px" : "right: 0")}; display: flex; padding: 0 4px; From ee9f6e289d04c767070a7457f7c9135253e38333 Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 8 Mar 2023 12:35:40 +0530 Subject: [PATCH 606/708] address code review comments 4 --- .../designSystems/appsmith/autoLayout/FlexBoxComponent.tsx | 4 ++-- .../designSystems/appsmith/autoLayout/FlexComponent.tsx | 4 ++-- app/client/src/widgets/AudioRecorderWidget/widget/index.tsx | 2 -- app/client/src/widgets/AudioWidget/widget/index.tsx | 2 -- app/client/src/widgets/BaseInputWidget/widget/index.tsx | 2 -- app/client/src/widgets/BaseWidget.tsx | 4 ++-- app/client/src/widgets/ButtonGroupWidget/widget/index.tsx | 2 -- app/client/src/widgets/ButtonWidget/widget/index.tsx | 2 -- app/client/src/widgets/CameraWidget/widget/index.tsx | 2 -- app/client/src/widgets/CanvasWidget.tsx | 2 +- .../widget/propertyConfig/contentConfig.ts | 2 -- app/client/src/widgets/ChartWidget/widget/propertyConfig.ts | 2 -- app/client/src/widgets/CheckboxGroupWidget/widget/index.tsx | 3 --- app/client/src/widgets/CheckboxWidget/widget/index.tsx | 2 -- .../CodeScannerWidget/widget/propertyConfig/contentConfig.ts | 2 -- app/client/src/widgets/ContainerWidget/widget/index.tsx | 2 -- app/client/src/widgets/DatePickerWidget2/widget/index.tsx | 2 -- app/client/src/widgets/DividerWidget/widget/index.tsx | 2 -- app/client/src/widgets/DocumentViewerWidget/widget/index.tsx | 2 -- app/client/src/widgets/FilePickerWidgetV2/widget/index.tsx | 2 -- app/client/src/widgets/IconButtonWidget/widget/index.tsx | 2 -- app/client/src/widgets/IframeWidget/widget/index.tsx | 2 -- app/client/src/widgets/ImageWidget/widget/index.tsx | 2 -- .../src/widgets/JSONFormWidget/widget/propertyConfig.ts | 2 -- app/client/src/widgets/ListWidget/widget/propertyConfig.ts | 2 -- app/client/src/widgets/MapChartWidget/widget/index.tsx | 2 -- app/client/src/widgets/MapWidget/widget/index.tsx | 2 -- .../MenuButtonWidget/widget/propertyConfig/contentConfig.ts | 2 -- .../src/widgets/MultiSelectTreeWidget/widget/index.tsx | 2 -- app/client/src/widgets/MultiSelectWidget/widget/index.tsx | 2 -- app/client/src/widgets/MultiSelectWidgetV2/widget/index.tsx | 2 -- .../widget/propertyConfig/contentConfig.ts | 2 -- app/client/src/widgets/ProgressWidget/widget/index.tsx | 2 -- app/client/src/widgets/QRGeneratorWidget/widget/index.tsx | 2 -- app/client/src/widgets/RadioGroupWidget/widget/index.tsx | 2 -- .../RangeSliderWidget/widget/propertyConfig/contentConfig.ts | 2 -- app/client/src/widgets/RateWidget/widget/index.tsx | 2 -- app/client/src/widgets/RichTextEditorWidget/widget/index.tsx | 2 -- app/client/src/widgets/SelectWidget/widget/index.tsx | 2 -- .../src/widgets/SingleSelectTreeWidget/widget/index.tsx | 2 -- app/client/src/widgets/StatboxWidget/widget/index.tsx | 2 -- app/client/src/widgets/SwitchGroupWidget/widget/index.tsx | 2 -- app/client/src/widgets/SwitchWidget/widget/index.tsx | 2 -- .../widget/propertyConfig/PanelConfig/ResponsiveBehavior.ts | 5 ----- .../TableWidgetV2/widget/propertyConfig/PanelConfig/index.ts | 1 - app/client/src/widgets/TabsWidget/widget/index.tsx | 2 -- app/client/src/widgets/TextWidget/widget/index.tsx | 2 -- app/client/src/widgets/VideoWidget/widget/index.tsx | 2 -- 48 files changed, 7 insertions(+), 98 deletions(-) delete mode 100644 app/client/src/widgets/TableWidgetV2/widget/propertyConfig/PanelConfig/ResponsiveBehavior.ts diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx index 3da2ba5b01fe..b72bbe9a5ac5 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexBoxComponent.tsx @@ -18,13 +18,13 @@ import { import { getColumnsForAllLayers } from "selectors/autoLayoutSelectors"; export interface FlexBoxProps { - direction?: LayoutDirection; + direction: LayoutDirection; stretchHeight: boolean; useAutoLayout: boolean; children?: ReactNode; widgetId: string; flexLayers: FlexLayer[]; - isMobile?: boolean; + isMobile: boolean; } export const DEFAULT_HIGHLIGHT_SIZE = 4; diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx index 4a32aa14968c..73a545e8ebe3 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx @@ -19,7 +19,7 @@ export type AutoLayoutProps = { children: ReactNode; componentHeight: number; componentWidth: number; - direction?: LayoutDirection; + direction: LayoutDirection; focused?: boolean; minWidth?: number; parentId?: string; @@ -30,7 +30,7 @@ export type AutoLayoutProps = { widgetType: WidgetType; parentColumnSpace: number; flexVerticalAlignment: FlexVerticalAlignment; - isMobile?: boolean; + isMobile: boolean; }; const FlexWidget = styled.div` diff --git a/app/client/src/widgets/AudioRecorderWidget/widget/index.tsx b/app/client/src/widgets/AudioRecorderWidget/widget/index.tsx index 59e09bc8dfb0..d83deeb6df43 100644 --- a/app/client/src/widgets/AudioRecorderWidget/widget/index.tsx +++ b/app/client/src/widgets/AudioRecorderWidget/widget/index.tsx @@ -5,7 +5,6 @@ import { WidgetType } from "constants/WidgetConstants"; import { ValidationTypes } from "constants/WidgetValidation"; import { Stylesheet } from "entities/AppTheming"; import { createBlobUrl } from "utils/AppsmithUtils"; -import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; import { FileDataTypes } from "widgets/constants"; @@ -70,7 +69,6 @@ class AudioRecorderWidget extends BaseWidget< }, ], }, - ...getResponsiveLayoutConfig(this.getWidgetType()), { sectionName: "Events", children: [ diff --git a/app/client/src/widgets/AudioWidget/widget/index.tsx b/app/client/src/widgets/AudioWidget/widget/index.tsx index 22a4a641b063..ea4b7307d71b 100644 --- a/app/client/src/widgets/AudioWidget/widget/index.tsx +++ b/app/client/src/widgets/AudioWidget/widget/index.tsx @@ -6,7 +6,6 @@ import React, { lazy, Suspense } from "react"; import ReactPlayer from "react-player"; import { retryPromise } from "utils/AppsmithUtils"; import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService"; -import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import BaseWidget, { WidgetProps, WidgetState } from "../../BaseWidget"; const AudioComponent = lazy(() => retryPromise(() => import("../component"))); @@ -84,7 +83,6 @@ class AudioWidget extends BaseWidget { }, ], }, - ...getResponsiveLayoutConfig(this.getWidgetType()), { sectionName: "Events", children: [ diff --git a/app/client/src/widgets/BaseInputWidget/widget/index.tsx b/app/client/src/widgets/BaseInputWidget/widget/index.tsx index 424ff38afaa0..6ce254b6a007 100644 --- a/app/client/src/widgets/BaseInputWidget/widget/index.tsx +++ b/app/client/src/widgets/BaseInputWidget/widget/index.tsx @@ -8,7 +8,6 @@ import { import { WidgetType } from "constants/WidgetConstants"; import { ValidationTypes } from "constants/WidgetValidation"; import React from "react"; -import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; import BaseInputComponent from "../component"; @@ -256,7 +255,6 @@ class BaseInputWidget< }, ], }, - ...getResponsiveLayoutConfig(this.getWidgetType()), { sectionName: "Events", children: [ diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index 26dda168d479..8d55e75e0158 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -557,12 +557,12 @@ abstract class BaseWidget< { }, ], }, - ...getResponsiveLayoutConfig(this.getWidgetType()), { sectionName: "Validation", children: [ diff --git a/app/client/src/widgets/CameraWidget/widget/index.tsx b/app/client/src/widgets/CameraWidget/widget/index.tsx index 82db2da6535a..98b1f21909c7 100644 --- a/app/client/src/widgets/CameraWidget/widget/index.tsx +++ b/app/client/src/widgets/CameraWidget/widget/index.tsx @@ -9,7 +9,6 @@ import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; import { FileDataTypes } from "widgets/constants"; import { Stylesheet } from "entities/AppTheming"; -import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import CameraComponent from "../component"; import { CameraMode, @@ -81,7 +80,6 @@ class CameraWidget extends BaseWidget { }, ], }, - ...getResponsiveLayoutConfig(this.getWidgetType()), { sectionName: "Events", children: [ diff --git a/app/client/src/widgets/CanvasWidget.tsx b/app/client/src/widgets/CanvasWidget.tsx index 731f6c95cf62..34dc9d15588c 100644 --- a/app/client/src/widgets/CanvasWidget.tsx +++ b/app/client/src/widgets/CanvasWidget.tsx @@ -125,7 +125,7 @@ class CanvasWidget extends ContainerWidget { { }, ], }, - ...getResponsiveLayoutConfig(this.getWidgetType()), { sectionName: "Events", children: [ diff --git a/app/client/src/widgets/CodeScannerWidget/widget/propertyConfig/contentConfig.ts b/app/client/src/widgets/CodeScannerWidget/widget/propertyConfig/contentConfig.ts index c62d1c9d4282..4342121013c1 100644 --- a/app/client/src/widgets/CodeScannerWidget/widget/propertyConfig/contentConfig.ts +++ b/app/client/src/widgets/CodeScannerWidget/widget/propertyConfig/contentConfig.ts @@ -1,6 +1,5 @@ import { PropertyPaneConfig } from "constants/PropertyControlConstants"; import { ValidationTypes } from "constants/WidgetValidation"; -import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import { CodeScannerWidgetProps, ScannerLayout, @@ -95,7 +94,6 @@ export default [ }, ], }, - ...getResponsiveLayoutConfig("CODE_SCANNER_WIDGET"), { sectionName: "Events", diff --git a/app/client/src/widgets/ContainerWidget/widget/index.tsx b/app/client/src/widgets/ContainerWidget/widget/index.tsx index 8fa2d45363b5..052c0e06b6c7 100644 --- a/app/client/src/widgets/ContainerWidget/widget/index.tsx +++ b/app/client/src/widgets/ContainerWidget/widget/index.tsx @@ -19,7 +19,6 @@ import WidgetsMultiSelectBox from "pages/Editor/WidgetsMultiSelectBox"; import { Stylesheet } from "entities/AppTheming"; import { Positioning } from "utils/autoLayout/constants"; -import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants"; import { isAutoHeightEnabledForWidget } from "widgets/WidgetUtils"; @@ -68,7 +67,6 @@ export class ContainerWidget extends BaseWidget< }, ], }, - ...getResponsiveLayoutConfig(this.getWidgetType()), ]; } diff --git a/app/client/src/widgets/DatePickerWidget2/widget/index.tsx b/app/client/src/widgets/DatePickerWidget2/widget/index.tsx index 0bfc8d8f8356..80d3b9546003 100644 --- a/app/client/src/widgets/DatePickerWidget2/widget/index.tsx +++ b/app/client/src/widgets/DatePickerWidget2/widget/index.tsx @@ -11,7 +11,6 @@ import { DerivedPropertiesMap } from "utils/WidgetFactory"; import { Alignment } from "@blueprintjs/core"; import { LabelPosition } from "components/constants"; import { Stylesheet } from "entities/AppTheming"; -import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import { GRID_DENSITY_MIGRATION_V1 } from "widgets/constants"; import { isAutoHeightEnabledForWidget } from "widgets/WidgetUtils"; import { DatePickerType, TimePrecision } from "../constants"; @@ -307,7 +306,6 @@ class DatePickerWidget extends BaseWidget { }, ], }, - ...getResponsiveLayoutConfig(this.getWidgetType()), { sectionName: "Events", children: [ diff --git a/app/client/src/widgets/DividerWidget/widget/index.tsx b/app/client/src/widgets/DividerWidget/widget/index.tsx index f5ecce497403..bb7fa5747820 100644 --- a/app/client/src/widgets/DividerWidget/widget/index.tsx +++ b/app/client/src/widgets/DividerWidget/widget/index.tsx @@ -4,7 +4,6 @@ import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; import DividerComponent from "../component"; import { ValidationTypes } from "constants/WidgetValidation"; -import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; class DividerWidget extends BaseWidget { static getPropertyPaneContentConfig() { @@ -35,7 +34,6 @@ class DividerWidget extends BaseWidget { }, ], }, - ...getResponsiveLayoutConfig(this.getWidgetType()), ]; } diff --git a/app/client/src/widgets/DocumentViewerWidget/widget/index.tsx b/app/client/src/widgets/DocumentViewerWidget/widget/index.tsx index e4da9cf0163c..276b71fd8867 100644 --- a/app/client/src/widgets/DocumentViewerWidget/widget/index.tsx +++ b/app/client/src/widgets/DocumentViewerWidget/widget/index.tsx @@ -4,7 +4,6 @@ import { } from "constants/WidgetValidation"; import React from "react"; import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService"; -import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; import DocumentViewerComponent from "../component"; @@ -129,7 +128,6 @@ class DocumentViewerWidget extends BaseWidget< }, ], }, - ...getResponsiveLayoutConfig(this.getWidgetType()), ]; } diff --git a/app/client/src/widgets/FilePickerWidgetV2/widget/index.tsx b/app/client/src/widgets/FilePickerWidgetV2/widget/index.tsx index 9d1875f70d11..a27f0618a3a4 100644 --- a/app/client/src/widgets/FilePickerWidgetV2/widget/index.tsx +++ b/app/client/src/widgets/FilePickerWidgetV2/widget/index.tsx @@ -21,7 +21,6 @@ import React from "react"; import shallowequal from "shallowequal"; import { createGlobalStyle } from "styled-components"; import { createBlobUrl, isBlobUrl } from "utils/AppsmithUtils"; -import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; import FilePickerComponent from "../component"; @@ -425,7 +424,6 @@ class FilePickerWidget extends BaseWidget< }, ], }, - ...getResponsiveLayoutConfig(this.getWidgetType()), { sectionName: "Events", diff --git a/app/client/src/widgets/IconButtonWidget/widget/index.tsx b/app/client/src/widgets/IconButtonWidget/widget/index.tsx index 22423c610984..58197949f175 100644 --- a/app/client/src/widgets/IconButtonWidget/widget/index.tsx +++ b/app/client/src/widgets/IconButtonWidget/widget/index.tsx @@ -9,7 +9,6 @@ import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; import { IconNames } from "@blueprintjs/icons"; import { ButtonVariant, ButtonVariantTypes } from "components/constants"; import { Stylesheet } from "entities/AppTheming"; -import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import IconButtonComponent from "../component"; const ICON_NAMES = Object.keys(IconNames).map( @@ -107,7 +106,6 @@ class IconButtonWidget extends BaseWidget { }, ], }, - ...getResponsiveLayoutConfig(this.getWidgetType()), ]; } diff --git a/app/client/src/widgets/IframeWidget/widget/index.tsx b/app/client/src/widgets/IframeWidget/widget/index.tsx index df244d5eefbb..ba841c6bcd38 100644 --- a/app/client/src/widgets/IframeWidget/widget/index.tsx +++ b/app/client/src/widgets/IframeWidget/widget/index.tsx @@ -2,7 +2,6 @@ import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; import { ValidationTypes } from "constants/WidgetValidation"; import { Stylesheet } from "entities/AppTheming"; import React from "react"; -import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import BaseWidget, { WidgetState } from "widgets/BaseWidget"; import IframeComponent from "../component"; import { IframeWidgetProps } from "../constants"; @@ -68,7 +67,6 @@ class IframeWidget extends BaseWidget { }, ], }, - ...getResponsiveLayoutConfig(this.getWidgetType()), { sectionName: "Events", children: [ diff --git a/app/client/src/widgets/ImageWidget/widget/index.tsx b/app/client/src/widgets/ImageWidget/widget/index.tsx index 2527808b8028..f5ef74b8cd24 100644 --- a/app/client/src/widgets/ImageWidget/widget/index.tsx +++ b/app/client/src/widgets/ImageWidget/widget/index.tsx @@ -6,7 +6,6 @@ import ImageComponent from "../component"; import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; import { ValidationTypes } from "constants/WidgetValidation"; import { Stylesheet } from "entities/AppTheming"; -import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; class ImageWidget extends BaseWidget { @@ -154,7 +153,6 @@ class ImageWidget extends BaseWidget { }, ], }, - ...getResponsiveLayoutConfig(this.getWidgetType()), { sectionName: "Events", children: [ diff --git a/app/client/src/widgets/JSONFormWidget/widget/propertyConfig.ts b/app/client/src/widgets/JSONFormWidget/widget/propertyConfig.ts index 8093e485267e..7aec3bdee51e 100644 --- a/app/client/src/widgets/JSONFormWidget/widget/propertyConfig.ts +++ b/app/client/src/widgets/JSONFormWidget/widget/propertyConfig.ts @@ -6,7 +6,6 @@ import { ValidationTypes } from "constants/WidgetValidation"; import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService"; import { EVALUATION_PATH } from "utils/DynamicBindingUtils"; -import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import { ButtonWidgetProps } from "widgets/ButtonWidget/widget"; import { JSONFormWidgetProps } from "."; import { ROOT_SCHEMA_KEY } from "../constants"; @@ -280,7 +279,6 @@ export const contentConfig = [ }, ], }, - ...getResponsiveLayoutConfig("JSON_FORM_WIDGET"), { sectionName: "Events", children: [ diff --git a/app/client/src/widgets/ListWidget/widget/propertyConfig.ts b/app/client/src/widgets/ListWidget/widget/propertyConfig.ts index 95c293d16b08..3118e94bb696 100644 --- a/app/client/src/widgets/ListWidget/widget/propertyConfig.ts +++ b/app/client/src/widgets/ListWidget/widget/propertyConfig.ts @@ -6,7 +6,6 @@ import { ValidationTypes } from "constants/WidgetValidation"; import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService"; import { EVAL_VALUE_PATH } from "utils/DynamicBindingUtils"; -import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; export const PropertyPaneContentConfig = [ { sectionName: "Data", @@ -91,7 +90,6 @@ export const PropertyPaneContentConfig = [ }, ], }, - ...getResponsiveLayoutConfig("LIST_WIDGET"), { sectionName: "Events", children: [ diff --git a/app/client/src/widgets/MapChartWidget/widget/index.tsx b/app/client/src/widgets/MapChartWidget/widget/index.tsx index b1371892b9a3..c12e1ced83a3 100644 --- a/app/client/src/widgets/MapChartWidget/widget/index.tsx +++ b/app/client/src/widgets/MapChartWidget/widget/index.tsx @@ -8,7 +8,6 @@ import { Stylesheet } from "entities/AppTheming"; import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; import { retryPromise } from "utils/AppsmithUtils"; import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService"; -import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; import { MapType } from "../component"; import { @@ -206,7 +205,6 @@ class MapChartWidget extends BaseWidget { }, ], }, - ...getResponsiveLayoutConfig(this.getWidgetType()), { sectionName: "Events", children: [ diff --git a/app/client/src/widgets/MapWidget/widget/index.tsx b/app/client/src/widgets/MapWidget/widget/index.tsx index 351876aab78a..4b408c0d35ee 100644 --- a/app/client/src/widgets/MapWidget/widget/index.tsx +++ b/app/client/src/widgets/MapWidget/widget/index.tsx @@ -8,7 +8,6 @@ import { ValidationTypes } from "constants/WidgetValidation"; import { Stylesheet } from "entities/AppTheming"; import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; import styled from "styled-components"; -import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; import { MarkerProps } from "../constants"; import { getBorderCSSShorthand } from "constants/DefaultTheme"; @@ -242,7 +241,6 @@ class MapWidget extends BaseWidget { }, ], }, - ...getResponsiveLayoutConfig(this.getWidgetType()), { sectionName: "Events", children: [ diff --git a/app/client/src/widgets/MenuButtonWidget/widget/propertyConfig/contentConfig.ts b/app/client/src/widgets/MenuButtonWidget/widget/propertyConfig/contentConfig.ts index 812a0759d5cb..b5dd5ffa5d9f 100644 --- a/app/client/src/widgets/MenuButtonWidget/widget/propertyConfig/contentConfig.ts +++ b/app/client/src/widgets/MenuButtonWidget/widget/propertyConfig/contentConfig.ts @@ -2,7 +2,6 @@ import { PropertyPaneConfig } from "constants/PropertyControlConstants"; import { ValidationTypes } from "constants/WidgetValidation"; import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService"; -import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import { sourceDataArrayValidation } from "widgets/MenuButtonWidget/validations"; import { MenuButtonWidgetProps, MenuItemsSource } from "../../constants"; import configureMenuItemsConfig from "./childPanels/configureMenuItemsConfig"; @@ -146,5 +145,4 @@ export default [ }, ], }, - ...getResponsiveLayoutConfig("MENU_BUTTON_WIDGET"), ] as PropertyPaneConfig[]; diff --git a/app/client/src/widgets/MultiSelectTreeWidget/widget/index.tsx b/app/client/src/widgets/MultiSelectTreeWidget/widget/index.tsx index e7c0957d1017..ed498fd0b622 100644 --- a/app/client/src/widgets/MultiSelectTreeWidget/widget/index.tsx +++ b/app/client/src/widgets/MultiSelectTreeWidget/widget/index.tsx @@ -14,7 +14,6 @@ import { DefaultValueType } from "rc-tree-select/lib/interface"; import { CheckedStrategy } from "rc-tree-select/lib/utils/strategyUtil"; import React, { ReactNode } from "react"; import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService"; -import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; import { GRID_DENSITY_MIGRATION_V1, MinimumPopupRows } from "widgets/constants"; import { isAutoHeightEnabledForWidget } from "widgets/WidgetUtils"; @@ -333,7 +332,6 @@ class MultiSelectTreeWidget extends BaseWidget< }, ], }, - ...getResponsiveLayoutConfig(this.getWidgetType()), { sectionName: "Events", children: [ diff --git a/app/client/src/widgets/MultiSelectWidget/widget/index.tsx b/app/client/src/widgets/MultiSelectWidget/widget/index.tsx index a248c629a017..da059d20afe4 100644 --- a/app/client/src/widgets/MultiSelectWidget/widget/index.tsx +++ b/app/client/src/widgets/MultiSelectWidget/widget/index.tsx @@ -15,7 +15,6 @@ import { Stylesheet } from "entities/AppTheming"; import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; import { DraftValueType } from "rc-select/lib/Select"; import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService"; -import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import { GRID_DENSITY_MIGRATION_V1, MinimumPopupRows } from "widgets/constants"; import MultiSelectComponent from "../component"; @@ -187,7 +186,6 @@ class MultiSelectWidget extends BaseWidget< isTriggerProperty: false, validation: { type: ValidationTypes.BOOLEAN }, }, - ...getResponsiveLayoutConfig(this.getWidgetType()), ], }, { diff --git a/app/client/src/widgets/MultiSelectWidgetV2/widget/index.tsx b/app/client/src/widgets/MultiSelectWidgetV2/widget/index.tsx index 0f8396f38c0b..3f514beb2d47 100644 --- a/app/client/src/widgets/MultiSelectWidgetV2/widget/index.tsx +++ b/app/client/src/widgets/MultiSelectWidgetV2/widget/index.tsx @@ -14,7 +14,6 @@ import { isArray, isFinite, isString, LoDashStatic, xorWith } from "lodash"; import { DraftValueType, LabelInValueType } from "rc-select/lib/Select"; import React from "react"; import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService"; -import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; import { GRID_DENSITY_MIGRATION_V1, MinimumPopupRows } from "widgets/constants"; import { isAutoHeightEnabledForWidget } from "widgets/WidgetUtils"; @@ -462,7 +461,6 @@ class MultiSelectWidget extends BaseWidget< }, ], }, - ...getResponsiveLayoutConfig(this.getWidgetType()), { sectionName: "Events", children: [ diff --git a/app/client/src/widgets/NumberSliderWidget/widget/propertyConfig/contentConfig.ts b/app/client/src/widgets/NumberSliderWidget/widget/propertyConfig/contentConfig.ts index f363b9cec673..2088b6f52161 100644 --- a/app/client/src/widgets/NumberSliderWidget/widget/propertyConfig/contentConfig.ts +++ b/app/client/src/widgets/NumberSliderWidget/widget/propertyConfig/contentConfig.ts @@ -2,7 +2,6 @@ import { Alignment } from "@blueprintjs/core"; import { LabelPosition } from "components/constants"; import { ValidationTypes } from "constants/WidgetValidation"; import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService"; -import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import { NumberSliderWidgetProps } from ".."; import { defaultValueValidation, @@ -275,7 +274,6 @@ export default [ }, ], }, - ...getResponsiveLayoutConfig("NUMBER_SLIDER_WIDGET"), { sectionName: "Events", children: [ diff --git a/app/client/src/widgets/ProgressWidget/widget/index.tsx b/app/client/src/widgets/ProgressWidget/widget/index.tsx index 636aa2f0d307..c4b8d556f7ce 100644 --- a/app/client/src/widgets/ProgressWidget/widget/index.tsx +++ b/app/client/src/widgets/ProgressWidget/widget/index.tsx @@ -6,7 +6,6 @@ import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; import { Colors } from "constants/Colors"; import { ValidationTypes } from "constants/WidgetValidation"; import { Stylesheet } from "entities/AppTheming"; -import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import ProgressComponent from "../component"; import { ProgressType, ProgressVariant } from "../constants"; @@ -126,7 +125,6 @@ class ProgressWidget extends BaseWidget { }, ], }, - ...getResponsiveLayoutConfig(this.getWidgetType()), ]; } diff --git a/app/client/src/widgets/QRGeneratorWidget/widget/index.tsx b/app/client/src/widgets/QRGeneratorWidget/widget/index.tsx index 193d43dfe2ec..3d6b85469c7e 100644 --- a/app/client/src/widgets/QRGeneratorWidget/widget/index.tsx +++ b/app/client/src/widgets/QRGeneratorWidget/widget/index.tsx @@ -10,7 +10,6 @@ import WidgetStyleContainer from "components/designSystems/appsmith/WidgetStyleC import { Color } from "constants/Colors"; import { pick } from "lodash"; import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService"; -import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; import { ContainerStyle } from "widgets/ContainerWidget/component"; import TextComponent, { TextAlign } from "../component"; @@ -387,7 +386,6 @@ class TextWidget extends BaseWidget { }, ], }, - ...getResponsiveLayoutConfig(this.getWidgetType()), ]; } diff --git a/app/client/src/widgets/RadioGroupWidget/widget/index.tsx b/app/client/src/widgets/RadioGroupWidget/widget/index.tsx index 3397f7beba82..54205093455d 100644 --- a/app/client/src/widgets/RadioGroupWidget/widget/index.tsx +++ b/app/client/src/widgets/RadioGroupWidget/widget/index.tsx @@ -12,7 +12,6 @@ import { import { Stylesheet } from "entities/AppTheming"; import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService"; -import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import { GRID_DENSITY_MIGRATION_V1 } from "widgets/constants"; import { isAutoHeightEnabledForWidget } from "widgets/WidgetUtils"; import BaseWidget, { WidgetProps, WidgetState } from "../../BaseWidget"; @@ -378,7 +377,6 @@ class RadioGroupWidget extends BaseWidget { }, ], }, - ...getResponsiveLayoutConfig(this.getWidgetType()), { sectionName: "Events", children: [ diff --git a/app/client/src/widgets/RangeSliderWidget/widget/propertyConfig/contentConfig.ts b/app/client/src/widgets/RangeSliderWidget/widget/propertyConfig/contentConfig.ts index 1f7d9ed7add5..26ac28904f82 100644 --- a/app/client/src/widgets/RangeSliderWidget/widget/propertyConfig/contentConfig.ts +++ b/app/client/src/widgets/RangeSliderWidget/widget/propertyConfig/contentConfig.ts @@ -2,7 +2,6 @@ import { Alignment } from "@blueprintjs/core"; import { LabelPosition } from "components/constants"; import { ValidationTypes } from "constants/WidgetValidation"; import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService"; -import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import { RangeSliderWidgetProps } from ".."; import { endValueValidation, @@ -317,7 +316,6 @@ export default [ }, ], }, - ...getResponsiveLayoutConfig("RANGE_SLIDER_WIDGET"), { sectionName: "Events", children: [ diff --git a/app/client/src/widgets/RateWidget/widget/index.tsx b/app/client/src/widgets/RateWidget/widget/index.tsx index b801457279cf..9f6d8953e6ef 100644 --- a/app/client/src/widgets/RateWidget/widget/index.tsx +++ b/app/client/src/widgets/RateWidget/widget/index.tsx @@ -8,7 +8,6 @@ import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; import { ValidationTypes } from "constants/WidgetValidation"; import { Stylesheet } from "entities/AppTheming"; import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService"; -import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; function validateDefaultRate(value: unknown, props: any, _: any) { @@ -199,7 +198,6 @@ class RateWidget extends BaseWidget { }, ], }, - ...getResponsiveLayoutConfig(this.getWidgetType()), { sectionName: "Events", children: [ diff --git a/app/client/src/widgets/RichTextEditorWidget/widget/index.tsx b/app/client/src/widgets/RichTextEditorWidget/widget/index.tsx index 027015ff5446..4b05e5972d9d 100644 --- a/app/client/src/widgets/RichTextEditorWidget/widget/index.tsx +++ b/app/client/src/widgets/RichTextEditorWidget/widget/index.tsx @@ -7,7 +7,6 @@ import { ValidationTypes } from "constants/WidgetValidation"; import React, { lazy, Suspense } from "react"; import showdown from "showdown"; import { retryPromise } from "utils/AppsmithUtils"; -import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; import { GRID_DENSITY_MIGRATION_V1 } from "widgets/constants"; import { isAutoHeightEnabledForWidget } from "widgets/WidgetUtils"; @@ -212,7 +211,6 @@ class RichTextEditorWidget extends BaseWidget< }, ], }, - ...getResponsiveLayoutConfig(this.getWidgetType()), { sectionName: "Events", diff --git a/app/client/src/widgets/SelectWidget/widget/index.tsx b/app/client/src/widgets/SelectWidget/widget/index.tsx index 8ccfc94aeec6..cbaf18bf9c91 100644 --- a/app/client/src/widgets/SelectWidget/widget/index.tsx +++ b/app/client/src/widgets/SelectWidget/widget/index.tsx @@ -19,7 +19,6 @@ import { } from "lodash"; import React from "react"; import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService"; -import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import { GRID_DENSITY_MIGRATION_V1, MinimumPopupRows } from "widgets/constants"; import { isAutoHeightEnabledForWidget } from "widgets/WidgetUtils"; import BaseWidget, { WidgetProps, WidgetState } from "../../BaseWidget"; @@ -381,7 +380,6 @@ class SelectWidget extends BaseWidget { }, ], }, - ...getResponsiveLayoutConfig(this.getWidgetType()), { sectionName: "Events", children: [ diff --git a/app/client/src/widgets/SingleSelectTreeWidget/widget/index.tsx b/app/client/src/widgets/SingleSelectTreeWidget/widget/index.tsx index b5d7405057e7..a0163c0395d9 100644 --- a/app/client/src/widgets/SingleSelectTreeWidget/widget/index.tsx +++ b/app/client/src/widgets/SingleSelectTreeWidget/widget/index.tsx @@ -13,7 +13,6 @@ import { isArray } from "lodash"; import { DefaultValueType } from "rc-tree-select/lib/interface"; import React, { ReactNode } from "react"; import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService"; -import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; import { GRID_DENSITY_MIGRATION_V1, MinimumPopupRows } from "widgets/constants"; import { isAutoHeightEnabledForWidget } from "widgets/WidgetUtils"; @@ -301,7 +300,6 @@ class SingleSelectTreeWidget extends BaseWidget< }, ], }, - ...getResponsiveLayoutConfig(this.getWidgetType()), { sectionName: "Events", children: [ diff --git a/app/client/src/widgets/StatboxWidget/widget/index.tsx b/app/client/src/widgets/StatboxWidget/widget/index.tsx index 856dea3c084b..5082662aca2f 100644 --- a/app/client/src/widgets/StatboxWidget/widget/index.tsx +++ b/app/client/src/widgets/StatboxWidget/widget/index.tsx @@ -3,7 +3,6 @@ import { ContainerWidget } from "widgets/ContainerWidget/widget"; import { ValidationTypes } from "constants/WidgetValidation"; import { Stylesheet } from "entities/AppTheming"; -import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; import { Positioning } from "utils/autoLayout/constants"; import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants"; @@ -45,7 +44,6 @@ class StatboxWidget extends ContainerWidget { }, ], }, - ...getResponsiveLayoutConfig(this.getWidgetType()), ]; } diff --git a/app/client/src/widgets/SwitchGroupWidget/widget/index.tsx b/app/client/src/widgets/SwitchGroupWidget/widget/index.tsx index 109ec9f86a1d..75b3b069e08f 100644 --- a/app/client/src/widgets/SwitchGroupWidget/widget/index.tsx +++ b/app/client/src/widgets/SwitchGroupWidget/widget/index.tsx @@ -10,7 +10,6 @@ import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; import { LabelPosition } from "components/constants"; import { TextSize } from "constants/WidgetConstants"; import { Stylesheet } from "entities/AppTheming"; -import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import { GRID_DENSITY_MIGRATION_V1 } from "widgets/constants"; import { isAutoHeightEnabledForWidget } from "widgets/WidgetUtils"; import SwitchGroupComponent, { OptionProps } from "../component"; @@ -232,7 +231,6 @@ class SwitchGroupWidget extends BaseWidget< }, ], }, - ...getResponsiveLayoutConfig(this.getWidgetType()), { sectionName: "Events", children: [ diff --git a/app/client/src/widgets/SwitchWidget/widget/index.tsx b/app/client/src/widgets/SwitchWidget/widget/index.tsx index 95f84e4d81cd..c8090af4677e 100644 --- a/app/client/src/widgets/SwitchWidget/widget/index.tsx +++ b/app/client/src/widgets/SwitchWidget/widget/index.tsx @@ -7,7 +7,6 @@ import { ValidationTypes } from "constants/WidgetValidation"; import { LabelPosition } from "components/constants"; import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; -import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import { DerivedPropertiesMap } from "utils/WidgetFactory"; import { AlignWidgetTypes } from "widgets/constants"; @@ -113,7 +112,6 @@ class SwitchWidget extends BaseWidget { }, ], }, - ...getResponsiveLayoutConfig(this.getWidgetType()), { sectionName: "Events", children: [ diff --git a/app/client/src/widgets/TableWidgetV2/widget/propertyConfig/PanelConfig/ResponsiveBehavior.ts b/app/client/src/widgets/TableWidgetV2/widget/propertyConfig/PanelConfig/ResponsiveBehavior.ts deleted file mode 100644 index a91fcb986cf6..000000000000 --- a/app/client/src/widgets/TableWidgetV2/widget/propertyConfig/PanelConfig/ResponsiveBehavior.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; -import Widget from "../../index"; -export const ResponsiveBehaviorConfig = [ - ...getResponsiveLayoutConfig(Widget.getWidgetType()), -]; diff --git a/app/client/src/widgets/TableWidgetV2/widget/propertyConfig/PanelConfig/index.ts b/app/client/src/widgets/TableWidgetV2/widget/propertyConfig/PanelConfig/index.ts index 901ea15dc51a..79446122f428 100644 --- a/app/client/src/widgets/TableWidgetV2/widget/propertyConfig/PanelConfig/index.ts +++ b/app/client/src/widgets/TableWidgetV2/widget/propertyConfig/PanelConfig/index.ts @@ -27,7 +27,6 @@ export default { Data, Basic, General, - // ...(ResponsiveBehaviorConfig as any), Validations, SaveButtonProperties, DiscardButtonproperties, diff --git a/app/client/src/widgets/TabsWidget/widget/index.tsx b/app/client/src/widgets/TabsWidget/widget/index.tsx index 9daf2cc71e7f..a1487b768b86 100644 --- a/app/client/src/widgets/TabsWidget/widget/index.tsx +++ b/app/client/src/widgets/TabsWidget/widget/index.tsx @@ -11,7 +11,6 @@ import React from "react"; import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; import { WidgetProperties } from "selectors/propertyPaneSelectors"; import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService"; -import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import WidgetFactory from "utils/WidgetFactory"; import BaseWidget, { WidgetState } from "../../BaseWidget"; import TabsComponent from "../component"; @@ -186,7 +185,6 @@ class TabsWidget extends BaseWidget< }, ], }, - ...getResponsiveLayoutConfig(this.getWidgetType()), { sectionName: "Events", children: [ diff --git a/app/client/src/widgets/TextWidget/widget/index.tsx b/app/client/src/widgets/TextWidget/widget/index.tsx index 0c58a33488f1..ba1f94d9e481 100644 --- a/app/client/src/widgets/TextWidget/widget/index.tsx +++ b/app/client/src/widgets/TextWidget/widget/index.tsx @@ -11,7 +11,6 @@ import { Color } from "constants/Colors"; import { Stylesheet } from "entities/AppTheming"; import { pick } from "lodash"; import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService"; -import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; import { ContainerStyle } from "widgets/ContainerWidget/component"; import TextComponent, { TextAlign } from "../component"; @@ -94,7 +93,6 @@ class TextWidget extends BaseWidget { }, ], }, - ...getResponsiveLayoutConfig(this.getWidgetType()), ]; } diff --git a/app/client/src/widgets/VideoWidget/widget/index.tsx b/app/client/src/widgets/VideoWidget/widget/index.tsx index bd787e3e1efa..3b08cbbb1e11 100644 --- a/app/client/src/widgets/VideoWidget/widget/index.tsx +++ b/app/client/src/widgets/VideoWidget/widget/index.tsx @@ -8,7 +8,6 @@ import React, { lazy, Suspense } from "react"; import ReactPlayer from "react-player"; import { retryPromise } from "utils/AppsmithUtils"; import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService"; -import { getResponsiveLayoutConfig } from "utils/layoutPropertiesUtils"; import BaseWidget, { WidgetProps, WidgetState } from "../../BaseWidget"; const VideoComponent = lazy(() => retryPromise(() => import("../component"))); @@ -86,7 +85,6 @@ class VideoWidget extends BaseWidget { }, ], }, - ...getResponsiveLayoutConfig(this.getWidgetType()), { sectionName: "Events", children: [ From 0fdfc3af31f6213c0937f551eb06e5fc1c8f824e Mon Sep 17 00:00:00 2001 From: Aswath K Date: Wed, 8 Mar 2023 14:13:49 +0530 Subject: [PATCH 607/708] feat: widget min-width & min-height constraints --- .../src/resizable/autolayoutresize/index.tsx | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/app/client/src/resizable/autolayoutresize/index.tsx b/app/client/src/resizable/autolayoutresize/index.tsx index 2a460056fdba..5fe43f492d0e 100644 --- a/app/client/src/resizable/autolayoutresize/index.tsx +++ b/app/client/src/resizable/autolayoutresize/index.tsx @@ -25,6 +25,7 @@ import { } from "resizable/common"; import { getWidgets } from "sagas/selectors"; import { + getCanvasWidth, getContainerOccupiedSpacesSelectorWhileResizing, getCurrentAppPositioningType, } from "selectors/editorSelectors"; @@ -37,6 +38,7 @@ import { FlexLayerAlignment, ResponsiveBehavior, } from "utils/autoLayout/constants"; +import { getWidgetMinMaxDimensionsInPixel } from "utils/autoLayout/flexWidgetUtils"; import { useReflow } from "utils/hooks/useReflow"; import PerformanceTracker, { PerformanceTransactionName, @@ -51,6 +53,7 @@ export function ReflowResizable(props: ResizableProps) { const occupiedSpacesBySiblingWidgets = useSelector( getContainerOccupiedSpacesSelectorWhileResizing(props.parentId), ); + const mainCanvasWidth = useSelector(getCanvasWidth); const checkForCollision = (widgetNewSize: { left: number; top: number; @@ -260,6 +263,10 @@ export function ReflowResizable(props: ResizableProps) { const handles = []; const widget = allWidgets[props.widgetId]; + const { + minHeight: widgetMinHeight, + minWidth: widgetMinWidth, + } = getWidgetMinMaxDimensionsInPixel(widget, mainCanvasWidth); const resizedPositions = { left: widget.leftColumn, right: widget.rightColumn, @@ -270,6 +277,12 @@ export function ReflowResizable(props: ResizableProps) { if (!(isAutoLayout && widget?.leftColumn === 0) && props.handles.left) { handles.push({ dragCallback: (x: number) => { + if ( + widgetMinWidth && + props.componentWidth - x < widgetMinWidth && + x > 0 + ) + return; const updatedPositions = { ...resizedPositions }; let dimensionUpdates = { reflectDimension: true, @@ -333,6 +346,12 @@ export function ReflowResizable(props: ResizableProps) { ) { handles.push({ dragCallback: (x: number) => { + if ( + widgetMinWidth && + props.componentWidth + x < widgetMinWidth && + x < 0 + ) + return; const updatedPositions = { ...resizedPositions }; let dimensionUpdates = { reflectDimension: true, @@ -389,6 +408,12 @@ export function ReflowResizable(props: ResizableProps) { if (props.handles.bottom) { handles.push({ dragCallback: (x: number, y: number) => { + if ( + widgetMinHeight && + props.componentHeight + y < widgetMinHeight && + y < 0 + ) + return; const updatedPositions = { ...resizedPositions }; updatedPositions.bottom = widget.bottomRow + x / widget.parentRowSpace; setNewDimensions(ReflowDirection.RIGHT, updatedPositions, { From 487ca35f3bff861d709fdee090a13907b36ef580 Mon Sep 17 00:00:00 2001 From: Aswath K Date: Wed, 8 Mar 2023 14:15:19 +0530 Subject: [PATCH 608/708] updates minHeight for Image widget --- app/client/src/widgets/ImageWidget/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/app/client/src/widgets/ImageWidget/index.ts b/app/client/src/widgets/ImageWidget/index.ts index eab729cfe72a..dac4562925b6 100644 --- a/app/client/src/widgets/ImageWidget/index.ts +++ b/app/client/src/widgets/ImageWidget/index.ts @@ -35,6 +35,7 @@ export const CONFIG = { configuration: () => { return { minWidth: "280px", + minHeight: "40px", }; }, }, From 5461c7873fd89f844245d49be0194f8ab1975b8f Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 8 Mar 2023 16:45:41 +0530 Subject: [PATCH 609/708] fix undefined issue --- .../src/resizable/autolayoutresize/index.tsx | 48 +++++++++---------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/app/client/src/resizable/autolayoutresize/index.tsx b/app/client/src/resizable/autolayoutresize/index.tsx index 2a460056fdba..9ddca850ab22 100644 --- a/app/client/src/resizable/autolayoutresize/index.tsx +++ b/app/client/src/resizable/autolayoutresize/index.tsx @@ -157,7 +157,7 @@ export function ReflowResizable(props: ResizableProps) { if ( childWidget && childWidget.responsiveBehavior === ResponsiveBehavior.Fill && - (childWidget.rightColumn - childWidget.leftColumn) * + (childWidget?.rightColumn - childWidget?.leftColumn) * childWidget.parentColumnSpace !== updatedWidth ) { @@ -261,11 +261,11 @@ export function ReflowResizable(props: ResizableProps) { const handles = []; const widget = allWidgets[props.widgetId]; const resizedPositions = { - left: widget.leftColumn, - right: widget.rightColumn, - top: widget.topRow, - bottom: widget.bottomRow, - id: widget.widgetId, + left: widget?.leftColumn, + right: widget?.rightColumn, + top: widget?.topRow, + bottom: widget?.bottomRow, + id: widget?.widgetId, }; if (!(isAutoLayout && widget?.leftColumn === 0) && props.handles.left) { handles.push({ @@ -284,7 +284,7 @@ export function ReflowResizable(props: ResizableProps) { if (isAutoLayout) { if (widgetAlignment === "start" || hasFillChild) { updatedPositions.right = - widget.rightColumn - x / widget.parentColumnSpace; + widget?.rightColumn - x / widget.parentColumnSpace; dimensionUpdates = { ...dimensionUpdates, width: props.componentWidth - x, @@ -292,9 +292,9 @@ export function ReflowResizable(props: ResizableProps) { }; } else if (widgetAlignment === "center") { updatedPositions.right = - widget.rightColumn - x / widget.parentColumnSpace; + widget?.rightColumn - x / widget.parentColumnSpace; updatedPositions.left = - widget.leftColumn - x / widget.parentColumnSpace; + widget?.leftColumn - x / widget.parentColumnSpace; dimensionUpdates = { ...dimensionUpdates, width: props.componentWidth - 2 * x, @@ -304,7 +304,7 @@ export function ReflowResizable(props: ResizableProps) { }; } else { updatedPositions.left = - widget.leftColumn + x / widget.parentColumnSpace; + widget?.leftColumn + x / widget.parentColumnSpace; dimensionUpdates = { ...dimensionUpdates, width: props.componentWidth - x, @@ -326,8 +326,8 @@ export function ReflowResizable(props: ResizableProps) { if ( !( isAutoLayout && - widget.leftColumn !== 0 && - widget.rightColumn === GridDefaults.DEFAULT_GRID_COLUMNS + widget?.leftColumn !== 0 && + widget?.rightColumn === GridDefaults.DEFAULT_GRID_COLUMNS ) && props.handles.right ) { @@ -347,7 +347,7 @@ export function ReflowResizable(props: ResizableProps) { if (isAutoLayout) { if (widgetAlignment === "start" || hasFillChild) { updatedPositions.right = - widget.rightColumn + x / widget.parentColumnSpace; + widget?.rightColumn + x / widget.parentColumnSpace; dimensionUpdates = { ...dimensionUpdates, width: props.componentWidth + x, @@ -355,9 +355,9 @@ export function ReflowResizable(props: ResizableProps) { }; } else if (widgetAlignment === "center") { updatedPositions.right = - widget.rightColumn + x / widget.parentColumnSpace; + widget?.rightColumn + x / widget.parentColumnSpace; updatedPositions.left = - widget.leftColumn + x / widget.parentColumnSpace; + widget?.leftColumn + x / widget.parentColumnSpace; dimensionUpdates = { ...dimensionUpdates, width: props.componentWidth + 2 * x, @@ -367,7 +367,7 @@ export function ReflowResizable(props: ResizableProps) { }; } else { updatedPositions.left = - widget.leftColumn - x / widget.parentColumnSpace; + widget?.leftColumn - x / widget.parentColumnSpace; dimensionUpdates = { ...dimensionUpdates, width: props.componentWidth + x, @@ -425,7 +425,7 @@ export function ReflowResizable(props: ResizableProps) { if (isAutoLayout) { if (widgetAlignment === "start" || hasFillChild) { updatedPositions.right = - widget.rightColumn + x / widget.parentColumnSpace; + widget?.rightColumn + x / widget.parentColumnSpace; dimensionUpdates = { ...dimensionUpdates, width: props.componentWidth + x, @@ -433,9 +433,9 @@ export function ReflowResizable(props: ResizableProps) { }; } else if (widgetAlignment === "center") { updatedPositions.right = - widget.rightColumn + x / widget.parentColumnSpace; + widget?.rightColumn + x / widget.parentColumnSpace; updatedPositions.left = - widget.leftColumn + x / widget.parentColumnSpace; + widget?.leftColumn + x / widget.parentColumnSpace; dimensionUpdates = { ...dimensionUpdates, width: props.componentWidth + 2 * x, @@ -445,7 +445,7 @@ export function ReflowResizable(props: ResizableProps) { }; } else { updatedPositions.left = - widget.leftColumn - x / widget.parentColumnSpace; + widget?.leftColumn - x / widget.parentColumnSpace; dimensionUpdates = { ...dimensionUpdates, width: props.componentWidth + x, @@ -482,7 +482,7 @@ export function ReflowResizable(props: ResizableProps) { if (isAutoLayout) { if (widgetAlignment === "start" || hasFillChild) { updatedPositions.right = - widget.rightColumn - x / widget.parentColumnSpace; + widget?.rightColumn - x / widget.parentColumnSpace; dimensionUpdates = { ...dimensionUpdates, width: props.componentWidth - x, @@ -490,9 +490,9 @@ export function ReflowResizable(props: ResizableProps) { }; } else if (widgetAlignment === "center") { updatedPositions.right = - widget.rightColumn - x / widget.parentColumnSpace; + widget?.rightColumn - x / widget.parentColumnSpace; updatedPositions.left = - widget.leftColumn - x / widget.parentColumnSpace; + widget?.leftColumn - x / widget.parentColumnSpace; dimensionUpdates = { ...dimensionUpdates, width: props.componentWidth - 2 * x, @@ -502,7 +502,7 @@ export function ReflowResizable(props: ResizableProps) { }; } else { updatedPositions.left = - widget.leftColumn + x / widget.parentColumnSpace; + widget?.leftColumn + x / widget.parentColumnSpace; dimensionUpdates = { ...dimensionUpdates, width: props.componentWidth - x, From aed2c3f100c8951fa98dcedf8b488a3431160594 Mon Sep 17 00:00:00 2001 From: Preet Date: Wed, 8 Mar 2023 16:48:39 +0530 Subject: [PATCH 610/708] minor update --- .../src/resizable/autolayoutresize/index.tsx | 45 ++++++++++--------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/app/client/src/resizable/autolayoutresize/index.tsx b/app/client/src/resizable/autolayoutresize/index.tsx index 9ddca850ab22..e0790427bee8 100644 --- a/app/client/src/resizable/autolayoutresize/index.tsx +++ b/app/client/src/resizable/autolayoutresize/index.tsx @@ -121,8 +121,8 @@ export function ReflowResizable(props: ResizableProps) { const layer = useMemo(() => { const { widgetId } = props; const widget = allWidgets[widgetId]; - if (!widget || !widget.parentId) return {}; - const parent = allWidgets[widget.parentId]; + if (!widget || !widget?.parentId) return {}; + const parent = allWidgets[widget?.parentId]; if (!parent) return {}; const flexLayers = parent.flexLayers; const layerIndex = getLayerIndexOfWidget(flexLayers, widgetId); @@ -134,7 +134,7 @@ export function ReflowResizable(props: ResizableProps) { layer?.children?.length && layer.children.some((each: any) => { const widget = allWidgets[each.id]; - return widget && widget.responsiveBehavior === ResponsiveBehavior.Fill; + return widget && widget?.responsiveBehavior === ResponsiveBehavior.Fill; }); const triggerAutoLayoutBasedReflow = (resizedPositions: OccupiedSpace) => { let canHorizontalMove = false; @@ -153,7 +153,7 @@ export function ReflowResizable(props: ResizableProps) { let correctedMovementMap: ReflowedSpaceMap = {}; for (const child of layer.children) { const childWidget = allWidgets[child.id]; - const updatedWidth = fillWidgetsLength * widget.parentColumnSpace; + const updatedWidth = fillWidgetsLength * widget?.parentColumnSpace; if ( childWidget && childWidget.responsiveBehavior === ResponsiveBehavior.Fill && @@ -165,7 +165,7 @@ export function ReflowResizable(props: ResizableProps) { correctedMovementMap = { ...correctedMovementMap, [child.id]: { - width: fillWidgetsLength * widget.parentColumnSpace, + width: fillWidgetsLength * widget?.parentColumnSpace, }, }; } @@ -284,7 +284,7 @@ export function ReflowResizable(props: ResizableProps) { if (isAutoLayout) { if (widgetAlignment === "start" || hasFillChild) { updatedPositions.right = - widget?.rightColumn - x / widget.parentColumnSpace; + widget?.rightColumn - x / widget?.parentColumnSpace; dimensionUpdates = { ...dimensionUpdates, width: props.componentWidth - x, @@ -292,9 +292,9 @@ export function ReflowResizable(props: ResizableProps) { }; } else if (widgetAlignment === "center") { updatedPositions.right = - widget?.rightColumn - x / widget.parentColumnSpace; + widget?.rightColumn - x / widget?.parentColumnSpace; updatedPositions.left = - widget?.leftColumn - x / widget.parentColumnSpace; + widget?.leftColumn - x / widget?.parentColumnSpace; dimensionUpdates = { ...dimensionUpdates, width: props.componentWidth - 2 * x, @@ -304,7 +304,7 @@ export function ReflowResizable(props: ResizableProps) { }; } else { updatedPositions.left = - widget?.leftColumn + x / widget.parentColumnSpace; + widget?.leftColumn + x / widget?.parentColumnSpace; dimensionUpdates = { ...dimensionUpdates, width: props.componentWidth - x, @@ -347,7 +347,7 @@ export function ReflowResizable(props: ResizableProps) { if (isAutoLayout) { if (widgetAlignment === "start" || hasFillChild) { updatedPositions.right = - widget?.rightColumn + x / widget.parentColumnSpace; + widget?.rightColumn + x / widget?.parentColumnSpace; dimensionUpdates = { ...dimensionUpdates, width: props.componentWidth + x, @@ -355,9 +355,9 @@ export function ReflowResizable(props: ResizableProps) { }; } else if (widgetAlignment === "center") { updatedPositions.right = - widget?.rightColumn + x / widget.parentColumnSpace; + widget?.rightColumn + x / widget?.parentColumnSpace; updatedPositions.left = - widget?.leftColumn + x / widget.parentColumnSpace; + widget?.leftColumn + x / widget?.parentColumnSpace; dimensionUpdates = { ...dimensionUpdates, width: props.componentWidth + 2 * x, @@ -367,7 +367,7 @@ export function ReflowResizable(props: ResizableProps) { }; } else { updatedPositions.left = - widget?.leftColumn - x / widget.parentColumnSpace; + widget?.leftColumn - x / widget?.parentColumnSpace; dimensionUpdates = { ...dimensionUpdates, width: props.componentWidth + x, @@ -390,7 +390,8 @@ export function ReflowResizable(props: ResizableProps) { handles.push({ dragCallback: (x: number, y: number) => { const updatedPositions = { ...resizedPositions }; - updatedPositions.bottom = widget.bottomRow + x / widget.parentRowSpace; + updatedPositions.bottom = + widget?.bottomRow + x / widget?.parentRowSpace; setNewDimensions(ReflowDirection.RIGHT, updatedPositions, { width: newDimensions.width, height: props.componentHeight + y, @@ -425,7 +426,7 @@ export function ReflowResizable(props: ResizableProps) { if (isAutoLayout) { if (widgetAlignment === "start" || hasFillChild) { updatedPositions.right = - widget?.rightColumn + x / widget.parentColumnSpace; + widget?.rightColumn + x / widget?.parentColumnSpace; dimensionUpdates = { ...dimensionUpdates, width: props.componentWidth + x, @@ -433,9 +434,9 @@ export function ReflowResizable(props: ResizableProps) { }; } else if (widgetAlignment === "center") { updatedPositions.right = - widget?.rightColumn + x / widget.parentColumnSpace; + widget?.rightColumn + x / widget?.parentColumnSpace; updatedPositions.left = - widget?.leftColumn + x / widget.parentColumnSpace; + widget?.leftColumn + x / widget?.parentColumnSpace; dimensionUpdates = { ...dimensionUpdates, width: props.componentWidth + 2 * x, @@ -445,7 +446,7 @@ export function ReflowResizable(props: ResizableProps) { }; } else { updatedPositions.left = - widget?.leftColumn - x / widget.parentColumnSpace; + widget?.leftColumn - x / widget?.parentColumnSpace; dimensionUpdates = { ...dimensionUpdates, width: props.componentWidth + x, @@ -482,7 +483,7 @@ export function ReflowResizable(props: ResizableProps) { if (isAutoLayout) { if (widgetAlignment === "start" || hasFillChild) { updatedPositions.right = - widget?.rightColumn - x / widget.parentColumnSpace; + widget?.rightColumn - x / widget?.parentColumnSpace; dimensionUpdates = { ...dimensionUpdates, width: props.componentWidth - x, @@ -490,9 +491,9 @@ export function ReflowResizable(props: ResizableProps) { }; } else if (widgetAlignment === "center") { updatedPositions.right = - widget?.rightColumn - x / widget.parentColumnSpace; + widget?.rightColumn - x / widget?.parentColumnSpace; updatedPositions.left = - widget?.leftColumn - x / widget.parentColumnSpace; + widget?.leftColumn - x / widget?.parentColumnSpace; dimensionUpdates = { ...dimensionUpdates, width: props.componentWidth - 2 * x, @@ -502,7 +503,7 @@ export function ReflowResizable(props: ResizableProps) { }; } else { updatedPositions.left = - widget?.leftColumn + x / widget.parentColumnSpace; + widget?.leftColumn + x / widget?.parentColumnSpace; dimensionUpdates = { ...dimensionUpdates, width: props.componentWidth - x, From 9677a1b6a28cf5f11f4851c294bd6b044ec1a42d Mon Sep 17 00:00:00 2001 From: Arsalan Yaldram Date: Wed, 8 Mar 2023 16:52:48 +0530 Subject: [PATCH 611/708] feat: add responsive min-height to all widgets (#21241) --- app/client/src/widgets/BaseInputWidget/component/index.tsx | 6 +++++- app/client/src/widgets/CategorySliderWidget/index.ts | 4 ++++ app/client/src/widgets/CheckboxWidget/component/index.tsx | 4 ++++ .../src/widgets/DatePickerWidget2/component/index.tsx | 5 +++++ .../MultiSelectTreeWidget/component/index.styled.tsx | 3 +++ .../widgets/MultiSelectWidgetV2/component/index.styled.tsx | 3 +++ app/client/src/widgets/NumberSliderWidget/index.ts | 4 ++++ app/client/src/widgets/RangeSliderWidget/index.ts | 4 ++++ app/client/src/widgets/RateWidget/component/index.tsx | 1 + .../src/widgets/SelectWidget/component/index.styled.tsx | 4 ++++ .../SingleSelectTreeWidget/component/index.styled.tsx | 4 ++++ app/client/src/widgets/SwitchWidget/component/index.tsx | 3 +++ 12 files changed, 44 insertions(+), 1 deletion(-) diff --git a/app/client/src/widgets/BaseInputWidget/component/index.tsx b/app/client/src/widgets/BaseInputWidget/component/index.tsx index ee5ec1c8d56d..d4b15dade869 100644 --- a/app/client/src/widgets/BaseInputWidget/component/index.tsx +++ b/app/client/src/widgets/BaseInputWidget/component/index.tsx @@ -79,7 +79,6 @@ const InputComponentWrapper = styled((props) => ( isMultiline?: boolean; }>` ${labelLayoutStyles} - cursor: ${({ disabled }) => (disabled ? "not-allowed" : "auto")}; .${Classes.INPUT_GROUP} { display: flex; @@ -143,6 +142,7 @@ const InputComponentWrapper = styled((props) => ( fill: ${(props) => props.theme.colors.icon?.hover}; } } + .${Classes.INPUT} { padding-left: 0.5rem; min-height: 36px; @@ -368,6 +368,10 @@ const TextInputWrapper = styled.div<{ box-shadow: ${({ boxShadow }) => `${boxShadow}`} !important; min-height: 32px; + .auto-layout & { + min-height: 36px; + } + &:hover { border-color: ${({ disabled, hasError }) => { if (disabled) { diff --git a/app/client/src/widgets/CategorySliderWidget/index.ts b/app/client/src/widgets/CategorySliderWidget/index.ts index 94ae822a5863..e8bc4716cc7a 100644 --- a/app/client/src/widgets/CategorySliderWidget/index.ts +++ b/app/client/src/widgets/CategorySliderWidget/index.ts @@ -48,6 +48,10 @@ export const CONFIG = { stylesheetConfig: Widget.getStylesheetConfig(), }, autoLayout: { + defaults: { + rows: 7, + columns: 40, + }, widgetSize: [ { viewportMinWidth: 0, diff --git a/app/client/src/widgets/CheckboxWidget/component/index.tsx b/app/client/src/widgets/CheckboxWidget/component/index.tsx index a796eec1a522..ef53a4df3fd5 100644 --- a/app/client/src/widgets/CheckboxWidget/component/index.tsx +++ b/app/client/src/widgets/CheckboxWidget/component/index.tsx @@ -25,6 +25,10 @@ const CheckboxContainer = styled.div` justify-content: start; width: 100%; + .auto-layout & { + min-height: 32px; + } + .${Classes.CHECKBOX} { width: 100%; } diff --git a/app/client/src/widgets/DatePickerWidget2/component/index.tsx b/app/client/src/widgets/DatePickerWidget2/component/index.tsx index d1d151229897..9ecae7cf5574 100644 --- a/app/client/src/widgets/DatePickerWidget2/component/index.tsx +++ b/app/client/src/widgets/DatePickerWidget2/component/index.tsx @@ -72,6 +72,11 @@ const StyledControlGroup = styled(ControlGroup)<{ width: 100%; height: 100%; min-height: 32px; + + .auto-layout & { + min-height: 36px; + } + align-items: center; transition: none; diff --git a/app/client/src/widgets/MultiSelectTreeWidget/component/index.styled.tsx b/app/client/src/widgets/MultiSelectTreeWidget/component/index.styled.tsx index 559fb53e9bf2..71407e36e043 100644 --- a/app/client/src/widgets/MultiSelectTreeWidget/component/index.styled.tsx +++ b/app/client/src/widgets/MultiSelectTreeWidget/component/index.styled.tsx @@ -1009,5 +1009,8 @@ export const InputContainer = styled.div<{ labelPosition?: LabelPosition; }>` ${multiSelectInputContainerStyles} + .auto-layout & { + height: 36px !important; + } ${({ labelPosition }) => labelPosition && `height: ${SELECT_DEFAULT_HEIGHT}`}; `; diff --git a/app/client/src/widgets/MultiSelectWidgetV2/component/index.styled.tsx b/app/client/src/widgets/MultiSelectWidgetV2/component/index.styled.tsx index 724a4fd6021e..71b867e9a805 100644 --- a/app/client/src/widgets/MultiSelectWidgetV2/component/index.styled.tsx +++ b/app/client/src/widgets/MultiSelectWidgetV2/component/index.styled.tsx @@ -720,5 +720,8 @@ export const InputContainer = styled.div<{ labelPosition?: LabelPosition; }>` ${multiSelectInputContainerStyles} + .auto-layout & { + height: 36px !important; + } ${({ labelPosition }) => labelPosition && `height: ${SELECT_DEFAULT_HEIGHT}`}; `; diff --git a/app/client/src/widgets/NumberSliderWidget/index.ts b/app/client/src/widgets/NumberSliderWidget/index.ts index 2b5dd447077d..605e79a2c6b7 100644 --- a/app/client/src/widgets/NumberSliderWidget/index.ts +++ b/app/client/src/widgets/NumberSliderWidget/index.ts @@ -50,6 +50,10 @@ export const CONFIG = { stylesheetConfig: Widget.getStylesheetConfig(), }, autoLayout: { + defaults: { + rows: 7, + columns: 40, + }, widgetSize: [ { viewportMinWidth: 0, diff --git a/app/client/src/widgets/RangeSliderWidget/index.ts b/app/client/src/widgets/RangeSliderWidget/index.ts index 8a1174c7f25a..19b6266df540 100644 --- a/app/client/src/widgets/RangeSliderWidget/index.ts +++ b/app/client/src/widgets/RangeSliderWidget/index.ts @@ -51,6 +51,10 @@ export const CONFIG = { stylesheetConfig: Widget.getStylesheetConfig(), }, autoLayout: { + defaults: { + rows: 7, + columns: 40, + }, widgetSize: [ { viewportMinWidth: 0, diff --git a/app/client/src/widgets/RateWidget/component/index.tsx b/app/client/src/widgets/RateWidget/component/index.tsx index c93ec643d8db..ed14fb9ed530 100644 --- a/app/client/src/widgets/RateWidget/component/index.tsx +++ b/app/client/src/widgets/RateWidget/component/index.tsx @@ -31,6 +31,7 @@ export const RateContainer = styled.div` overflow: auto; .auto-layout && { + min-height: 32px; overflow: unset; > span { diff --git a/app/client/src/widgets/SelectWidget/component/index.styled.tsx b/app/client/src/widgets/SelectWidget/component/index.styled.tsx index 8fba81e94f7c..50db0b194744 100644 --- a/app/client/src/widgets/SelectWidget/component/index.styled.tsx +++ b/app/client/src/widgets/SelectWidget/component/index.styled.tsx @@ -93,6 +93,7 @@ export const StyledSingleDropDown = styled(SingleDropDown)< height: 100%; } } + &&&& .${Classes.BUTTON} { display: flex; width: 100%; @@ -100,6 +101,9 @@ export const StyledSingleDropDown = styled(SingleDropDown)< align-items: center; justify-content: space-between; background: white; + .auto-layout & { + min-height: 36px; + } min-height: 32px; padding-left: 12px; padding: 0px 10px; diff --git a/app/client/src/widgets/SingleSelectTreeWidget/component/index.styled.tsx b/app/client/src/widgets/SingleSelectTreeWidget/component/index.styled.tsx index 0a5344925bc0..e131bcf5c1e0 100644 --- a/app/client/src/widgets/SingleSelectTreeWidget/component/index.styled.tsx +++ b/app/client/src/widgets/SingleSelectTreeWidget/component/index.styled.tsx @@ -1024,6 +1024,10 @@ export const InputContainer = styled.div<{ &, & .rc-tree-select { + .auto-layout & { + height: 36px !important; + } + ${({ labelPosition }) => labelPosition && `height: ${SELECT_DEFAULT_HEIGHT}`}; } diff --git a/app/client/src/widgets/SwitchWidget/component/index.tsx b/app/client/src/widgets/SwitchWidget/component/index.tsx index edc3bd4c95f7..3f94d5c03d8a 100644 --- a/app/client/src/widgets/SwitchWidget/component/index.tsx +++ b/app/client/src/widgets/SwitchWidget/component/index.tsx @@ -31,6 +31,9 @@ const SwitchComponentContainer = styled.div<{ flex-direction: row; align-items: center; justify-content: stretch; + .auto-layout & { + min-height: 32px; + } ${BlueprintControlTransform} `; From d9a18212df9bae49ffb1e86cce870ac30b9e896a Mon Sep 17 00:00:00 2001 From: Aswath K Date: Wed, 8 Mar 2023 16:59:13 +0530 Subject: [PATCH 612/708] feat: minWidth for Button Group (#20930) --- app/client/src/utils/layoutPropertiesUtils.ts | 1 + .../ButtonGroupWidget/component/index.tsx | 5 ++++ .../src/widgets/ButtonGroupWidget/index.ts | 30 +++++++++++-------- 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/app/client/src/utils/layoutPropertiesUtils.ts b/app/client/src/utils/layoutPropertiesUtils.ts index eafb3d535a27..a6cdf561fb63 100644 --- a/app/client/src/utils/layoutPropertiesUtils.ts +++ b/app/client/src/utils/layoutPropertiesUtils.ts @@ -270,6 +270,7 @@ export const WIDGET_WITH_DYNAMIC_WIDTH = [ // TODO(aswathkk): See if this needs to be moved to widget config // This is used only for autoLayout export const WIDGET_WITH_DYNAMIC_HEIGHT = [ + "BUTTON_GROUP_WIDGET", "CHECKBOX_GROUP_WIDGET", "SWITCH_GROUP_WIDGET", "RADIO_GROUP_WIDGET", diff --git a/app/client/src/widgets/ButtonGroupWidget/component/index.tsx b/app/client/src/widgets/ButtonGroupWidget/component/index.tsx index 88b7043aa755..90ebd37b01b5 100644 --- a/app/client/src/widgets/ButtonGroupWidget/component/index.tsx +++ b/app/client/src/widgets/ButtonGroupWidget/component/index.tsx @@ -181,6 +181,11 @@ const StyledButton = styled.button` align-items: center; padding: 0px 10px; + .auto-layout & { + min-height: 32px; + min-width: 120px; + } + &:hover, &:active, &:focus { diff --git a/app/client/src/widgets/ButtonGroupWidget/index.ts b/app/client/src/widgets/ButtonGroupWidget/index.ts index b8ba7ca37e46..9e6389d9c8fb 100644 --- a/app/client/src/widgets/ButtonGroupWidget/index.ts +++ b/app/client/src/widgets/ButtonGroupWidget/index.ts @@ -6,7 +6,7 @@ import { getDefaultResponsiveBehavior } from "utils/layoutPropertiesUtils"; import { WidgetProps } from "widgets/BaseWidget"; import { BlueprintOperationTypes } from "widgets/constants"; import IconSVG from "./icon.svg"; -import Widget from "./widget"; +import Widget, { ButtonGroupWidgetProps } from "./widget"; export const CONFIG = { type: Widget.getWidgetType(), @@ -142,27 +142,33 @@ export const CONFIG = { ], }, }, - properties: { - derived: Widget.getDerivedPropertiesMap(), - default: Widget.getDefaultPropertiesMap(), - meta: Widget.getMetaPropertiesMap(), - config: Widget.getPropertyPaneConfig(), - contentConfig: Widget.getPropertyPaneContentConfig(), - styleConfig: Widget.getPropertyPaneStyleConfig(), - stylesheetConfig: Widget.getStylesheetConfig(), - }, autoLayout: { widgetSize: [ { viewportMinWidth: 0, - configuration: () => { + configuration: (props: ButtonGroupWidgetProps) => { + let minWidth = 128; + const buttonLength = Object.keys(props.groupButtons).length; + if (props.orientation === "horizontal") { + // 120 is the width of the button, 8 is widget padding, 1 is the gap between buttons + minWidth = 120 * buttonLength + 8 + (buttonLength - 1) * 1; + } return { - minWidth: "120px", + minWidth: `${minWidth}px`, }; }, }, ], }, + properties: { + derived: Widget.getDerivedPropertiesMap(), + default: Widget.getDefaultPropertiesMap(), + meta: Widget.getMetaPropertiesMap(), + config: Widget.getPropertyPaneConfig(), + contentConfig: Widget.getPropertyPaneContentConfig(), + styleConfig: Widget.getPropertyPaneStyleConfig(), + stylesheetConfig: Widget.getStylesheetConfig(), + }, }; export default Widget; From 2c05589b8acd2847cf7311f0d722cc2ea08f36c1 Mon Sep 17 00:00:00 2001 From: Aswath K Date: Wed, 8 Mar 2023 17:07:59 +0530 Subject: [PATCH 613/708] change height config for Textwidget --- app/client/src/widgets/TextWidget/index.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/client/src/widgets/TextWidget/index.ts b/app/client/src/widgets/TextWidget/index.ts index 6fd7bb9e89f3..10ccc07d6ae7 100644 --- a/app/client/src/widgets/TextWidget/index.ts +++ b/app/client/src/widgets/TextWidget/index.ts @@ -42,13 +42,16 @@ export const CONFIG = { stylesheetConfig: Widget.getStylesheetConfig(), }, autoLayout: { + defaults: { + columns: 4, + }, widgetSize: [ { viewportMinWidth: 0, configuration: () => { return { minWidth: "120px", - minHeight: "70px", + minHeight: "40px", }; }, }, From b7e128f6ece299cbe1cfe5ec815bf31e712cc2aa Mon Sep 17 00:00:00 2001 From: Aswath K Date: Wed, 8 Mar 2023 17:08:35 +0530 Subject: [PATCH 614/708] change default value for RateWidget --- app/client/src/widgets/RateWidget/index.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/client/src/widgets/RateWidget/index.ts b/app/client/src/widgets/RateWidget/index.ts index 916717ead48e..c4c7614deb27 100644 --- a/app/client/src/widgets/RateWidget/index.ts +++ b/app/client/src/widgets/RateWidget/index.ts @@ -31,6 +31,10 @@ export const CONFIG = { widgetName: "Rating", }, autoLayout: { + defaults: { + columns: 7.272727, + rows: 4, + }, widgetSize: [ { viewportMinWidth: 0, From 9c362c6563134f19d297ed66ea08f755cd8b403d Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Wed, 8 Mar 2023 17:32:00 +0530 Subject: [PATCH 615/708] Disabling drag to select in autolayout mode. --- .../pages/common/CanvasArenas/CanvasSelectionArena.tsx | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/app/client/src/pages/common/CanvasArenas/CanvasSelectionArena.tsx b/app/client/src/pages/common/CanvasArenas/CanvasSelectionArena.tsx index 47021133d71c..13eff141454c 100644 --- a/app/client/src/pages/common/CanvasArenas/CanvasSelectionArena.tsx +++ b/app/client/src/pages/common/CanvasArenas/CanvasSelectionArena.tsx @@ -15,7 +15,10 @@ import React, { useCallback, useEffect, useRef } from "react"; import { useDispatch, useSelector } from "react-redux"; import { getWidget } from "sagas/selectors"; import { getAppMode } from "selectors/applicationSelectors"; -import { getIsDraggingForSelection } from "selectors/canvasSelectors"; +import { + getIsAutoLayout, + getIsDraggingForSelection, +} from "selectors/canvasSelectors"; import { getCurrentApplicationLayout, getCurrentPageId, @@ -486,9 +489,10 @@ export function CanvasSelectionArena({ ]); // Resizing state still shows selection arena to aid with scroll behavior - + const isAutoLayout = useSelector(getIsAutoLayout); const shouldShow = - appMode === APP_MODE.EDIT && !(isDragging || isPreviewMode || dropDisabled); + appMode === APP_MODE.EDIT && + !(isAutoLayout || isDragging || isPreviewMode || dropDisabled); const canvasRef = React.useRef({ slidingArenaRef, From 6f014871bf4570b9599bb8a840258c79ad5b0859 Mon Sep 17 00:00:00 2001 From: rahulramesha Date: Wed, 8 Mar 2023 17:17:27 +0530 Subject: [PATCH 616/708] layout conversion UI first flow --- app/client/package.json | 4 +- app/client/src/actions/autoLayoutActions.ts | 23 +++ app/client/src/api/ApplicationApi.tsx | 32 ++++ app/client/src/api/PageApi.tsx | 21 +++ app/client/src/ce/constants/ApiConstants.tsx | 3 + .../src/ce/constants/ReduxActionConstants.tsx | 9 + app/client/src/ce/constants/messages.ts | 44 +++++ .../src/ce/pages/Applications/index.tsx | 4 + app/client/src/ce/reducers/index.tsx | 2 + app/client/src/ce/sagas/index.tsx | 4 + .../form/FormDialogComponent.tsx | 10 +- app/client/src/constants/Colors.tsx | 3 + .../src/pages/AppViewer/AppViewerHeader.tsx | 8 +- app/client/src/pages/AppViewer/PageMenu.tsx | 8 +- .../ConversionButton.tsx | 54 ++++++ .../ConversionCompleteLayout.tsx | 41 ++++ .../CanvasLayoutConversion/ConversionForm.tsx | 177 ++++++++++++++++++ .../CanvasLayoutConversion/InfoBlock.tsx | 27 +++ .../CanvasLayoutConversion/SnapShotButton.tsx | 58 ++++++ .../hooks/useAutoToFixedLayoutFlow.ts | 156 +++++++++++++++ .../hooks/useCommonConversionFlows.ts | 61 ++++++ .../hooks/useConversionForm.ts | 32 ++++ .../hooks/useFixedToAutoLayoutFlow.ts | 63 +++++++ .../hooks/useSnapShotFlow.ts | 74 ++++++++ .../pages/Editor/CanvasPropertyPane/index.tsx | 3 + app/client/src/pages/Editor/EditorHeader.tsx | 8 +- .../Explorer/Entity/EntityProperties.tsx | 2 +- .../src/pages/Editor/Explorer/Pages/index.tsx | 2 +- .../Explorer/Widgets/WidgetContextMenu.tsx | 6 +- .../Editor/WidgetsEditor/CanvasContainer.tsx | 55 +++++- app/client/src/pages/common/SubHeader.tsx | 7 +- app/client/src/pages/workspace/settings.tsx | 8 +- app/client/src/reducers/uiReducers/index.tsx | 2 + .../uiReducers/layoutConversionReducer.ts | 65 +++++++ .../reducers/uiReducers/pageWidgetsReducer.ts | 23 ++- app/client/src/sagas/ErrorSagas.tsx | 3 + app/client/src/sagas/PageSagas.tsx | 36 +++- app/client/src/sagas/SnapshotSagas.ts | 152 +++++++++++++++ app/client/src/sagas/layoutConversionSagas.ts | 140 ++++++++++++++ .../src/selectors/autoLayoutSelectors.tsx | 63 +++++++ app/client/src/selectors/canvasSelectors.ts | 17 ++ app/client/src/selectors/entitiesSelector.ts | 6 +- .../utils/DSLConversions/fixedToAutoLayout.ts | 2 +- .../src/utils/WidgetPropsUtils.test.tsx | 4 +- app/client/src/utils/WidgetPropsUtils.tsx | 7 +- app/client/test/testCommon.ts | 2 +- app/client/yarn.lock | 8 +- 47 files changed, 1491 insertions(+), 48 deletions(-) create mode 100644 app/client/src/pages/Editor/CanvasLayoutConversion/ConversionButton.tsx create mode 100644 app/client/src/pages/Editor/CanvasLayoutConversion/ConversionCompleteLayout.tsx create mode 100644 app/client/src/pages/Editor/CanvasLayoutConversion/ConversionForm.tsx create mode 100644 app/client/src/pages/Editor/CanvasLayoutConversion/InfoBlock.tsx create mode 100644 app/client/src/pages/Editor/CanvasLayoutConversion/SnapShotButton.tsx create mode 100644 app/client/src/pages/Editor/CanvasLayoutConversion/hooks/useAutoToFixedLayoutFlow.ts create mode 100644 app/client/src/pages/Editor/CanvasLayoutConversion/hooks/useCommonConversionFlows.ts create mode 100644 app/client/src/pages/Editor/CanvasLayoutConversion/hooks/useConversionForm.ts create mode 100644 app/client/src/pages/Editor/CanvasLayoutConversion/hooks/useFixedToAutoLayoutFlow.ts create mode 100644 app/client/src/pages/Editor/CanvasLayoutConversion/hooks/useSnapShotFlow.ts create mode 100644 app/client/src/reducers/uiReducers/layoutConversionReducer.ts create mode 100644 app/client/src/sagas/SnapshotSagas.ts create mode 100644 app/client/src/sagas/layoutConversionSagas.ts diff --git a/app/client/package.json b/app/client/package.json index f2cbc503ec2b..ef496b56024d 100644 --- a/app/client/package.json +++ b/app/client/package.json @@ -49,7 +49,7 @@ "cypress-log-to-output": "^1.1.2", "dayjs": "^1.10.6", "deep-diff": "^1.0.2", - "design-system-old": "npm:@appsmithorg/design-system-old@1.0.48", + "design-system-old": "npm:@appsmithorg/design-system-old@1.0.53-alpha.0", "downloadjs": "^1.4.7", "draft-js": "^0.11.7", "exceljs-lightweight": "^1.14.0", @@ -307,4 +307,4 @@ "loader-utils": "^2.0.4", "json5": "2.2.3" } -} +} \ No newline at end of file diff --git a/app/client/src/actions/autoLayoutActions.ts b/app/client/src/actions/autoLayoutActions.ts index ba41e19dca92..d10a598d99e7 100644 --- a/app/client/src/actions/autoLayoutActions.ts +++ b/app/client/src/actions/autoLayoutActions.ts @@ -1,5 +1,9 @@ import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants"; import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; +import { + CONVERSION_STATES, + SnapShotDetails, +} from "reducers/uiReducers/layoutConversionReducer"; export const removeWrappersAction = (parentId: string) => ({ type: ReduxActionTypes.REMOVE_CHILD_WRAPPERS, @@ -34,3 +38,22 @@ export const updateLayoutPositioning = ( payload: positioningType, }; }; + +export const setLayoutConversionStateAction = ( + conversionState: CONVERSION_STATES, + error?: Error, +) => { + return { + type: ReduxActionTypes.SET_LAYOUT_CONVERSION_STATE, + payload: { conversionState, error }, + }; +}; + +export const updateSnapshotDetails = ( + snapShotDetails: SnapShotDetails | undefined, +) => { + return { + type: ReduxActionTypes.UPDATE_SNAPSHOT_DETAILS, + payload: snapShotDetails, + }; +}; diff --git a/app/client/src/api/ApplicationApi.tsx b/app/client/src/api/ApplicationApi.tsx index 11fe04daab02..c08c3278ff3a 100644 --- a/app/client/src/api/ApplicationApi.tsx +++ b/app/client/src/api/ApplicationApi.tsx @@ -209,6 +209,11 @@ export interface PageDefaultMeta { default: boolean; } +export interface snapShotApplicationRequest { + applicationId: string; + branchName?: string; +} + class ApplicationApi extends Api { static baseURL = "v1/applications"; static publishURLPath = (applicationId: string) => @@ -338,6 +343,33 @@ class ApplicationApi extends Api { }, ); } + + static createSnapShotOfApplication(request: snapShotApplicationRequest) { + return Api.post( + ApplicationApi.baseURL + "/snapshot/" + request.applicationId, + ); + } + + static getSnapShotDetails(request: snapShotApplicationRequest) { + return Api.get( + ApplicationApi.baseURL + "/snapshot/" + request.applicationId, + ); + } + + static restoreSnapShotOfApplication(request: snapShotApplicationRequest) { + return Api.post( + ApplicationApi.baseURL + + "/snapshot/" + + request.applicationId + + "/restore", + ); + } + + static deleteSnapShotOfApplication(request: snapShotApplicationRequest) { + return Api.delete( + ApplicationApi.baseURL + "/snapshot/" + request.applicationId, + ); + } } export default ApplicationApi; diff --git a/app/client/src/api/PageApi.tsx b/app/client/src/api/PageApi.tsx index c5b3f346b4b1..db286fce308d 100644 --- a/app/client/src/api/PageApi.tsx +++ b/app/client/src/api/PageApi.tsx @@ -38,6 +38,14 @@ export type PageLayout = { layoutOnLoadActionErrors?: LayoutOnLoadActionErrors[]; }; +export interface PageLayoutsRequest { + layoutId: string; + pageId: string; + layout: { + dsl: DSLWidget; + }; +} + export type FetchPageResponseData = { id: string; name: string; @@ -180,6 +188,10 @@ class PageApi extends Api { return !!bustCache ? url + "?v=" + +new Date() : url; }; + static getSaveAllPagesURL = (applicationId: string) => { + return `v1/layouts/application/${applicationId}`; + }; + static updatePageUrl = (pageId: string) => `${PageApi.url}/${pageId}`; static setPageOrderUrl = ( applicationId: string, @@ -213,6 +225,15 @@ class PageApi extends Api { ); } + static saveAllPages( + applicationId: string, + pageLayouts: PageLayoutsRequest[], + ) { + return Api.put(PageApi.getSaveAllPagesURL(applicationId), { + pageLayouts, + }); + } + static fetchPublishedPage( pageRequest: FetchPublishedPageRequest, ): AxiosPromise { diff --git a/app/client/src/ce/constants/ApiConstants.tsx b/app/client/src/ce/constants/ApiConstants.tsx index 587759b11a0c..46f3cc848186 100644 --- a/app/client/src/ce/constants/ApiConstants.tsx +++ b/app/client/src/ce/constants/ApiConstants.tsx @@ -36,3 +36,6 @@ export const SUPER_USER_SUBMIT_PATH = `${SIGNUP_SUBMIT_PATH}/super`; export const getExportAppAPIRoute = (applicationId: string) => `/api/v1/applications/export/${applicationId}`; + +export const getSnapShotAppAPIRoute = (applicationId: string) => + `/api/v1/applications/snapshot/${applicationId}`; diff --git a/app/client/src/ce/constants/ReduxActionConstants.tsx b/app/client/src/ce/constants/ReduxActionConstants.tsx index 8458b9003e00..0c258ce12ae8 100644 --- a/app/client/src/ce/constants/ReduxActionConstants.tsx +++ b/app/client/src/ce/constants/ReduxActionConstants.tsx @@ -765,6 +765,15 @@ export const ReduxActionTypes = { UPDATE_FILL_CHILD_LAYER: "UPDATE_FILL_CHILD_LAYER", RECALCULATE_COLUMNS: "RECALCULATE_COLUMNS", UPDATE_LAYOUT_POSITIONING: "UPDATE_LAYOUT_POSITIONING", + SET_LAYOUT_CONVERSION_STATE: "SET_LAYOUT_CONVERSION_STATE", + START_CONVERSION_FLOW: "START_CONVERSION_FLOW", + UPDATE_SNAPSHOT_DETAILS: "UPDATE_SNAPSHOT_DETAILS", + CONVERT_AUTO_TO_FIXED: "CONVERT_AUTO_TO_FIXED", + CONVERT_FIXED_TO_AUTO: "CONVERT_FIXED_TO_AUTO", + LOG_LAYOUT_CONVERSION_ERROR: "LOG_LAYOUT_CONVERSION_ERROR", + RESTORE_SNAPSHOT: "RESTORE_SNAPSHOT", + FETCH_SNAPSHOT: "FETCH_SNAPSHOT", + DELETE_SNAPSHOT: "DELETE_SNAPSHOT", }; export type ReduxActionType = typeof ReduxActionTypes[keyof typeof ReduxActionTypes]; diff --git a/app/client/src/ce/constants/messages.ts b/app/client/src/ce/constants/messages.ts index a2f0a74510ca..271e97dff23c 100644 --- a/app/client/src/ce/constants/messages.ts +++ b/app/client/src/ce/constants/messages.ts @@ -422,6 +422,7 @@ export const NAVIGATE_TO = () => `Navigate to`; export const SHOW_MESSAGE = () => `Show message`; export const OPEN_MODAL = () => `Open modal`; export const CLOSE_MODAL = () => `Close modal`; +export const CLOSE = () => `CLOSE`; export const STORE_VALUE = () => `Store value`; export const REMOVE_VALUE = () => `Remove value`; export const CLEAR_STORE = () => `Clear store`; @@ -1455,6 +1456,49 @@ export const SAVE_BUTTON_TEXT = () => "SAVE"; export const SAVE_AND_AUTHORIZE_BUTTON_TEXT = () => "SAVE AND AUTHORIZE"; export const DISCARD_POPUP_DONT_SAVE_BUTTON_TEXT = () => "DON'T SAVE"; +//Layout Conversion flow +export const CONVERT = () => "CONVERT"; +export const BUILD_RESPONSIVE = () => "Build Responsive Apps"; +export const BUILD_RESPONSIVE_TEXT = () => + "Appsmith will convert your application's UI to auto layout, a new mode designed for building mobile-friendly apps in no time"; +export const BUILD_FIXED_LAYOUT = () => "Use Fixed Layout"; +export const BUILD_FIXED_LAYOUT_TEXT = () => + "Appsmith will convert your application’s UI to fixed layout, the default mode."; +export const USE_SNAPSHOT = () => "USE SNAPSHOT"; +export const USE_SNAPSHOT_HEADER = () => "Use Snapshot"; +export const SAVE_SNAPSHOT = () => "We Save a Snapshot of Your App"; +export const SAVE_SNAPSHOT_TEXT = () => + "We will create a snapshot of your whole app before the conversion, so that you can go back if auto layout is just not right for you"; +export const CREATE_SNAPSHOT = () => "Creating a snapshot"; +export const CONVERTING_APP = () => "Converting your app"; +export const REFRESH_THE_APP = () => "REFRESH THE APP"; +export const CONVERT_ANYWAYS = () => "CONVERT ANYWAYS"; +export const CONVERSION_SUCCESS_HEADER = () => "All done"; +export const CONVERSION_SUCCESS_TEXT = () => + "Check all your pages and start using your new layout"; +export const CONVERSION_WARNING_HEADER = () => + "All done, some adjustments needed"; +export const CONVERSION_WARNING_TEXT = () => + "You might need to manually position some of the widgets your layout contains"; +export const CONVERSION_ERROR_HEADER = () => "Conversion Failed"; +export const CONVERSION_ERROR = () => + "Appsmith ran into a critical error while trying to convert to auto layout"; +export const SEND_REPORT = () => "SEND US A REPORT"; +export const CONVERSION_ERROR_TEXT = () => "No changes were made to your app"; +export const DROPDOWN_LABEL_TEXT = () => "Target canvas size"; +export const CONVERSION_WARNING = () => "Conversion will change your layout"; +export const SNAPSHOT_LABEL = () => + "To revert back to the original state use this snapshot"; +export const USE_SNAPSHOT_TEXT = () => + "Your app will look and work exactly like it used to before the conversion. Widgets, datasources, queries, JS objects added and any changes you made after conversion will not be present."; +export const CONVERT_TO_FIXED_TITLE = () => "Convert to Fixed Layout"; +export const CONVERT_TO_FIXED_BUTTON = () => "CONVERT TO Fixed-LAYOUT"; +export const CONVERT_TO_AUTO_TITLE = () => "Convert to Auto Layout"; +export const CONVERT_TO_AUTO_BUTTON = () => "CONVERT TO AUTO-LAYOUT"; +export const SNAPSHOT_BANNER_MESSAGE = () => + "Confirm all the pages are converted correctly before editing. Use the snapshot to go back. Discard only after reviewing all the pages."; +export const USE_SNAPSHOT_CTA = () => "USE THE SNAPSHOT"; +export const DISCARD_SNAPSHOT_CTA = () => "DISCARD THE SNAPSHOT"; // Alert options and labels for showMessage types export const ALERT_STYLE_OPTIONS = [ { label: "Info", value: "'info'", id: "info" }, diff --git a/app/client/src/ce/pages/Applications/index.tsx b/app/client/src/ce/pages/Applications/index.tsx index 59a17cbcd837..ecbe70235aa2 100644 --- a/app/client/src/ce/pages/Applications/index.tsx +++ b/app/client/src/ce/pages/Applications/index.tsx @@ -64,6 +64,7 @@ import { } from "design-system-old"; import { duplicateApplication, + setShowAppInviteUsersDialog, updateApplication, } from "actions/applicationActions"; import { Position } from "@blueprintjs/core/lib/esm/common/position"; @@ -755,6 +756,9 @@ export function ApplicationsSection(props: any) { INVITE_USERS_MESSAGE, cloudHosting, )} + onOpenOrClose={(isOpen) => + dispatch(setShowAppInviteUsersDialog(isOpen)) + } placeholder={createMessage( INVITE_USERS_PLACEHOLDER, cloudHosting, diff --git a/app/client/src/ce/reducers/index.tsx b/app/client/src/ce/reducers/index.tsx index 86a65d876e9e..34d43a962351 100644 --- a/app/client/src/ce/reducers/index.tsx +++ b/app/client/src/ce/reducers/index.tsx @@ -75,6 +75,7 @@ import lintErrorReducer from "reducers/lintingReducers"; import { AutoHeightUIState } from "reducers/uiReducers/autoHeightReducer"; import { AnalyticsReduxState } from "reducers/uiReducers/analyticsReducer"; import { MultiPaneReduxState } from "reducers/uiReducers/multiPaneReducer"; +import { layoutConversionReduxState } from "reducers/uiReducers/layoutConversionReducer"; export const reducerObject = { entities: entityReducer, @@ -136,6 +137,7 @@ export interface AppState { libraries: LibraryState; autoHeightUI: AutoHeightUIState; multiPaneConfig: MultiPaneReduxState; + layoutConversion: layoutConversionReduxState; }; entities: { canvasWidgetsStructure: CanvasWidgetStructure; diff --git a/app/client/src/ce/sagas/index.tsx b/app/client/src/ce/sagas/index.tsx index 8548dad43cbd..0c119b7dba8f 100644 --- a/app/client/src/ce/sagas/index.tsx +++ b/app/client/src/ce/sagas/index.tsx @@ -29,6 +29,7 @@ import initSagas from "sagas/InitSagas"; import { watchJSActionSagas } from "sagas/JSActionSagas"; import JSLibrarySaga from "sagas/JSLibrarySaga"; import jsPaneSagas from "sagas/JSPaneSagas"; +import layoutConversionSagas from "sagas/layoutConversionSagas"; import LintingSaga from "sagas/LintingSagas"; import modalSagas from "sagas/ModalSagas"; import onboardingSagas from "sagas/OnboardingSagas"; @@ -39,6 +40,7 @@ import providersSagas from "sagas/ProvidersSaga"; import queryPaneSagas from "sagas/QueryPaneSagas"; import replaySaga from "sagas/ReplaySaga"; import saaSPaneSagas from "sagas/SaaSPaneSagas"; +import snapshotSagas from "sagas/SnapshotSagas"; import snipingModeSagas from "sagas/SnipingModeSagas"; import templateSagas from "sagas/TemplatesSagas"; import themeSagas from "sagas/ThemeSaga"; @@ -96,4 +98,6 @@ export const sagas = [ LintingSaga, autoLayoutUpdateSagas, autoLayoutDraggingSagas, + layoutConversionSagas, + snapshotSagas, ]; diff --git a/app/client/src/components/editorComponents/form/FormDialogComponent.tsx b/app/client/src/components/editorComponents/form/FormDialogComponent.tsx index 58bafbfc0100..ef1df37abde4 100644 --- a/app/client/src/components/editorComponents/form/FormDialogComponent.tsx +++ b/app/client/src/components/editorComponents/form/FormDialogComponent.tsx @@ -1,7 +1,5 @@ import React, { ReactNode, useState, useEffect } from "react"; import { isPermitted } from "@appsmith/utils/permissionHelpers"; -import { useDispatch } from "react-redux"; -import { setShowAppInviteUsersDialog } from "actions/applicationActions"; import { DialogComponent as Dialog, TabComponent, @@ -41,6 +39,8 @@ const TabCloseBtnContainer = styled.div` type FormDialogComponentProps = { isOpen?: boolean; canOutsideClickClose?: boolean; + canEscapeKeyClose?: boolean; + isCloseButtonShown?: boolean; noModalBodyMarginTop?: boolean; workspaceId?: string; title?: string; @@ -48,6 +48,7 @@ type FormDialogComponentProps = { Form: any; trigger: ReactNode; onClose?: () => void; + onOpenOrClose?: (isOpen: boolean) => void; customProps?: any; permissionRequired?: string; permissions?: string[]; @@ -107,7 +108,6 @@ const getTabs = ( export function FormDialogComponent(props: FormDialogComponentProps) { const [isOpen, setIsOpenState] = useState(!!props.isOpen); const [selectedTabIndex, setSelectedTabIndex] = useState(0); - const dispatch = useDispatch(); useEffect(() => { setIsOpen(!!props.isOpen); @@ -115,7 +115,7 @@ export function FormDialogComponent(props: FormDialogComponentProps) { const setIsOpen = (isOpen: boolean) => { setIsOpenState(isOpen); - dispatch(setShowAppInviteUsersDialog(isOpen)); + props.onOpenOrClose && props.onOpenOrClose(isOpen); }; const onCloseHandler = () => { @@ -139,8 +139,10 @@ export function FormDialogComponent(props: FormDialogComponentProps) { return ( ; return ( @@ -142,6 +145,9 @@ export function AppViewerHeader(props: AppViewerHeaderProps) { }} isOpen={showAppInviteUsersDialog} message={createMessage(INVITE_USERS_MESSAGE, cloudHosting)} + onOpenOrClose={(isOpen) => + dispatch(setShowAppInviteUsersDialog(isOpen)) + } placeholder={createMessage( INVITE_USERS_PLACEHOLDER, cloudHosting, diff --git a/app/client/src/pages/AppViewer/PageMenu.tsx b/app/client/src/pages/AppViewer/PageMenu.tsx index a51812ec3292..e28c4b536460 100644 --- a/app/client/src/pages/AppViewer/PageMenu.tsx +++ b/app/client/src/pages/AppViewer/PageMenu.tsx @@ -8,7 +8,7 @@ import { getAppMode, showAppInviteUsersDialogSelector, } from "selectors/applicationSelectors"; -import { useSelector } from "react-redux"; +import { useDispatch, useSelector } from "react-redux"; import classNames from "classnames"; import PrimaryCTA from "./PrimaryCTA"; import Button from "./AppViewerButton"; @@ -29,6 +29,7 @@ import { INVITE_USERS_PLACEHOLDER, } from "@appsmith/constants/messages"; import { getAppsmithConfigs } from "@appsmith/configs"; +import { setShowAppInviteUsersDialog } from "actions/applicationActions"; const { cloudHosting } = getAppsmithConfigs(); @@ -53,6 +54,8 @@ export function PageMenu(props: AppViewerHeaderProps) { const [query, setQuery] = useState(""); const { hideWatermark } = getAppsmithConfigs(); + const dispatch = useDispatch(); + // hide menu on click outside useOnClickOutside( [menuRef, headerRef as React.RefObject], @@ -120,6 +123,9 @@ export function PageMenu(props: AppViewerHeaderProps) { }} isOpen={showAppInviteUsersDialog} message={createMessage(INVITE_USERS_MESSAGE, cloudHosting)} + onOpenOrClose={(isOpen) => + dispatch(setShowAppInviteUsersDialog(isOpen)) + } placeholder={createMessage( INVITE_USERS_PLACEHOLDER, cloudHosting, diff --git a/app/client/src/pages/Editor/CanvasLayoutConversion/ConversionButton.tsx b/app/client/src/pages/Editor/CanvasLayoutConversion/ConversionButton.tsx new file mode 100644 index 000000000000..43c885ac8d22 --- /dev/null +++ b/app/client/src/pages/Editor/CanvasLayoutConversion/ConversionButton.tsx @@ -0,0 +1,54 @@ +import * as Sentry from "@sentry/react"; + +import React from "react"; + +import { Button, Category, Size } from "design-system-old"; +import FormDialogComponent from "components/editorComponents/form/FormDialogComponent"; +import { ConversionForm } from "./ConversionForm"; +import { useConversionForm } from "./hooks/useConversionForm"; +import { useSelector } from "react-redux"; +import { getIsAutoLayout } from "selectors/canvasSelectors"; +import { + CONVERT_TO_AUTO_BUTTON, + CONVERT_TO_AUTO_TITLE, + CONVERT_TO_FIXED_BUTTON, + CONVERT_TO_FIXED_TITLE, + createMessage, +} from "@appsmith/constants/messages"; + +export function ConversionButton() { + const isAutoLayout = useSelector(getIsAutoLayout); + + const titleText = isAutoLayout + ? CONVERT_TO_FIXED_TITLE + : CONVERT_TO_AUTO_TITLE; + const buttonText = isAutoLayout + ? CONVERT_TO_FIXED_BUTTON + : CONVERT_TO_AUTO_BUTTON; + + return ( + (useConversionForm, { + isAutoLayout, + })} + canEscapeKeyClose={false} + canOutsideClickClose={false} + isCloseButtonShown={false} + title={createMessage(titleText)} + trigger={ +
+
+ + ); + }; +} diff --git a/app/client/src/pages/Editor/CanvasLayoutConversion/InfoBlock.tsx b/app/client/src/pages/Editor/CanvasLayoutConversion/InfoBlock.tsx new file mode 100644 index 000000000000..d8070a510577 --- /dev/null +++ b/app/client/src/pages/Editor/CanvasLayoutConversion/InfoBlock.tsx @@ -0,0 +1,27 @@ +import { Colors } from "constants/Colors"; +import { Icon, IconSize } from "design-system-old"; +import React from "react"; + +export type InfoBlockProps = { + icon: string; + header: string; + info: string; +}; + +export const InfoBlock = (props: InfoBlockProps) => { + return ( +
+ +
+

{props.header}

+

{props.info}

+
+
+ ); +}; diff --git a/app/client/src/pages/Editor/CanvasLayoutConversion/SnapShotButton.tsx b/app/client/src/pages/Editor/CanvasLayoutConversion/SnapShotButton.tsx new file mode 100644 index 000000000000..dbec851913b1 --- /dev/null +++ b/app/client/src/pages/Editor/CanvasLayoutConversion/SnapShotButton.tsx @@ -0,0 +1,58 @@ +import * as Sentry from "@sentry/react"; + +import React, { useEffect } from "react"; + +import FormDialogComponent from "components/editorComponents/form/FormDialogComponent"; +import { ConversionForm } from "./ConversionForm"; +import { useDispatch, useSelector } from "react-redux"; +import { AppState } from "@appsmith/reducers"; +import { useSnapShotFlow } from "./hooks/useSnapShotFlow"; +import { getReadableSnapShotDetails } from "selectors/autoLayoutSelectors"; +import { + createMessage, + USE_SNAPSHOT_CTA, + USE_SNAPSHOT_HEADER, +} from "@appsmith/constants/messages"; +import { setLayoutConversionStateAction } from "actions/autoLayoutActions"; +import { CONVERSION_STATES } from "reducers/uiReducers/layoutConversionReducer"; + +const useSnapShotForm = (onCancel: () => void) => { + const conversionState = useSelector( + (state: AppState) => state.ui.layoutConversion.conversionState, + ); + const readableSnapShotDetails = useSelector(getReadableSnapShotDetails); + const dispatch = useDispatch(); + + useEffect(() => { + dispatch(setLayoutConversionStateAction(CONVERSION_STATES.SNAPSHOT_START)); + }, []); + + const snapshotFlowStates = useSnapShotFlow( + dispatch, + readableSnapShotDetails, + onCancel, + ); + + return snapshotFlowStates[conversionState] || {}; +}; + +export function SnapShotButton() { + return ( + + {createMessage(USE_SNAPSHOT_CTA)} +

+ } + /> + ); +} + +SnapShotButton.displayName = "SnapShotButton"; + +export default Sentry.withProfiler(SnapShotButton); diff --git a/app/client/src/pages/Editor/CanvasLayoutConversion/hooks/useAutoToFixedLayoutFlow.ts b/app/client/src/pages/Editor/CanvasLayoutConversion/hooks/useAutoToFixedLayoutFlow.ts new file mode 100644 index 000000000000..519c9066b025 --- /dev/null +++ b/app/client/src/pages/Editor/CanvasLayoutConversion/hooks/useAutoToFixedLayoutFlow.ts @@ -0,0 +1,156 @@ +import { + BUILD_FIXED_LAYOUT, + BUILD_FIXED_LAYOUT_TEXT, + CANCEL_DIALOG, + CONVERSION_WARNING, + CONVERT, + CONVERTING_APP, + CONVERT_ANYWAYS, + createMessage, + CREATE_SNAPSHOT, + DROPDOWN_LABEL_TEXT, + SAVE_SNAPSHOT, + SAVE_SNAPSHOT_TEXT, + SNAPSHOT_LABEL, + USE_SNAPSHOT, +} from "@appsmith/constants/messages"; +import { ConversionProps } from "../ConversionForm"; + +import { Dispatch } from "redux"; +import { useState } from "react"; +import { DropdownOption } from "design-system-old"; +import { CONVERSION_STATES } from "reducers/uiReducers/layoutConversionReducer"; +import { setLayoutConversionStateAction } from "actions/autoLayoutActions"; +import { useCommonConversionFlows } from "./useCommonConversionFlows"; +import { Colors } from "constants/Colors"; +import { useSelector } from "react-redux"; +import { + buildSnapshotTimeString, + getReadableSnapShotDetails, +} from "selectors/autoLayoutSelectors"; +import { ReduxActionTypes } from "ce/constants/ReduxActionConstants"; +import { useSnapShotFlow } from "./useSnapShotFlow"; + +export const useAutoToFixedLayoutFlow = ( + dispatch: Dispatch, + onCancel: () => void, +): { + [key: string]: ConversionProps; +} => { + const [selectedLayout, setSelectedLayout] = useState({ + label: "Desktop", + value: "DESKTOP", + icon: "desktop", + }); + + const readableSnapShotDetails = useSelector(getReadableSnapShotDetails); + + return { + [CONVERSION_STATES.START]: { + cancelButtonText: createMessage(CANCEL_DIALOG), + infoBlocks: [ + { + icon: "grid", + header: createMessage(BUILD_FIXED_LAYOUT), + info: createMessage(BUILD_FIXED_LAYOUT_TEXT), + }, + { + icon: "history-line", + header: createMessage(SAVE_SNAPSHOT), + info: createMessage(SAVE_SNAPSHOT_TEXT), + }, + ], + selectDropDown: { + options: [ + { + label: "Desktop", + value: "DESKTOP", + icon: "desktop", + }, + { + label: "Mobile Device", + value: "MOBILE", + icon: "mobile", + }, + ], + selected: selectedLayout, + onSelect: (value: string, option: DropdownOption) => { + setSelectedLayout(option); + }, + labelText: createMessage(DROPDOWN_LABEL_TEXT), + }, + primaryButton: { + text: createMessage(CONVERT), + onClick: () => { + if (readableSnapShotDetails) { + dispatch( + setLayoutConversionStateAction( + CONVERSION_STATES.CONFIRM_CONVERSION, + ), + ); + } else { + dispatch( + setLayoutConversionStateAction( + CONVERSION_STATES.SNAPSHOT_SPINNER, + ), + ); + dispatch({ + type: ReduxActionTypes.CONVERT_AUTO_TO_FIXED, + payload: selectedLayout.value, + }); + } + }, + }, + }, + [CONVERSION_STATES.CONFIRM_CONVERSION]: { + cancelButtonText: createMessage(CANCEL_DIALOG), + bannerMessageDetails: { + message: createMessage(CONVERSION_WARNING), + backgroundColor: Colors.WARNING_ORANGE, + iconName: "warning-line", + iconColor: Colors.WARNING_SOLID, + textColor: Colors.GRAY_800, + }, + snapShotDetails: { + labelText: createMessage(SNAPSHOT_LABEL), + icon: "history-line", + text: buildSnapshotTimeString(readableSnapShotDetails), + }, + primaryButton: { + text: createMessage(USE_SNAPSHOT), + onClick: () => { + dispatch( + setLayoutConversionStateAction(CONVERSION_STATES.SNAPSHOT_START), + ); + }, + }, + secondaryButton: { + text: createMessage(CONVERT_ANYWAYS), + onClick: () => { + dispatch( + setLayoutConversionStateAction(CONVERSION_STATES.SNAPSHOT_SPINNER), + ); + dispatch({ + type: ReduxActionTypes.CONVERT_AUTO_TO_FIXED, + payload: selectedLayout.value, + }); + }, + }, + }, + [CONVERSION_STATES.SNAPSHOT_SPINNER]: { + cancelButtonText: createMessage(CANCEL_DIALOG), + spinner: createMessage(CREATE_SNAPSHOT), + }, + [CONVERSION_STATES.CONVERSION_SPINNER]: { + cancelButtonText: createMessage(CANCEL_DIALOG), + spinner: createMessage(CONVERTING_APP), + }, + ...useCommonConversionFlows(dispatch, onCancel), + ...useSnapShotFlow( + dispatch, + readableSnapShotDetails, + onCancel, + CONVERSION_STATES.CONFIRM_CONVERSION, + ), + }; +}; diff --git a/app/client/src/pages/Editor/CanvasLayoutConversion/hooks/useCommonConversionFlows.ts b/app/client/src/pages/Editor/CanvasLayoutConversion/hooks/useCommonConversionFlows.ts new file mode 100644 index 000000000000..cfabcd1fe1f8 --- /dev/null +++ b/app/client/src/pages/Editor/CanvasLayoutConversion/hooks/useCommonConversionFlows.ts @@ -0,0 +1,61 @@ +import { + CLOSE, + CONVERSION_ERROR, + CONVERSION_ERROR_HEADER, + CONVERSION_ERROR_TEXT, + CONVERSION_SUCCESS_HEADER, + CONVERSION_SUCCESS_TEXT, + createMessage, + REFRESH_THE_APP, + SEND_REPORT, +} from "@appsmith/constants/messages"; +import { ConversionProps } from "../ConversionForm"; + +import { Dispatch } from "redux"; +import { + AlertType, + CONVERSION_STATES, +} from "reducers/uiReducers/layoutConversionReducer"; +import { ReduxActionTypes } from "ce/constants/ReduxActionConstants"; + +export const useCommonConversionFlows = ( + dispatch: Dispatch, + onCancel: () => void, +): { + [key: string]: ConversionProps; +} => { + return { + [CONVERSION_STATES.COMPLETED_SUCCESS]: { + conversionComplete: { + alertType: AlertType.SUCCESS, + headerText: createMessage(CONVERSION_SUCCESS_HEADER), + infoText: createMessage(CONVERSION_SUCCESS_TEXT), + }, + primaryButton: { + text: createMessage(REFRESH_THE_APP), + onClick: () => { + onCancel(); + location.reload(); + }, + }, + }, + [CONVERSION_STATES.COMPLETED_ERROR]: { + cancelButtonText: createMessage(CLOSE), + conversionComplete: { + alertType: AlertType.ERROR, + headerText: createMessage(CONVERSION_ERROR_HEADER), + infoText: createMessage(CONVERSION_ERROR_TEXT), + errorText: createMessage(CONVERSION_ERROR), + }, + primaryButton: { + text: createMessage(SEND_REPORT), + onClick: () => { + onCancel(); + dispatch({ + type: ReduxActionTypes.LOG_LAYOUT_CONVERSION_ERROR, + }); + }, + }, + }, + }; +}; diff --git a/app/client/src/pages/Editor/CanvasLayoutConversion/hooks/useConversionForm.ts b/app/client/src/pages/Editor/CanvasLayoutConversion/hooks/useConversionForm.ts new file mode 100644 index 000000000000..3fea9c685ea0 --- /dev/null +++ b/app/client/src/pages/Editor/CanvasLayoutConversion/hooks/useConversionForm.ts @@ -0,0 +1,32 @@ +import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants"; +import { AppState } from "@appsmith/reducers"; +import { useEffect } from "react"; +import { useDispatch, useSelector } from "react-redux"; +import { useAutoToFixedLayoutFlow } from "./useAutoToFixedLayoutFlow"; +import { useFixedToAutoLayoutFlow } from "./useFixedToAutoLayoutFlow"; + +export const useConversionForm = ( + onCancel: () => void, + hookProps?: { isAutoLayout: boolean }, +) => { + const dispatch = useDispatch(); + const conversionState = useSelector( + (state: AppState) => state.ui.layoutConversion.conversionState, + ); + + useEffect(() => { + dispatch({ + type: ReduxActionTypes.START_CONVERSION_FLOW, + }); + }, []); + + const autoToFixedWorkflow = useAutoToFixedLayoutFlow(dispatch, onCancel); + + const fixedToAutoWorkflow = useFixedToAutoLayoutFlow(dispatch, onCancel); + + return hookProps?.isAutoLayout + ? autoToFixedWorkflow[conversionState] || {} + : fixedToAutoWorkflow[conversionState] || {}; + + return {}; +}; diff --git a/app/client/src/pages/Editor/CanvasLayoutConversion/hooks/useFixedToAutoLayoutFlow.ts b/app/client/src/pages/Editor/CanvasLayoutConversion/hooks/useFixedToAutoLayoutFlow.ts new file mode 100644 index 000000000000..b687429221d9 --- /dev/null +++ b/app/client/src/pages/Editor/CanvasLayoutConversion/hooks/useFixedToAutoLayoutFlow.ts @@ -0,0 +1,63 @@ +import { + BUILD_RESPONSIVE, + BUILD_RESPONSIVE_TEXT, + CANCEL_DIALOG, + CONVERT, + CONVERTING_APP, + createMessage, + CREATE_SNAPSHOT, + SAVE_SNAPSHOT, + SAVE_SNAPSHOT_TEXT, +} from "@appsmith/constants/messages"; +import { ConversionProps } from "../ConversionForm"; + +import { Dispatch } from "redux"; +import { CONVERSION_STATES } from "reducers/uiReducers/layoutConversionReducer"; +import { setLayoutConversionStateAction } from "actions/autoLayoutActions"; +import { ReduxActionTypes } from "ce/constants/ReduxActionConstants"; +import { useCommonConversionFlows } from "./useCommonConversionFlows"; + +export const useFixedToAutoLayoutFlow = ( + dispatch: Dispatch, + onCancel: () => void, +): { + [key: string]: ConversionProps; +} => { + return { + [CONVERSION_STATES.START]: { + cancelButtonText: createMessage(CANCEL_DIALOG), + infoBlocks: [ + { + icon: "devices", + header: createMessage(BUILD_RESPONSIVE), + info: createMessage(BUILD_RESPONSIVE_TEXT), + }, + { + icon: "history-line", + header: createMessage(SAVE_SNAPSHOT), + info: createMessage(SAVE_SNAPSHOT_TEXT), + }, + ], + primaryButton: { + text: createMessage(CONVERT), + onClick: () => { + dispatch( + setLayoutConversionStateAction(CONVERSION_STATES.SNAPSHOT_SPINNER), + ); + dispatch({ + type: ReduxActionTypes.CONVERT_FIXED_TO_AUTO, + }); + }, + }, + }, + [CONVERSION_STATES.SNAPSHOT_SPINNER]: { + cancelButtonText: createMessage(CANCEL_DIALOG), + spinner: createMessage(CREATE_SNAPSHOT), + }, + [CONVERSION_STATES.CONVERSION_SPINNER]: { + cancelButtonText: createMessage(CANCEL_DIALOG), + spinner: createMessage(CONVERTING_APP), + }, + ...useCommonConversionFlows(dispatch, onCancel), + }; +}; diff --git a/app/client/src/pages/Editor/CanvasLayoutConversion/hooks/useSnapShotFlow.ts b/app/client/src/pages/Editor/CanvasLayoutConversion/hooks/useSnapShotFlow.ts new file mode 100644 index 000000000000..aa833e2df869 --- /dev/null +++ b/app/client/src/pages/Editor/CanvasLayoutConversion/hooks/useSnapShotFlow.ts @@ -0,0 +1,74 @@ +import { + BACK, + CANCEL_DIALOG, + createMessage, + CREATE_SNAPSHOT, + SNAPSHOT_LABEL, + USE_SNAPSHOT, + USE_SNAPSHOT_TEXT, +} from "ce/constants/messages"; +import { ConversionProps } from "../ConversionForm"; + +import { Dispatch } from "redux"; +import { CONVERSION_STATES } from "reducers/uiReducers/layoutConversionReducer"; +import { setLayoutConversionStateAction } from "actions/autoLayoutActions"; +import { ReduxActionTypes } from "ce/constants/ReduxActionConstants"; +import { useCommonConversionFlows } from "./useCommonConversionFlows"; +import { + buildSnapshotTimeString, + ReadableSnapShotDetails, +} from "selectors/autoLayoutSelectors"; +import { Colors } from "constants/Colors"; + +export const useSnapShotFlow = ( + dispatch: Dispatch, + readableSnapShotDetails: ReadableSnapShotDetails | undefined, + onCancel: () => void, + backState?: CONVERSION_STATES, +): { + [key: string]: ConversionProps; +} => { + return { + [CONVERSION_STATES.SNAPSHOT_START]: { + cancelButtonText: createMessage(CANCEL_DIALOG), + bannerMessageDetails: { + message: createMessage(USE_SNAPSHOT_TEXT), + backgroundColor: Colors.GRAY_100, + iconName: "question-line", + iconColor: Colors.GRAY_600, + textColor: Colors.GRAY_800, + }, + snapShotDetails: { + labelText: createMessage(SNAPSHOT_LABEL), + icon: "history-line", + text: buildSnapshotTimeString(readableSnapShotDetails), + }, + primaryButton: { + text: createMessage(USE_SNAPSHOT), + onClick: () => { + dispatch( + setLayoutConversionStateAction( + CONVERSION_STATES.RESTORING_SNAPSHOT_SPINNER, + ), + ); + dispatch({ + type: ReduxActionTypes.RESTORE_SNAPSHOT, + }); + }, + }, + secondaryButton: backState + ? { + text: createMessage(BACK), + onClick: () => { + dispatch(setLayoutConversionStateAction(backState)); + }, + } + : undefined, + }, + [CONVERSION_STATES.RESTORING_SNAPSHOT_SPINNER]: { + cancelButtonText: createMessage(CANCEL_DIALOG), + spinner: createMessage(CREATE_SNAPSHOT), + }, + ...useCommonConversionFlows(dispatch, onCancel), + }; +}; diff --git a/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx b/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx index efae16a5ce9b..d0f5f4ab029a 100644 --- a/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx +++ b/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx @@ -8,6 +8,7 @@ import { Button, Category, Size, TooltipComponent } from "design-system-old"; import { openAppSettingsPaneAction } from "actions/appSettingsPaneActions"; import { AppPositionTypeControl } from "../AppPositionTypeControl"; +import ConversionButton from "../CanvasLayoutConversion/ConversionButton"; export function CanvasPropertyPane() { const dispatch = useDispatch(); @@ -15,6 +16,7 @@ export function CanvasPropertyPane() { const openAppSettingsPane = () => { dispatch(openAppSettingsPaneAction()); }; + return (

Properties

@@ -22,6 +24,7 @@ export function CanvasPropertyPane() {
+ diff --git a/app/client/src/pages/Editor/EditorHeader.tsx b/app/client/src/pages/Editor/EditorHeader.tsx index fb0e44019ae4..2747dda5c38f 100644 --- a/app/client/src/pages/Editor/EditorHeader.tsx +++ b/app/client/src/pages/Editor/EditorHeader.tsx @@ -32,7 +32,10 @@ import { } from "@appsmith/selectors/workspaceSelectors"; import { connect, useDispatch, useSelector } from "react-redux"; import DeployLinkButtonDialog from "components/designSystems/appsmith/header/DeployLinkButton"; -import { updateApplication } from "actions/applicationActions"; +import { + setShowAppInviteUsersDialog, + updateApplication, +} from "actions/applicationActions"; import { getApplicationList, getIsSavingAppName, @@ -487,6 +490,9 @@ export function EditorHeader(props: EditorHeaderProps) { canOutsideClickClose isOpen={showAppInviteUsersDialog} noModalBodyMarginTop + onOpenOrClose={(isOpen) => + dispatch(setShowAppInviteUsersDialog(isOpen)) + } placeholder={createMessage( INVITE_USERS_PLACEHOLDER, cloudHosting, diff --git a/app/client/src/pages/Editor/Explorer/Entity/EntityProperties.tsx b/app/client/src/pages/Editor/Explorer/Entity/EntityProperties.tsx index 0d777ffa06d5..9ce75e1e16d9 100644 --- a/app/client/src/pages/Editor/Explorer/Entity/EntityProperties.tsx +++ b/app/client/src/pages/Editor/Explorer/Entity/EntityProperties.tsx @@ -53,7 +53,7 @@ export function EntityProperties() { ); }); const widgetEntity = useSelector((state: AppState) => { - const pageWidgets = state.ui.pageWidgets[pageId]; + const pageWidgets = state.ui.pageWidgets[pageId].dsl; if (pageWidgets) { return pageWidgets[entityId]; } diff --git a/app/client/src/pages/Editor/Explorer/Pages/index.tsx b/app/client/src/pages/Editor/Explorer/Pages/index.tsx index 89d5606f1848..7da1c3818374 100644 --- a/app/client/src/pages/Editor/Explorer/Pages/index.tsx +++ b/app/client/src/pages/Editor/Explorer/Pages/index.tsx @@ -152,7 +152,7 @@ function Pages() { ); // Default layout is extracted by adding dynamically computed properties like min-height. const defaultPageLayouts = [ - { dsl: extractCurrentDSL(), layoutOnLoadActions: [] }, + { dsl: extractCurrentDSL().dsl, layoutOnLoadActions: [] }, ]; dispatch(createPage(applicationId, name, defaultPageLayouts)); }, [dispatch, pages, applicationId]); diff --git a/app/client/src/pages/Editor/Explorer/Widgets/WidgetContextMenu.tsx b/app/client/src/pages/Editor/Explorer/Widgets/WidgetContextMenu.tsx index dea58bad995a..7a352f714d0a 100644 --- a/app/client/src/pages/Editor/Explorer/Widgets/WidgetContextMenu.tsx +++ b/app/client/src/pages/Editor/Explorer/Widgets/WidgetContextMenu.tsx @@ -26,14 +26,14 @@ export function WidgetContextMenu(props: { }) { const { widgetId } = props; const parentId = useSelector((state: AppState) => { - return state.ui.pageWidgets[props.pageId][props.widgetId].parentId; + return state.ui.pageWidgets[props.pageId].dsl[props.widgetId].parentId; }); const widget = useSelector((state: AppState) => { - return state.ui.pageWidgets[props.pageId][props.widgetId]; + return state.ui.pageWidgets[props.pageId].dsl[props.widgetId]; }); const parentWidget: any = useSelector((state: AppState) => { - if (parentId) return state.ui.pageWidgets[props.pageId][parentId]; + if (parentId) return state.ui.pageWidgets[props.pageId].dsl[parentId]; return {}; }); const guidedTourEnabled = useSelector(inGuidedTour); diff --git a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx index 7a88932bda55..c870bcf10c00 100644 --- a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx +++ b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx @@ -19,7 +19,7 @@ import { forceOpenWidgetPanel } from "actions/widgetSidebarActions"; import classNames from "classnames"; import Centered from "components/designSystems/appsmith/CenteredWrapper"; import { layoutConfigurations } from "constants/WidgetConstants"; -import { IconSize, Spinner } from "design-system-old"; +import { BannerMessage, IconSize, Spinner } from "design-system-old"; import equal from "fast-deep-equal/es6"; import { WidgetGlobaStyles } from "globalStyles/WidgetGlobalStyles"; import { useDispatch } from "react-redux"; @@ -36,6 +36,18 @@ import useGoogleFont from "utils/hooks/useGoogleFont"; // import useHorizontalResize from "utils/hooks/useHorizontalResize"; import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; import Canvas from "../Canvas"; +import { Colors } from "constants/Colors"; +import { + createMessage, + SNAPSHOT_BANNER_MESSAGE, + DISCARD_SNAPSHOT_CTA, +} from "@appsmith/constants/messages"; +import { + buildSnapshotExpirationTimeString, + getReadableSnapShotDetails, +} from "selectors/autoLayoutSelectors"; +import { SnapShotButton } from "../CanvasLayoutConversion/SnapShotButton"; +import { ReduxActionTypes } from "ce/constants/ReduxActionConstants"; const Container = styled.section<{ background: string; @@ -71,6 +83,9 @@ function CanvasContainer() { const isAppThemeChanging = useSelector(getAppThemeIsChanging); const showCanvasTopSection = useSelector(showCanvasTopSectionSelector); const canvasScale = useSelector(getCanvasScale); + const readableSnapShotDetails = useSelector(getReadableSnapShotDetails); + + const shouldShowSnapShotBanner = readableSnapShotDetails && !isPreviewMode; const isLayoutingInitialized = useDynamicAppLayout(); const isPageInitializing = isFetchingPage || !isLayoutingInitialized; @@ -187,6 +202,18 @@ function CanvasContainer() { } }, [appLayout, isPreviewMode, currentPageId, appPositioningType]); + const bannerMessageCTA = ( + <> + +

dispatch({ type: ReduxActionTypes.DELETE_SNAPSHOT })} + > + {createMessage(DISCARD_SNAPSHOT_CTA)} +

+ + ); + // calculating exact height to not allow scroll at this component, // calculating total height minus margin on top, top bar and bottom bar const heightWithTopMargin = `calc(100vh - 2.25rem - ${theme.smallHeaderHeight} - ${theme.bottomBarHeight})`; @@ -199,9 +226,12 @@ function CanvasContainer() { } className={classNames({ [`${getCanvasClassName()} scrollbar-thin`]: true, - "mt-0": !shouldHaveTopMargin, - "mt-4": showCanvasTopSection, - "mt-8": shouldHaveTopMargin && !showCanvasTopSection, + "mt-0": shouldShowSnapShotBanner || !shouldHaveTopMargin, + "mt-4": !shouldShowSnapShotBanner && showCanvasTopSection, + "mt-8": + !shouldShowSnapShotBanner && + shouldHaveTopMargin && + !showCanvasTopSection, })} id={"canvas-viewport"} key={currentPageId} @@ -210,6 +240,23 @@ function CanvasContainer() { fontFamily: fontFamily, }} > + {shouldShowSnapShotBanner && ( + + )} {appPositioningType === AppPositioningTypes.AUTO && ( <> + dispatch(setShowAppInviteUsersDialog(isOpen)) + } title={props.add.title} trigger={createTrigger} /> diff --git a/app/client/src/pages/workspace/settings.tsx b/app/client/src/pages/workspace/settings.tsx index 6334f2fbea3f..a87b9646452b 100644 --- a/app/client/src/pages/workspace/settings.tsx +++ b/app/client/src/pages/workspace/settings.tsx @@ -14,7 +14,10 @@ import styled from "styled-components"; import MemberSettings from "@appsmith/pages/workspace/Members"; import { GeneralSettings } from "./General"; import * as Sentry from "@sentry/react"; -import { getAllApplications } from "actions/applicationActions"; +import { + getAllApplications, + setShowAppInviteUsersDialog, +} from "actions/applicationActions"; import { useMediaQuery } from "react-responsive"; import { BackButton, StickyHeader } from "components/utils/helperComponents"; import { debounce } from "lodash"; @@ -219,6 +222,9 @@ export default function Settings() { canOutsideClickClose isOpen={showModal} onClose={() => setShowModal(false)} + onOpenOrClose={(isOpen) => + dispatch(setShowAppInviteUsersDialog(isOpen)) + } placeholder={createMessage(INVITE_USERS_PLACEHOLDER, cloudHosting)} title={`Invite Users to ${currentWorkspace?.name}`} trigger diff --git a/app/client/src/reducers/uiReducers/index.tsx b/app/client/src/reducers/uiReducers/index.tsx index d0c308f8ca59..899773c57693 100644 --- a/app/client/src/reducers/uiReducers/index.tsx +++ b/app/client/src/reducers/uiReducers/index.tsx @@ -47,6 +47,7 @@ import appSettingsPaneReducer from "./appSettingsPaneReducer"; import autoHeightUIReducer from "./autoHeightReducer"; import analyticsReducer from "./analyticsReducer"; import multiPaneReducer from "./multiPaneReducer"; +import layoutConversionReducer from "./layoutConversionReducer"; const uiReducer = combineReducers({ analytics: analyticsReducer, @@ -97,6 +98,7 @@ const uiReducer = combineReducers({ libraries: libraryReducer, autoHeightUI: autoHeightUIReducer, multiPaneConfig: multiPaneReducer, + layoutConversion: layoutConversionReducer, }); export default uiReducer; diff --git a/app/client/src/reducers/uiReducers/layoutConversionReducer.ts b/app/client/src/reducers/uiReducers/layoutConversionReducer.ts new file mode 100644 index 000000000000..d8ac8a11fa10 --- /dev/null +++ b/app/client/src/reducers/uiReducers/layoutConversionReducer.ts @@ -0,0 +1,65 @@ +import { createImmerReducer } from "utils/ReducerUtils"; +import { + ReduxAction, + ReduxActionTypes, +} from "@appsmith/constants/ReduxActionConstants"; + +export enum AlertType { + SUCCESS = "SUCCESS", + WARNING = "WARNING", + ERROR = "ERROR", +} + +export enum CONVERSION_STATES { + START = "START", + CONFIRM_CONVERSION = "CONFIRM_CONVERSION", + SNAPSHOT_SPINNER = "SNAPSHOT_SPINNER", + CONVERSION_SPINNER = "CONVERSION_SPINNER", + COMPLETED_SUCCESS = "COMPLETED_SUCCESS", + COMPLETED_WARNING = "COMPLETED_WARNING", + COMPLETED_ERROR = "COMPLETED_ERROR", + SNAPSHOT_START = "SNAPSHOT_START", + RESTORING_SNAPSHOT_SPINNER = "RESTORING_SNAPSHOT_SPINNER", +} + +export type SnapShotDetails = { + lastUpdatedTime: string; +}; + +const initialState: layoutConversionReduxState = { + snapshotDetails: undefined, + conversionError: undefined, + conversionState: CONVERSION_STATES.START, +}; + +const layoutConversionReducer = createImmerReducer(initialState, { + [ReduxActionTypes.SET_LAYOUT_CONVERSION_STATE]: ( + state: layoutConversionReduxState, + action: ReduxAction<{ conversionState: CONVERSION_STATES; error: Error }>, + ) => { + state.conversionState = action.payload.conversionState; + if (action.payload.error) { + state.conversionError = action.payload.error; + } + }, + [ReduxActionTypes.START_CONVERSION_FLOW]: ( + state: layoutConversionReduxState, + ) => { + state.conversionState = CONVERSION_STATES.START; + state.conversionError = undefined; + }, + [ReduxActionTypes.UPDATE_SNAPSHOT_DETAILS]: ( + state: layoutConversionReduxState, + action: ReduxAction, + ) => { + state.snapshotDetails = action.payload; + }, +}); + +export interface layoutConversionReduxState { + snapshotDetails: SnapShotDetails | undefined; + conversionError: Error | undefined; + conversionState: CONVERSION_STATES; +} + +export default layoutConversionReducer; diff --git a/app/client/src/reducers/uiReducers/pageWidgetsReducer.ts b/app/client/src/reducers/uiReducers/pageWidgetsReducer.ts index 4ba220718f31..ba30162f3d72 100644 --- a/app/client/src/reducers/uiReducers/pageWidgetsReducer.ts +++ b/app/client/src/reducers/uiReducers/pageWidgetsReducer.ts @@ -9,7 +9,8 @@ import CanvasWidgetsNormalizer from "normalizers/CanvasWidgetsNormalizer"; export interface PageWidgetsReduxState { [pageId: string]: { - [widgetId: string]: WidgetProps & { children: string[] }; + dsl: { [widgetId: string]: WidgetProps & { children: string[] } }; + layoutId: string; }; } @@ -21,24 +22,28 @@ const pageWidgetsReducer = createImmerReducer(initialState, { [ReduxActionTypes.RESET_APPLICATION_WIDGET_STATE_REQUEST]: () => ({}), [ReduxActionTypes.FETCH_PAGE_DSLS_SUCCESS]: ( state: PageWidgetsReduxState, - action: ReduxAction>, + action: ReduxAction>, ) => { action.payload.forEach((entry) => { - state[entry.pageId] = CanvasWidgetsNormalizer.normalize( - entry.dsl, - ).entities.canvasWidgets; + state[entry.pageId] = { + dsl: CanvasWidgetsNormalizer.normalize(entry.dsl).entities + .canvasWidgets, + layoutId: entry.layoutId, + }; }); }, [ReduxActionTypes.FETCH_PAGE_DSL_SUCCESS]: ( state: PageWidgetsReduxState, - action: ReduxAction<{ pageId: string; dsl?: DSL }>, + action: ReduxAction<{ pageId: string; dsl?: DSL; layoutId: string }>, ) => { if (!action.payload.dsl) { delete state[action.payload.pageId]; } else { - state[action.payload.pageId] = CanvasWidgetsNormalizer.normalize( - action.payload.dsl, - ).entities.canvasWidgets; + state[action.payload.pageId] = { + dsl: CanvasWidgetsNormalizer.normalize(action.payload.dsl).entities + .canvasWidgets, + layoutId: action.payload.layoutId, + }; } }, }); diff --git a/app/client/src/sagas/ErrorSagas.tsx b/app/client/src/sagas/ErrorSagas.tsx index 11c81cf7dd31..6812cffc247d 100644 --- a/app/client/src/sagas/ErrorSagas.tsx +++ b/app/client/src/sagas/ErrorSagas.tsx @@ -94,12 +94,15 @@ export function* validateResponse( if (!response.responseMeta && !response.status) { throw Error(getErrorMessage(0)); } + if (!response.responseMeta && response.status) { throw Error(getErrorMessage(response.status, response.resourceType)); } + if (response.responseMeta.success) { return true; } + if ( response.responseMeta.error.code === SERVER_ERROR_CODES.INCORRECT_BINDING_LIST_OF_WIDGET diff --git a/app/client/src/sagas/PageSagas.tsx b/app/client/src/sagas/PageSagas.tsx index 039e0573dedc..483ea8e3bd1d 100644 --- a/app/client/src/sagas/PageSagas.tsx +++ b/app/client/src/sagas/PageSagas.tsx @@ -39,6 +39,7 @@ import PageApi, { FetchPageResponse, FetchPublishedPageRequest, PageLayout, + PageLayoutsRequest, SavePageRequest, SavePageResponse, SavePageResponseData, @@ -214,7 +215,7 @@ export const getCanvasWidgetsPayload = ( pageResponse: FetchPageResponse, ): UpdateCanvasPayload => { const normalizedResponse = CanvasWidgetsNormalizer.normalize( - extractCurrentDSL(pageResponse), + extractCurrentDSL(pageResponse).dsl, ); return { pageWidgetId: normalizedResponse.result, @@ -269,7 +270,7 @@ export function* handleFetchedPage({ // Sets last updated time yield put(setLastUpdatedTime(lastUpdatedTime)); - const extractedDSL = extractCurrentDSL(fetchPageResponse); + const extractedDSL = extractCurrentDSL(fetchPageResponse).dsl; yield put({ type: ReduxActionTypes.UPDATE_CANVAS_STRUCTURE, payload: extractedDSL, @@ -446,6 +447,7 @@ function* savePageSaga(action: ReduxAction<{ isRetry?: boolean }>) { payload: { pageId: savePageRequest.pageId, dsl: savePageRequest.dsl, + layoutId: savePageRequest.layoutId, }, }); @@ -560,6 +562,22 @@ function* savePageSaga(action: ReduxAction<{ isRetry?: boolean }>) { } } +export function* saveAllPagesSaga(pageLayouts: PageLayoutsRequest[]) { + let response: ApiResponse | undefined; + try { + const applicationId: string = yield select(getCurrentApplicationId); + response = yield PageApi.saveAllPages(applicationId, pageLayouts); + + const isValidResponse: boolean = yield validateResponse(response, false); + + if (isValidResponse) { + return true; + } + } catch (error) { + throw error; + } +} + function getLayoutSavePayload( widgets: { [widgetId: string]: FlattenedWidgetProps; @@ -637,7 +655,8 @@ export function* createPageSaga( type: ReduxActionTypes.FETCH_PAGE_DSL_SUCCESS, payload: { pageId: response.data.id, - dsl: extractCurrentDSL(response), + dsl: extractCurrentDSL(response).dsl, + layoutId: response.data.layouts[0].id, }, }); // TODO: Update URL params here @@ -745,11 +764,13 @@ export function* clonePageSaga( ), ); // Add this to the page DSLs for entity explorer + const { dsl, layoutId } = extractCurrentDSL(response); yield put({ type: ReduxActionTypes.FETCH_PAGE_DSL_SUCCESS, payload: { pageId: response.data.id, - dsl: extractCurrentDSL(response), + dsl, + layoutId, }, }); @@ -895,6 +916,7 @@ export function* updateWidgetNameSaga( payload: { pageId: pageId, dsl: response.data.dsl, + layoutId, }, }); checkAndLogErrorsIfCyclicDependency( @@ -966,9 +988,11 @@ export function* fetchPageDSLSaga(pageId: string) { }); const isValidResponse: boolean = yield validateResponse(fetchPageResponse); if (isValidResponse) { + const { dsl, layoutId } = extractCurrentDSL(fetchPageResponse); return { - pageId: pageId, - dsl: extractCurrentDSL(fetchPageResponse), + pageId, + dsl, + layoutId, userPermissions: fetchPageResponse.data?.userPermissions, }; } diff --git a/app/client/src/sagas/SnapshotSagas.ts b/app/client/src/sagas/SnapshotSagas.ts new file mode 100644 index 000000000000..a3a3d78ef5d3 --- /dev/null +++ b/app/client/src/sagas/SnapshotSagas.ts @@ -0,0 +1,152 @@ +import { + setLayoutConversionStateAction, + updateSnapshotDetails, +} from "actions/autoLayoutActions"; +import { ApiResponse } from "api/ApiResponses"; +import ApplicationApi from "api/ApplicationApi"; +import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants"; +import log from "loglevel"; +import { + CONVERSION_STATES, + SnapShotDetails, +} from "reducers/uiReducers/layoutConversionReducer"; +import { all, call, put, select, takeLatest } from "redux-saga/effects"; +import { getCurrentApplicationId } from "selectors/editorSelectors"; +import { getLogToSentryFromResponse } from "utils/helpers"; +import { validateResponse } from "./ErrorSagas"; + +export function* createSnapshotSaga() { + let response: ApiResponse | undefined; + try { + const applicationId: string = yield select(getCurrentApplicationId); + response = yield ApplicationApi.createSnapShotOfApplication({ + applicationId, + }); + + const isValidResponse: boolean = yield validateResponse( + response, + false, + getLogToSentryFromResponse(response), + ); + + if (isValidResponse) { + return true; + } + } catch (error) { + throw error; + } +} + +export function* fetchSnapshotSaga() { + let response: ApiResponse | undefined; + try { + const applicationId: string = yield select(getCurrentApplicationId); + response = yield ApplicationApi.getSnapShotDetails({ + applicationId, + }); + + const isValidResponse: boolean = yield validateResponse( + response, + false, + getLogToSentryFromResponse(response), + ); + + if (isValidResponse) { + const snapShotDetails = response?.data; + + return snapShotDetails; + } + } catch (error) { + if (getLogToSentryFromResponse(response)) { + log.error(error); + throw error; + } + } +} + +function* restoreApplicationFromSnapshotSaga() { + let response: ApiResponse | undefined; + try { + const applicationId: string = yield select(getCurrentApplicationId); + response = yield ApplicationApi.restoreSnapShotOfApplication({ + applicationId, + }); + + const isValidResponse: boolean = yield validateResponse( + response, + false, + getLogToSentryFromResponse(response), + ); + + if (isValidResponse) { + yield put(updateSnapshotDetails(undefined)); + yield put( + setLayoutConversionStateAction(CONVERSION_STATES.COMPLETED_SUCCESS), + ); + } + } catch (error) { + log.error(error); + yield put( + setLayoutConversionStateAction(CONVERSION_STATES.COMPLETED_ERROR), + ); + throw error; + } +} + +function* deleteApplicationSnapshotSaga() { + let response: ApiResponse | undefined; + try { + const applicationId: string = yield select(getCurrentApplicationId); + response = yield ApplicationApi.deleteSnapShotOfApplication({ + applicationId, + }); + + const isValidResponse: boolean = yield validateResponse( + response, + false, + getLogToSentryFromResponse(response), + ); + + if (isValidResponse) { + yield put(updateSnapshotDetails(undefined)); + } + } catch (error) { + log.error(error); + throw error; + } +} + +function* updateSnapshotDetailsSaga() { + try { + const snapShotDetails: { updatedTime: Date } | undefined = yield call( + fetchSnapshotSaga, + ); + yield put( + updateSnapshotDetails( + snapShotDetails + ? { lastUpdatedTime: snapShotDetails.updatedTime?.toString() } + : undefined, + ), + ); + } catch (error) { + throw error; + } +} + +export default function* snapshotSagas() { + yield all([ + takeLatest( + ReduxActionTypes.RESTORE_SNAPSHOT, + restoreApplicationFromSnapshotSaga, + ), + takeLatest( + [ + ReduxActionTypes.INIT_CANVAS_LAYOUT, + ReduxActionTypes.FETCH_SNAPSHOT, + ReduxActionTypes.START_CONVERSION_FLOW, + ], + updateSnapshotDetailsSaga, + ), + takeLatest(ReduxActionTypes.DELETE_SNAPSHOT, deleteApplicationSnapshotSaga), + ]); +} diff --git a/app/client/src/sagas/layoutConversionSagas.ts b/app/client/src/sagas/layoutConversionSagas.ts new file mode 100644 index 000000000000..267630984a8a --- /dev/null +++ b/app/client/src/sagas/layoutConversionSagas.ts @@ -0,0 +1,140 @@ +import { setLayoutConversionStateAction } from "actions/autoLayoutActions"; +import { + ReduxAction, + ReduxActionTypes, +} from "@appsmith/constants/ReduxActionConstants"; +import { AppState } from "@appsmith/reducers"; +import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; +import CanvasWidgetsNormalizer from "normalizers/CanvasWidgetsNormalizer"; +import { SupportedLayouts } from "reducers/entityReducers/pageListReducer"; +import { CONVERSION_STATES } from "reducers/uiReducers/layoutConversionReducer"; +import { PageWidgetsReduxState } from "reducers/uiReducers/pageWidgetsReducer"; +import { all, call, put, select, takeLatest } from "redux-saga/effects"; +import { getPageWidgets } from "selectors/entitiesSelector"; +import { convertNormalizedDSLToFixed } from "utils/DSLConversions/autoToFixedLayout"; +import convertDSLtoAuto from "utils/DSLConversions/fixedToAutoLayout"; +import { DSLWidget } from "widgets/constants"; +import { createSnapshotSaga } from "./SnapshotSagas"; +import * as Sentry from "@sentry/react"; +import log from "loglevel"; +import { saveAllPagesSaga } from "./PageSagas"; + +function* convertFromAutoToFixedSaga(action: ReduxAction) { + try { + yield call(createSnapshotSaga); + yield put( + setLayoutConversionStateAction(CONVERSION_STATES.CONVERSION_SPINNER), + ); + const pageWidgetsList: PageWidgetsReduxState = yield select(getPageWidgets); + + const pageLayouts = []; + + for (const [pageId, page] of Object.entries(pageWidgetsList)) { + const { dsl: normalizedDSL, layoutId } = page; + + const fixedLayoutDSL = convertNormalizedDSLToFixed( + normalizedDSL, + action.payload, + ); + + const dsl: DSLWidget = CanvasWidgetsNormalizer.denormalize( + MAIN_CONTAINER_WIDGET_ID, + { canvasWidgets: fixedLayoutDSL }, + ); + + pageLayouts.push({ + pageId, + layoutId, + layout: { + dsl, + }, + }); + } + + yield call(saveAllPagesSaga, pageLayouts); + yield put( + setLayoutConversionStateAction(CONVERSION_STATES.COMPLETED_SUCCESS), + ); + } catch (e) { + log.error(e); + yield put( + setLayoutConversionStateAction( + CONVERSION_STATES.COMPLETED_ERROR, + e as Error, + ), + ); + } +} + +function* convertFromFixedToAutoSaga() { + try { + yield call(createSnapshotSaga); + yield put( + setLayoutConversionStateAction(CONVERSION_STATES.CONVERSION_SPINNER), + ); + const pageWidgetsList: PageWidgetsReduxState = yield select(getPageWidgets); + + const pageLayouts = []; + + for (const [pageId, page] of Object.entries(pageWidgetsList)) { + const { dsl: normalizedDSL, layoutId } = page; + + const fixedDSL: DSLWidget = CanvasWidgetsNormalizer.denormalize( + MAIN_CONTAINER_WIDGET_ID, + { canvasWidgets: normalizedDSL }, + ); + + const dsl: DSLWidget = convertDSLtoAuto(fixedDSL); + + pageLayouts.push({ + pageId, + layoutId, + layout: { + dsl, + }, + }); + } + + yield call(saveAllPagesSaga, pageLayouts); + yield put( + setLayoutConversionStateAction(CONVERSION_STATES.COMPLETED_SUCCESS), + ); + } catch (e) { + log.error(e); + yield put( + setLayoutConversionStateAction( + CONVERSION_STATES.COMPLETED_ERROR, + e as Error, + ), + ); + } +} + +function* logLayoutConversionErrorSaga() { + try { + const error: Error = yield select( + (state: AppState) => state.ui.layoutConversion.conversionError, + ); + + yield call(Sentry.captureException, error); + } catch (e) { + throw e; + } +} + +export default function* layoutConversionSagas() { + yield all([ + takeLatest( + ReduxActionTypes.CONVERT_AUTO_TO_FIXED, + convertFromAutoToFixedSaga, + ), + takeLatest( + ReduxActionTypes.CONVERT_FIXED_TO_AUTO, + convertFromFixedToAutoSaga, + ), + takeLatest( + ReduxActionTypes.LOG_LAYOUT_CONVERSION_ERROR, + logLayoutConversionErrorSaga, + ), + ]); +} diff --git a/app/client/src/selectors/autoLayoutSelectors.tsx b/app/client/src/selectors/autoLayoutSelectors.tsx index 46b4f7b1e1ca..7905063faf5a 100644 --- a/app/client/src/selectors/autoLayoutSelectors.tsx +++ b/app/client/src/selectors/autoLayoutSelectors.tsx @@ -4,10 +4,17 @@ import { LayerChild, } from "components/designSystems/appsmith/autoLayout/FlexBoxComponent"; import { FLEXBOX_PADDING, GridDefaults } from "constants/WidgetConstants"; +import moment from "moment"; import { createSelector } from "reselect"; import { getWidgets } from "sagas/selectors"; import { getIsMobile } from "./mainCanvasSelectors"; +export type ReadableSnapShotDetails = { + timeSince: string; + timeTillExpiration: string; + readableDate: string; +}; + export const getFlexLayers = (parentId: string) => { return createSelector(getWidgets, (widgets): FlexLayer[] => { const parent = widgets[parentId]; @@ -63,3 +70,59 @@ export const getParentOffsetTop = (widgetId: string) => : parent.topRow; return top * GridDefaults.DEFAULT_GRID_ROW_HEIGHT + FLEXBOX_PADDING; }); + +export const getReadableSnapShotDetails = createSelector( + (state: AppState) => + state.ui.layoutConversion.snapshotDetails?.lastUpdatedTime, + ( + lastUpdatedDateString: string | undefined, + ): ReadableSnapShotDetails | undefined => { + if (!lastUpdatedDateString) return; + + const lastUpdatedDate = new Date(lastUpdatedDateString); + + if (Date.now() - lastUpdatedDate.getTime() <= 0) return; + + const millisecondsPerHour = 60 * 60 * 1000; + const ExpirationHours = 5 * 24; + const hoursPassedSince = + (Date.now() - lastUpdatedDate.getTime()) / millisecondsPerHour; + + const timeSince: string = getStringFromHours(hoursPassedSince); + const timeTillExpiration: string = getStringFromHours( + ExpirationHours - hoursPassedSince, + ); + + const readableDate = moment(lastUpdatedDate).format("Do MMMM, YYYY h:mm a"); + + return { + timeSince, + timeTillExpiration, + readableDate, + }; + }, +); + +function getStringFromHours(hours: number) { + if (hours > 48) return `${Math.round(hours / 24)} days`; + else if (hours < 48 && hours > 24) return `1 day`; + else if (hours > 1) return `${Math.round(hours)} hours`; + else if (hours > 0) return `less than an hour`; + else return ""; +} + +export function buildSnapshotTimeString( + readableSnapShotDetails: ReadableSnapShotDetails | undefined, +) { + if (!readableSnapShotDetails) return ""; + const { readableDate, timeSince } = readableSnapShotDetails; + return `Snapshot from ${timeSince} ago (${readableDate})`; +} + +export function buildSnapshotExpirationTimeString( + readableSnapShotDetails: ReadableSnapShotDetails | undefined, +) { + if (!readableSnapShotDetails) return ""; + const { timeTillExpiration } = readableSnapShotDetails; + return `Snapshot expires in ${timeTillExpiration}`; +} diff --git a/app/client/src/selectors/canvasSelectors.ts b/app/client/src/selectors/canvasSelectors.ts index 45cb3a969c05..2c590d51856e 100644 --- a/app/client/src/selectors/canvasSelectors.ts +++ b/app/client/src/selectors/canvasSelectors.ts @@ -1,5 +1,22 @@ import { AppState } from "@appsmith/reducers"; +import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; +import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; +import { createSelector } from "reselect"; +import { getWidgets } from "sagas/selectors"; +import { Positioning } from "utils/autoLayout/constants"; export const getIsDraggingForSelection = (state: AppState) => { return state.ui.canvasSelection.isDraggingForSelection; }; + +export const getIsAutoLayout = createSelector( + getWidgets, + (widgets: CanvasWidgetsReduxState): boolean => { + const mainContainer = widgets[MAIN_CONTAINER_WIDGET_ID]; + + return ( + mainContainer.useAutoLayout && + mainContainer.positioning === Positioning.Vertical + ); + }, +); diff --git a/app/client/src/selectors/entitiesSelector.ts b/app/client/src/selectors/entitiesSelector.ts index 4d9eb90f21ba..b88520d4e71e 100644 --- a/app/client/src/selectors/entitiesSelector.ts +++ b/app/client/src/selectors/entitiesSelector.ts @@ -492,12 +492,12 @@ export const getCanvasWidgets = (state: AppState): CanvasWidgetsReduxState => export const getCanvasWidgetsStructure = (state: AppState) => state.entities.canvasWidgetsStructure; -const getPageWidgets = (state: AppState) => state.ui.pageWidgets; +export const getPageWidgets = (state: AppState) => state.ui.pageWidgets; export const getCurrentPageWidgets = createSelector( getPageWidgets, getCurrentPageId, (widgetsByPage, currentPageId) => - currentPageId ? widgetsByPage[currentPageId] : {}, + currentPageId ? widgetsByPage[currentPageId].dsl : {}, ); export const getParentModalId = ( @@ -540,7 +540,7 @@ export const getAllWidgetsMap = createSelector( (widgetsByPage) => { return Object.entries(widgetsByPage).reduce( (res: any, [pageId, pageWidgets]: any) => { - const widgetsMap = Object.entries(pageWidgets).reduce( + const widgetsMap = Object.entries(pageWidgets.dsl).reduce( (res, [widgetId, widget]: any) => { const parentModalId = getParentModalId(widget, pageWidgets); diff --git a/app/client/src/utils/DSLConversions/fixedToAutoLayout.ts b/app/client/src/utils/DSLConversions/fixedToAutoLayout.ts index aedc857a5d17..47465c2485e8 100644 --- a/app/client/src/utils/DSLConversions/fixedToAutoLayout.ts +++ b/app/client/src/utils/DSLConversions/fixedToAutoLayout.ts @@ -433,7 +433,7 @@ export function getAlignmentScore( ).toFixed(2), ); - return score === undefined || score === NaN ? -1 : score; + return score === undefined || Number.isNaN(score) ? -1 : score; } /** diff --git a/app/client/src/utils/WidgetPropsUtils.test.tsx b/app/client/src/utils/WidgetPropsUtils.test.tsx index d3e7bd45acfb..127f62693263 100644 --- a/app/client/src/utils/WidgetPropsUtils.test.tsx +++ b/app/client/src/utils/WidgetPropsUtils.test.tsx @@ -914,9 +914,9 @@ describe("Initial value migration test", () => { }, }; }; - const migratedDslV2: any = extractCurrentDSL(tabsWidgetDSL()); + const migratedDslV2: any = extractCurrentDSL(tabsWidgetDSL()).dsl; expect(migratedDslV2.children[0].children[0].leftColumn).toBeNaN(); - const migratedDslV3: any = extractCurrentDSL(tabsWidgetDSL(2)); + const migratedDslV3: any = extractCurrentDSL(tabsWidgetDSL(2)).dsl; expect(migratedDslV3.children[0].version).toBe(3); expect(migratedDslV3.children[0].children[0].leftColumn).not.toBeNaN(); expect(migratedDslV3.children[0].children[0].leftColumn).toBe(0); diff --git a/app/client/src/utils/WidgetPropsUtils.tsx b/app/client/src/utils/WidgetPropsUtils.tsx index 5f24790b1317..4823f0651c2d 100644 --- a/app/client/src/utils/WidgetPropsUtils.tsx +++ b/app/client/src/utils/WidgetPropsUtils.tsx @@ -34,12 +34,15 @@ const defaultDSL = defaultTemplate; export const extractCurrentDSL = ( fetchPageResponse?: FetchPageResponse, -): DSLWidget => { +): { dsl: DSLWidget; layoutId: string | undefined } => { const newPage = !fetchPageResponse; const currentDSL = fetchPageResponse?.data.layouts[0].dsl || { ...defaultDSL, }; - return transformDSL(currentDSL as ContainerWidgetProps, newPage); + return { + dsl: transformDSL(currentDSL as ContainerWidgetProps, newPage), + layoutId: fetchPageResponse?.data.layouts[0].id, + }; }; /** diff --git a/app/client/test/testCommon.ts b/app/client/test/testCommon.ts index d2b7881f88f3..bab55203f0cc 100644 --- a/app/client/test/testCommon.ts +++ b/app/client/test/testCommon.ts @@ -54,7 +54,7 @@ export const useMockDsl = (dsl: any, mode?: APP_MODE) => { payload: [ { pageId: mockResp.data.id, - dsl: extractCurrentDSL(mockResp), + dsl: extractCurrentDSL(mockResp).dsl, }, ], }); diff --git a/app/client/yarn.lock b/app/client/yarn.lock index 6605ed0b6793..be30b7ff7633 100644 --- a/app/client/yarn.lock +++ b/app/client/yarn.lock @@ -6258,10 +6258,10 @@ depd@~1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz" -"design-system-old@npm:@appsmithorg/design-system-old@1.0.48": - version "1.0.48" - resolved "https://registry.yarnpkg.com/@appsmithorg/design-system-old/-/design-system-old-1.0.48.tgz#69c1a1421486af8be52cbad4d3d7c261ba206ed2" - integrity sha512-W03JOzbyhDIEdT0KGnXJh36xrve2/bLi/cGJxpWVSfvYgxWjEFITFszpAt8MORg9JF4rD9DKMrvUEG/dnTzyLg== +"design-system-old@npm:@appsmithorg/design-system-old@1.0.53-alpha.0": + version "1.0.53-alpha.0" + resolved "https://registry.yarnpkg.com/@appsmithorg/design-system-old/-/design-system-old-1.0.53-alpha.0.tgz#cb1b6c6e5f76c6927ff161c4cbf855e6a61c7a81" + integrity sha512-yTguRo3SJrqyYHSUkVxfscGLT7uybXaYpIsXfLIcGvufbNW5GdoInHt6g7+kj/C9KLfWJkKotZ31tzELYolrFg== dependencies: emoji-mart "3.0.1" From 1da08ab33845bc5898ad1effd125e058f5d2c84c Mon Sep 17 00:00:00 2001 From: rahulramesha Date: Wed, 8 Mar 2023 22:43:05 +0530 Subject: [PATCH 617/708] modify auto to fixed algorithm to use existing position calculations --- .../utils/DSLConversions/autoToFixedLayout.ts | 367 +++--------------- 1 file changed, 48 insertions(+), 319 deletions(-) diff --git a/app/client/src/utils/DSLConversions/autoToFixedLayout.ts b/app/client/src/utils/DSLConversions/autoToFixedLayout.ts index 67e397b3f405..b8a647babe4a 100644 --- a/app/client/src/utils/DSLConversions/autoToFixedLayout.ts +++ b/app/client/src/utils/DSLConversions/autoToFixedLayout.ts @@ -3,42 +3,22 @@ import { layoutConfigurations, MAIN_CONTAINER_WIDGET_ID, } from "constants/WidgetConstants"; -import { partition } from "lodash"; import CanvasWidgetsNormalizer from "normalizers/CanvasWidgetsNormalizer"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; import { SupportedLayouts } from "reducers/entityReducers/pageListReducer"; import { HORIZONTAL_RESIZE_MIN_LIMIT } from "reflow/reflowTypes"; -import { FlexLayer } from "utils/autoLayout/autoLayoutTypes"; import { alterLayoutForDesktop, alterLayoutForMobile, } from "utils/autoLayout/AutoLayoutUtils"; +import { Positioning } from "utils/autoLayout/constants"; import { - FlexLayerAlignment, - FlexVerticalAlignment, - Positioning, -} from "utils/autoLayout/constants"; -import { - getWidgetWidth, - getWidgetHeight, getTopRow, getBottomRow, getLeftColumn, getRightColumn, } from "utils/autoLayout/flexWidgetUtils"; -import { DSLWidget, FlattenedWidgetProps } from "widgets/constants"; - -type LayerAlignmentData = { - widgets: FlattenedWidgetProps[]; - width: number; - type: FlexLayerAlignment; -}; - -type AlignmentLayerMap = { - start: LayerAlignmentData; - center: LayerAlignmentData; - end: LayerAlignmentData; -}; +import { DSLWidget } from "widgets/constants"; const nonFlexLayerWidgets = ["MODAL_WIDGET"]; @@ -172,40 +152,9 @@ function getFixedCanvasWidget( //if Mobile, use the existing already calculated positions in `alterLayoutForMobile` if (isMobile) { return processMobileCanvasChildren(widgets, canvasId); + } else { + return processCanvasChildren(widgets, canvasId); } - - //If not mobile/wrapped use the flexLayer alignments to updated the positions of the widgets - const { children, updatedWidgets } = processLayers( - widgets, - canvasWidget.flexLayers, - ); - - //separate the widgets to be skipped - const [nonLayerChildren, layerChildren] = partition( - canvasWidget.children, - (widgetId) => - nonFlexLayerWidgets.indexOf(updatedWidgets[widgetId].type) > -1, - ); - - // delete widgets that are in Layer Children but not in children - const deletedWidgets = layerChildren.filter((f) => !children.includes(f)); - - for (const deletedWidgetId of deletedWidgets) { - delete updatedWidgets[deletedWidgetId]; - } - - // Delete Canvas widget responsive properties - delete canvasWidget.flexLayers; - delete canvasWidget.responsiveBehavior; - - updatedWidgets[canvasId] = { - ...canvasWidget, - children: [...children, ...nonLayerChildren], - useAutoLayout: false, - positioning: Positioning.Fixed, - }; - - return updatedWidgets; } /** @@ -250,93 +199,70 @@ function processMobileCanvasChildren( } /** - * Process Layers iteratively to calculate positions based on layer and alignment + * Process the mobile canvas Widgets with already existing positions/dimensions * @param widgets - * @param flexLayers + * @param canvasId * @returns */ -function processLayers( +function processCanvasChildren( widgets: CanvasWidgetsReduxState, - flexLayers: FlexLayer[], + canvasId: string, ) { - let bottomRow = 0; - let updatedWidgets = { ...widgets }; - let children: string[] = []; - - for (const flexLayer of flexLayers) { - let currChildren; - ({ bottomRow, currChildren, updatedWidgets } = processIndividualLayer( - updatedWidgets, - flexLayer, - bottomRow, - )); - - children = [...children, ...currChildren]; - } + const canvasWidget = { ...widgets[canvasId] }; - return { updatedWidgets, children }; -} + let currWidgets = { ...widgets }; -/** - * Process Individual layer to calculate positions based on layer and alignment - * @param widgets - * @param flexLayer - * @param currentBottomRow - * @returns - */ -function processIndividualLayer( - widgets: CanvasWidgetsReduxState, - flexLayer: FlexLayer, - currentBottomRow: number, -) { - const { children: flexChildren } = flexLayer; + const widgetsToDelete: string[] = []; - let currChildren: string[] = []; + for (const childId of canvasWidget.children || []) { + const currWidget = currWidgets[childId]; - let layerHeight = 0; + if (nonFlexLayerWidgets.includes(currWidget.type)) { + continue; + } - const alignmentLayerMap: AlignmentLayerMap = { - start: { widgets: [], width: 0, type: FlexLayerAlignment.Start }, - center: { widgets: [], width: 0, type: FlexLayerAlignment.Center }, - end: { widgets: [], width: 0, type: FlexLayerAlignment.End }, - }; + const leftColumn = getLeftColumn(currWidget, false); + let rightColumn = getRightColumn(currWidget, false); - //update alignment layer map by iterating each children within layer - for (const child of flexChildren) { - const widget = widgets[child.id]; + if ( + leftColumn > + GridDefaults.DEFAULT_GRID_COLUMNS - HORIZONTAL_RESIZE_MIN_LIMIT + ) { + delete currWidgets[childId]; + widgetsToDelete.push(childId); + continue; + } - if (widget.detachFromLayout) continue; + if (rightColumn > GridDefaults.DEFAULT_GRID_COLUMNS) { + rightColumn = GridDefaults.DEFAULT_GRID_COLUMNS; + } - currChildren.push(child.id); - layerHeight = Math.max(layerHeight, getWidgetHeight(widget, false)); + currWidgets[childId] = { + ...currWidget, + topRow: getTopRow(currWidget, false), + bottomRow: getBottomRow(currWidget, false), + leftColumn, + rightColumn, + }; - if (child.align === "end") { - alignmentLayerMap.end.widgets.push(widget); - alignmentLayerMap.end.width += getWidgetWidth(widget, false); - } else if (child.align === "center") { - alignmentLayerMap.center.widgets.push(widget); - alignmentLayerMap.center.width += getWidgetWidth(widget, false); - } else { - alignmentLayerMap.start.widgets.push(widget); - alignmentLayerMap.start.width += getWidgetWidth(widget, false); - } + currWidgets = convertAutoWidgetToFixed(currWidgets, childId, false); } - //using alignmentLayerMap calculate and update positions of each children within layer - const { children, currWidgets, nextBottomRow } = placeWidgetsWithoutWrap( - widgets, - alignmentLayerMap, - currentBottomRow, - layerHeight, + canvasWidget.children = canvasWidget.children?.filter( + (childId) => !widgetsToDelete.includes(childId), ); - currChildren = [...children]; + // Delete Canvas widget responsive properties + delete canvasWidget.flexLayers; + delete canvasWidget.responsiveBehavior; - return { - updatedWidgets: currWidgets, - bottomRow: nextBottomRow, - currChildren, + currWidgets[canvasId] = { + ...canvasWidget, + useAutoLayout: false, + positioning: Positioning.Fixed, }; + + return currWidgets; } /** @@ -347,200 +273,3 @@ function processIndividualLayer( function getIsMobile(destinationLayout: SupportedLayouts) { return destinationLayout === "MOBILE"; } - -/** - * using alignmentLayerMap calculate and update positions of each children within layer without wrapping - * @param widgets - * @param alignmentLayerMap - * @param currentBottomRow - * @returns - */ -function placeWidgetsWithoutWrap( - widgets: CanvasWidgetsReduxState, - alignmentLayerMap: AlignmentLayerMap, - currentBottomRow: number, - layerHeight: number, -): { - currWidgets: CanvasWidgetsReduxState; - nextBottomRow: number; - children: string[]; -} { - let children: string[] = []; - let availableLeftColumn = 0; - - let maxBottomRow = currentBottomRow; - - let currWidgets = { ...widgets }; - - const layerAlignmentDataList = Object.values(alignmentLayerMap); - - for (const layerAlignmentData of layerAlignmentDataList) { - const { type, widgets, width } = layerAlignmentData; - - if (widgets.length > 0) { - const { - children: currChildren, - convertedWidgets, - currentAvailableLeftColumn, - maxBottomRow: currMaxBottomRow, - } = calculateWidgetDimensionForAlignment( - currWidgets, - availableLeftColumn, - widgets, - width, - type, - currentBottomRow, - layerHeight, - ); - currWidgets = { ...convertedWidgets }; - children = [...children, ...currChildren]; - availableLeftColumn = currentAvailableLeftColumn; - maxBottomRow = Math.max(currMaxBottomRow, maxBottomRow); - } - } - - return { currWidgets, children, nextBottomRow: maxBottomRow }; -} - -/** - * Calculate widget positions of each widget in alignedWidgets - * @param widgets - * @param availableLeftColumn currently available left column - * @param alignedWidgets widgets to be updated in the current Alignment - * @param alignedWidth width of all widgets in the alignment - * @param flexLayerAlignment - * @param currentBottomRow Current bottom Row. - * @returns - */ -function calculateWidgetDimensionForAlignment( - widgets: CanvasWidgetsReduxState, - availableLeftColumn: number, - alignedWidgets: FlattenedWidgetProps[], - alignedWidth: number, - flexLayerAlignment: FlexLayerAlignment, - currentBottomRow: number, - layerHeight: number, -): { - convertedWidgets: CanvasWidgetsReduxState; - children: string[]; - currentAvailableLeftColumn: number; - maxBottomRow: number; -} { - //Compare calculated leftColumn based on width to availableLeftColumn - let currentAvailableLeftColumn = Math.max( - getCalculatedLeftColumn( - alignedWidth, - GridDefaults.DEFAULT_GRID_COLUMNS, - flexLayerAlignment, - ), - availableLeftColumn, - ); - let maxBottomRow = currentBottomRow; - - let currWidgets: CanvasWidgetsReduxState = { ...widgets }; - const children = []; - - for (const widget of alignedWidgets) { - const width = getWidgetWidth(widget, false); - const height = getWidgetHeight(widget, false); - - //if the currentAvailableLeftColumn cannot possibly fit another widget within, - //return with not adding the ids in the children, which can be further used to delete those widgets - if ( - currentAvailableLeftColumn >= - GridDefaults.DEFAULT_GRID_COLUMNS - HORIZONTAL_RESIZE_MIN_LIMIT - ) - return { - convertedWidgets: currWidgets, - children, - currentAvailableLeftColumn, - maxBottomRow, - }; - - const leftColumn = currentAvailableLeftColumn; - const rightColumn = Math.min( - currentAvailableLeftColumn + width, - GridDefaults.DEFAULT_GRID_COLUMNS, - ); - const topRow = getTopRowFromVerticalAlignment( - currentBottomRow, - widget.flexVerticalAlignment, - layerHeight, - getWidgetHeight(widget, false), - ); - - //update positions - currWidgets[widget.widgetId] = { - ...widget, - topRow, - bottomRow: topRow + height, - leftColumn: leftColumn, - rightColumn: rightColumn, - }; - - //call convertAutoWidgetToFixed to recursively calculate positions - currWidgets = convertAutoWidgetToFixed(currWidgets, widget.widgetId, false); - children.push(widget.widgetId); - currentAvailableLeftColumn = rightColumn; - maxBottomRow = Math.max(maxBottomRow, currentBottomRow + height); - } - - return { - convertedWidgets: currWidgets, - children, - currentAvailableLeftColumn, - maxBottomRow, - }; -} - -/** - * return the leftColumn required to add the widgets based - * on totalWidth of all widgets and alignment - * @param width - * @param totalWidth - * @param flexLayerAlignment - * @returns - */ -function getCalculatedLeftColumn( - width: number, - totalWidth: number, - flexLayerAlignment: FlexLayerAlignment, -): number { - if ( - flexLayerAlignment === FlexLayerAlignment.Start || - flexLayerAlignment === FlexLayerAlignment.None - ) { - return 0; - } else if (flexLayerAlignment === FlexLayerAlignment.Center) { - return Math.ceil((totalWidth - width) / 2); - } else { - return totalWidth - width; - } -} - -/** - * Get topRow based on vertical alignment of widget - * @param currentBottomRow - * @param flexVerticalAlignment - * @param layerHeight - * @param widgetHeight - * @returns - */ -function getTopRowFromVerticalAlignment( - currentBottomRow: number, - flexVerticalAlignment: FlexVerticalAlignment | undefined, - layerHeight: number, - widgetHeight: number, -): number { - if (!flexVerticalAlignment || !layerHeight || layerHeight <= widgetHeight) { - return currentBottomRow; - } - - if (flexVerticalAlignment === FlexVerticalAlignment.Center) { - return Math.floor(currentBottomRow + (layerHeight - widgetHeight) / 2); - } else if (flexVerticalAlignment === FlexVerticalAlignment.Bottom) { - return currentBottomRow + layerHeight - widgetHeight; - } else { - return currentBottomRow; - } -} From 0bfdf31061e12c8c78263b9c1ee2390f4bbf4029 Mon Sep 17 00:00:00 2001 From: rahulramesha Date: Wed, 8 Mar 2023 22:43:39 +0530 Subject: [PATCH 618/708] fix restoring snapshot spinner message --- app/client/src/ce/constants/messages.ts | 1 + .../Editor/CanvasLayoutConversion/hooks/useSnapShotFlow.ts | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/client/src/ce/constants/messages.ts b/app/client/src/ce/constants/messages.ts index 51bb8c0f7e40..5200e99fa6c9 100644 --- a/app/client/src/ce/constants/messages.ts +++ b/app/client/src/ce/constants/messages.ts @@ -1476,6 +1476,7 @@ export const SAVE_SNAPSHOT_TEXT = () => "We will create a snapshot of your whole app before the conversion, so that you can go back if auto layout is just not right for you"; export const CREATE_SNAPSHOT = () => "Creating a snapshot"; export const CONVERTING_APP = () => "Converting your app"; +export const RESTORING_SNAPSHOT = () => "Removing changes made"; export const REFRESH_THE_APP = () => "REFRESH THE APP"; export const CONVERT_ANYWAYS = () => "CONVERT ANYWAYS"; export const CONVERSION_SUCCESS_HEADER = () => "All done"; diff --git a/app/client/src/pages/Editor/CanvasLayoutConversion/hooks/useSnapShotFlow.ts b/app/client/src/pages/Editor/CanvasLayoutConversion/hooks/useSnapShotFlow.ts index aa833e2df869..77a2e79f76e3 100644 --- a/app/client/src/pages/Editor/CanvasLayoutConversion/hooks/useSnapShotFlow.ts +++ b/app/client/src/pages/Editor/CanvasLayoutConversion/hooks/useSnapShotFlow.ts @@ -3,6 +3,7 @@ import { CANCEL_DIALOG, createMessage, CREATE_SNAPSHOT, + RESTORING_SNAPSHOT, SNAPSHOT_LABEL, USE_SNAPSHOT, USE_SNAPSHOT_TEXT, @@ -67,7 +68,7 @@ export const useSnapShotFlow = ( }, [CONVERSION_STATES.RESTORING_SNAPSHOT_SPINNER]: { cancelButtonText: createMessage(CANCEL_DIALOG), - spinner: createMessage(CREATE_SNAPSHOT), + spinner: createMessage(RESTORING_SNAPSHOT), }, ...useCommonConversionFlows(dispatch, onCancel), }; From 844928914abbdbfc37c1b132c288a6ea88d52ad4 Mon Sep 17 00:00:00 2001 From: rahulramesha Date: Wed, 8 Mar 2023 22:44:41 +0530 Subject: [PATCH 619/708] fix margin at the top of Use snapshot banner --- .../src/pages/Editor/WidgetsEditor/CanvasContainer.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx index 3c555e549a77..66a8a3a92e72 100644 --- a/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx +++ b/app/client/src/pages/Editor/WidgetsEditor/CanvasContainer.tsx @@ -149,9 +149,12 @@ function CanvasContainer() { } className={classNames({ [`${getCanvasClassName()} scrollbar-thin`]: true, - "mt-0": !shouldHaveTopMargin, - "mt-4": showCanvasTopSection, - "mt-8": shouldHaveTopMargin && !showCanvasTopSection, + "mt-0": shouldShowSnapShotBanner || !shouldHaveTopMargin, + "mt-4": !shouldShowSnapShotBanner && showCanvasTopSection, + "mt-8": + !shouldShowSnapShotBanner && + shouldHaveTopMargin && + !showCanvasTopSection, })} id={"canvas-viewport"} key={currentPageId} From c120a50887540270d31452e910d7623b40d945c4 Mon Sep 17 00:00:00 2001 From: rahulramesha Date: Thu, 9 Mar 2023 00:21:34 +0530 Subject: [PATCH 620/708] add Beta card to conversion dialog title --- .../editorComponents/form/FormDialogComponent.tsx | 2 ++ .../CanvasLayoutConversion/ConversionButton.tsx | 12 +++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/app/client/src/components/editorComponents/form/FormDialogComponent.tsx b/app/client/src/components/editorComponents/form/FormDialogComponent.tsx index ef1df37abde4..6e823657d779 100644 --- a/app/client/src/components/editorComponents/form/FormDialogComponent.tsx +++ b/app/client/src/components/editorComponents/form/FormDialogComponent.tsx @@ -64,6 +64,7 @@ type FormDialogComponentProps = { tabs?: any[]; options?: any[]; placeholder?: string; + getHeader?: () => ReactNode; }; const getTabs = ( @@ -141,6 +142,7 @@ export function FormDialogComponent(props: FormDialogComponentProps) { { + return ( +
+

{createMessage(titleText)}

+ +
+ ); + }; + return ( (useConversionForm, { @@ -33,8 +43,8 @@ export function ConversionButton() { })} canEscapeKeyClose={false} canOutsideClickClose={false} + getHeader={header} isCloseButtonShown={false} - title={createMessage(titleText)} trigger={ -
- ); - })} -
-
- - ) : null} - {selectedOption === AppPositioningTypes.FIXED && ( - <> - Canvas Size - - - )} - - ); -} diff --git a/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx b/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx index d0f5f4ab029a..a48a2264dc80 100644 --- a/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx +++ b/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx @@ -1,21 +1,28 @@ import * as Sentry from "@sentry/react"; import React from "react"; -import { useDispatch } from "react-redux"; +import { useDispatch, useSelector } from "react-redux"; import { PopoverPosition } from "@blueprintjs/core"; import { Button, Category, Size, TooltipComponent } from "design-system-old"; import { openAppSettingsPaneAction } from "actions/appSettingsPaneActions"; -import { AppPositionTypeControl } from "../AppPositionTypeControl"; import ConversionButton from "../CanvasLayoutConversion/ConversionButton"; - +import { MainContainerLayoutControl } from "../MainContainerLayoutControl"; +import { isAutoLayoutEnabled } from "selectors/editorSelectors"; +import styled from "styled-components"; +import { Colors } from "constants/Colors"; + +const Title = styled.p` + color: ${Colors.GRAY_800}; +`; export function CanvasPropertyPane() { const dispatch = useDispatch(); const openAppSettingsPane = () => { dispatch(openAppSettingsPaneAction()); }; + const isAutoLayoutFeatureEnabled = useSelector(isAutoLayoutEnabled); return (
@@ -23,8 +30,10 @@ export function CanvasPropertyPane() {
- - + Canvas Size + + {isAutoLayoutFeatureEnabled && } + From 7638727670458107d7528df09afcb4e118effffe Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Tue, 21 Mar 2023 13:24:52 +0530 Subject: [PATCH 695/708] display canvas size only fixed layout. --- .../pages/Editor/CanvasPropertyPane/index.tsx | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx b/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx index a48a2264dc80..8f383f67ef8e 100644 --- a/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx +++ b/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx @@ -9,7 +9,10 @@ import { Button, Category, Size, TooltipComponent } from "design-system-old"; import { openAppSettingsPaneAction } from "actions/appSettingsPaneActions"; import ConversionButton from "../CanvasLayoutConversion/ConversionButton"; import { MainContainerLayoutControl } from "../MainContainerLayoutControl"; -import { isAutoLayoutEnabled } from "selectors/editorSelectors"; +import { + getIsAutoLayout, + isAutoLayoutEnabled, +} from "selectors/editorSelectors"; import styled from "styled-components"; import { Colors } from "constants/Colors"; @@ -23,15 +26,20 @@ export function CanvasPropertyPane() { dispatch(openAppSettingsPaneAction()); }; const isAutoLayoutFeatureEnabled = useSelector(isAutoLayoutEnabled); - + const isAutoLayout = useSelector(getIsAutoLayout); return (

Properties

- Canvas Size - + {!isAutoLayout && ( + <> + Canvas Size + + + )} + {isAutoLayoutFeatureEnabled && } Date: Tue, 21 Mar 2023 15:41:04 +0530 Subject: [PATCH 696/708] Re enable canvas scroll. --- app/client/src/pages/Editor/CanvasPropertyPane/index.tsx | 2 -- .../src/pages/common/CanvasArenas/CanvasSelectionArena.tsx | 6 +++--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx b/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx index 8f383f67ef8e..c5df684bbc1a 100644 --- a/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx +++ b/app/client/src/pages/Editor/CanvasPropertyPane/index.tsx @@ -39,9 +39,7 @@ export function CanvasPropertyPane() { )} - {isAutoLayoutFeatureEnabled && } - diff --git a/app/client/src/pages/common/CanvasArenas/CanvasSelectionArena.tsx b/app/client/src/pages/common/CanvasArenas/CanvasSelectionArena.tsx index abff4b0db310..866f776f0b4f 100644 --- a/app/client/src/pages/common/CanvasArenas/CanvasSelectionArena.tsx +++ b/app/client/src/pages/common/CanvasArenas/CanvasSelectionArena.tsx @@ -152,9 +152,11 @@ export function CanvasSelectionArena({ snapRows, canExtend, ); + const isAutoLayout = useSelector(getIsAutoLayout); useEffect(() => { if ( appMode === APP_MODE.EDIT && + !isAutoLayout && !isDragging && slidingArenaRef.current && stickyCanvasRef.current @@ -482,10 +484,8 @@ export function CanvasSelectionArena({ ]); // Resizing state still shows selection arena to aid with scroll behavior - const isAutoLayout = useSelector(getIsAutoLayout); const shouldShow = - appMode === APP_MODE.EDIT && - !(isAutoLayout || isDragging || isPreviewMode || dropDisabled); + appMode === APP_MODE.EDIT && !(isDragging || isPreviewMode || dropDisabled); const canvasRef = React.useRef({ slidingArenaRef, From d31a9ac8d8d420eb824b776acdec46914b4b2bfe Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Tue, 21 Mar 2023 15:41:46 +0530 Subject: [PATCH 697/708] fix scroll not showing up in deploy mode. --- app/client/src/widgets/CanvasWidget.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/client/src/widgets/CanvasWidget.tsx b/app/client/src/widgets/CanvasWidget.tsx index 9422548f1338..88b07c6ffd27 100644 --- a/app/client/src/widgets/CanvasWidget.tsx +++ b/app/client/src/widgets/CanvasWidget.tsx @@ -165,7 +165,7 @@ class CanvasWidget extends ContainerWidget { height = snapRows * GridDefaults.DEFAULT_GRID_ROW_HEIGHT; const style: CSSProperties = { width: "100%", - height: this.props.useAutoLayout ? "100%" : `${height}px`, + height: `${height}px`, background: "none", position: "relative", }; From 7adacc14d21637f7431ae98db671914cc0477f30 Mon Sep 17 00:00:00 2001 From: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Date: Tue, 21 Mar 2023 16:28:47 +0530 Subject: [PATCH 698/708] Release merge fixes. --- .../appsmith/autoLayout/AutoLayoutLayer.tsx | 1 + .../src/resizable/autolayoutresize/index.tsx | 34 ++++++++----------- app/client/src/resizable/common.tsx | 16 ++++----- app/client/src/widgets/CanvasResizer.tsx | 6 ++-- 4 files changed, 27 insertions(+), 30 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx index 4d822af67a31..154cc187e746 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutLayer.tsx @@ -1,3 +1,4 @@ +import { GridDefaults } from "constants/WidgetConstants"; import type { ReactNode } from "react"; import React from "react"; import styled from "styled-components"; diff --git a/app/client/src/resizable/autolayoutresize/index.tsx b/app/client/src/resizable/autolayoutresize/index.tsx index 3951f746fcff..69e0f7475621 100644 --- a/app/client/src/resizable/autolayoutresize/index.tsx +++ b/app/client/src/resizable/autolayoutresize/index.tsx @@ -1,6 +1,6 @@ import { reflowMoveAction, stopReflowAction } from "actions/reflowActions"; import { isHandleResizeAllowed } from "components/editorComponents/ResizableUtils"; -import { OccupiedSpace } from "constants/CanvasEditorConstants"; +import type { OccupiedSpace } from "constants/CanvasEditorConstants"; import { GridDefaults, WIDGET_PADDING, @@ -9,16 +9,15 @@ import { import React, { useEffect, useMemo, useRef, useState } from "react"; import { useDispatch, useSelector } from "react-redux"; import { Spring } from "react-spring"; -import { +import type { MovementLimitMap, - ReflowDirection, ReflowedSpace, ReflowedSpaceMap, } from "reflow/reflowTypes"; +import { ReflowDirection } from "reflow/reflowTypes"; +import type { DimensionUpdateProps, ResizableProps } from "resizable/common"; import { - DimensionUpdateProps, ResizableHandle, - ResizableProps, RESIZE_BORDER_BUFFER, ResizeWrapper, } from "resizable/common"; @@ -177,10 +176,8 @@ export function ReflowResizable(props: ResizableProps) { resizedPositions: OccupiedSpace, rect: DimensionUpdateProps, ) => { - const { - canResizeHorizontally, - canResizeVertically, - } = props.getResizedPositions(resizedPositions); + const { canResizeHorizontally, canResizeVertically } = + props.getResizedPositions(resizedPositions); const canResize = canResizeHorizontally || canResizeVertically; if (canResize) { @@ -205,9 +202,8 @@ export function ReflowResizable(props: ResizableProps) { movementLimitMap && movementLimitMap[resizedPositions.id] ) { - ({ canHorizontalMove, canVerticalMove } = movementLimitMap[ - resizedPositions.id - ]); + ({ canHorizontalMove, canVerticalMove } = + movementLimitMap[resizedPositions.id]); } if (isAutoLayout && hasFillChild) { canHorizontalMove = triggerAutoLayoutBasedReflow(resizedPositions); @@ -614,13 +610,13 @@ export function ReflowResizable(props: ResizableProps) { maxHeight: (props.maxDynamicHeight || WidgetHeightLimits.MAX_HEIGHT_IN_ROWS) * GridDefaults.DEFAULT_GRID_ROW_HEIGHT, - transform: `translate3d(${(newDimensions.reflectPosition - ? newDimensions.x - : 0) - - RESIZE_BORDER_BUFFER / 2}px,${(newDimensions.reflectPosition - ? newDimensions.y - : 0) - - RESIZE_BORDER_BUFFER / 2}px,0)`, + transform: `translate3d(${ + (newDimensions.reflectPosition ? newDimensions.x : 0) - + RESIZE_BORDER_BUFFER / 2 + }px,${ + (newDimensions.reflectPosition ? newDimensions.y : 0) - + RESIZE_BORDER_BUFFER / 2 + }px,0)`, }} > {(_props) => ( diff --git a/app/client/src/resizable/common.tsx b/app/client/src/resizable/common.tsx index 5bf0e8461ea5..5c9abcbaa2cb 100644 --- a/app/client/src/resizable/common.tsx +++ b/app/client/src/resizable/common.tsx @@ -1,11 +1,13 @@ -import { OccupiedSpace } from "constants/CanvasEditorConstants"; +import type { OccupiedSpace } from "constants/CanvasEditorConstants"; import { Colors } from "constants/Colors"; -import React, { ReactNode } from "react"; +import type { ReactNode } from "react"; +import React from "react"; import { animated } from "react-spring"; import { useDrag } from "react-use-gesture"; -import { GridProps, ReflowDirection } from "reflow/reflowTypes"; -import styled, { StyledComponent } from "styled-components"; -import { +import type { GridProps, ReflowDirection } from "reflow/reflowTypes"; +import styled from "styled-components"; +import type { StyledComponent } from "styled-components"; +import type { LayoutDirection, ResponsiveBehavior, } from "utils/autoLayout/constants"; @@ -177,9 +179,7 @@ export type ResizableProps = { componentHeight: number; children: ReactNode; updateBottomRow: (bottomRow: number) => void; - getResizedPositions: ( - resizedPositions: OccupiedSpace, - ) => { + getResizedPositions: (resizedPositions: OccupiedSpace) => { canResizeHorizontally: boolean; canResizeVertically: boolean; }; diff --git a/app/client/src/widgets/CanvasResizer.tsx b/app/client/src/widgets/CanvasResizer.tsx index 9cd8f056ca87..ea035ed8e7fe 100644 --- a/app/client/src/widgets/CanvasResizer.tsx +++ b/app/client/src/widgets/CanvasResizer.tsx @@ -97,7 +97,7 @@ export function CanvasResizer({ // Handle the mousedown event // that's triggered when user drags the resizer - const mouseDownHandler = function(e: any) { + const mouseDownHandler = function (e: any) { maxWidth = wrapperElement.offsetWidth - AUTOLAYOUT_RESIZER_WIDTH_BUFFER; // Get the current mouse position @@ -117,7 +117,7 @@ export function CanvasResizer({ // e.stopPropagation(); }; - const mouseMoveHandler = function(e: any) { + const mouseMoveHandler = function (e: any) { // How far the mouse has been moved // const multiplier = rightHandle ? 2 : -2; const multiplier = 2; @@ -135,7 +135,7 @@ export function CanvasResizer({ // e.stopPropagation(); }; - const mouseUpHandler = function(e: any) { + const mouseUpHandler = function (e: any) { // Remove the handlers of `mousemove` and `mouseup` mouseMoveHandler(e); dispatch(setAutoCanvasResizing(false)); From 00dc05ba3512f7b02ac6d33e2b8535b01cdccd79 Mon Sep 17 00:00:00 2001 From: rahulramesha Date: Tue, 21 Mar 2023 17:53:01 +0530 Subject: [PATCH 699/708] fix restore snapshot flow, by updating appPositioning type --- app/client/src/sagas/SnapshotSagas.ts | 27 ++++++++++++++++---- app/client/src/selectors/editorSelectors.tsx | 2 +- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/app/client/src/sagas/SnapshotSagas.ts b/app/client/src/sagas/SnapshotSagas.ts index 0f7486d1c560..35f64f0a8cc8 100644 --- a/app/client/src/sagas/SnapshotSagas.ts +++ b/app/client/src/sagas/SnapshotSagas.ts @@ -4,15 +4,20 @@ import { } from "actions/autoLayoutActions"; import type { ApiResponse } from "api/ApiResponses"; import ApplicationApi from "api/ApplicationApi"; +import type { PageDefaultMeta } from "api/ApplicationApi"; import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants"; import log from "loglevel"; import type { SnapShotDetails } from "reducers/uiReducers/layoutConversionReducer"; import { CONVERSION_STATES } from "reducers/uiReducers/layoutConversionReducer"; import { all, call, put, select, takeLatest } from "redux-saga/effects"; -import { getCurrentApplicationId } from "selectors/editorSelectors"; +import { + getAppPositioningType, + getCurrentApplicationId, +} from "selectors/editorSelectors"; import { getLogToSentryFromResponse } from "utils/helpers"; import { validateResponse } from "./ErrorSagas"; -import type { Application } from "ce/reducers/uiReducers/applicationsReducer"; +import { updateApplicationLayoutType } from "./AutoLayoutUpdateSagas"; +import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer"; //Saga to create application snapshot export function* createSnapshotSaga() { @@ -67,13 +72,17 @@ export function* fetchSnapshotSaga() { //Saga to restore application snapshot function* restoreApplicationFromSnapshotSaga() { - let response: ApiResponse | undefined; + let response: ApiResponse | undefined; try { const applicationId: string = yield select(getCurrentApplicationId); response = yield ApplicationApi.restoreApplicationFromSnapshot({ applicationId, }); + const currentAppPositioningType: AppPositioningTypes = yield select( + getAppPositioningType, + ); + const isValidResponse: boolean = yield validateResponse( response, false, @@ -81,11 +90,11 @@ function* restoreApplicationFromSnapshotSaga() { ); // update the pages list temporarily with incomplete data. - if (response?.data.pages) { + if (response?.data?.pages) { yield put({ type: ReduxActionTypes.FETCH_PAGE_LIST_SUCCESS, payload: { - pages: response.data.pages.map((page) => ({ + pages: response.data.pages.map((page: PageDefaultMeta) => ({ pageId: page.id, isDefault: page.isDefault, })), @@ -94,6 +103,14 @@ function* restoreApplicationFromSnapshotSaga() { }); } + //update layout positioning type from + yield call( + updateApplicationLayoutType, + currentAppPositioningType === AppPositioningTypes.FIXED + ? AppPositioningTypes.AUTO + : AppPositioningTypes.FIXED, + ); + if (isValidResponse) { //update conversion form state to success yield put( diff --git a/app/client/src/selectors/editorSelectors.tsx b/app/client/src/selectors/editorSelectors.tsx index 62c957ce5a05..116ced0379ae 100644 --- a/app/client/src/selectors/editorSelectors.tsx +++ b/app/client/src/selectors/editorSelectors.tsx @@ -242,7 +242,7 @@ const defaultLayout: AppLayoutConfig = { const getAppLayout = (state: AppState) => state.ui.applications.currentApplication?.appLayout || defaultLayout; -const getAppPositioningType = (state: AppState) => { +export const getAppPositioningType = (state: AppState) => { if ( state.ui.applications?.currentApplication?.applicationDetail?.appPositioning ?.type From 69ef081d67785dd350975d02f5c81798a1c48daf Mon Sep 17 00:00:00 2001 From: rahulramesha Date: Tue, 21 Mar 2023 18:46:35 +0530 Subject: [PATCH 700/708] implement widthInPercentage while converting by using positionUtils --- .../utils/DSLConversions/autoToFixedLayout.ts | 1 + .../utils/DSLConversions/fixedToAutoLayout.ts | 66 ++++++++++++------- .../tests/fixedToAutoLayout.test.ts | 12 ---- .../src/utils/autoLayout/AutoLayoutUtils.ts | 33 ++++++++-- .../utils/autoLayout/positionUtils.test.ts | 51 +++++++------- .../src/utils/autoLayout/positionUtils.ts | 24 ++++--- 6 files changed, 115 insertions(+), 72 deletions(-) diff --git a/app/client/src/utils/DSLConversions/autoToFixedLayout.ts b/app/client/src/utils/DSLConversions/autoToFixedLayout.ts index e32d84d4673a..2c9ada366500 100644 --- a/app/client/src/utils/DSLConversions/autoToFixedLayout.ts +++ b/app/client/src/utils/DSLConversions/autoToFixedLayout.ts @@ -30,6 +30,7 @@ const deletedResponsiveProperties = [ "responsiveBehavior", "alignment", "flexVerticalAlignment", + "widthInPercentage", ]; /** diff --git a/app/client/src/utils/DSLConversions/fixedToAutoLayout.ts b/app/client/src/utils/DSLConversions/fixedToAutoLayout.ts index 705c6fe45fe6..bccd59a1b945 100644 --- a/app/client/src/utils/DSLConversions/fixedToAutoLayout.ts +++ b/app/client/src/utils/DSLConversions/fixedToAutoLayout.ts @@ -1,4 +1,3 @@ -import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; import { GridDefaults, layoutConfigurations, @@ -15,6 +14,7 @@ import { ResponsiveBehavior, } from "utils/autoLayout/constants"; import WidgetFactory from "utils/WidgetFactory"; +import type { WidgetProps } from "widgets/BaseWidget"; import type { DSLWidget } from "widgets/constants"; const unHandledWidgets = ["LIST_WIDGET", "FORM_WIDGET", "MODAL_WIDGET"]; @@ -40,6 +40,7 @@ export default function convertDSLtoAutoAndUpdatePositions( normalizedAutoDSL, MAIN_CONTAINER_WIDGET_ID, canvasWidth, + true, ); const alteredAutoDSL: DSLWidget = CanvasWidgetsNormalizer.denormalize( @@ -199,32 +200,12 @@ function getNextLayer(currWidgets: DSLWidget[]): { //Recursively call convertDSLtoAuto to convert Children Widgets for (const widget of widgetsInLayer) { - let currWidget = + const currWidget = unHandledWidgets.indexOf(widget.type) < 0 ? convertDSLtoAuto(widget) : { ...widget, positioning: Positioning.Fixed }; - const widgetConfig = WidgetFactory.widgetConfigMap.get(currWidget.type); - //get Responsive Behaviour - const responsiveBehavior = - (widgetConfig?.responsiveBehavior as ResponsiveBehavior) || - ResponsiveBehavior.Hug; - - if (widgetConfig?.dynamicHeight && widgetConfig.isCanvas) { - currWidget.dynamicHeight = widgetConfig.dynamicHeight; - } - - //Add widget specific property Defaults, for autoLayout widget - const { disabledPropsDefaults } = - WidgetFactory.getWidgetAutoLayoutConfig(currWidget.type) || {}; - if (disabledPropsDefaults) { - currWidget = { - ...currWidget, - ...disabledPropsDefaults, - }; - } - //get minWidth of the type - currWidget.minWidth = widgetConfig?.minWidth || FILL_WIDGET_MIN_WIDTH; + const propUpdates = getPropertyUpdatesBasedOnConfig(currWidget); //Get Alignment of the Widget alignment = alignmentMap[currWidget.widgetId] || FlexLayerAlignment.Start; @@ -236,7 +217,7 @@ function getNextLayer(currWidgets: DSLWidget[]): { modifiedWidgetsInLayer.push({ ...currWidget, - responsiveBehavior, + ...propUpdates, alignment, flexVerticalAlignment, }); @@ -721,3 +702,40 @@ function areWidgetsOverlapping(r1: DSLWidget, r2: DSLWidget) { r2.bottomRow <= r1.topRow ); } + +/** + * Methods to get all the property updates required based on teh config + * @param widget + * @returns + */ +function getPropertyUpdatesBasedOnConfig(widget: DSLWidget) { + const widgetConfig = WidgetFactory.widgetConfigMap.get(widget.type); + + let propertyUpdates: Partial = {}; + + //get Responsive Behaviour + propertyUpdates.responsiveBehavior = + (widgetConfig?.responsiveBehavior as ResponsiveBehavior) || + ResponsiveBehavior.Hug; + + if (widgetConfig?.dynamicHeight && widgetConfig.isCanvas) { + propertyUpdates.dynamicHeight = widgetConfig.dynamicHeight; + } + + //Add widget specific property Defaults, for autoLayout widget + const { disabledPropsDefaults } = + WidgetFactory.getWidgetAutoLayoutConfig(widget.type) || {}; + if (disabledPropsDefaults) { + propertyUpdates = { + ...propertyUpdates, + ...disabledPropsDefaults, + }; + } + + //get minWidth of the type + if (widgetConfig?.minWidth) { + propertyUpdates.minWidth = widgetConfig.minWidth; + } + + return propertyUpdates; +} diff --git a/app/client/src/utils/DSLConversions/tests/fixedToAutoLayout.test.ts b/app/client/src/utils/DSLConversions/tests/fixedToAutoLayout.test.ts index 8db90b63edac..1a2621035168 100644 --- a/app/client/src/utils/DSLConversions/tests/fixedToAutoLayout.test.ts +++ b/app/client/src/utils/DSLConversions/tests/fixedToAutoLayout.test.ts @@ -239,7 +239,6 @@ describe("test fixed to Auto Conversion methods", () => { boxShadow: "none", flexVerticalAlignment: "start", leftColumn: 0, - minWidth: 450, responsiveBehavior: "hug", rightColumn: 16, topRow: 0, @@ -252,7 +251,6 @@ describe("test fixed to Auto Conversion methods", () => { bottomRow: 9, flexVerticalAlignment: "end", leftColumn: 23, - minWidth: 450, responsiveBehavior: "hug", rightColumn: 43, topRow: 2, @@ -265,7 +263,6 @@ describe("test fixed to Auto Conversion methods", () => { bottomRow: 4, flexVerticalAlignment: "start", leftColumn: 48, - minWidth: 450, responsiveBehavior: "hug", rightColumn: 64, topRow: 0, @@ -321,7 +318,6 @@ describe("test fixed to Auto Conversion methods", () => { ], flexVerticalAlignment: "start", leftColumn: 2, - minWidth: 450, parentId: "0", positioning: "fixed", responsiveBehavior: "hug", @@ -336,7 +332,6 @@ describe("test fixed to Auto Conversion methods", () => { bottomRow: 30, flexVerticalAlignment: "center", leftColumn: 30, - minWidth: 450, parentId: "0", parentRowSpace: 10, responsiveBehavior: "hug", @@ -365,7 +360,6 @@ describe("test fixed to Auto Conversion methods", () => { bottomRow: 4, flexVerticalAlignment: "start", leftColumn: 2, - minWidth: 450, parentId: "se4m3djd2t", responsiveBehavior: "hug", rightColumn: 18, @@ -379,7 +373,6 @@ describe("test fixed to Auto Conversion methods", () => { bottomRow: 11, flexVerticalAlignment: "start", leftColumn: 33, - minWidth: 450, parentId: "se4m3djd2t", responsiveBehavior: "hug", rightColumn: 49, @@ -422,7 +415,6 @@ describe("test fixed to Auto Conversion methods", () => { ], flexVerticalAlignment: "end", leftColumn: 0, - minWidth: 450, parentId: "b6wgydyko8", responsiveBehavior: "hug", rightColumn: 24, @@ -443,7 +435,6 @@ describe("test fixed to Auto Conversion methods", () => { bottomRow: 4, flexVerticalAlignment: "start", leftColumn: 2, - minWidth: 450, parentId: "vap4aivehm", responsiveBehavior: "hug", rightColumn: 18, @@ -457,7 +448,6 @@ describe("test fixed to Auto Conversion methods", () => { bottomRow: 11, flexVerticalAlignment: "start", leftColumn: 33, - minWidth: 450, parentId: "vap4aivehm", responsiveBehavior: "hug", rightColumn: 49, @@ -500,7 +490,6 @@ describe("test fixed to Auto Conversion methods", () => { ], flexVerticalAlignment: "start", leftColumn: 36, - minWidth: 450, parentId: "b6wgydyko8", renderMode: "CANVAS", responsiveBehavior: "hug", @@ -540,7 +529,6 @@ describe("test fixed to Auto Conversion methods", () => { ], flexVerticalAlignment: "start", leftColumn: 38, - minWidth: 450, parentId: "0", responsiveBehavior: "hug", rightColumn: 62, diff --git a/app/client/src/utils/autoLayout/AutoLayoutUtils.ts b/app/client/src/utils/autoLayout/AutoLayoutUtils.ts index b1579cef6a8b..c5a4360868ec 100644 --- a/app/client/src/utils/autoLayout/AutoLayoutUtils.ts +++ b/app/client/src/utils/autoLayout/AutoLayoutUtils.ts @@ -98,6 +98,7 @@ export function alterLayoutForMobile( parentId: string, canvasWidth: number, mainCanvasWidth: number, + firstTimeDSLUpdate = false, ): CanvasWidgetsReduxState { let widgets = { ...allWidgets }; const parent = widgets[parentId]; @@ -148,9 +149,21 @@ export function alterLayoutForMobile( mainCanvasWidth, ); widgets[child] = widget; - widgets = updateWidgetPositions(widgets, child, true, mainCanvasWidth); + widgets = updateWidgetPositions( + widgets, + child, + true, + mainCanvasWidth, + firstTimeDSLUpdate, + ); } - widgets = updateWidgetPositions(widgets, parentId, true, mainCanvasWidth); + widgets = updateWidgetPositions( + widgets, + parentId, + true, + mainCanvasWidth, + firstTimeDSLUpdate, + ); return widgets; } @@ -158,6 +171,7 @@ export function alterLayoutForDesktop( allWidgets: CanvasWidgetsReduxState, parentId: string, mainCanvasWidth: number, + firstTimeDSLUpdate = false, ): CanvasWidgetsReduxState { let widgets = { ...allWidgets }; const parent = widgets[parentId]; @@ -166,9 +180,20 @@ export function alterLayoutForDesktop( if (!isStack(allWidgets, parent)) return widgets; if (!children || !children.length) return widgets; - widgets = updateWidgetPositions(widgets, parentId, false, mainCanvasWidth); + widgets = updateWidgetPositions( + widgets, + parentId, + false, + mainCanvasWidth, + firstTimeDSLUpdate, + ); for (const child of children) { - widgets = alterLayoutForDesktop(widgets, child, mainCanvasWidth); + widgets = alterLayoutForDesktop( + widgets, + child, + mainCanvasWidth, + firstTimeDSLUpdate, + ); } return widgets; } diff --git a/app/client/src/utils/autoLayout/positionUtils.test.ts b/app/client/src/utils/autoLayout/positionUtils.test.ts index 68e70fa41692..789b88593573 100644 --- a/app/client/src/utils/autoLayout/positionUtils.test.ts +++ b/app/client/src/utils/autoLayout/positionUtils.test.ts @@ -77,29 +77,31 @@ describe("test PositionUtils methods", () => { { id: "3", align: FlexLayerAlignment.End }, ], }; - expect(extractAlignmentInfo(widgets, layer, false, 64, 1)).toEqual({ - info: [ - { - alignment: FlexLayerAlignment.Start, - columns: 40, - children: [ - { widget: widgets["1"], columns: 16, rows: 4 }, - { widget: widgets["2"], columns: 24, rows: 7 }, - ], - }, - { - alignment: FlexLayerAlignment.Center, - columns: 0, - children: [], - }, - { - alignment: FlexLayerAlignment.End, - columns: 16, - children: [{ widget: widgets["3"], columns: 16, rows: 7 }], - }, - ], - fillWidgetLength: 64, - }); + expect(extractAlignmentInfo(widgets, layer, false, 64, 1, false)).toEqual( + { + info: [ + { + alignment: FlexLayerAlignment.Start, + columns: 40, + children: [ + { widget: widgets["1"], columns: 16, rows: 4 }, + { widget: widgets["2"], columns: 24, rows: 7 }, + ], + }, + { + alignment: FlexLayerAlignment.Center, + columns: 0, + children: [], + }, + { + alignment: FlexLayerAlignment.End, + columns: 16, + children: [{ widget: widgets["3"], columns: 16, rows: 7 }], + }, + ], + fillWidgetLength: 64, + }, + ); }); it("should calculate columns for fill widgets", () => { const widgets = { @@ -160,7 +162,8 @@ describe("test PositionUtils methods", () => { ], }; expect( - extractAlignmentInfo(widgets, layer, false, 64, 1).fillWidgetLength, + extractAlignmentInfo(widgets, layer, false, 64, 1, false) + .fillWidgetLength, ).toEqual(24); }); }); diff --git a/app/client/src/utils/autoLayout/positionUtils.ts b/app/client/src/utils/autoLayout/positionUtils.ts index 854c24fed408..5f6d522d0f5a 100644 --- a/app/client/src/utils/autoLayout/positionUtils.ts +++ b/app/client/src/utils/autoLayout/positionUtils.ts @@ -43,6 +43,7 @@ export function updateWidgetPositions( parentId: string, isMobile = false, mainCanvasWidth: number, + firstTimeDSLUpdate = false, ): CanvasWidgetsReduxState { let widgets = { ...allWidgets }; try { @@ -78,6 +79,7 @@ export function updateWidgetPositions( isMobile, mainCanvasWidth, columnSpace, + firstTimeDSLUpdate, ); widgets = payload.widgets; height += payload.height; @@ -117,6 +119,7 @@ export function updateWidgetPositions( parent.parentId, isMobile, mainCanvasWidth, + firstTimeDSLUpdate, ); } return widgets; @@ -133,6 +136,7 @@ function calculateWidgetPositions( isMobile = false, mainCanvasWidth: number, columnSpace: number, + firstTimeDSLUpdate: boolean, ): { height: number; widgets: CanvasWidgetsReduxState } { /** * Get information break down on each alignment within the layer. @@ -145,6 +149,7 @@ function calculateWidgetPositions( isMobile, mainCanvasWidth, columnSpace, + firstTimeDSLUpdate, ); /** * Check if this layer is wrapped by css flex. @@ -287,6 +292,7 @@ export function extractAlignmentInfo( isMobile: boolean, mainCanvasWidth: number, columnSpace: number, + firstTimeDSLUpdate: boolean, ): { info: AlignmentInfo[]; fillWidgetLength: number } { const startChildren: AlignmentChildren[] = [], centerChildren: AlignmentChildren[] = [], @@ -314,14 +320,14 @@ export function extractAlignmentInfo( // For hug widgets with horizontal resizing enabled, // make sure the width is not getting greater than user defined width - if ( - !isFillWidget && - !disableResizeHandles?.horizontal && - widget.widthInPercentage - ) { - const userDefinedWidth = widget.widthInPercentage * mainCanvasWidth; - if (columns * columnSpace > userDefinedWidth) { - columns = (widget.widthInPercentage * mainCanvasWidth) / columnSpace; + if (!isFillWidget && !disableResizeHandles?.horizontal) { + if (widget.widthInPercentage) { + const userDefinedWidth = widget.widthInPercentage * mainCanvasWidth; + if (columns * columnSpace > userDefinedWidth) { + columns = (widget.widthInPercentage * mainCanvasWidth) / columnSpace; + } + } else if (firstTimeDSLUpdate) { + widget.widthInPercentage = (columns * columnSpace) / mainCanvasWidth; } } @@ -679,6 +685,7 @@ export function updatePositionsOfParentAndSiblings( layerIndex: number, isMobile: boolean, mainCanvasWidth: number, + firstTimeDSLUpdate = false, ): CanvasWidgetsReduxState { let widgets = { ...allWidgets }; const parent = widgets[parentId]; @@ -712,6 +719,7 @@ export function updatePositionsOfParentAndSiblings( widgetId, isMobile, mainCanvasWidth, + firstTimeDSLUpdate, ); } From 4f660ef08374789fa2b1e8c49ee395f7504a27e9 Mon Sep 17 00:00:00 2001 From: rahulramesha Date: Tue, 21 Mar 2023 22:54:58 +0530 Subject: [PATCH 701/708] prevent saving snapshot for single empty page --- app/client/src/sagas/layoutConversionSagas.ts | 27 ++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/app/client/src/sagas/layoutConversionSagas.ts b/app/client/src/sagas/layoutConversionSagas.ts index d9b66b9d3b1c..f4b413ac594d 100644 --- a/app/client/src/sagas/layoutConversionSagas.ts +++ b/app/client/src/sagas/layoutConversionSagas.ts @@ -27,12 +27,16 @@ import { updateApplicationLayoutType } from "./AutoLayoutUpdateSagas"; */ function* convertFromAutoToFixedSaga(action: ReduxAction) { try { - yield call(createSnapshotSaga); + const pageWidgetsList: PageWidgetsReduxState = yield select(getPageWidgets); + + if (getShouldSaveSnapShot(pageWidgetsList)) { + yield call(createSnapshotSaga); + } + //Set conversion form to indicated conversion loading state yield put( setLayoutConversionStateAction(CONVERSION_STATES.CONVERSION_SPINNER), ); - const pageWidgetsList: PageWidgetsReduxState = yield select(getPageWidgets); const pageLayouts = []; @@ -85,11 +89,15 @@ function* convertFromAutoToFixedSaga(action: ReduxAction) { */ function* convertFromFixedToAutoSaga() { try { - yield call(createSnapshotSaga); + const pageWidgetsList: PageWidgetsReduxState = yield select(getPageWidgets); + + if (getShouldSaveSnapShot(pageWidgetsList)) { + yield call(createSnapshotSaga); + } + yield put( setLayoutConversionStateAction(CONVERSION_STATES.CONVERSION_SPINNER), ); - const pageWidgetsList: PageWidgetsReduxState = yield select(getPageWidgets); const pageLayouts = []; @@ -180,3 +188,14 @@ export default function* layoutConversionSagas() { ), ]); } + +//Function returns boolean, SnapShot should not be saved for a single empty canvas +function getShouldSaveSnapShot(pageWidgetsList: PageWidgetsReduxState) { + const pageList = Object.values(pageWidgetsList); + + if (pageList.length !== 1) return true; + + const { dsl: pageDSL } = pageList[0]; + + return Object.keys(pageDSL).length !== 1; +} From cc6ff11f2d4a4c39a7b1ebb49a37747b5f4929d3 Mon Sep 17 00:00:00 2001 From: rahulramesha Date: Tue, 21 Mar 2023 23:15:47 +0530 Subject: [PATCH 702/708] prevent recalculating columns while converting or using snapshot --- app/client/src/actions/autoLayoutActions.ts | 13 +++++++++++++ .../CanvasLayoutConversion/ConversionButton.tsx | 14 +++++++------- .../CanvasLayoutConversion/SnapShotBannerCTA.tsx | 9 +++++++-- .../reducers/uiReducers/layoutConversionReducer.ts | 3 ++- 4 files changed, 29 insertions(+), 10 deletions(-) diff --git a/app/client/src/actions/autoLayoutActions.ts b/app/client/src/actions/autoLayoutActions.ts index 5a983007e423..453db8beb77d 100644 --- a/app/client/src/actions/autoLayoutActions.ts +++ b/app/client/src/actions/autoLayoutActions.ts @@ -67,6 +67,19 @@ export function updateWidgetDimensionAction( }; } +export const setConversionStart = (conversionState: CONVERSION_STATES) => { + return { + type: ReduxActionTypes.START_CONVERSION_FLOW, + payload: conversionState, + }; +}; + +export const setConversionStop = () => { + return { + type: ReduxActionTypes.STOP_CONVERSION_FLOW, + }; +}; + export const setAutoCanvasResizing = (isAutoCanvasResizing: boolean) => { return { type: ReduxActionTypes.SET_AUTO_CANVAS_RESIZING, diff --git a/app/client/src/pages/Editor/CanvasLayoutConversion/ConversionButton.tsx b/app/client/src/pages/Editor/CanvasLayoutConversion/ConversionButton.tsx index b4b58267c371..72adce8e2650 100644 --- a/app/client/src/pages/Editor/CanvasLayoutConversion/ConversionButton.tsx +++ b/app/client/src/pages/Editor/CanvasLayoutConversion/ConversionButton.tsx @@ -16,8 +16,12 @@ import { createMessage, } from "@appsmith/constants/messages"; import BetaCard from "components/editorComponents/BetaCard"; -import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants"; import store from "store"; +import { + setConversionStart, + setConversionStop, +} from "actions/autoLayoutActions"; +import { CONVERSION_STATES } from "reducers/uiReducers/layoutConversionReducer"; export function ConversionButton() { const isAutoLayout = getIsAutoLayout(store.getState()); @@ -33,13 +37,9 @@ export function ConversionButton() { const onOpenOrClose = useCallback((isOpen: boolean) => { if (isOpen) { - dispatch({ - type: ReduxActionTypes.START_CONVERSION_FLOW, - }); + dispatch(setConversionStart(CONVERSION_STATES.START)); } else { - dispatch({ - type: ReduxActionTypes.STOP_CONVERSION_FLOW, - }); + dispatch(setConversionStop()); } }, []); diff --git a/app/client/src/pages/Editor/CanvasLayoutConversion/SnapShotBannerCTA.tsx b/app/client/src/pages/Editor/CanvasLayoutConversion/SnapShotBannerCTA.tsx index fe5c16ce7b2b..858ee89dfd2a 100644 --- a/app/client/src/pages/Editor/CanvasLayoutConversion/SnapShotBannerCTA.tsx +++ b/app/client/src/pages/Editor/CanvasLayoutConversion/SnapShotBannerCTA.tsx @@ -13,7 +13,10 @@ import { } from "@appsmith/constants/messages"; import { Text, TextType } from "design-system-old"; import { useSnapShotForm } from "./hooks/useSnapShotForm"; -import { setLayoutConversionStateAction } from "actions/autoLayoutActions"; +import { + setConversionStart, + setConversionStop, +} from "actions/autoLayoutActions"; import { CONVERSION_STATES } from "reducers/uiReducers/layoutConversionReducer"; import { useDispatch } from "react-redux"; @@ -25,7 +28,9 @@ export function SnapShotBannerCTA() { conversionState: CONVERSION_STATES, ) => { if (isOpen) { - dispatch(setLayoutConversionStateAction(conversionState)); + dispatch(setConversionStart(conversionState)); + } else { + dispatch(setConversionStop()); } }; diff --git a/app/client/src/reducers/uiReducers/layoutConversionReducer.ts b/app/client/src/reducers/uiReducers/layoutConversionReducer.ts index 098138f2fc8e..ddb309d30eb9 100644 --- a/app/client/src/reducers/uiReducers/layoutConversionReducer.ts +++ b/app/client/src/reducers/uiReducers/layoutConversionReducer.ts @@ -44,8 +44,9 @@ const layoutConversionReducer = createImmerReducer(initialState, { }, [ReduxActionTypes.START_CONVERSION_FLOW]: ( state: layoutConversionReduxState, + action: ReduxAction, ) => { - state.conversionState = CONVERSION_STATES.START; + state.conversionState = action.payload; state.conversionError = undefined; state.isConverting = true; }, From 99c3e1cdb6e08cb815c8e7d42c49e853824ad012 Mon Sep 17 00:00:00 2001 From: Aswath K Date: Wed, 22 Mar 2023 08:38:18 +0530 Subject: [PATCH 703/708] updates widget configs --- app/client/src/widgets/AudioRecorderWidget/index.ts | 3 +++ app/client/src/widgets/ButtonGroupWidget/index.ts | 2 +- app/client/src/widgets/CheckboxGroupWidget/index.ts | 3 +++ app/client/src/widgets/CodeScannerWidget/index.ts | 2 ++ app/client/src/widgets/CurrencyInputWidget/index.ts | 2 +- app/client/src/widgets/DocumentViewerWidget/index.ts | 3 +++ app/client/src/widgets/FormWidget/index.ts | 3 +++ app/client/src/widgets/IconButtonWidget/index.ts | 2 ++ app/client/src/widgets/IframeWidget/index.ts | 2 ++ app/client/src/widgets/MapChartWidget/index.ts | 2 +- app/client/src/widgets/MapWidget/index.ts | 2 +- app/client/src/widgets/MultiSelectTreeWidget/index.ts | 2 +- app/client/src/widgets/MultiSelectWidgetV2/index.ts | 2 +- app/client/src/widgets/ProgressWidget/index.ts | 7 ++++++- app/client/src/widgets/RadioGroupWidget/index.ts | 3 +++ app/client/src/widgets/SingleSelectTreeWidget/index.ts | 2 +- app/client/src/widgets/StatboxWidget/index.ts | 5 +++++ app/client/src/widgets/SwitchGroupWidget/index.ts | 3 +++ app/client/src/widgets/TableWidgetV2/index.ts | 1 + app/client/src/widgets/TabsWidget/index.ts | 3 +++ app/client/src/widgets/VideoWidget/index.ts | 3 +++ 21 files changed, 49 insertions(+), 8 deletions(-) diff --git a/app/client/src/widgets/AudioRecorderWidget/index.ts b/app/client/src/widgets/AudioRecorderWidget/index.ts index 8b17d9924645..b1425937562d 100644 --- a/app/client/src/widgets/AudioRecorderWidget/index.ts +++ b/app/client/src/widgets/AudioRecorderWidget/index.ts @@ -43,6 +43,9 @@ export const CONFIG = { }, }, ], + disableResizeHandles: { + vertical: true, + }, }, }; diff --git a/app/client/src/widgets/ButtonGroupWidget/index.ts b/app/client/src/widgets/ButtonGroupWidget/index.ts index ef1c224b2825..249bc06b042f 100644 --- a/app/client/src/widgets/ButtonGroupWidget/index.ts +++ b/app/client/src/widgets/ButtonGroupWidget/index.ts @@ -151,7 +151,7 @@ export const CONFIG = { { viewportMinWidth: 0, configuration: (props: ButtonGroupWidgetProps) => { - let minWidth = 128; + let minWidth = 120; const buttonLength = Object.keys(props.groupButtons).length; if (props.orientation === "horizontal") { // 120 is the width of the button, 8 is widget padding, 1 is the gap between buttons diff --git a/app/client/src/widgets/CheckboxGroupWidget/index.ts b/app/client/src/widgets/CheckboxGroupWidget/index.ts index edb0888cd1a0..e38e13a1957c 100644 --- a/app/client/src/widgets/CheckboxGroupWidget/index.ts +++ b/app/client/src/widgets/CheckboxGroupWidget/index.ts @@ -63,6 +63,9 @@ export const CONFIG = { }, }, ], + disableResizeHandles: { + vertical: true, + }, }, }; diff --git a/app/client/src/widgets/CodeScannerWidget/index.ts b/app/client/src/widgets/CodeScannerWidget/index.ts index f584cd0bf847..eb62bfbdef71 100644 --- a/app/client/src/widgets/CodeScannerWidget/index.ts +++ b/app/client/src/widgets/CodeScannerWidget/index.ts @@ -2,6 +2,7 @@ import IconSVG from "./icon.svg"; import Widget from "./widget"; import { ButtonPlacementTypes } from "components/constants"; import { ScannerLayout } from "./constants"; +import { ResponsiveBehavior } from "utils/autoLayout/constants"; export const CONFIG = { type: Widget.getWidgetType(), @@ -26,6 +27,7 @@ export const CONFIG = { isDisabled: false, animateLoading: true, placement: ButtonPlacementTypes.CENTER, + responsiveBehavior: ResponsiveBehavior.Fill, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/CurrencyInputWidget/index.ts b/app/client/src/widgets/CurrencyInputWidget/index.ts index 625587488ca4..a61e7ded4354 100644 --- a/app/client/src/widgets/CurrencyInputWidget/index.ts +++ b/app/client/src/widgets/CurrencyInputWidget/index.ts @@ -59,7 +59,7 @@ export const CONFIG = { viewportMinWidth: 0, configuration: () => { return { - minWidth: "160px", + minWidth: "120px", }; }, }, diff --git a/app/client/src/widgets/DocumentViewerWidget/index.ts b/app/client/src/widgets/DocumentViewerWidget/index.ts index 78867ac66950..a2133280d364 100644 --- a/app/client/src/widgets/DocumentViewerWidget/index.ts +++ b/app/client/src/widgets/DocumentViewerWidget/index.ts @@ -1,3 +1,4 @@ +import { ResponsiveBehavior } from "utils/autoLayout/constants"; import IconSVG from "./icon.svg"; import Widget from "./widget"; @@ -16,6 +17,7 @@ export const CONFIG = { columns: 24, version: 1, animateLoading: true, + responsiveBehavior: ResponsiveBehavior.Fill, }, properties: { derived: Widget.getDerivedPropertiesMap(), @@ -31,6 +33,7 @@ export const CONFIG = { configuration: () => { return { minWidth: "280px", + minHeight: "280px", }; }, }, diff --git a/app/client/src/widgets/FormWidget/index.ts b/app/client/src/widgets/FormWidget/index.ts index fb725b9e30f3..4b8061a75415 100644 --- a/app/client/src/widgets/FormWidget/index.ts +++ b/app/client/src/widgets/FormWidget/index.ts @@ -135,6 +135,9 @@ export const CONFIG = { }, }, ], + disableResizeHandles: { + vertical: true, + }, }, }; diff --git a/app/client/src/widgets/IconButtonWidget/index.ts b/app/client/src/widgets/IconButtonWidget/index.ts index b7ceabf759b8..dee3681021a9 100644 --- a/app/client/src/widgets/IconButtonWidget/index.ts +++ b/app/client/src/widgets/IconButtonWidget/index.ts @@ -1,6 +1,7 @@ import { IconNames } from "@blueprintjs/icons"; import { ButtonVariantTypes } from "components/constants"; import { ICON_BUTTON_MIN_WIDTH } from "constants/minWidthConstants"; +import { ResponsiveBehavior } from "utils/autoLayout/constants"; import IconSVG from "./icon.svg"; import Widget from "./widget"; @@ -19,6 +20,7 @@ export const CONFIG = { widgetName: "IconButton", version: 1, animateLoading: true, + responsiveBehavior: ResponsiveBehavior.Hug, minWidth: ICON_BUTTON_MIN_WIDTH, }, properties: { diff --git a/app/client/src/widgets/IframeWidget/index.ts b/app/client/src/widgets/IframeWidget/index.ts index f1b2d32104ba..4eb58f474362 100644 --- a/app/client/src/widgets/IframeWidget/index.ts +++ b/app/client/src/widgets/IframeWidget/index.ts @@ -1,3 +1,4 @@ +import { ResponsiveBehavior } from "utils/autoLayout/constants"; import IconSVG from "./icon.svg"; import Widget from "./widget"; @@ -16,6 +17,7 @@ export const CONFIG = { widgetName: "Iframe", version: 1, animateLoading: true, + responsiveBehavior: ResponsiveBehavior.Fill, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/MapChartWidget/index.ts b/app/client/src/widgets/MapChartWidget/index.ts index 116fffeec1cc..4c660cb890ce 100644 --- a/app/client/src/widgets/MapChartWidget/index.ts +++ b/app/client/src/widgets/MapChartWidget/index.ts @@ -56,7 +56,7 @@ export const CONFIG = { viewportMinWidth: 0, configuration: () => { return { - minWidth: "400px", + minWidth: "280px", minHeight: "300px", }; }, diff --git a/app/client/src/widgets/MapWidget/index.ts b/app/client/src/widgets/MapWidget/index.ts index bfca9e24b1da..dd1be221af64 100644 --- a/app/client/src/widgets/MapWidget/index.ts +++ b/app/client/src/widgets/MapWidget/index.ts @@ -42,7 +42,7 @@ export const CONFIG = { viewportMinWidth: 0, configuration: () => { return { - minWidth: "400px", + minWidth: "280px", minHeight: "300px", }; }, diff --git a/app/client/src/widgets/MultiSelectTreeWidget/index.ts b/app/client/src/widgets/MultiSelectTreeWidget/index.ts index 94ff6b8ab368..1f5cbb153ca5 100644 --- a/app/client/src/widgets/MultiSelectTreeWidget/index.ts +++ b/app/client/src/widgets/MultiSelectTreeWidget/index.ts @@ -83,7 +83,7 @@ export const CONFIG = { viewportMinWidth: 0, configuration: () => { return { - minWidth: "120px", + minWidth: "160px", }; }, }, diff --git a/app/client/src/widgets/MultiSelectWidgetV2/index.ts b/app/client/src/widgets/MultiSelectWidgetV2/index.ts index 0fbd9062e477..7f2a407fbae7 100644 --- a/app/client/src/widgets/MultiSelectWidgetV2/index.ts +++ b/app/client/src/widgets/MultiSelectWidgetV2/index.ts @@ -68,7 +68,7 @@ export const CONFIG = { viewportMinWidth: 0, configuration: () => { return { - minWidth: "120px", + minWidth: "160px", }; }, }, diff --git a/app/client/src/widgets/ProgressWidget/index.ts b/app/client/src/widgets/ProgressWidget/index.ts index afbba1456871..87fbcfb26604 100644 --- a/app/client/src/widgets/ProgressWidget/index.ts +++ b/app/client/src/widgets/ProgressWidget/index.ts @@ -2,6 +2,7 @@ import Widget from "./widget"; import IconSVG from "./icon.svg"; import { ProgressType } from "./constants"; import { Colors } from "constants/Colors"; +import { ResponsiveBehavior } from "utils/autoLayout/constants"; export const CONFIG = { type: Widget.getWidgetType(), @@ -23,6 +24,7 @@ export const CONFIG = { progressType: ProgressType.LINEAR, progress: 50, version: 1, + responsiveBehavior: ResponsiveBehavior.Fill, }, properties: { derived: Widget.getDerivedPropertiesMap(), @@ -39,12 +41,15 @@ export const CONFIG = { viewportMinWidth: 0, configuration: () => { return { - minWidth: "160px", + minWidth: "120px", minHeight: "70px", }; }, }, ], + disableResizeHandles: { + vertical: true, + }, }, }; diff --git a/app/client/src/widgets/RadioGroupWidget/index.ts b/app/client/src/widgets/RadioGroupWidget/index.ts index 33a617f6a4f9..27feb1ef39be 100644 --- a/app/client/src/widgets/RadioGroupWidget/index.ts +++ b/app/client/src/widgets/RadioGroupWidget/index.ts @@ -63,6 +63,9 @@ export const CONFIG = { }, }, ], + disableResizeHandles: { + vertical: true, + }, }, }; diff --git a/app/client/src/widgets/SingleSelectTreeWidget/index.ts b/app/client/src/widgets/SingleSelectTreeWidget/index.ts index 91520ecfce7e..915ef271737d 100644 --- a/app/client/src/widgets/SingleSelectTreeWidget/index.ts +++ b/app/client/src/widgets/SingleSelectTreeWidget/index.ts @@ -81,7 +81,7 @@ export const CONFIG = { viewportMinWidth: 0, configuration: () => { return { - minWidth: "120px", + minWidth: "160px", }; }, }, diff --git a/app/client/src/widgets/StatboxWidget/index.ts b/app/client/src/widgets/StatboxWidget/index.ts index f4a9914eade0..ed0ee7e6e0f6 100644 --- a/app/client/src/widgets/StatboxWidget/index.ts +++ b/app/client/src/widgets/StatboxWidget/index.ts @@ -52,6 +52,7 @@ export const CONFIG = { minDynamicHeight: 14, children: [], positioning: Positioning.Fixed, + responsiveBehavior: ResponsiveBehavior.Fill, blueprint: { view: [ { @@ -257,10 +258,14 @@ export const CONFIG = { configuration: () => { return { minWidth: "280px", + minHeight: "300px", }; }, }, ], + disableResizeHandles: { + vertical: true, + }, }, }; diff --git a/app/client/src/widgets/SwitchGroupWidget/index.ts b/app/client/src/widgets/SwitchGroupWidget/index.ts index 49d2f2bcb613..3aa2ea696f09 100644 --- a/app/client/src/widgets/SwitchGroupWidget/index.ts +++ b/app/client/src/widgets/SwitchGroupWidget/index.ts @@ -65,6 +65,9 @@ export const CONFIG = { }, }, ], + disableResizeHandles: { + vertical: true, + }, }, }; diff --git a/app/client/src/widgets/TableWidgetV2/index.ts b/app/client/src/widgets/TableWidgetV2/index.ts index e6739309ff49..3ae0570a1a3c 100644 --- a/app/client/src/widgets/TableWidgetV2/index.ts +++ b/app/client/src/widgets/TableWidgetV2/index.ts @@ -256,6 +256,7 @@ export const CONFIG = { configuration: () => { return { minWidth: "280px", + minHeight: "300px", }; }, }, diff --git a/app/client/src/widgets/TabsWidget/index.ts b/app/client/src/widgets/TabsWidget/index.ts index 51f60e6bb184..4b50c1ae03a9 100644 --- a/app/client/src/widgets/TabsWidget/index.ts +++ b/app/client/src/widgets/TabsWidget/index.ts @@ -157,6 +157,9 @@ export const CONFIG = { }, }, ], + disableResizeHandles: { + vertical: true, + }, }, }; diff --git a/app/client/src/widgets/VideoWidget/index.ts b/app/client/src/widgets/VideoWidget/index.ts index 02158f6ee464..7eb68fa5c513 100644 --- a/app/client/src/widgets/VideoWidget/index.ts +++ b/app/client/src/widgets/VideoWidget/index.ts @@ -1,3 +1,4 @@ +import { ResponsiveBehavior } from "utils/autoLayout/constants"; import IconSVG from "./icon.svg"; import Widget from "./widget"; @@ -16,6 +17,7 @@ export const CONFIG = { version: 1, animateLoading: true, backgroundColor: "#000", + responsiveBehavior: ResponsiveBehavior.Fill, }, properties: { derived: Widget.getDerivedPropertiesMap(), @@ -33,6 +35,7 @@ export const CONFIG = { configuration: () => { return { minWidth: "280px", + minHeight: "300px", }; }, }, From c6fdcaab52fc90cc8c1f9498d7dba0bc7e04d28f Mon Sep 17 00:00:00 2001 From: Aswath K Date: Wed, 22 Mar 2023 18:24:54 +0530 Subject: [PATCH 704/708] fix: resized widgets gets back to original size & widget config updates --- app/client/src/sagas/AutoLayoutUpdateSagas.tsx | 8 ++++++-- app/client/src/widgets/CheckboxGroupWidget/index.ts | 4 ++++ app/client/src/widgets/RadioGroupWidget/index.ts | 4 ++++ app/client/src/widgets/RateWidget/index.ts | 2 +- app/client/src/widgets/SwitchGroupWidget/index.ts | 4 ++++ 5 files changed, 19 insertions(+), 3 deletions(-) diff --git a/app/client/src/sagas/AutoLayoutUpdateSagas.tsx b/app/client/src/sagas/AutoLayoutUpdateSagas.tsx index 9a7a152b26be..39d5213452c9 100644 --- a/app/client/src/sagas/AutoLayoutUpdateSagas.tsx +++ b/app/client/src/sagas/AutoLayoutUpdateSagas.tsx @@ -188,6 +188,10 @@ function* updateWidgetDimensionsSaga( mainCanvasWidth, ); + if (widget.widthInPercentage) { + width = widget.widthInPercentage * mainCanvasWidth; + } + if ( widgetMinMaxDimensions.minHeight && height < widgetMinMaxDimensions.minHeight @@ -258,8 +262,8 @@ function* processAutoLayoutDimensionUpdatesSaga() { ...widgets, [widgetId]: { ...widget, - bottomRow: widget.topRow + height / rowSpace, - rightColumn: widget.leftColumn + width / columnSpace, + bottomRow: widget.topRow + Math.ceil(height / rowSpace), + rightColumn: widget.leftColumn + Math.ceil(width / columnSpace), }, }; } diff --git a/app/client/src/widgets/CheckboxGroupWidget/index.ts b/app/client/src/widgets/CheckboxGroupWidget/index.ts index e38e13a1957c..e4b57f8bf407 100644 --- a/app/client/src/widgets/CheckboxGroupWidget/index.ts +++ b/app/client/src/widgets/CheckboxGroupWidget/index.ts @@ -47,6 +47,10 @@ export const CONFIG = { stylesheetConfig: Widget.getStylesheetConfig(), }, autoLayout: { + defaults: { + columns: 14, + rows: 6.6, + }, disabledPropsDefaults: { labelPosition: LabelPosition.Top, }, diff --git a/app/client/src/widgets/RadioGroupWidget/index.ts b/app/client/src/widgets/RadioGroupWidget/index.ts index 27feb1ef39be..fa53fead1016 100644 --- a/app/client/src/widgets/RadioGroupWidget/index.ts +++ b/app/client/src/widgets/RadioGroupWidget/index.ts @@ -47,6 +47,10 @@ export const CONFIG = { stylesheetConfig: Widget.getStylesheetConfig(), }, autoLayout: { + defaults: { + columns: 14, + rows: 6.6, + }, disabledPropsDefaults: { labelPosition: LabelPosition.Top, }, diff --git a/app/client/src/widgets/RateWidget/index.ts b/app/client/src/widgets/RateWidget/index.ts index 813a054743bb..6aa8be7b1871 100644 --- a/app/client/src/widgets/RateWidget/index.ts +++ b/app/client/src/widgets/RateWidget/index.ts @@ -33,7 +33,7 @@ export const CONFIG = { }, autoLayout: { disabledPropsDefaults: { - size: "SMALL", + size: "LARGE", }, defaults: { columns: 7.272727, diff --git a/app/client/src/widgets/SwitchGroupWidget/index.ts b/app/client/src/widgets/SwitchGroupWidget/index.ts index 3aa2ea696f09..c8cc09056489 100644 --- a/app/client/src/widgets/SwitchGroupWidget/index.ts +++ b/app/client/src/widgets/SwitchGroupWidget/index.ts @@ -52,6 +52,10 @@ export const CONFIG = { disabledPropsDefaults: { labelPosition: LabelPosition.Top, }, + defaults: { + columns: 14, + rows: 6.6, + }, autoDimension: { height: true, }, From f86520040acf14763d9ed0845c7c7008796d530f Mon Sep 17 00:00:00 2001 From: Aswath K Date: Wed, 22 Mar 2023 20:22:22 +0530 Subject: [PATCH 705/708] fix: infinite update on auto dimension --- .../appsmith/autoLayout/AutoLayoutDimensionObeserver.tsx | 2 +- app/client/src/sagas/AutoLayoutUpdateSagas.tsx | 4 ++-- app/client/src/widgets/ButtonWidget/index.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutDimensionObeserver.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutDimensionObeserver.tsx index 2cfa9214b3be..02d96827020e 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutDimensionObeserver.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/AutoLayoutDimensionObeserver.tsx @@ -46,7 +46,7 @@ export default function AutoLayoutDimensionObserver( const heightDiff = Math.abs( props.height - 2 * FLEXBOX_PADDING - currentDimension.height, ); - if (widthDiff > 0 || heightDiff > 0) { + if (widthDiff >= 1 || heightDiff >= 1) { onDimensionUpdate(currentDimension.width, currentDimension.height); } }, [ diff --git a/app/client/src/sagas/AutoLayoutUpdateSagas.tsx b/app/client/src/sagas/AutoLayoutUpdateSagas.tsx index 39d5213452c9..454492e2fb7e 100644 --- a/app/client/src/sagas/AutoLayoutUpdateSagas.tsx +++ b/app/client/src/sagas/AutoLayoutUpdateSagas.tsx @@ -262,8 +262,8 @@ function* processAutoLayoutDimensionUpdatesSaga() { ...widgets, [widgetId]: { ...widget, - bottomRow: widget.topRow + Math.ceil(height / rowSpace), - rightColumn: widget.leftColumn + Math.ceil(width / columnSpace), + bottomRow: widget.topRow + Math.ceil(height) / rowSpace, + rightColumn: widget.leftColumn + Math.ceil(width) / columnSpace, }, }; } diff --git a/app/client/src/widgets/ButtonWidget/index.ts b/app/client/src/widgets/ButtonWidget/index.ts index 9615cfe5c3ad..619ae1b29da3 100644 --- a/app/client/src/widgets/ButtonWidget/index.ts +++ b/app/client/src/widgets/ButtonWidget/index.ts @@ -45,7 +45,7 @@ export const CONFIG = { autoLayout: { defaults: { rows: 4, - columns: 6.632, + columns: 6.453, }, autoDimension: { width: true, From df26d0cd27532669ddb87dd22a9e65c834966339 Mon Sep 17 00:00:00 2001 From: Aswath K Date: Thu, 23 Mar 2023 10:32:14 +0530 Subject: [PATCH 706/708] udpates widget config from Widgets in Stacks doc --- app/client/src/widgets/CameraWidget/index.ts | 2 +- app/client/src/widgets/ChartWidget/index.ts | 2 +- app/client/src/widgets/CheckboxGroupWidget/index.ts | 3 ++- app/client/src/widgets/CodeScannerWidget/index.ts | 2 +- app/client/src/widgets/IframeWidget/index.ts | 1 + app/client/src/widgets/ListWidget/index.ts | 2 +- app/client/src/widgets/RadioGroupWidget/index.ts | 3 ++- app/client/src/widgets/RichTextEditorWidget/index.ts | 1 + app/client/src/widgets/SwitchGroupWidget/index.ts | 3 ++- app/client/src/widgets/TabsWidget/index.ts | 1 + 10 files changed, 13 insertions(+), 7 deletions(-) diff --git a/app/client/src/widgets/CameraWidget/index.ts b/app/client/src/widgets/CameraWidget/index.ts index 9b261dc98495..90f2675b410e 100644 --- a/app/client/src/widgets/CameraWidget/index.ts +++ b/app/client/src/widgets/CameraWidget/index.ts @@ -37,7 +37,7 @@ export const CONFIG = { configuration: () => { return { minWidth: "280px", - maxWidth: "300px", + minHeight: "300px", }; }, }, diff --git a/app/client/src/widgets/ChartWidget/index.ts b/app/client/src/widgets/ChartWidget/index.ts index 4d3b55d32693..2b8da6a55921 100644 --- a/app/client/src/widgets/ChartWidget/index.ts +++ b/app/client/src/widgets/ChartWidget/index.ts @@ -116,7 +116,7 @@ export const CONFIG = { configuration: () => { return { minWidth: "280px", - maxWidth: "300px", + minHeight: "300px", }; }, }, diff --git a/app/client/src/widgets/CheckboxGroupWidget/index.ts b/app/client/src/widgets/CheckboxGroupWidget/index.ts index e4b57f8bf407..e4f704182353 100644 --- a/app/client/src/widgets/CheckboxGroupWidget/index.ts +++ b/app/client/src/widgets/CheckboxGroupWidget/index.ts @@ -49,7 +49,7 @@ export const CONFIG = { autoLayout: { defaults: { columns: 14, - rows: 6.6, + rows: 7, }, disabledPropsDefaults: { labelPosition: LabelPosition.Top, @@ -63,6 +63,7 @@ export const CONFIG = { configuration: () => { return { minWidth: "240px", + minHeight: "70px", }; }, }, diff --git a/app/client/src/widgets/CodeScannerWidget/index.ts b/app/client/src/widgets/CodeScannerWidget/index.ts index eb62bfbdef71..7cd909959d71 100644 --- a/app/client/src/widgets/CodeScannerWidget/index.ts +++ b/app/client/src/widgets/CodeScannerWidget/index.ts @@ -44,7 +44,7 @@ export const CONFIG = { configuration: () => { return { minWidth: "280px", - maxWidth: "300px", + minHeight: "300px", }; }, }, diff --git a/app/client/src/widgets/IframeWidget/index.ts b/app/client/src/widgets/IframeWidget/index.ts index 4eb58f474362..0f279b176ef9 100644 --- a/app/client/src/widgets/IframeWidget/index.ts +++ b/app/client/src/widgets/IframeWidget/index.ts @@ -35,6 +35,7 @@ export const CONFIG = { configuration: () => { return { minWidth: "280px", + minHeight: "300px", }; }, }, diff --git a/app/client/src/widgets/ListWidget/index.ts b/app/client/src/widgets/ListWidget/index.ts index 5e5d25f3185b..d7c9c5b4a0c1 100644 --- a/app/client/src/widgets/ListWidget/index.ts +++ b/app/client/src/widgets/ListWidget/index.ts @@ -423,7 +423,7 @@ export const CONFIG = { configuration: () => { return { minWidth: "280px", - maxWidth: "300px", + minHeight: "300px", }; }, }, diff --git a/app/client/src/widgets/RadioGroupWidget/index.ts b/app/client/src/widgets/RadioGroupWidget/index.ts index fa53fead1016..c602e12bb925 100644 --- a/app/client/src/widgets/RadioGroupWidget/index.ts +++ b/app/client/src/widgets/RadioGroupWidget/index.ts @@ -49,7 +49,7 @@ export const CONFIG = { autoLayout: { defaults: { columns: 14, - rows: 6.6, + rows: 7, }, disabledPropsDefaults: { labelPosition: LabelPosition.Top, @@ -63,6 +63,7 @@ export const CONFIG = { configuration: () => { return { minWidth: "240px", + minHeight: "70px", }; }, }, diff --git a/app/client/src/widgets/RichTextEditorWidget/index.ts b/app/client/src/widgets/RichTextEditorWidget/index.ts index b8bd0376914b..22bedd3a3afd 100644 --- a/app/client/src/widgets/RichTextEditorWidget/index.ts +++ b/app/client/src/widgets/RichTextEditorWidget/index.ts @@ -55,6 +55,7 @@ export const CONFIG = { configuration: () => { return { minWidth: "280px", + minHeight: "300px", }; }, }, diff --git a/app/client/src/widgets/SwitchGroupWidget/index.ts b/app/client/src/widgets/SwitchGroupWidget/index.ts index c8cc09056489..90ee732c27b1 100644 --- a/app/client/src/widgets/SwitchGroupWidget/index.ts +++ b/app/client/src/widgets/SwitchGroupWidget/index.ts @@ -54,7 +54,7 @@ export const CONFIG = { }, defaults: { columns: 14, - rows: 6.6, + rows: 7, }, autoDimension: { height: true, @@ -65,6 +65,7 @@ export const CONFIG = { configuration: () => { return { minWidth: "240px", + minHeight: "70px", }; }, }, diff --git a/app/client/src/widgets/TabsWidget/index.ts b/app/client/src/widgets/TabsWidget/index.ts index 4b50c1ae03a9..3b5ed49e1bc1 100644 --- a/app/client/src/widgets/TabsWidget/index.ts +++ b/app/client/src/widgets/TabsWidget/index.ts @@ -153,6 +153,7 @@ export const CONFIG = { configuration: () => { return { minWidth: "280px", + minHeight: "300px", }; }, }, From 7d65f6e5e444c74db18d92c62abe390401caaf6a Mon Sep 17 00:00:00 2001 From: Arsalan Date: Thu, 23 Mar 2023 14:38:43 +0530 Subject: [PATCH 707/708] feat: added vertical alignment bottom to widgets. --- app/client/src/widgets/AudioRecorderWidget/index.ts | 6 +++++- app/client/src/widgets/BaseInputWidget/index.ts | 6 +++++- app/client/src/widgets/ButtonGroupWidget/index.ts | 6 +++++- app/client/src/widgets/ButtonWidget/index.ts | 6 +++++- app/client/src/widgets/CategorySliderWidget/index.ts | 6 +++++- app/client/src/widgets/CheckboxWidget/index.ts | 6 +++++- app/client/src/widgets/DatePickerWidget2/index.ts | 6 +++++- app/client/src/widgets/FilePickerWidgetV2/index.ts | 6 +++++- app/client/src/widgets/IconButtonWidget/index.ts | 6 +++++- app/client/src/widgets/MenuButtonWidget/index.ts | 2 ++ app/client/src/widgets/MultiSelectTreeWidget/index.ts | 6 +++++- app/client/src/widgets/MultiSelectWidgetV2/index.ts | 6 +++++- app/client/src/widgets/NumberSliderWidget/index.ts | 6 +++++- app/client/src/widgets/RangeSliderWidget/index.ts | 6 +++++- app/client/src/widgets/RateWidget/index.ts | 2 ++ app/client/src/widgets/SelectWidget/index.ts | 6 +++++- app/client/src/widgets/SingleSelectTreeWidget/index.ts | 6 +++++- app/client/src/widgets/SwitchWidget/index.ts | 6 +++++- app/client/src/widgets/TextWidget/index.ts | 6 +++++- 19 files changed, 89 insertions(+), 17 deletions(-) diff --git a/app/client/src/widgets/AudioRecorderWidget/index.ts b/app/client/src/widgets/AudioRecorderWidget/index.ts index b1425937562d..07f3840ed467 100644 --- a/app/client/src/widgets/AudioRecorderWidget/index.ts +++ b/app/client/src/widgets/AudioRecorderWidget/index.ts @@ -1,5 +1,8 @@ import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; -import { ResponsiveBehavior } from "utils/autoLayout/constants"; +import { + FlexVerticalAlignment, + ResponsiveBehavior, +} from "utils/autoLayout/constants"; import IconSVG from "./icon.svg"; import Widget from "./widget"; @@ -21,6 +24,7 @@ export const CONFIG = { animateLoading: true, responsiveBehavior: ResponsiveBehavior.Fill, minWidth: FILL_WIDGET_MIN_WIDTH, + flexVerticalAlignment: FlexVerticalAlignment.Bottom, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/BaseInputWidget/index.ts b/app/client/src/widgets/BaseInputWidget/index.ts index d0d5cdd199dd..549bcca3aca3 100644 --- a/app/client/src/widgets/BaseInputWidget/index.ts +++ b/app/client/src/widgets/BaseInputWidget/index.ts @@ -1,7 +1,10 @@ import { Alignment } from "@blueprintjs/core"; import { LabelPosition } from "components/constants"; import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; -import { ResponsiveBehavior } from "utils/autoLayout/constants"; +import { + FlexVerticalAlignment, + ResponsiveBehavior, +} from "utils/autoLayout/constants"; import IconSVG from "./icon.svg"; import Widget from "./widget"; @@ -32,6 +35,7 @@ export const CONFIG = { animateLoading: true, responsiveBehavior: ResponsiveBehavior.Fill, minWidth: FILL_WIDGET_MIN_WIDTH, + flexVerticalAlignment: FlexVerticalAlignment.Bottom, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/ButtonGroupWidget/index.ts b/app/client/src/widgets/ButtonGroupWidget/index.ts index 249bc06b042f..5ce5907812fe 100644 --- a/app/client/src/widgets/ButtonGroupWidget/index.ts +++ b/app/client/src/widgets/ButtonGroupWidget/index.ts @@ -3,7 +3,10 @@ import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; import { klona as clone } from "klona/full"; import { get } from "lodash"; import type { WidgetProps } from "widgets/BaseWidget"; -import { ResponsiveBehavior } from "utils/autoLayout/constants"; +import { + FlexVerticalAlignment, + ResponsiveBehavior, +} from "utils/autoLayout/constants"; import { BlueprintOperationTypes } from "widgets/constants"; import IconSVG from "./icon.svg"; import Widget from "./widget"; @@ -27,6 +30,7 @@ export const CONFIG = { animateLoading: true, responsiveBehavior: ResponsiveBehavior.Fill, minWidth: FILL_WIDGET_MIN_WIDTH, + flexVerticalAlignment: FlexVerticalAlignment.Bottom, groupButtons: { groupButton1: { label: "Favorite", diff --git a/app/client/src/widgets/ButtonWidget/index.ts b/app/client/src/widgets/ButtonWidget/index.ts index 619ae1b29da3..b78f5b5df9de 100644 --- a/app/client/src/widgets/ButtonWidget/index.ts +++ b/app/client/src/widgets/ButtonWidget/index.ts @@ -4,7 +4,10 @@ import { RecaptchaTypes, } from "components/constants"; import { BUTTON_MIN_WIDTH } from "constants/minWidthConstants"; -import { ResponsiveBehavior } from "utils/autoLayout/constants"; +import { + FlexVerticalAlignment, + ResponsiveBehavior, +} from "utils/autoLayout/constants"; import IconSVG from "./icon.svg"; import Widget from "./widget"; @@ -32,6 +35,7 @@ export const CONFIG = { version: 1, responsiveBehavior: ResponsiveBehavior.Hug, minWidth: BUTTON_MIN_WIDTH, + flexVerticalAlignment: FlexVerticalAlignment.Bottom, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/CategorySliderWidget/index.ts b/app/client/src/widgets/CategorySliderWidget/index.ts index a96dab3cffe8..f4d3fbd0e2cf 100644 --- a/app/client/src/widgets/CategorySliderWidget/index.ts +++ b/app/client/src/widgets/CategorySliderWidget/index.ts @@ -1,6 +1,9 @@ import { Alignment } from "@blueprintjs/core"; import { LabelPosition } from "components/constants"; -import { ResponsiveBehavior } from "utils/autoLayout/constants"; +import { + FlexVerticalAlignment, + ResponsiveBehavior, +} from "utils/autoLayout/constants"; import IconSVG from "./icon.svg"; import Widget from "./widget"; @@ -37,6 +40,7 @@ export const CONFIG = { labelTextSize: "0.875rem", sliderSize: "m", responsiveBehavior: ResponsiveBehavior.Fill, + flexVerticalAlignment: FlexVerticalAlignment.Bottom, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/CheckboxWidget/index.ts b/app/client/src/widgets/CheckboxWidget/index.ts index fb80ea13b314..6edc68a92dd1 100644 --- a/app/client/src/widgets/CheckboxWidget/index.ts +++ b/app/client/src/widgets/CheckboxWidget/index.ts @@ -1,6 +1,9 @@ import { LabelPosition } from "components/constants"; import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; -import { ResponsiveBehavior } from "utils/autoLayout/constants"; +import { + FlexVerticalAlignment, + ResponsiveBehavior, +} from "utils/autoLayout/constants"; import { AlignWidgetTypes } from "widgets/constants"; import IconSVG from "./icon.svg"; @@ -32,6 +35,7 @@ export const CONFIG = { animateLoading: true, responsiveBehavior: ResponsiveBehavior.Fill, minWidth: FILL_WIDGET_MIN_WIDTH, + flexVerticalAlignment: FlexVerticalAlignment.Bottom, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/DatePickerWidget2/index.ts b/app/client/src/widgets/DatePickerWidget2/index.ts index 4a86e2f59d22..c6cc1a1da86f 100644 --- a/app/client/src/widgets/DatePickerWidget2/index.ts +++ b/app/client/src/widgets/DatePickerWidget2/index.ts @@ -2,7 +2,10 @@ import { Alignment } from "@blueprintjs/core"; import { LabelPosition } from "components/constants"; import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; import moment from "moment"; -import { ResponsiveBehavior } from "utils/autoLayout/constants"; +import { + FlexVerticalAlignment, + ResponsiveBehavior, +} from "utils/autoLayout/constants"; import { DynamicHeight } from "utils/WidgetFeatures"; import { TimePrecision } from "./constants"; @@ -46,6 +49,7 @@ export const CONFIG = { animateLoading: true, responsiveBehavior: ResponsiveBehavior.Fill, minWidth: FILL_WIDGET_MIN_WIDTH, + flexVerticalAlignment: FlexVerticalAlignment.Bottom, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/FilePickerWidgetV2/index.ts b/app/client/src/widgets/FilePickerWidgetV2/index.ts index 308e294fd0ac..cc5a5d5ee06d 100644 --- a/app/client/src/widgets/FilePickerWidgetV2/index.ts +++ b/app/client/src/widgets/FilePickerWidgetV2/index.ts @@ -1,5 +1,8 @@ import { BUTTON_MIN_WIDTH } from "constants/minWidthConstants"; -import { ResponsiveBehavior } from "utils/autoLayout/constants"; +import { + FlexVerticalAlignment, + ResponsiveBehavior, +} from "utils/autoLayout/constants"; import FileDataTypes from "./constants"; import IconSVG from "./icon.svg"; @@ -30,6 +33,7 @@ export const CONFIG = { animateLoading: true, responsiveBehavior: ResponsiveBehavior.Hug, minWidth: BUTTON_MIN_WIDTH, + flexVerticalAlignment: FlexVerticalAlignment.Bottom, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/IconButtonWidget/index.ts b/app/client/src/widgets/IconButtonWidget/index.ts index dee3681021a9..78940cd38667 100644 --- a/app/client/src/widgets/IconButtonWidget/index.ts +++ b/app/client/src/widgets/IconButtonWidget/index.ts @@ -1,7 +1,10 @@ import { IconNames } from "@blueprintjs/icons"; import { ButtonVariantTypes } from "components/constants"; import { ICON_BUTTON_MIN_WIDTH } from "constants/minWidthConstants"; -import { ResponsiveBehavior } from "utils/autoLayout/constants"; +import { + FlexVerticalAlignment, + ResponsiveBehavior, +} from "utils/autoLayout/constants"; import IconSVG from "./icon.svg"; import Widget from "./widget"; @@ -22,6 +25,7 @@ export const CONFIG = { animateLoading: true, responsiveBehavior: ResponsiveBehavior.Hug, minWidth: ICON_BUTTON_MIN_WIDTH, + flexVerticalAlignment: FlexVerticalAlignment.Bottom, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/MenuButtonWidget/index.ts b/app/client/src/widgets/MenuButtonWidget/index.ts index 0f1eb9013939..cf96ba760b60 100644 --- a/app/client/src/widgets/MenuButtonWidget/index.ts +++ b/app/client/src/widgets/MenuButtonWidget/index.ts @@ -2,6 +2,7 @@ import Widget from "./widget"; import IconSVG from "./icon.svg"; import { ButtonPlacementTypes, ButtonVariantTypes } from "components/constants"; import { MenuItemsSource } from "./constants"; +import { FlexVerticalAlignment } from "utils/autoLayout/constants"; export const CONFIG = { type: Widget.getWidgetType(), @@ -46,6 +47,7 @@ export const CONFIG = { columns: 16, widgetName: "MenuButton", version: 1, + flexVerticalAlignment: FlexVerticalAlignment.Bottom, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/MultiSelectTreeWidget/index.ts b/app/client/src/widgets/MultiSelectTreeWidget/index.ts index 1f5cbb153ca5..1f652c0a3643 100644 --- a/app/client/src/widgets/MultiSelectTreeWidget/index.ts +++ b/app/client/src/widgets/MultiSelectTreeWidget/index.ts @@ -1,7 +1,10 @@ import { Alignment } from "@blueprintjs/core"; import { LabelPosition } from "components/constants"; import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; -import { ResponsiveBehavior } from "utils/autoLayout/constants"; +import { + FlexVerticalAlignment, + ResponsiveBehavior, +} from "utils/autoLayout/constants"; import { DynamicHeight } from "utils/WidgetFeatures"; import IconSVG from "./icon.svg"; @@ -59,6 +62,7 @@ export const CONFIG = { labelTextSize: "0.875rem", responsiveBehavior: ResponsiveBehavior.Fill, minWidth: FILL_WIDGET_MIN_WIDTH, + flexVerticalAlignment: FlexVerticalAlignment.Bottom, }, properties: { diff --git a/app/client/src/widgets/MultiSelectWidgetV2/index.ts b/app/client/src/widgets/MultiSelectWidgetV2/index.ts index 7f2a407fbae7..11581e1caa50 100644 --- a/app/client/src/widgets/MultiSelectWidgetV2/index.ts +++ b/app/client/src/widgets/MultiSelectWidgetV2/index.ts @@ -1,7 +1,10 @@ import { Alignment } from "@blueprintjs/core"; import { LabelPosition } from "components/constants"; import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; -import { ResponsiveBehavior } from "utils/autoLayout/constants"; +import { + FlexVerticalAlignment, + ResponsiveBehavior, +} from "utils/autoLayout/constants"; import { DynamicHeight } from "utils/WidgetFeatures"; import IconSVG from "./icon.svg"; @@ -44,6 +47,7 @@ export const CONFIG = { placeholderText: "Select option(s)", responsiveBehavior: ResponsiveBehavior.Fill, minWidth: FILL_WIDGET_MIN_WIDTH, + flexVerticalAlignment: FlexVerticalAlignment.Bottom, }, properties: { diff --git a/app/client/src/widgets/NumberSliderWidget/index.ts b/app/client/src/widgets/NumberSliderWidget/index.ts index 0f6903663afa..c4227a071f1d 100644 --- a/app/client/src/widgets/NumberSliderWidget/index.ts +++ b/app/client/src/widgets/NumberSliderWidget/index.ts @@ -1,6 +1,9 @@ import { Alignment } from "@blueprintjs/core"; import { LabelPosition } from "components/constants"; -import { ResponsiveBehavior } from "utils/autoLayout/constants"; +import { + FlexVerticalAlignment, + ResponsiveBehavior, +} from "utils/autoLayout/constants"; import IconSVG from "./icon.svg"; import Widget from "./widget"; @@ -39,6 +42,7 @@ export const CONFIG = { labelTextSize: "0.875rem", sliderSize: "m", responsiveBehavior: ResponsiveBehavior.Fill, + flexVerticalAlignment: FlexVerticalAlignment.Bottom, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/RangeSliderWidget/index.ts b/app/client/src/widgets/RangeSliderWidget/index.ts index f065ea0c2c30..ee3bed5d4be2 100644 --- a/app/client/src/widgets/RangeSliderWidget/index.ts +++ b/app/client/src/widgets/RangeSliderWidget/index.ts @@ -1,6 +1,9 @@ import { Alignment } from "@blueprintjs/core"; import { LabelPosition } from "components/constants"; -import { ResponsiveBehavior } from "utils/autoLayout/constants"; +import { + FlexVerticalAlignment, + ResponsiveBehavior, +} from "utils/autoLayout/constants"; import IconSVG from "./icon.svg"; import Widget from "./widget"; @@ -40,6 +43,7 @@ export const CONFIG = { animateLoading: true, sliderSize: "m", responsiveBehavior: ResponsiveBehavior.Fill, + flexVerticalAlignment: FlexVerticalAlignment.Bottom, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/RateWidget/index.ts b/app/client/src/widgets/RateWidget/index.ts index 6aa8be7b1871..0d4cfb39d8d0 100644 --- a/app/client/src/widgets/RateWidget/index.ts +++ b/app/client/src/widgets/RateWidget/index.ts @@ -2,6 +2,7 @@ import { Colors } from "constants/Colors"; import IconSVG from "./icon.svg"; import Widget from "./widget"; import type { RateWidgetProps } from "./widget"; +import { FlexVerticalAlignment } from "utils/autoLayout/constants"; export const CONFIG = { features: { @@ -30,6 +31,7 @@ export const CONFIG = { isReadOnly: false, tooltips: ["Terrible", "Bad", "Neutral", "Good", "Great"], widgetName: "Rating", + flexVerticalAlignment: FlexVerticalAlignment.Bottom, }, autoLayout: { disabledPropsDefaults: { diff --git a/app/client/src/widgets/SelectWidget/index.ts b/app/client/src/widgets/SelectWidget/index.ts index 2a8560ef0400..ed0aab6318ef 100644 --- a/app/client/src/widgets/SelectWidget/index.ts +++ b/app/client/src/widgets/SelectWidget/index.ts @@ -1,7 +1,10 @@ import { Alignment } from "@blueprintjs/core"; import { LabelPosition } from "components/constants"; import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; -import { ResponsiveBehavior } from "utils/autoLayout/constants"; +import { + FlexVerticalAlignment, + ResponsiveBehavior, +} from "utils/autoLayout/constants"; import { DynamicHeight } from "utils/WidgetFeatures"; import IconSVG from "./icon.svg"; @@ -44,6 +47,7 @@ export const CONFIG = { labelTextSize: "0.875rem", responsiveBehavior: ResponsiveBehavior.Fill, minWidth: FILL_WIDGET_MIN_WIDTH, + flexVerticalAlignment: FlexVerticalAlignment.Bottom, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/SingleSelectTreeWidget/index.ts b/app/client/src/widgets/SingleSelectTreeWidget/index.ts index 915ef271737d..6075b3f72610 100644 --- a/app/client/src/widgets/SingleSelectTreeWidget/index.ts +++ b/app/client/src/widgets/SingleSelectTreeWidget/index.ts @@ -1,7 +1,10 @@ import { Alignment } from "@blueprintjs/core"; import { LabelPosition } from "components/constants"; import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; -import { ResponsiveBehavior } from "utils/autoLayout/constants"; +import { + FlexVerticalAlignment, + ResponsiveBehavior, +} from "utils/autoLayout/constants"; import { DynamicHeight } from "utils/WidgetFeatures"; import IconSVG from "./icon.svg"; @@ -58,6 +61,7 @@ export const CONFIG = { labelTextSize: "0.875rem", responsiveBehavior: ResponsiveBehavior.Fill, minWidth: FILL_WIDGET_MIN_WIDTH, + flexVerticalAlignment: FlexVerticalAlignment.Bottom, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/SwitchWidget/index.ts b/app/client/src/widgets/SwitchWidget/index.ts index d7eeaf56f62e..411ece0059b1 100644 --- a/app/client/src/widgets/SwitchWidget/index.ts +++ b/app/client/src/widgets/SwitchWidget/index.ts @@ -1,5 +1,8 @@ import { LabelPosition } from "components/constants"; -import { ResponsiveBehavior } from "utils/autoLayout/constants"; +import { + FlexVerticalAlignment, + ResponsiveBehavior, +} from "utils/autoLayout/constants"; import { AlignWidgetTypes } from "widgets/constants"; import IconSVG from "./icon.svg"; @@ -29,6 +32,7 @@ export const CONFIG = { isDisabled: false, animateLoading: true, responsiveBehavior: ResponsiveBehavior.Fill, + flexVerticalAlignment: FlexVerticalAlignment.Bottom, }, properties: { derived: Widget.getDerivedPropertiesMap(), diff --git a/app/client/src/widgets/TextWidget/index.ts b/app/client/src/widgets/TextWidget/index.ts index 4713f60ecdaf..4990d1599c6e 100644 --- a/app/client/src/widgets/TextWidget/index.ts +++ b/app/client/src/widgets/TextWidget/index.ts @@ -1,6 +1,9 @@ import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; import { DEFAULT_FONT_SIZE } from "constants/WidgetConstants"; -import { ResponsiveBehavior } from "utils/autoLayout/constants"; +import { + FlexVerticalAlignment, + ResponsiveBehavior, +} from "utils/autoLayout/constants"; import { OverflowTypes } from "./constants"; import IconSVG from "./icon.svg"; @@ -32,6 +35,7 @@ export const CONFIG = { animateLoading: true, responsiveBehavior: ResponsiveBehavior.Fill, minWidth: FILL_WIDGET_MIN_WIDTH, + flexVerticalAlignment: FlexVerticalAlignment.Bottom, }, properties: { derived: Widget.getDerivedPropertiesMap(), From af9805edcaac481f53a93c362ae0f105514af0ab Mon Sep 17 00:00:00 2001 From: Arsalan Date: Thu, 23 Mar 2023 17:20:26 +0530 Subject: [PATCH 708/708] feat: add margin bottom 6px to select widgets. --- .../designSystems/appsmith/autoLayout/FlexComponent.tsx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx index 74351d581b5a..bcf90292058c 100644 --- a/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/autoLayout/FlexComponent.tsx @@ -43,6 +43,12 @@ const FlexWidget = styled.div` position: relative; `; +const WIDGETS_WITH_MARGIN = [ + "BUTTON_WIDGET", + "BUTTON_GROUP_WIDGET", + "CHECKBOX_WIDGET", +]; + export function FlexComponent(props: AutoLayoutProps) { const isSnipingMode = useSelector(snipingModeSelector); @@ -94,6 +100,9 @@ export function FlexComponent(props: AutoLayoutProps) { props.componentHeight - WIDGET_PADDING * 2 + RESIZE_BORDER_BUFFER }px`, margin: WIDGET_PADDING / 2 + "px", + marginBottom: WIDGETS_WITH_MARGIN.includes(props.widgetType) + ? "6px" + : "0px", }; const flexComponentStyle: CSSProperties = useMemo(() => { return {
@@ -160,6 +191,7 @@ export default function LeftPane() { triggerAnalytics("Usage")} to="/settings/usage" >
@@ -170,7 +202,7 @@ export default function LeftPane() { )} - + ); } diff --git a/app/client/src/ce/pages/AdminSettings/Main.tsx b/app/client/src/ce/pages/AdminSettings/Main.tsx index b2e7ea1b41d3..541ece19fa08 100644 --- a/app/client/src/ce/pages/AdminSettings/Main.tsx +++ b/app/client/src/ce/pages/AdminSettings/Main.tsx @@ -2,15 +2,21 @@ import React from "react"; import AdminConfig from "./config"; import { Redirect, useParams } from "react-router"; import { SettingCategories } from "@appsmith/pages/AdminSettings/config/types"; -import { ADMIN_SETTINGS_CATEGORY_DEFAULT_PATH } from "constants/routes"; import SettingsForm from "pages/Settings/SettingsForm"; import { AuditLogsUpgradePage } from "../Upgrade/AuditLogsUpgradePage"; import { AccessControlUpgradePage } from "../Upgrade/AccessControlUpgradePage"; import { UsageUpgradePage } from "../Upgrade/UsageUpgradePage"; +import { getDefaultAdminSettingsPath } from "@appsmith/utils/adminSettingsHelpers"; +import { useSelector } from "react-redux"; +import { getCurrentUser } from "selectors/usersSelectors"; +import { getTenantPermissions } from "@appsmith/selectors/tenantSelectors"; const Main = () => { const params = useParams() as any; const { category, selected: subCategory } = params; + const user = useSelector(getCurrentUser); + const tenantPermissions = useSelector(getTenantPermissions); + const isSuperUser = user?.isSuperUser || false; const wrapperCategory = AdminConfig.wrapperCategories[subCategory ?? category]; @@ -30,12 +36,16 @@ const Main = () => { /* Old, still working flow; config, factory based */ if (!!wrapperCategory?.component) { const { component: WrapperCategoryComponent } = wrapperCategory; - return ; + return ; } else if ( !Object.values(SettingCategories).includes(category) || (subCategory && !Object.values(SettingCategories).includes(subCategory)) ) { - return ; + return ( + + ); } else { return ; } diff --git a/app/client/src/ce/pages/AdminSettings/config/authentication/AuthPage.tsx b/app/client/src/ce/pages/AdminSettings/config/authentication/AuthPage.tsx index d72e6ee66eee..516f5ff2205c 100644 --- a/app/client/src/ce/pages/AdminSettings/config/authentication/AuthPage.tsx +++ b/app/client/src/ce/pages/AdminSettings/config/authentication/AuthPage.tsx @@ -13,31 +13,29 @@ import { AUTHENTICATION_METHOD_ENABLED, } from "@appsmith/constants/messages"; import { CalloutV2, CalloutType } from "design-system"; -import { getAppsmithConfigs } from "@appsmith/configs"; import { Colors } from "constants/Colors"; import { Button, Category, Icon, TooltipComponent } from "design-system"; import { adminSettingsCategoryUrl } from "RouteBuilder"; import AnalyticsUtil from "utils/AnalyticsUtil"; +import useOnUpgrade from "utils/hooks/useOnUpgrade"; -const { intercomAppID } = getAppsmithConfigs(); - -const Wrapper = styled.div` +export const Wrapper = styled.div` flex-basis: calc(100% - ${(props) => props.theme.homePage.leftPane.width}px); - padding-top: 40px; + padding: 40px 0 0 24px; height: calc(100vh - ${(props) => props.theme.homePage.header}px); overflow: auto; `; -const SettingsFormWrapper = styled.div``; +export const SettingsFormWrapper = styled.div``; -const SettingsHeader = styled.h2` +export const SettingsHeader = styled.h2` font-size: 24px; font-weight: 500; text-transform: capitalize; margin-bottom: 0; `; -const SettingsSubHeader = styled.div` +export const SettingsSubHeader = styled.div` font-size: 14px; margin-bottom: 0; `; @@ -109,10 +107,10 @@ const StyledAuthButton = styled(Button)` padding: 8px 16px; `; -const Label = styled.span<{ enterprise?: boolean }>` +const Label = styled.span<{ business?: boolean }>` display: inline; ${(props) => - props.enterprise + props.business ? ` border: 1px solid ${Colors.COD_GRAY}; color: ${Colors.COD_GRAY}; @@ -126,18 +124,19 @@ const Label = styled.span<{ enterprise?: boolean }>` font-size: 12px; `; +export function Upgrade(message: string, method: string) { + const { onUpgrade } = useOnUpgrade({ + logEventName: "ADMIN_SETTINGS_UPGRADE_AUTH_METHOD", + logEventData: { method }, + intercomMessage: message, + }); + + return onUpgrade(); +} + export function AuthPage({ authMethods }: { authMethods: AuthMethodType[] }) { const history = useHistory(); - const triggerIntercom = (authLabel: string) => { - if (intercomAppID && window.Intercom) { - window.Intercom( - "showNewMessage", - createMessage(UPGRADE_TO_EE, authLabel), - ); - } - }; - const onClickHandler = (method: AuthMethodType) => { if (!method.needsUpgrade || method.isConnected) { AnalyticsUtil.logEvent( @@ -155,10 +154,7 @@ export function AuthPage({ authMethods }: { authMethods: AuthMethodType[] }) { }), ); } else { - AnalyticsUtil.logEvent("ADMIN_SETTINGS_UPGRADE_AUTH_METHOD", { - method: method.label, - }); - triggerIntercom(method.label); + Upgrade(createMessage(UPGRADE_TO_EE, method.label), method.label); } }; @@ -181,7 +177,7 @@ export function AuthPage({ authMethods }: { authMethods: AuthMethodType[] }) { {method.label}  {method.needsUpgrade && ( <> - +   )} diff --git a/app/client/src/ce/pages/AdminSettings/config/branding/UpgradeBanner.tsx b/app/client/src/ce/pages/AdminSettings/config/branding/UpgradeBanner.tsx new file mode 100644 index 000000000000..295be6c9e846 --- /dev/null +++ b/app/client/src/ce/pages/AdminSettings/config/branding/UpgradeBanner.tsx @@ -0,0 +1,51 @@ +import React from "react"; +import { Button } from "design-system"; + +import { + ADMIN_BRANDING_SETTINGS_SUBTITLE, + ADMIN_BRANDING_SETTINGS_TITLE, + createMessage, +} from "@appsmith/constants/messages"; +import useOnUpgrade from "utils/hooks/useOnUpgrade"; +import { + SettingsHeader, + SettingsSubHeader, +} from "@appsmith/pages/AdminSettings/config/authentication/AuthPage"; + +const UpgradeBanner = () => { + const { onUpgrade } = useOnUpgrade({ + logEventName: "BRANDING_UPGRADE_CLICK", + intercomMessage: + "Hello, I would like to upgrade my appsmith instance to use the custom branding feature", + }); + + return ( +
+
+
+
+ Business +
+ + {createMessage(ADMIN_BRANDING_SETTINGS_TITLE)} + + + {createMessage(ADMIN_BRANDING_SETTINGS_SUBTITLE)} + +
+ +
+
+ ); +}; + +export default UpgradeBanner; diff --git a/app/client/src/ce/pages/AdminSettings/config/branding/index.tsx b/app/client/src/ce/pages/AdminSettings/config/branding/index.tsx new file mode 100644 index 000000000000..b93f7dcbf258 --- /dev/null +++ b/app/client/src/ce/pages/AdminSettings/config/branding/index.tsx @@ -0,0 +1,15 @@ +import { + AdminConfigType, + SettingCategories, + SettingTypes, +} from "@appsmith/pages/AdminSettings/config/types"; +import BrandingPage from "pages/Settings/config/branding/BrandingPage"; + +export const config: AdminConfigType = { + type: SettingCategories.BRANDING, + controlType: SettingTypes.PAGE, + canSave: false, + title: "Branding", + icon: "pantone", + component: BrandingPage, +}; diff --git a/app/client/src/ce/pages/AdminSettings/config/branding/useBrandingForm.tsx b/app/client/src/ce/pages/AdminSettings/config/branding/useBrandingForm.tsx new file mode 100644 index 000000000000..5dea0ffdbbee --- /dev/null +++ b/app/client/src/ce/pages/AdminSettings/config/branding/useBrandingForm.tsx @@ -0,0 +1,13 @@ +/* eslint-disable */ +//@ts-nocheck +export const useBrandingForm = (props: any) => { + /* eslint-disable */ + //@ts-nocheck + const onSubmit = (data: any) => { + // + }; + + return { + onSubmit, + }; +}; diff --git a/app/client/src/ce/pages/AdminSettings/config/general.tsx b/app/client/src/ce/pages/AdminSettings/config/general.tsx index ef7d2e1f8c5e..58e5762d24a4 100644 --- a/app/client/src/ce/pages/AdminSettings/config/general.tsx +++ b/app/client/src/ce/pages/AdminSettings/config/general.tsx @@ -10,6 +10,10 @@ import { Setting, } from "@appsmith/pages/AdminSettings/config/types"; import BrandingBadge from "pages/AppViewer/BrandingBadge"; +import { TagInput } from "design-system"; +import QuestionFillIcon from "remixicon-react/QuestionFillIcon"; +import localStorage from "utils/localStorage"; +import isUndefined from "lodash/isUndefined"; export const APPSMITH_INSTANCE_NAME_SETTING_SETTING: Setting = { id: "APPSMITH_INSTANCE_NAME", @@ -81,6 +85,83 @@ export const APPSMITH_HIDE_WATERMARK_SETTING: Setting = { "Hello, I would like to upgrade and remove the watermark.", }; +export const APPSMITH_ALLOWED_FRAME_ANCESTORS_SETTING: Setting = { + id: "APPSMITH_ALLOWED_FRAME_ANCESTORS", + name: "APPSMITH_ALLOWED_FRAME_ANCESTORS", + category: SettingCategories.GENERAL, + controlType: SettingTypes.RADIO, + label: "Embed Settings", + controlTypeProps: { + options: [ + { + badge: "NOT RECOMMENDED", + tooltip: { + icon: , + text: + "Lets all domains, including malicious ones, embed your Appsmith apps. ", + linkText: "SEE WHY THIS IS RISKY", + link: + "https://docs.appsmith.com/getting-started/setup/instance-configuration/frame-ancestors#why-should-i-control-this", + }, + label: "Allow embedding everywhere", + value: "ALLOW_EMBEDDING_EVERYWHERE", + }, + { + label: "Limit embedding to certain URLs", + value: "LIMIT_EMBEDDING", + nodeLabel: "You can add one or more URLs", + node: , + nodeInputPath: "input", + nodeParentClass: "tag-input", + }, + { + label: "Disable embedding everywhere", + value: "DISABLE_EMBEDDING_EVERYWHERE", + }, + ], + }, + format: (value: string) => { + if (value === "*") { + return { + value: "ALLOW_EMBEDDING_EVERYWHERE", + }; + } else if (value === "'none'") { + return { + value: "DISABLE_EMBEDDING_EVERYWHERE", + }; + } else { + return { + value: "LIMIT_EMBEDDING", + additionalData: value ? value.replaceAll(" ", ",") : "", + }; + } + }, + parse: (value: { value: string; additionalData?: any }) => { + // Retrieve values from local storage while switching to limit by url option + const sources = isUndefined(value.additionalData) + ? localStorage.getItem("ALLOWED_FRAME_ANCESTORS") ?? "" + : value.additionalData.replaceAll(",", " "); + // If they are one of the other options we don't store it in storage since it will + // set in the env variable on save + if (sources !== "*" && sources !== "'none'") { + localStorage.setItem("ALLOWED_FRAME_ANCESTORS", sources); + } + + if (value.value === "ALLOW_EMBEDDING_EVERYWHERE") { + return "*"; + } else if (value.value === "DISABLE_EMBEDDING_EVERYWHERE") { + return "'none'"; + } else { + return sources; + } + }, + validate: (value: string) => { + if (!value) { + return "This field cannot be empty"; + } + }, +}; + export const config: AdminConfigType = { icon: "settings-2-line", type: SettingCategories.GENERAL, @@ -93,5 +174,6 @@ export const config: AdminConfigType = { APPSMITH_DOWNLOAD_DOCKER_COMPOSE_FILE_SETTING, APPSMITH_DISABLE_TELEMETRY_SETTING, APPSMITH_HIDE_WATERMARK_SETTING, + APPSMITH_ALLOWED_FRAME_ANCESTORS_SETTING, ], } as AdminConfigType; diff --git a/app/client/src/ce/pages/AdminSettings/config/index.ts b/app/client/src/ce/pages/AdminSettings/config/index.ts index b9c86751be73..0a0a10711b1c 100644 --- a/app/client/src/ce/pages/AdminSettings/config/index.ts +++ b/app/client/src/ce/pages/AdminSettings/config/index.ts @@ -6,6 +6,7 @@ import { config as MapsConfig } from "pages/Settings/config/googleMaps"; import { config as VersionConfig } from "pages/Settings/config/version"; import { config as AdvancedConfig } from "pages/Settings/config/advanced"; import { config as Authentication } from "@appsmith/pages/AdminSettings/config/authentication"; +import { config as BrandingConfig } from "@appsmith/pages/AdminSettings/config/branding"; ConfigFactory.register(GeneralConfig); ConfigFactory.register(EmailConfig); @@ -13,5 +14,6 @@ ConfigFactory.register(MapsConfig); ConfigFactory.register(Authentication); ConfigFactory.register(AdvancedConfig); ConfigFactory.register(VersionConfig); +ConfigFactory.register(BrandingConfig); export default ConfigFactory; diff --git a/app/client/src/ce/pages/AdminSettings/config/types.ts b/app/client/src/ce/pages/AdminSettings/config/types.ts index f144a2c1a120..54a5bb79fa21 100644 --- a/app/client/src/ce/pages/AdminSettings/config/types.ts +++ b/app/client/src/ce/pages/AdminSettings/config/types.ts @@ -2,8 +2,32 @@ import React from "react"; import { ReduxAction } from "@appsmith/constants/ReduxActionConstants"; import { Dispatch } from "react"; import { EventName } from "utils/AnalyticsUtil"; +import { RadioProps } from "pages/Settings/FormGroup/Radio"; + +type ControlType = { + [K in keyof ControlPropsType]: { + controlType: K; + controlTypeProps?: ControlPropsType[K]; + }; +}[keyof ControlPropsType]; + +type ControlPropsType = { + [SettingTypes.RADIO]: RadioProps; + [SettingTypes.TEXTINPUT]: unknown; + [SettingTypes.TOGGLE]: unknown; + [SettingTypes.LINK]: unknown; + [SettingTypes.BUTTON]: unknown; + [SettingTypes.GROUP]: unknown; + [SettingTypes.TEXT]: unknown; + [SettingTypes.UNEDITABLEFIELD]: unknown; + [SettingTypes.ACCORDION]: unknown; + [SettingTypes.TAGINPUT]: unknown; + [SettingTypes.DROPDOWN]: unknown; + [SettingTypes.CHECKBOX]: unknown; +}; export enum SettingTypes { + RADIO = "RADIO", TEXTINPUT = "TEXTINPUT", TOGGLE = "TOGGLE", LINK = "LINK", @@ -25,11 +49,12 @@ export enum SettingSubtype { PASSWORD = "password", } -export interface Setting { +export type Setting = ControlType & { id: string; category?: string; - controlType: SettingTypes; controlSubType?: SettingSubtype; + format?: (value: string) => any; + parse?: (value: any) => any; helpText?: string; label?: string; name?: string; @@ -60,7 +85,7 @@ export interface Setting { needsUpgrade?: boolean; upgradeLogEventName?: EventName; upgradeIntercomMessage?: string; -} +}; export interface Category { title: string; @@ -82,6 +107,8 @@ export const SettingCategories = { GOOGLE_AUTH: "google-auth", GITHUB_AUTH: "github-auth", AUDIT_LOGS: "audit-logs", + ACCESS_CONTROL: "access-control", + BRANDING: "branding", }; export const SettingSubCategories = { @@ -101,4 +128,5 @@ export type AdminConfigType = { canSave: boolean; isConnected?: boolean; icon?: string; + needsUpgrade?: boolean; }; diff --git a/app/client/src/ce/pages/AdminSettings/index.tsx b/app/client/src/ce/pages/AdminSettings/index.tsx index 38252d307fcb..5ba11b847abc 100644 --- a/app/client/src/ce/pages/AdminSettings/index.tsx +++ b/app/client/src/ce/pages/AdminSettings/index.tsx @@ -11,20 +11,30 @@ import WithSuperUserHOC from "pages/Settings/WithSuperUserHoc"; import { getCurrentUser } from "selectors/usersSelectors"; import bootIntercom from "utils/bootIntercom"; import { LoaderContainer } from "pages/Settings/components"; +import { useParams } from "react-router"; +import AdminConfig from "@appsmith/pages/AdminSettings/config"; const FlexContainer = styled.div` display: flex; + height: 100%; `; function Settings() { const dispatch = useDispatch(); const user = useSelector(getCurrentUser); const isLoading = useSelector(getSettingsLoadingState); + const params = useParams() as any; + const { category, selected: subCategory } = params; + const isSavable = AdminConfig.savableCategories.includes( + subCategory ?? category, + ); useEffect(() => { - dispatch({ - type: ReduxActionTypes.FETCH_ADMIN_SETTINGS, - }); + if (user?.isSuperUser) { + dispatch({ + type: ReduxActionTypes.FETCH_ADMIN_SETTINGS, + }); + } }, []); useEffect(() => { @@ -32,7 +42,7 @@ function Settings() { }, [user?.email]); return ( - + {isLoading ? ( diff --git a/app/client/src/pages/AppViewer/BackToHomeButton.tsx b/app/client/src/ce/pages/AppViewer/BackToHomeButton.tsx similarity index 91% rename from app/client/src/pages/AppViewer/BackToHomeButton.tsx rename to app/client/src/ce/pages/AppViewer/BackToHomeButton.tsx index cabd61bdfcc4..df2998943ced 100644 --- a/app/client/src/pages/AppViewer/BackToHomeButton.tsx +++ b/app/client/src/ce/pages/AppViewer/BackToHomeButton.tsx @@ -10,7 +10,7 @@ function BackToHomeButton() { return ( , + Prevent accidental damage to data, + Restrict public exposure of sensitive data, + ], design: "split-left-trigger", }; const footer = { - onClick: () => null, - message: "Access control not published yet!", + onClick: () => { + onUpgrade(); + }, + message: createMessage(ACCESS_CONTROL_UPGRADE_PAGE_FOOTER), }; const props = { header, carousel, footer }; return ; diff --git a/app/client/src/ce/pages/Upgrade/AuditLogsUpgradePage.tsx b/app/client/src/ce/pages/Upgrade/AuditLogsUpgradePage.tsx index 6d9b3867483a..51f895e851b6 100644 --- a/app/client/src/ce/pages/Upgrade/AuditLogsUpgradePage.tsx +++ b/app/client/src/ce/pages/Upgrade/AuditLogsUpgradePage.tsx @@ -4,8 +4,6 @@ import UpgradePage from "./UpgradePage"; import DebuggingImage from "assets/svg/upgrade/audit-logs/debugging.svg"; import IncidentManagementImage from "assets/svg/upgrade/audit-logs/incident-management.svg"; import SecurityAndComplianceImage from "assets/svg/upgrade/audit-logs/security-and-compliance.svg"; -import AnalyticsUtil from "../../../utils/AnalyticsUtil"; -import { getAppsmithConfigs } from "../../configs"; import { createMessage } from "design-system/build/constants/messages"; import { AUDIT_LOGS, @@ -19,12 +17,17 @@ import { SECURITY_AND_COMPLIANCE, SECURITY_AND_COMPLIANCE_DETAIL1, SECURITY_AND_COMPLIANCE_DETAIL2, - UPGRADE, -} from "../../constants/messages"; - -const { intercomAppID } = getAppsmithConfigs(); + UPGRADE_TO_EE_FEATURE, +} from "@appsmith/constants/messages"; +import useOnUpgrade from "utils/hooks/useOnUpgrade"; export function AuditLogsUpgradePage() { + const { onUpgrade } = useOnUpgrade({ + logEventName: "ADMIN_SETTINGS_UPGRADE_HOOK", + logEventData: { source: "AuditLogs" }, + intercomMessage: createMessage(UPGRADE_TO_EE_FEATURE, "Audit Logs"), + }); + const header: Header = { heading: createMessage(INTRODUCING, createMessage(AUDIT_LOGS)), subHeadings: [createMessage(AUDIT_LOGS_UPGRADE_PAGE_SUB_HEADING)], @@ -68,12 +71,7 @@ export function AuditLogsUpgradePage() { const footer = { onClick: () => { - AnalyticsUtil.logEvent("ADMIN_SETTINGS_UPGRADE_HOOK", { - source: "AuditLogs", - }); - if (intercomAppID && window.Intercom) { - window.Intercom("showNewMessage", createMessage(UPGRADE)); - } + onUpgrade(); }, message: createMessage(EXCLUSIVE_TO_BUSINESS, ["audit logs"]), }; diff --git a/app/client/src/ce/pages/Upgrade/Carousel.tsx b/app/client/src/ce/pages/Upgrade/Carousel.tsx index 1ac033e7865d..651cbe1665f3 100644 --- a/app/client/src/ce/pages/Upgrade/Carousel.tsx +++ b/app/client/src/ce/pages/Upgrade/Carousel.tsx @@ -9,7 +9,7 @@ const CarouselContainer = styled.div` gap: 64px; justify-content: center; align-items: center; - padding: 16px; + padding: 16px 52px; & .carousel-triggers { display: flex; @@ -18,10 +18,11 @@ const CarouselContainer = styled.div` justify-content: center; align-items: center; width: 50%; + max-width: 420px; & .carousel-trigger { padding: 16px; - width: 384px; + width: 100%; height: 56px; cursor: pointer; @@ -55,10 +56,6 @@ const CarouselContainer = styled.div` */ margin-top: -2px; } - - & .trigger-details-container { - width: 290px; - } } } } @@ -70,8 +67,8 @@ const CarouselContainer = styled.div` } & .carousel-targets { - width: 680px; - height: 472px; + width: 600px; + min-height: 400px; display: flex; justify-content: center; align-items: center; diff --git a/app/client/src/ce/pages/Upgrade/Header.tsx b/app/client/src/ce/pages/Upgrade/Header.tsx index aaa09b355612..5b23d297c1ae 100644 --- a/app/client/src/ce/pages/Upgrade/Header.tsx +++ b/app/client/src/ce/pages/Upgrade/Header.tsx @@ -4,10 +4,9 @@ import { HeaderProps } from "./types"; import { FontWeight, Text, TextType } from "design-system"; export const HeaderContainer = styled.div` - width: 496px; + padding: 32px 32px 20px; + margin: auto; text-align: center; - height: 120px; - padding: 32px; & .header-heading-container { & .cs-text { @@ -17,6 +16,8 @@ export const HeaderContainer = styled.div` } & .header-subHeadings-container { + margin: 8px auto; + max-width: 720px; & .header-subHeading-container { & .cs-text { font-size: 16px; diff --git a/app/client/src/ce/pages/Upgrade/UpgradePage.tsx b/app/client/src/ce/pages/Upgrade/UpgradePage.tsx index 484e572ec974..eb8c44af8dab 100644 --- a/app/client/src/ce/pages/Upgrade/UpgradePage.tsx +++ b/app/client/src/ce/pages/Upgrade/UpgradePage.tsx @@ -6,18 +6,13 @@ import { FooterComponent as Footer } from "./Footer"; import { UpgradePageProps } from "./types"; export const Container = styled.div` - display: flex; - flex-direction: column; - flex: 1 1; border-left: thin solid var(--appsmith-color-black-50); background-color: var(--ads-color-black-50); - align-items: center; - justify-items: center; - height: calc(100vh - 50px - 112px); - min-width: 1180px; + height: auto; min-height: 0; overflow: auto; - gap: 32px; + height: calc(100vh - 50px - 112px); + width: 100%; `; export default function UpgradePage(props: UpgradePageProps) { diff --git a/app/client/src/ce/pages/UserAuth/Login.tsx b/app/client/src/ce/pages/UserAuth/Login.tsx index d61555c2f80d..564a23e8098a 100644 --- a/app/client/src/ce/pages/UserAuth/Login.tsx +++ b/app/client/src/ce/pages/UserAuth/Login.tsx @@ -29,6 +29,7 @@ import { LOGIN_PAGE_INVALID_CREDS_FORGOT_PASSWORD_LINK, NEW_TO_APPSMITH, createMessage, + LOGIN_PAGE_SUBTITLE, } from "@appsmith/constants/messages"; import { Button, FormGroup, FormMessage, Size } from "design-system"; import FormTextField from "components/utils/ReduxFormTextField"; @@ -42,9 +43,6 @@ import { Theme } from "constants/DefaultTheme"; import { SpacedSubmitForm, FormActions, - AuthCardHeader, - AuthCardNavLink, - SignUpLinkSection, ForgotPasswordLink, } from "pages/UserAuth/StyledComponents"; import AnalyticsUtil from "utils/AnalyticsUtil"; @@ -55,6 +53,7 @@ import PerformanceTracker, { } from "utils/PerformanceTracker"; import { getIsSafeRedirectURL } from "utils/helpers"; import { getCurrentUser } from "selectors/usersSelectors"; +import Container from "pages/UserAuth/Container"; const { disableLoginForm } = getAppsmithConfigs(); const validate = (values: LoginFormValues, props: ValidateProps) => { @@ -97,6 +96,9 @@ export function Login(props: LoginFormProps) { const location = useLocation(); const socialLoginList = ThirdPartyLoginRegistry.get(); const queryParams = new URLSearchParams(location.search); + const invalidCredsForgotPasswordLinkText = createMessage( + LOGIN_PAGE_INVALID_CREDS_FORGOT_PASSWORD_LINK, + ); let showError = false; let errorMessage = ""; const currentUser = useSelector(getCurrentUser); @@ -121,23 +123,24 @@ export function Login(props: LoginFormProps) { forgotPasswordURL += `?email=${props.emailValue}`; } + const footerSection = !disableLoginForm && ( +
+ {createMessage(NEW_TO_APPSMITH)} + + {createMessage(LOGIN_PAGE_SIGN_UP_LINK_TEXT)} + +
+ ); + return ( - <> - -

{createMessage(LOGIN_PAGE_TITLE)}

-
- {!disableLoginForm && ( - - {createMessage(NEW_TO_APPSMITH)} - - {createMessage(LOGIN_PAGE_SIGN_UP_LINK_TEXT)} - - - )} + {showError && ( + {invalidCredsForgotPasswordLinkText} + ), + text: invalidCredsForgotPasswordLinkText, intent: "success", }, ] } intent="danger" + linkAs={Link} message={ !!errorMessage && errorMessage !== "true" ? errorMessage @@ -217,7 +223,7 @@ export function Login(props: LoginFormProps) { )} - + ); } diff --git a/app/client/src/ce/pages/UserAuth/SignUp.tsx b/app/client/src/ce/pages/UserAuth/SignUp.tsx index bfe4c169079f..4801177de2a3 100644 --- a/app/client/src/ce/pages/UserAuth/SignUp.tsx +++ b/app/client/src/ce/pages/UserAuth/SignUp.tsx @@ -7,14 +7,9 @@ import { useHistory, useLocation, withRouter, + Link, } from "react-router-dom"; -import { - AuthCardHeader, - AuthCardNavLink, - SpacedSubmitForm, - FormActions, - SignUpLinkSection, -} from "pages/UserAuth/StyledComponents"; +import { SpacedSubmitForm, FormActions } from "pages/UserAuth/StyledComponents"; import { SIGNUP_PAGE_TITLE, SIGNUP_PAGE_EMAIL_INPUT_LABEL, @@ -28,6 +23,7 @@ import { SIGNUP_PAGE_SUBMIT_BUTTON_TEXT, ALREADY_HAVE_AN_ACCOUNT, createMessage, + SIGNUP_PAGE_SUBTITLE, } from "@appsmith/constants/messages"; import FormTextField from "components/utils/ReduxFormTextField"; import ThirdPartyAuth from "@appsmith/pages/UserAuth/ThirdPartyAuth"; @@ -53,6 +49,7 @@ import { useScript, ScriptStatus, AddScriptTo } from "utils/hooks/useScript"; import { withTheme } from "styled-components"; import { Theme } from "constants/DefaultTheme"; import { getIsSafeRedirectURL } from "utils/helpers"; +import Container from "pages/UserAuth/Container"; declare global { interface Window { @@ -151,21 +148,25 @@ export function SignUp(props: SignUpFormProps) { } }; + const footerSection = ( +
+ {createMessage(ALREADY_HAVE_AN_ACCOUNT)} + + {createMessage(SIGNUP_PAGE_LOGIN_LINK_TEXT)} + +
+ ); + return ( - <> + {showError && } - -

{createMessage(SIGNUP_PAGE_TITLE)}

-
- - {createMessage(ALREADY_HAVE_AN_ACCOUNT)} - - {createMessage(SIGNUP_PAGE_LOGIN_LINK_TEXT)} - - {socialLoginList.length > 0 && ( )} @@ -220,7 +221,7 @@ export function SignUp(props: SignUpFormProps) { )} - +
); } diff --git a/app/client/src/ce/pages/workspace/Members.tsx b/app/client/src/ce/pages/workspace/Members.tsx index 686f965a70f0..c729663c4ab3 100644 --- a/app/client/src/ce/pages/workspace/Members.tsx +++ b/app/client/src/ce/pages/workspace/Members.tsx @@ -360,17 +360,18 @@ export default function MemberSettings(props: PageProps) { ? allRoles.map((role: any) => { return { id: role.id, - name: role.name, + name: role.name?.split(" - ")[0], desc: role.description, }; }) : []; const index = roles.findIndex( (role: { id: string; name: string; desc: string }) => - role.name === cellProps.cell.value, + role.name?.split(" - ")[0] === + cellProps.cell.value?.split(" - ")[0], ); if (data.username === currentUser?.username) { - return cellProps.cell.value; + return cellProps.cell.value?.split(" - ")[0]; } return ( { return { id: role.id, - value: role.name, + value: role.name?.split(" - ")[0], label: role.description, }; }) @@ -476,7 +477,7 @@ export default function MemberSettings(props: PageProps) { {isOwner && ( - {member.permissionGroupName} + {member.permissionGroupName?.split(" - ")[0]} )} {!isOwner && ( diff --git a/app/client/src/ce/pages/workspace/WorkspaceInviteUsersForm.tsx b/app/client/src/ce/pages/workspace/WorkspaceInviteUsersForm.tsx index d9d4af35a694..d8aa3e52a99f 100644 --- a/app/client/src/ce/pages/workspace/WorkspaceInviteUsersForm.tsx +++ b/app/client/src/ce/pages/workspace/WorkspaceInviteUsersForm.tsx @@ -376,7 +376,7 @@ function WorkspaceInviteUsersForm(props: any) { : props.roles.map((role: any) => { return { id: role.id, - value: role.name, + value: role.name?.split(" - ")[0], label: role.description, }; }); @@ -565,7 +565,7 @@ function WorkspaceInviteUsersForm(props: any) { - {user.permissionGroupName} + {user.permissionGroupName?.split(" - ")[0]} diff --git a/app/client/src/ce/reducers/index.tsx b/app/client/src/ce/reducers/index.tsx index d8f1c5d2156e..f5afe8ae3528 100644 --- a/app/client/src/ce/reducers/index.tsx +++ b/app/client/src/ce/reducers/index.tsx @@ -61,6 +61,7 @@ import SettingsReducer, { import { GuidedTourState } from "reducers/uiReducers/guidedTourReducer"; import { TriggerValuesEvaluationState } from "reducers/evaluationReducers/triggerReducer"; import { CanvasWidgetStructure } from "widgets/constants"; +import { AppSettingsPaneReduxState } from "reducers/uiReducers/appSettingsPaneReducer"; import tenantReducer, { TenantReduxState, } from "@appsmith/reducers/tenantReducer"; @@ -70,6 +71,7 @@ import { AutoHeightLayoutTreeReduxState } from "reducers/entityReducers/autoHeig import { CanvasLevelsReduxState } from "reducers/entityReducers/autoHeightReducers/canvasLevelsReducer"; import { LintErrors } from "reducers/lintingReducers/lintErrorsReducers"; import lintErrorReducer from "reducers/lintingReducers"; +import { AutoHeightUIState } from "reducers/uiReducers/autoHeightReducer"; export const reducerObject = { entities: entityReducer, @@ -124,8 +126,10 @@ export interface AppState { widgetReflow: widgetReflow; appTheming: AppThemingState; mainCanvas: MainCanvasReduxState; + appSettingsPane: AppSettingsPaneReduxState; focusHistory: FocusHistoryState; editorContext: EditorContextState; + autoHeightUI: AutoHeightUIState; }; entities: { canvasWidgetsStructure: CanvasWidgetStructure; diff --git a/app/client/src/ce/reducers/tenantReducer.ts b/app/client/src/ce/reducers/tenantReducer.ts index a025ce2d3af0..447c42b0d6c0 100644 --- a/app/client/src/ce/reducers/tenantReducer.ts +++ b/app/client/src/ce/reducers/tenantReducer.ts @@ -3,17 +3,30 @@ import { ReduxActionErrorTypes, ReduxActionTypes, } from "@appsmith/constants/ReduxActionConstants"; +import { createBrandColorsFromPrimaryColor } from "utils/BrandingUtils"; import { createReducer } from "utils/ReducerUtils"; export interface TenantReduxState { userPermissions: string[]; - tenantConfiguration: Record; + tenantConfiguration: Record; new: boolean; } +export const defaultBrandingConfig = { + brandFaviconUrl: "https://assets.appsmith.com/appsmith-favicon-orange.ico", + brandColors: { + ...createBrandColorsFromPrimaryColor("#F86A2B"), + }, + brandLogoUrl: "https://assets.appsmith.com/appsmith-logo.svg", +}; + export const initialState: TenantReduxState = { userPermissions: [], - tenantConfiguration: {}, + tenantConfiguration: { + brandColors: { + ...createBrandColorsFromPrimaryColor("#000"), + }, + }, new: false, }; @@ -23,7 +36,11 @@ export const handlers = { action: ReduxAction, ) => ({ ...state, - ...action.payload, + userPermissions: action.payload.userPermissions || [], + tenantConfiguration: { + ...defaultBrandingConfig, + ...action.payload.tenantConfiguration, + }, }), [ReduxActionErrorTypes.FETCH_CURRENT_TENANT_CONFIG_ERROR]: ( state: TenantReduxState, diff --git a/app/client/src/ce/reducers/uiReducers/applicationsReducer.tsx b/app/client/src/ce/reducers/uiReducers/applicationsReducer.tsx index f6cb0fc6ead9..7ea147b6d3a4 100644 --- a/app/client/src/ce/reducers/uiReducers/applicationsReducer.tsx +++ b/app/client/src/ce/reducers/uiReducers/applicationsReducer.tsx @@ -13,10 +13,11 @@ import { createMessage, ERROR_MESSAGE_CREATE_APPLICATION, } from "@appsmith/constants/messages"; -import { UpdateApplicationRequest } from "api/ApplicationApi"; +import { PageDefaultMeta, UpdateApplicationRequest } from "api/ApplicationApi"; import { CreateApplicationFormValues } from "pages/Applications/helpers"; import { AppLayoutConfig } from "reducers/entityReducers/pageListReducer"; import { ConnectToGitResponse } from "actions/gitSyncActions"; +import { AppIconName } from "design-system"; export const initialState: ApplicationsReduxState = { isFetchingApplications: false, @@ -141,6 +142,16 @@ export const handlers = { slug: action.payload.slug, }, }), + [ReduxActionTypes.CURRENT_APPLICATION_ICON_UPDATE]: ( + state: ApplicationsReduxState, + action: ReduxAction, + ) => ({ + ...state, + currentApplication: { + ...state.currentApplication, + icon: action.payload, + }, + }), [ReduxActionTypes.CURRENT_APPLICATION_LAYOUT_UPDATE]: ( state: ApplicationsReduxState, action: ReduxAction<{ appLayout: AppLayoutConfig }>, @@ -533,7 +544,7 @@ export interface Application { appIsExample: boolean; new: boolean; defaultPageId: string; - pages: Array<{ id: string; isDefault: boolean; default: boolean }>; + pages: PageDefaultMeta[]; userPermissions: string[]; } diff --git a/app/client/src/ce/sagas/SuperUserSagas.tsx b/app/client/src/ce/sagas/SuperUserSagas.tsx index c5a4fad9916d..1fc0a080b20f 100644 --- a/app/client/src/ce/sagas/SuperUserSagas.tsx +++ b/app/client/src/ce/sagas/SuperUserSagas.tsx @@ -40,6 +40,14 @@ export function* FetchAdminSettingsSaga() { cloudHosting, ), }; + + // Converting empty values to boolean false + Object.keys(settings).forEach((key) => { + if ((settings[key] as string).trim() === "") { + settings[key] = false; + } + }); + yield put({ type: ReduxActionTypes.FETCH_ADMIN_SETTINGS_SUCCESS, payload: settings, @@ -57,9 +65,13 @@ export function* FetchAdminSettingsErrorSaga() { } export function* SaveAdminSettingsSaga( - action: ReduxAction>, + action: ReduxAction<{ + settings: Record; + needsRestart: boolean; + }>, ) { - const settings = action.payload; + const { needsRestart = true, settings } = action.payload; + try { const response: ApiResponse = yield call( UserApi.saveAdminSettings, @@ -75,13 +87,21 @@ export function* SaveAdminSettingsSaga( yield put({ type: ReduxActionTypes.SAVE_ADMIN_SETTINGS_SUCCESS, }); + yield put({ - type: ReduxActionTypes.FETCH_ADMIN_SETTINGS_SUCCESS, - payload: settings, + type: ReduxActionTypes.FETCH_CURRENT_TENANT_CONFIG, }); + yield put({ - type: ReduxActionTypes.RESTART_SERVER_POLL, + type: ReduxActionTypes.FETCH_ADMIN_SETTINGS_SUCCESS, + payload: settings, }); + + if (needsRestart) { + yield put({ + type: ReduxActionTypes.RESTART_SERVER_POLL, + }); + } } else { yield put({ type: ReduxActionTypes.SAVE_ADMIN_SETTINGS_ERROR, diff --git a/app/client/src/ce/sagas/index.tsx b/app/client/src/ce/sagas/index.tsx index 9e1b2b9d37ce..1b3265d4465d 100644 --- a/app/client/src/ce/sagas/index.tsx +++ b/app/client/src/ce/sagas/index.tsx @@ -28,7 +28,6 @@ import utilSagas from "sagas/UtilSagas"; import saaSPaneSagas from "sagas/SaaSPaneSagas"; import actionExecutionChangeListeners from "sagas/WidgetLoadingSaga"; import globalSearchSagas from "sagas/GlobalSearchSagas"; -import recentEntitiesSagas from "sagas/RecentEntitiesSagas"; import websocketSagas from "sagas/WebsocketSagas/WebsocketSagas"; import debuggerSagas from "sagas/DebuggerSagas"; import replaySaga from "sagas/ReplaySaga"; @@ -41,6 +40,7 @@ import SuperUserSagas from "@appsmith/sagas/SuperUserSagas"; import NavigationSagas from "sagas/NavigationSagas"; import editorContextSagas from "sagas/editorContextSagas"; import PageVisibilitySaga from "sagas/PageVisibilitySagas"; +import AutoHeightSagas from "sagas/autoHeightSagas"; import tenantSagas from "@appsmith/sagas/tenantSagas"; import layoutUpdateSagas from "sagas/LayoutUpdateSagas"; import autoLayoutDraggingSagas from "sagas/CanvasSagas/AutoLayoutDraggingSagas"; @@ -76,7 +76,6 @@ export const sagas = [ formEvaluationChangeListener, utilSagas, globalSearchSagas, - recentEntitiesSagas, websocketSagas, debuggerSagas, saaSPaneSagas, @@ -89,6 +88,7 @@ export const sagas = [ NavigationSagas, editorContextSagas, PageVisibilitySaga, + AutoHeightSagas, tenantSagas, layoutUpdateSagas, autoLayoutDraggingSagas, diff --git a/app/client/src/ce/sagas/tenantSagas.tsx b/app/client/src/ce/sagas/tenantSagas.tsx index 56ff39134b51..0039e8de2492 100644 --- a/app/client/src/ce/sagas/tenantSagas.tsx +++ b/app/client/src/ce/sagas/tenantSagas.tsx @@ -2,7 +2,6 @@ import { ReduxActionTypes, ReduxActionErrorTypes, } from "@appsmith/constants/ReduxActionConstants"; -import { PERMISSION_TYPE } from "@appsmith/utils/permissionHelpers"; import { put } from "redux-saga/effects"; // On CE we don't expose tenant config so this shouldn't make any API calls and should just return necessary permissions for the user @@ -10,9 +9,7 @@ export function* fetchCurrentTenantConfigSaga() { try { yield put({ type: ReduxActionTypes.FETCH_CURRENT_TENANT_CONFIG_SUCCESS, - payload: { - userPermissions: [PERMISSION_TYPE.CREATE_WORKSPACE], - }, + payload: {}, }); } catch (error) { yield put({ diff --git a/app/client/src/ce/selectors/tenantSelectors.tsx b/app/client/src/ce/selectors/tenantSelectors.tsx index ce35412c835b..0467a234b646 100644 --- a/app/client/src/ce/selectors/tenantSelectors.tsx +++ b/app/client/src/ce/selectors/tenantSelectors.tsx @@ -3,3 +3,22 @@ import { AppState } from "@appsmith/reducers"; export const getTenantPermissions = (state: AppState) => { return state.tenant?.userPermissions; }; + +/** + * selects the tenant config + * + * @param state + * @returns + */ +export const getTenantConfig = (state: AppState) => { + return state.tenant?.tenantConfiguration; +}; + +/** + * selects the tenant brand colors + * + * @returns + */ +export const getBrandColors = () => { + return {} as Record; +}; diff --git a/app/client/src/ce/utils/adminSettingsHelpers.ts b/app/client/src/ce/utils/adminSettingsHelpers.ts index 04b97c286270..64ed8bce9c84 100644 --- a/app/client/src/ce/utils/adminSettingsHelpers.ts +++ b/app/client/src/ce/utils/adminSettingsHelpers.ts @@ -1,4 +1,6 @@ import { getAppsmithConfigs } from "@appsmith/configs"; +import { ADMIN_SETTINGS_CATEGORY_DEFAULT_PATH } from "constants/routes"; +import { User } from "constants/userConstants"; const { disableLoginForm, enableGithubOAuth, @@ -29,3 +31,15 @@ export const saveAllowed = (settings: any) => { return connectedMethods.length >= 2; } }; + +/* get default admin settings path */ +export const getDefaultAdminSettingsPath = ( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + { isSuperUser, tenantPermissions = [] }: Record, +): string => { + return ADMIN_SETTINGS_CATEGORY_DEFAULT_PATH; +}; + +export const showAdminSettings = (user?: User): boolean => { + return (user?.isSuperUser && user?.isConfigurable) || false; +}; diff --git a/app/client/src/ce/utils/permissionHelpers.tsx b/app/client/src/ce/utils/permissionHelpers.tsx index 6adca2c528d0..5ce863e1c616 100644 --- a/app/client/src/ce/utils/permissionHelpers.tsx +++ b/app/client/src/ce/utils/permissionHelpers.tsx @@ -1,16 +1,22 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ export enum PERMISSION_TYPE { /* Workspace permissions */ - CREATE_WORKSPACE = "create:workspaces", + CREATE_WORKSPACE = "createWorkspaces:tenant", MANAGE_WORKSPACE = "manage:workspaces", READ_WORKSPACE = "read:workspaces", INVITE_USER_TO_WORKSPACE = "inviteUsers:workspace", /* Application permissions */ - CREATE_APPLICATION = "manage:workspaceApplications", + MANAGE_WORKSPACE_APPLICATION = "manage:workspaceApplications", MANAGE_APPLICATION = "manage:applications", EXPORT_APPLICATION = "export:applications", + DELETE_WORKSPACE_APPLICATIONS = "delete:workspaceApplications", + READ_WORKSPACE_APPLICATIONS = "read:workspaceApplications", + EXPORT_WORKSPACE_APPLICATIONS = "export:workspaceApplications", READ_APPLICATION = "read:applications", MAKE_PUBLIC_APPLICATION = "makePublic:applications", + MAKE_PUBLIC_WORKSPACE_APPLICATIONS = "makePublic:workspaceApplications", PUBLISH_APPLICATION = "publish:workspaceApplications", + CREATE_APPLICATION = "create:applications", /* Datasource permissions */ CREATE_DATASOURCES = "create:datasources", EXECUTE_DATASOURCES = "execute:datasources", @@ -18,6 +24,8 @@ export enum PERMISSION_TYPE { DELETE_DATASOURCES = "delete:datasources", MANAGE_DATASOURCES = "manage:datasources", EXECUTE_WORKSPACE_DATASOURCES = "execute:workspaceDatasources", + MANAGE_WORKSPACE_DATASOURCES = "manage:workspaceDatasources", + READ_WORKSPACE_DATASOURCES = "read:workspaceDatasources", /* Page permissions */ CREATE_PAGES = "create:pages", MANAGE_PAGES = "manage:pages", @@ -48,3 +56,40 @@ export const isPermitted = ( } return permissions.includes(type); }; + +export const hasDeleteApplicationPermission = (permissions: string[] = []) => { + return isPermitted(permissions, PERMISSION_TYPE.MANAGE_APPLICATION); +}; + +export const hasCreateNewAppPermission = (permissions: string[] = []) => { + return isPermitted(permissions, PERMISSION_TYPE.CREATE_APPLICATION); +}; + +export const hasDeleteWorkspacePermission = (permissions: string[] = []) => { + return isPermitted(permissions, PERMISSION_TYPE.MANAGE_WORKSPACE); +}; + +export const hasCreateWorkspacePermission = (_permissions?: string[]) => true; + +export const hasCreateDatasourcePermission = (_permissions?: string[]) => true; + +export const hasManageDatasourcePermission = (_permissions?: string[]) => true; + +export const hasDeleteDatasourcePermission = (_permissions?: string[]) => true; + +export const hasCreateDatasourceActionPermission = (_permissions?: string[]) => + true; + +export const hasCreatePagePermission = (_permissions?: string[]) => true; + +export const hasManagePagePermission = (_permissions?: string[]) => true; + +export const hasDeletePagePermission = (_permissions?: string[]) => true; + +export const hasCreateActionPermission = (_permissions?: string[]) => true; + +export const hasManageActionPermission = (_permissions?: string[]) => true; + +export const hasDeleteActionPermission = (_permissions?: string[]) => true; + +export const hasExecuteActionPermission = (_permissions?: string[]) => true; diff --git a/app/client/src/components/autoHeight/AutoHeightContainer.test.tsx b/app/client/src/components/autoHeight/AutoHeightContainer.test.tsx index c4074f93b583..3c6c581ba6d5 100644 --- a/app/client/src/components/autoHeight/AutoHeightContainer.test.tsx +++ b/app/client/src/components/autoHeight/AutoHeightContainer.test.tsx @@ -16,15 +16,16 @@ describe("", () => { maxDynamicHeight={0} minDynamicHeight={0} onHeightUpdate={onHeightUpdate} + widgetHeightInPixels={200} >
, ) .toJSON(); - expect(tree).toHaveStyleRule("height", "auto"); + expect(tree).toHaveStyleRule("height", "auto !important"); }); - describe("when isAutoHeightWithLimits is false", () => { + describe("when isAutoHeightWithLimits is false.", () => { it("should wrap the children in a simple div with class auto-height-container", async () => { const getTestComponent = () => ( ", () => { maxDynamicHeight={0} minDynamicHeight={0} onHeightUpdate={onHeightUpdate} + widgetHeightInPixels={200} >
@@ -53,6 +55,7 @@ describe("", () => { maxDynamicHeight={0} minDynamicHeight={0} onHeightUpdate={onHeightUpdate} + widgetHeightInPixels={200} >
diff --git a/app/client/src/components/autoHeight/AutoHeightContainer.tsx b/app/client/src/components/autoHeight/AutoHeightContainer.tsx index 30b91d1af528..a9be60af326d 100644 --- a/app/client/src/components/autoHeight/AutoHeightContainer.tsx +++ b/app/client/src/components/autoHeight/AutoHeightContainer.tsx @@ -1,10 +1,13 @@ -import React, { PropsWithChildren, useRef, useEffect, useState } from "react"; -import { GridDefaults } from "constants/WidgetConstants"; +import React, { PropsWithChildren, useEffect, useRef, useState } from "react"; +import { GridDefaults, WIDGET_PADDING } from "constants/WidgetConstants"; import styled from "styled-components"; +import { WidgetProps } from "widgets/BaseWidget"; const StyledAutoHeightContainer = styled.div<{ isOverflow?: boolean }>` overflow-y: ${(props) => (props.isOverflow ? "auto" : "unset")}; overflow-x: ${(props) => (props.isOverflow ? "hidden" : "unset")}; + padding-right: 4px; + height: 100%; `; interface AutoHeightContainerProps { @@ -12,10 +15,12 @@ interface AutoHeightContainerProps { minDynamicHeight: number; isAutoHeightWithLimits: boolean; onHeightUpdate: (height: number) => void; + widgetHeightInPixels: number; + widgetProps?: WidgetProps; } const SimpleContainer = styled.div` - height: auto; + height: auto !important; `; export default function AutoHeightContainer({ @@ -24,12 +29,14 @@ export default function AutoHeightContainer({ maxDynamicHeight, minDynamicHeight, onHeightUpdate, + widgetHeightInPixels, + widgetProps, }: PropsWithChildren) { const [expectedHeight, setExpectedHeight] = useState(0); const ref = useRef(null); - const observer = useRef( + const observer = React.useRef( new ResizeObserver((entries) => { const height = entries[0].contentRect.height; setExpectedHeight(height); @@ -53,15 +60,34 @@ export default function AutoHeightContainer({ onHeightUpdate(expectedHeight); }, [minDynamicHeight, maxDynamicHeight]); + useEffect(() => { + if ( + widgetHeightInPixels !== + Math.ceil( + Math.ceil(expectedHeight + WIDGET_PADDING * 2) / + GridDefaults.DEFAULT_GRID_ROW_HEIGHT, + ) * + GridDefaults.DEFAULT_GRID_ROW_HEIGHT + ) { + onHeightUpdate(expectedHeight); + } + }, [widgetHeightInPixels]); + if (isAutoHeightWithLimits) { const expectedHeightInRows = Math.ceil( expectedHeight / GridDefaults.DEFAULT_GRID_ROW_HEIGHT, ); + const backgroundColor = + widgetProps?.type === "TEXT_WIDGET" + ? widgetProps?.backgroundColor + : undefined; + return ( {children} diff --git a/app/client/src/components/autoHeight/AutoHeightContainerWrapper.tsx b/app/client/src/components/autoHeight/AutoHeightContainerWrapper.tsx new file mode 100644 index 000000000000..b0a228e88d18 --- /dev/null +++ b/app/client/src/components/autoHeight/AutoHeightContainerWrapper.tsx @@ -0,0 +1,51 @@ +import { GridDefaults } from "constants/WidgetConstants"; +import React, { ReactNode } from "react"; +import useWidgetConfig from "utils/hooks/useWidgetConfig"; +import { DynamicHeight } from "utils/WidgetFeatures"; +import { WidgetProps } from "widgets/BaseWidget"; +import { + getWidgetMaxAutoHeight, + getWidgetMinAutoHeight, +} from "widgets/WidgetUtils"; +import AutoHeightContainer from "./AutoHeightContainer"; + +export type AutoHeightWrapperProps = { + widgetProps: WidgetProps; + children: ReactNode; + onUpdateDynamicHeight: (height: number) => void; +}; + +function AutoHeightContainerWrapper(props: AutoHeightWrapperProps) { + const { children, widgetProps } = props; + const isCanvas = useWidgetConfig(widgetProps.type, "isCanvas"); + // eslint-disable-next-line react/jsx-no-useless-fragment + if (isCanvas) return <>{children}; + + const onHeightUpdate = (height: number) => { + props.onUpdateDynamicHeight(height); + }; + + const maxDynamicHeight = getWidgetMaxAutoHeight(widgetProps); + const minDynamicHeight = getWidgetMinAutoHeight(widgetProps); + + const widgetHeightInPixels = + (widgetProps.bottomRow - widgetProps.topRow) * + GridDefaults.DEFAULT_GRID_ROW_HEIGHT; + const isAutoHeightWithLimits = + widgetProps.dynamicHeight === DynamicHeight.AUTO_HEIGHT_WITH_LIMITS; + + return ( + + {children} + + ); +} + +export default AutoHeightContainerWrapper; diff --git a/app/client/src/components/autoHeightOverlay/AutoHeightLimitHandleGroup.tsx b/app/client/src/components/autoHeightOverlay/AutoHeightLimitHandleGroup.tsx new file mode 100644 index 000000000000..91c510432943 --- /dev/null +++ b/app/client/src/components/autoHeightOverlay/AutoHeightLimitHandleGroup.tsx @@ -0,0 +1,164 @@ +import React, { useRef } from "react"; +import styled from "styled-components"; +import AutoHeightLimitHandleBorder from "./ui/AutoHeightLimitHandleBorder"; +import { useDrag } from "react-use-gesture"; +import { heightToRows } from "./utils"; +import AutoHeightLimitHandleLabel from "./ui/AutoHeightLimitHandleLabel"; +import { onDragCallbacksProps, onMouseHoverCallbacksProps } from "./types"; +import AutoHeightLimitHandleDot from "./ui/AutoHeightLimitHandleDot"; + +const AutoHeightLimitHandleGroupContainer = styled.div` + position: absolute; + left: 50%; + transform: translateX(-50%); + pointer-events: all; + width: 100%; + z-index: 1; +`; + +interface AutoHeightLimitHandleGroupProps { + isMaxDotActive: boolean; + isMinDotActive: boolean; + isMaxDotDragging: boolean; + isMinDotDragging: boolean; + maxY: number; + minY: number; + onMaxLimitDragCallbacks: onDragCallbacksProps; + onMinLimitDragCallbacks: onDragCallbacksProps; + onMaxHeightSet: (height: number) => void; + onMinHeightSet: (height: number) => void; + onMaxLimitMouseHoverCallbacks: onMouseHoverCallbacksProps; + onMinLimitMouseHoverCallbacks: onMouseHoverCallbacksProps; +} + +interface AutoHeightLimitHandleContainerProps { + height: number; +} + +const AutoHeightLimitHandleContainer = styled.div< + AutoHeightLimitHandleContainerProps +>` + position: absolute; + display: flex; + align-items: center; + width: 100%; + height: 13px; + transform: translateY(${(props) => props.height - 6}px); + cursor: ns-resize; + display: flex; + align-items: center; +`; + +interface AutoHeightLimitHandleProps { + cypressDataID: string; + height: number; + isActive: boolean; + isColliding: boolean; + isDragging: boolean; + label: string; + onDragCallbacks: onDragCallbacksProps; + onMouseHoverFunctions: onMouseHoverCallbacksProps; +} + +const AutoHeightLimitHandle = ({ + cypressDataID, + height, + isActive, + isColliding, + isDragging, + label, + onDragCallbacks, + onMouseHoverFunctions, +}: AutoHeightLimitHandleProps) => { + const ref = useRef(null); + const { onStart, onStop, onUpdate } = onDragCallbacks; + + const bind = useDrag((state) => { + if (state.first) { + onStart(); + return; + } + + if (state.last) { + onStop(); + return; + } + const [mx, my] = state.movement; + + onUpdate(mx, my); + }); + + const bindings = bind(); + + return ( + { + e.stopPropagation(); + }} + onDragStart={(e) => { + e.preventDefault(); + e.stopPropagation(); + }} + onMouseDown={(e) => { + e.preventDefault(); + e.stopPropagation(); + bindings?.onMouseDown && bindings.onMouseDown(e); + }} + {...onMouseHoverFunctions} + > + + + {!isColliding ? ( + + {label}: {heightToRows(height)} rows + + ) : null} + + ); +}; + +const AutoHeightLimitHandleGroup: React.FC = ({ + isMaxDotActive, + isMaxDotDragging, + isMinDotActive, + isMinDotDragging, + maxY, + minY, + onMaxLimitDragCallbacks, + onMaxLimitMouseHoverCallbacks, + onMinLimitDragCallbacks, + onMinLimitMouseHoverCallbacks, +}) => { + const isColliding = maxY === minY; + + return ( + + + + + ); +}; + +export default AutoHeightLimitHandleGroup; diff --git a/app/client/src/components/autoHeightOverlay/constants.ts b/app/client/src/components/autoHeightOverlay/constants.ts new file mode 100644 index 000000000000..67144554ff13 --- /dev/null +++ b/app/client/src/components/autoHeightOverlay/constants.ts @@ -0,0 +1,5 @@ +export const OVERLAY_COLOR = "#F32B8B"; + +// During dragging we have to scale the size +// of the dot to increase its focus +export const OVERLAY_HANDLE_DOT_DRAGGING_SCALE = "1.67"; diff --git a/app/client/src/components/autoHeightOverlay/hooks.ts b/app/client/src/components/autoHeightOverlay/hooks.ts new file mode 100644 index 000000000000..6243f6d81180 --- /dev/null +++ b/app/client/src/components/autoHeightOverlay/hooks.ts @@ -0,0 +1,144 @@ +import { CONTAINER_GRID_PADDING } from "constants/WidgetConstants"; +import { CSSProperties, useEffect, useMemo, useState } from "react"; +import { CallbackHandlerEventType } from "utils/CallbackHandler/CallbackHandlerEventType"; +import DynamicHeightCallbackHandler from "utils/CallbackHandler/DynamicHeightCallbackHandler"; +import { onMouseHoverCallbacksProps } from "./types"; + +type UseHoverStateReturnType = [boolean, onMouseHoverCallbacksProps]; + +export function useHoverState(): UseHoverStateReturnType { + const [isActive, setIsActive] = useState(false); + + function handleMouseEnter(state: boolean) { + setIsActive(state); + } + + return [ + isActive, + { + onMouseEnter: () => handleMouseEnter(true), + onMouseLeave: () => handleMouseEnter(false), + }, + ]; +} + +interface UsePositionedStylesProps { + bottomRow: number; + leftColumn: number; + noContainerOffset?: boolean; + parentColumnSpace: number; + parentRowSpace: number; + rightColumn: number; + topRow: number; +} + +export const usePositionedStyles = ({ + bottomRow, + leftColumn, + noContainerOffset, + parentColumnSpace, + parentRowSpace, + rightColumn, + topRow, +}: UsePositionedStylesProps) => { + const styles: CSSProperties = useMemo( + () => ({ + height: (bottomRow - topRow) * parentRowSpace, + width: (rightColumn - leftColumn) * parentColumnSpace, + left: + leftColumn * parentColumnSpace + + (noContainerOffset ? 0 : CONTAINER_GRID_PADDING), + top: + topRow * parentRowSpace + + (noContainerOffset ? 0 : CONTAINER_GRID_PADDING), + }), + [ + bottomRow, + leftColumn, + noContainerOffset, + parentColumnSpace, + parentRowSpace, + rightColumn, + topRow, + ], + ); + + return styles; +}; + +export const useMaxMinPropertyPaneFieldsFocused = () => { + const [ + isPropertyPaneMinFieldFocused, + setPropertyPaneMinFieldFocused, + ] = useState(false); + + const [ + isPropertyPaneMaxFieldFocused, + setPropertyPaneMaxFieldFocused, + ] = useState(false); + + function handleOnMaxLimitPropertyPaneFieldFocus() { + setPropertyPaneMaxFieldFocused(true); + } + + function handleOnMaxLimitPropertyPaneFieldBlur() { + setPropertyPaneMaxFieldFocused(false); + } + + function handleOnMinLimitPropertyPaneFieldFocus() { + setPropertyPaneMinFieldFocused(true); + } + + function handleOnMinLimitPropertyPaneFieldBlur() { + setPropertyPaneMinFieldFocused(false); + } + + useEffect(() => { + DynamicHeightCallbackHandler.add( + CallbackHandlerEventType.MAX_HEIGHT_LIMIT_FOCUS, + handleOnMaxLimitPropertyPaneFieldFocus, + ); + + DynamicHeightCallbackHandler.add( + CallbackHandlerEventType.MAX_HEIGHT_LIMIT_BLUR, + handleOnMaxLimitPropertyPaneFieldBlur, + ); + + DynamicHeightCallbackHandler.add( + CallbackHandlerEventType.MIN_HEIGHT_LIMIT_FOCUS, + handleOnMinLimitPropertyPaneFieldFocus, + ); + + DynamicHeightCallbackHandler.add( + CallbackHandlerEventType.MIN_HEIGHT_LIMIT_BLUR, + handleOnMinLimitPropertyPaneFieldBlur, + ); + + return () => { + DynamicHeightCallbackHandler.remove( + CallbackHandlerEventType.MAX_HEIGHT_LIMIT_FOCUS, + handleOnMaxLimitPropertyPaneFieldFocus, + ); + + DynamicHeightCallbackHandler.remove( + CallbackHandlerEventType.MAX_HEIGHT_LIMIT_BLUR, + handleOnMaxLimitPropertyPaneFieldBlur, + ); + + DynamicHeightCallbackHandler.remove( + CallbackHandlerEventType.MIN_HEIGHT_LIMIT_FOCUS, + handleOnMinLimitPropertyPaneFieldFocus, + ); + + DynamicHeightCallbackHandler.remove( + CallbackHandlerEventType.MIN_HEIGHT_LIMIT_BLUR, + handleOnMinLimitPropertyPaneFieldBlur, + ); + }; + }, []); + + return { + isPropertyPaneMaxFieldFocused, + isPropertyPaneMinFieldFocused, + }; +}; diff --git a/app/client/src/components/autoHeightOverlay/index.tsx b/app/client/src/components/autoHeightOverlay/index.tsx new file mode 100644 index 000000000000..795a9fea6396 --- /dev/null +++ b/app/client/src/components/autoHeightOverlay/index.tsx @@ -0,0 +1,398 @@ +import { focusWidget } from "actions/widgetActions"; +import React, { + CSSProperties, + memo, + useEffect, + useMemo, + useReducer, +} from "react"; +import { useSelector } from "react-redux"; +import { AppState } from "@appsmith/reducers"; +import styled from "styled-components"; +import { + useShowPropertyPane, + useShowTableFilterPane, +} from "utils/hooks/dragResizeHooks"; +import { useWidgetSelection } from "utils/hooks/useWidgetSelection"; +import { WidgetProps } from "widgets/BaseWidget"; +import { GridDefaults, WidgetHeightLimits } from "constants/WidgetConstants"; +import { getParentToOpenSelector } from "selectors/widgetSelectors"; +import AutoHeightLimitHandleGroup from "./AutoHeightLimitHandleGroup"; +import AutoHeightLimitOverlayDisplay from "./ui/AutoHeightLimitOverlayDisplay"; +import { useHoverState, usePositionedStyles } from "./hooks"; +import { getSnappedValues } from "./utils"; +import { useAutoHeightUIState } from "utils/hooks/autoHeightUIHooks"; +import { LayersContext } from "constants/Layers"; +import { + AutoHeightOverlayUIStateReducer, + createInitialAutoHeightUIState, +} from "./store"; +import { previewModeSelector } from "selectors/editorSelectors"; + +interface StyledAutoHeightOverlayProps { + layerIndex: number; + isHidden: boolean; +} + +const StyledAutoHeightOverlay = styled.div` + width: 100%; + height: 100%; + position: absolute; + z-index: ${(props) => props.layerIndex}; + pointer-events: none; + display: ${(props) => (props.isHidden ? "none" : "block")}; +`; + +interface MinMaxHeightProps { + maxDynamicHeight: number; + minDynamicHeight: number; +} + +interface AutoHeightOverlayContainerProps + extends MinMaxHeightProps, + WidgetProps { + batchUpdate: (height: number) => void; + onMaxHeightSet: (height: number) => void; + onMinHeightSet: (height: number) => void; + style?: CSSProperties; +} + +interface AutoHeightOverlayProps extends AutoHeightOverlayContainerProps { + isHidden: boolean; +} + +const AutoHeightOverlay: React.FC = memo( + ({ + batchUpdate, + isHidden, + maxDynamicHeight, + minDynamicHeight, + onMaxHeightSet, + onMinHeightSet, + style, + ...props + }) => { + const showPropertyPane = useShowPropertyPane(); + const { selectWidget } = useWidgetSelection(); + const selectedWidget = useSelector( + (state: AppState) => state.ui.widgetDragResize.lastSelectedWidget, + ); + + const parentWidgetToSelect = useSelector( + getParentToOpenSelector(props.widgetId), + ); + const showTableFilterPane = useShowTableFilterPane(); + const { + isAutoHeightWithLimitsChanging, + setIsAutoHeightWithLimitsChanging, + } = useAutoHeightUIState(); + + const [autoHeightUIState, autoHeightUIStateDispatch] = useReducer( + AutoHeightOverlayUIStateReducer, + createInitialAutoHeightUIState({ maxDynamicHeight, minDynamicHeight }), + ); + + const { + isMaxDotDragging, + isMinDotDragging, + maxdY, + maxY, + mindY, + minY, + } = autoHeightUIState; + + function setIsMaxDotDragging(isMaxDotDragging: boolean) { + autoHeightUIStateDispatch({ + type: "SET_IS_MAX_DOT_DRAGGING", + payload: { + isMaxDotDragging, + }, + }); + } + + function setIsMinDotDragging(isMinDotDragging: boolean) { + autoHeightUIStateDispatch({ + type: "SET_IS_MIN_DOT_DRAGGING", + payload: { + isMinDotDragging, + }, + }); + } + + function setMaxY(maxY: number) { + autoHeightUIStateDispatch({ + type: "SET_MAX_Y", + payload: { + maxY, + }, + }); + } + + function setMinY(minY: number) { + autoHeightUIStateDispatch({ + type: "SET_MIN_Y", + payload: { + minY, + }, + }); + } + + function setMaxdY(maxdY: number) { + autoHeightUIStateDispatch({ + type: "SET_MAX_D_Y", + payload: { + maxdY, + }, + }); + } + + function setMindY(mindY: number) { + autoHeightUIStateDispatch({ + type: "SET_MIN_D_Y", + payload: { + mindY, + }, + }); + } + + const finalMaxY = maxY + maxdY; + const finalMinY = minY + mindY; + + useEffect(() => { + // reset the diff on backend update + setMindY(0); + setMaxdY(0); + setMaxY(maxDynamicHeight * GridDefaults.DEFAULT_GRID_ROW_HEIGHT); + }, [maxDynamicHeight]); + + function onAnyDotStop() { + // Tell the Canvas that we've stopped resizing + // Put it later in the stack so that other updates like click, are not propagated to the parent container + setTimeout(() => { + setIsAutoHeightWithLimitsChanging && + setIsAutoHeightWithLimitsChanging(false); + }, 0); + + selectWidget && selectWidget(props.widgetId); + + if (parentWidgetToSelect) { + selectWidget && + selectedWidget !== parentWidgetToSelect.widgetId && + selectWidget(parentWidgetToSelect.widgetId); + focusWidget(parentWidgetToSelect.widgetId); + } else { + selectWidget && + selectedWidget !== props.widgetId && + selectWidget(props.widgetId); + } + // Property pane closes after a resize/drag + showPropertyPane && showPropertyPane(); + } + + function onMaxUpdate(dx: number, dy: number) { + if ( + maxY + dy <= + WidgetHeightLimits.MIN_HEIGHT_IN_ROWS * + GridDefaults.DEFAULT_GRID_ROW_HEIGHT + ) { + return; + } + + const snapped = getSnappedValues(dx, dy, snapGrid); + + if (maxY + snapped.y <= minY) { + setMindY(snapped.y + (maxY - minY)); + } + + setMaxdY(snapped.y); + } + + function updateMaxHeight(height: number) { + setMaxY(height); + onMaxHeightSet(height); + } + + function updateMinHeight(height: number) { + setMinY(height); + onMinHeightSet(height); + } + + function onMaxStop() { + setIsMaxDotDragging(false); + const heightToSet = maxY + maxdY; + + if (heightToSet === minY + mindY) { + batchUpdate(heightToSet); + } else { + updateMaxHeight(heightToSet); + setMaxdY(0); + } + + onAnyDotStop(); + } + + useEffect(() => { + // reset the diff on backend update + setMindY(0); + setMaxdY(0); + setMinY(minDynamicHeight * GridDefaults.DEFAULT_GRID_ROW_HEIGHT); + }, [minDynamicHeight]); + + function onMinUpdate(dx: number, dy: number) { + if ( + minY + dy <= + WidgetHeightLimits.MIN_HEIGHT_IN_ROWS * + GridDefaults.DEFAULT_GRID_ROW_HEIGHT + ) { + return; + } + + const snapped = getSnappedValues(dx, dy, snapGrid); + + if (minY + snapped.y >= maxY) { + setMaxdY(snapped.y - (maxY - minY)); + } + + setMindY(snapped.y); + } + + function onMinStop() { + setIsMinDotDragging(false); + const heightToSet = minY + mindY; + + if (heightToSet === maxY + maxdY) { + batchUpdate(heightToSet); + } else { + updateMinHeight(heightToSet); + setMindY(0); + } + + onAnyDotStop(); + } + + function onMinDotStart() { + setIsMinDotDragging(true); + onAnyDotStart(); + } + + function onAnyDotStart() { + setIsAutoHeightWithLimitsChanging && + !isAutoHeightWithLimitsChanging && + setIsAutoHeightWithLimitsChanging(true); + selectWidget && + selectedWidget !== props.widgetId && + selectWidget(props.widgetId); + // Make sure that this tableFilterPane should close + showTableFilterPane && showTableFilterPane(); + } + + function onMaxDotStart() { + setIsMaxDotDragging(true); + onAnyDotStart(); + } + + const [isMinDotActive, minHoverFns] = useHoverState(); + const [isMaxDotActive, maxHoverFns] = useHoverState(); + + const snapGrid = useMemo( + () => ({ + x: props.parentColumnSpace, + y: props.parentRowSpace, + }), + [props.parentColumnSpace, props.parentRowSpace], + ); + + const { + bottomRow, + leftColumn, + noContainerOffset, + parentColumnSpace, + parentRowSpace, + rightColumn, + topRow, + } = props; + + const styles = usePositionedStyles({ + bottomRow, + leftColumn, + noContainerOffset, + parentColumnSpace, + parentRowSpace, + rightColumn, + topRow, + }); + + const { autoHeightWithLimitsOverlay } = React.useContext(LayersContext); + + return ( + { + // avoid DropTarget handleFocus + e.stopPropagation(); + }} + style={style ?? styles} + > + + + + + ); + }, +); + +const AutoHeightOverlayContainer: React.FC = memo( + (props) => { + const widgetId = props.widgetId; + const { + isDragging, + isResizing, + lastSelectedWidget: selectedWidget, + selectedWidgets, + } = useSelector((state: AppState) => state.ui.widgetDragResize); + + const isPreviewMode = useSelector(previewModeSelector); + + const isWidgetSelected = selectedWidget === widgetId; + const multipleWidgetsSelected = selectedWidgets.length > 1; + const isHidden = multipleWidgetsSelected || isDragging || isResizing; + + if (isWidgetSelected && !isPreviewMode) { + return ; + } + + return null; + }, +); + +export default AutoHeightOverlayContainer; diff --git a/app/client/src/components/autoHeightOverlay/store.ts b/app/client/src/components/autoHeightOverlay/store.ts new file mode 100644 index 000000000000..d1174da88582 --- /dev/null +++ b/app/client/src/components/autoHeightOverlay/store.ts @@ -0,0 +1,100 @@ +import { GridDefaults } from "constants/WidgetConstants"; + +interface AutoHeightLimitsUIState { + isMaxDotDragging: boolean; + isMinDotDragging: boolean; + maxY: number; // the actual value + maxdY: number; // the difference during dragging + minY: number; // the actual value + mindY: number; // the difference during dragging +} + +type SET_MAX_Y = { type: "SET_MAX_Y"; payload: { maxY: number } }; +type SET_MIN_Y = { type: "SET_MIN_Y"; payload: { minY: number } }; +type SET_MAX_D_Y = { type: "SET_MAX_D_Y"; payload: { maxdY: number } }; +type SET_MIN_D_Y = { type: "SET_MIN_D_Y"; payload: { mindY: number } }; +type SET_IS_MIN_DOT_DRAGGING = { + type: "SET_IS_MIN_DOT_DRAGGING"; + payload: { isMinDotDragging: boolean }; +}; + +type SET_IS_MAX_DOT_DRAGGING = { + type: "SET_IS_MAX_DOT_DRAGGING"; + payload: { isMaxDotDragging: boolean }; +}; + +type AutoHeightLimitsUIAction = + | SET_MAX_Y + | SET_MIN_Y + | SET_MAX_D_Y + | SET_MIN_D_Y + | SET_IS_MIN_DOT_DRAGGING + | SET_IS_MAX_DOT_DRAGGING; + +export function AutoHeightOverlayUIStateReducer( + state: AutoHeightLimitsUIState, + action: AutoHeightLimitsUIAction, +) { + if (action.type === "SET_IS_MAX_DOT_DRAGGING") { + return { + ...state, + isMaxDotDragging: action.payload.isMaxDotDragging, + }; + } + + if (action.type === "SET_IS_MIN_DOT_DRAGGING") { + return { + ...state, + isMinDotDragging: action.payload.isMinDotDragging, + }; + } + + if (action.type === "SET_MAX_Y") { + return { + ...state, + maxY: action.payload.maxY, + }; + } + + if (action.type === "SET_MIN_Y") { + return { + ...state, + minY: action.payload.minY, + }; + } + + if (action.type === "SET_MAX_D_Y") { + return { + ...state, + maxdY: action.payload.maxdY, + }; + } + + if (action.type === "SET_MIN_D_Y") { + return { + ...state, + mindY: action.payload.mindY, + }; + } + + return state; +} + +interface CreateInitialAutoHeightUIStateProps { + maxDynamicHeight: number; + minDynamicHeight: number; +} + +export function createInitialAutoHeightUIState({ + maxDynamicHeight, + minDynamicHeight, +}: CreateInitialAutoHeightUIStateProps) { + return { + isMinDotDragging: false, + isMaxDotDragging: false, + maxY: maxDynamicHeight * GridDefaults.DEFAULT_GRID_ROW_HEIGHT, // the actual value + maxdY: 0, // the difference during dragging + minY: minDynamicHeight * GridDefaults.DEFAULT_GRID_ROW_HEIGHT, // the actual value + mindY: 0, // the difference during dragging + }; +} diff --git a/app/client/src/components/autoHeightOverlay/types.ts b/app/client/src/components/autoHeightOverlay/types.ts new file mode 100644 index 000000000000..ec8fee4f2b53 --- /dev/null +++ b/app/client/src/components/autoHeightOverlay/types.ts @@ -0,0 +1,10 @@ +export interface onDragCallbacksProps { + onStart: () => void; + onStop: () => void; + onUpdate: (x: number, y: number) => void; +} + +export interface onMouseHoverCallbacksProps { + onMouseEnter: () => void; + onMouseLeave: () => void; +} diff --git a/app/client/src/components/autoHeightOverlay/ui/AutoHeightLimitHandleBorder.test.tsx b/app/client/src/components/autoHeightOverlay/ui/AutoHeightLimitHandleBorder.test.tsx new file mode 100644 index 000000000000..c94a996d4172 --- /dev/null +++ b/app/client/src/components/autoHeightOverlay/ui/AutoHeightLimitHandleBorder.test.tsx @@ -0,0 +1,22 @@ +import React from "react"; +import "@testing-library/jest-dom"; +import AutoHeightLimitHandleBorder from "./AutoHeightLimitHandleBorder"; +import "jest-styled-components"; +import renderer from "react-test-renderer"; +import { OVERLAY_COLOR } from "../constants"; + +describe("", () => { + it("should have background-color style set to OVERLAY_COLOR when isActive is true", () => { + const tree = renderer + .create() + .toJSON(); + expect(tree).toHaveStyleRule("background-color", OVERLAY_COLOR); + }); + + it("should have background-color style set to undefined when isActive is false", () => { + const tree = renderer + .create() + .toJSON(); + expect(tree).toHaveStyleRule("background-color"); + }); +}); diff --git a/app/client/src/components/autoHeightOverlay/ui/AutoHeightLimitHandleBorder.tsx b/app/client/src/components/autoHeightOverlay/ui/AutoHeightLimitHandleBorder.tsx new file mode 100644 index 000000000000..74e92e8a2235 --- /dev/null +++ b/app/client/src/components/autoHeightOverlay/ui/AutoHeightLimitHandleBorder.tsx @@ -0,0 +1,24 @@ +import styled from "styled-components"; +import { OVERLAY_COLOR } from "../constants"; + +interface AutoHeightLimitHandleBorderProps { + isActive: boolean; +} + +const AutoHeightLimitHandleBorder = styled.div< + AutoHeightLimitHandleBorderProps +>` + background-image: linear-gradient( + to right, + ${OVERLAY_COLOR} 50%, + rgba(255, 255, 255, 0) 0% + ); + background-size: 8% 1px; + background-repeat: repeat-x; + height: 1px; + width: 100%; + + ${(props) => (props.isActive ? `background-color: ${OVERLAY_COLOR}` : "")} +`; + +export default AutoHeightLimitHandleBorder; diff --git a/app/client/src/components/autoHeightOverlay/ui/AutoHeightLimitHandleDot.test.tsx b/app/client/src/components/autoHeightOverlay/ui/AutoHeightLimitHandleDot.test.tsx new file mode 100644 index 000000000000..9aba34b0a05b --- /dev/null +++ b/app/client/src/components/autoHeightOverlay/ui/AutoHeightLimitHandleDot.test.tsx @@ -0,0 +1,22 @@ +import React from "react"; +import "@testing-library/jest-dom"; +import AutoHeightLimitHandleDot from "./AutoHeightLimitHandleDot"; +import "jest-styled-components"; +import renderer from "react-test-renderer"; +import { OVERLAY_COLOR } from "../constants"; + +describe("", () => { + it("should have scale style set to 1 when isDragging is false", () => { + const tree = renderer + .create() + .toJSON(); + expect(tree).toHaveStyleRule("transform", "translateX(-50%) scale( 1 )"); + }); + + it("should have scale style set to 1.67 when isDragging is true", () => { + const tree = renderer + .create() + .toJSON(); + expect(tree).toHaveStyleRule("transform", "translateX(-50%) scale( 1.67 )"); + }); +}); diff --git a/app/client/src/components/autoHeightOverlay/ui/AutoHeightLimitHandleDot.tsx b/app/client/src/components/autoHeightOverlay/ui/AutoHeightLimitHandleDot.tsx new file mode 100644 index 000000000000..59e41d70b366 --- /dev/null +++ b/app/client/src/components/autoHeightOverlay/ui/AutoHeightLimitHandleDot.tsx @@ -0,0 +1,23 @@ +import styled from "styled-components"; +import { OVERLAY_COLOR, OVERLAY_HANDLE_DOT_DRAGGING_SCALE } from "../constants"; + +interface AutoHeightLimitHandleDotProps { + isDragging: boolean; +} + +const AutoHeightLimitHandleDot = styled.div` + position: absolute; + left: 50%; + border-radius: 50%; + width: 7px; + height: 7px; + transform: translateX(-50%) + scale( + ${(props) => (props.isDragging ? OVERLAY_HANDLE_DOT_DRAGGING_SCALE : "1")} + ); + border: 1px solid ${OVERLAY_COLOR}; + background-color: ${OVERLAY_COLOR}; + box-shadow: 0px 0px 0px 2px white; +`; + +export default AutoHeightLimitHandleDot; diff --git a/app/client/src/components/autoHeightOverlay/ui/AutoHeightLimitHandleLabel.test.tsx b/app/client/src/components/autoHeightOverlay/ui/AutoHeightLimitHandleLabel.test.tsx new file mode 100644 index 000000000000..81459b9dcd1b --- /dev/null +++ b/app/client/src/components/autoHeightOverlay/ui/AutoHeightLimitHandleLabel.test.tsx @@ -0,0 +1,21 @@ +import React from "react"; +import "@testing-library/jest-dom"; +import AutoHeightLimitHandleLabel from "./AutoHeightLimitHandleLabel"; +import "jest-styled-components"; +import renderer from "react-test-renderer"; + +describe("", () => { + it("should have display none when isActive is false", () => { + const tree = renderer + .create() + .toJSON(); + expect(tree).toHaveStyleRule("display", "none"); + }); + + it("should have display initial when isActive is true", () => { + const tree = renderer + .create() + .toJSON(); + expect(tree).toHaveStyleRule("display", "initial"); + }); +}); diff --git a/app/client/src/components/autoHeightOverlay/ui/AutoHeightLimitHandleLabel.tsx b/app/client/src/components/autoHeightOverlay/ui/AutoHeightLimitHandleLabel.tsx new file mode 100644 index 000000000000..c4143a86f820 --- /dev/null +++ b/app/client/src/components/autoHeightOverlay/ui/AutoHeightLimitHandleLabel.tsx @@ -0,0 +1,23 @@ +import styled from "styled-components"; + +interface AutoHeightLimitHandleLabel { + isActive: boolean; +} + +const AutoHeightLimitHandleLabel = styled.div` + position: absolute; + pointer-events: none; + padding: 1px 4px; + background: #191919; + font-weight: 400; + font-size: 10px; + line-height: 16px; + color: #ffffff; + text-align: center; + white-space: nowrap; + left: 0px; + transform: translate(calc(-100% - 4px), -2px); + display: ${(props) => (props.isActive ? "initial" : "none")}; +`; + +export default AutoHeightLimitHandleLabel; diff --git a/app/client/src/components/autoHeightOverlay/ui/AutoHeightLimitOverlayDisplay.test.tsx b/app/client/src/components/autoHeightOverlay/ui/AutoHeightLimitOverlayDisplay.test.tsx new file mode 100644 index 000000000000..e83cb876999c --- /dev/null +++ b/app/client/src/components/autoHeightOverlay/ui/AutoHeightLimitOverlayDisplay.test.tsx @@ -0,0 +1,28 @@ +import React from "react"; +import "@testing-library/jest-dom"; +import AutoHeightLimitOverlayDisplay from "./AutoHeightLimitOverlayDisplay"; +import "jest-styled-components"; +import renderer from "react-test-renderer"; + +describe("", () => { + it("should have display none when isActive is false", () => { + const tree = renderer + .create() + .toJSON(); + expect(tree).toHaveStyleRule("display", "none"); + }); + + it("should have display block when isActive is true", () => { + const tree = renderer + .create() + .toJSON(); + expect(tree).toHaveStyleRule("display", "block"); + }); + + it("should have height style equal to the height passed in props", () => { + const tree = renderer + .create() + .toJSON(); + expect(tree).toHaveStyleRule("height", "10px"); + }); +}); diff --git a/app/client/src/components/autoHeightOverlay/ui/AutoHeightLimitOverlayDisplay.tsx b/app/client/src/components/autoHeightOverlay/ui/AutoHeightLimitOverlayDisplay.tsx new file mode 100644 index 000000000000..8deee0122da6 --- /dev/null +++ b/app/client/src/components/autoHeightOverlay/ui/AutoHeightLimitOverlayDisplay.tsx @@ -0,0 +1,20 @@ +import styled from "styled-components"; + +interface AutoHeightLimitOverlayDisplayProps { + isActive: boolean; + height: number; +} + +const AutoHeightLimitOverlayDisplay = styled.div< + AutoHeightLimitOverlayDisplayProps +>` + display: ${(props) => (props.isActive ? "block" : "none")}; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: ${(props) => props.height}px; + background-color: rgba(243, 43, 139, 0.1); +`; + +export default AutoHeightLimitOverlayDisplay; diff --git a/app/client/src/components/autoHeightOverlay/utils.ts b/app/client/src/components/autoHeightOverlay/utils.ts new file mode 100644 index 000000000000..c0214db3a1a4 --- /dev/null +++ b/app/client/src/components/autoHeightOverlay/utils.ts @@ -0,0 +1,15 @@ +import { GridDefaults } from "constants/WidgetConstants"; + +export const heightToRows = (height: number) => + Math.floor(height / GridDefaults.DEFAULT_GRID_ROW_HEIGHT); + +export const getSnappedValues = ( + x: number, + y: number, + snapGrid: { x: number; y: number }, +) => { + return { + x: Math.round(x / snapGrid.x) * snapGrid.x, + y: Math.round(y / snapGrid.y) * snapGrid.y, + }; +}; diff --git a/app/client/src/components/designSystems/appsmith/ModalComponent.tsx b/app/client/src/components/designSystems/appsmith/ModalComponent.tsx index 09afa7ace3be..8982daf15398 100644 --- a/app/client/src/components/designSystems/appsmith/ModalComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/ModalComponent.tsx @@ -3,7 +3,10 @@ import { Overlay, Classes } from "@blueprintjs/core"; import styled from "styled-components"; import { getCanvasClassName } from "utils/generators"; import { Layers } from "constants/Layers"; -import { MODAL_PORTAL_CLASSNAME } from "constants/WidgetConstants"; +import { + MODAL_PORTAL_CLASSNAME, + MODAL_PORTAL_OVERLAY_CLASSNAME, +} from "constants/WidgetConstants"; const Container = styled.div<{ width?: number; @@ -101,7 +104,7 @@ export function ModalComponent(props: ModalComponentProps) { hasBackdrop={false} isOpen={props.isOpen} onClose={props.onClose} - portalClassName={`${MODAL_PORTAL_CLASSNAME} ${props.portalClassName}`} + portalClassName={`${MODAL_PORTAL_CLASSNAME} ${props.portalClassName} ${MODAL_PORTAL_OVERLAY_CLASSNAME}`} usePortal > { - Connect to Git Repository + {CONNECT_TO_GIT_OPTION()} )} @@ -118,7 +122,7 @@ export const DeployLinkButton = withTheme((props: Props) => { icon="share" /> - Current deployed version + {CURRENT_DEPLOY_PREVIEW_OPTION()} } diff --git a/app/client/src/components/editorComponents/ActionCreator/viewComponents/TextView/index.tsx b/app/client/src/components/editorComponents/ActionCreator/viewComponents/TextView/index.tsx index 7a861bfeb84f..b15e5ad7736a 100644 --- a/app/client/src/components/editorComponents/ActionCreator/viewComponents/TextView/index.tsx +++ b/app/client/src/components/editorComponents/ActionCreator/viewComponents/TextView/index.tsx @@ -4,7 +4,7 @@ import { FieldWrapper, } from "components/propertyControls/StyledControls"; import { InputText } from "components/propertyControls/InputTextControl"; -import { AutocompleteDataType } from "utils/autocomplete/TernServer"; +import { AutocompleteDataType } from "utils/autocomplete/CodemirrorTernService"; import React from "react"; export function TextView(props: TextViewProps) { diff --git a/app/client/src/components/editorComponents/ActionNameEditor.tsx b/app/client/src/components/editorComponents/ActionNameEditor.tsx index 373bb9513759..19ba8a154e6d 100644 --- a/app/client/src/components/editorComponents/ActionNameEditor.tsx +++ b/app/client/src/components/editorComponents/ActionNameEditor.tsx @@ -60,6 +60,7 @@ type ActionNameEditorProps = { In future, when default component will be ads editable-text, then we can remove this prop. */ page?: string; + disabled?: boolean; }; function ActionNameEditor(props: ActionNameEditorProps) { @@ -109,6 +110,7 @@ function ActionNameEditor(props: ActionNameEditorProps) { props.theme.spaces[0]}px @@ -250,8 +252,12 @@ function ActionSidebar({ }, [pageId]); const hasWidgets = Object.keys(widgets).length > 1; + const pagePermissions = useSelector(getPagePermissions); + + const canEditPage = hasManagePagePermission(pagePermissions); + const showSuggestedWidgets = - hasResponse && suggestedWidgets && !!suggestedWidgets.length; + canEditPage && hasResponse && suggestedWidgets && !!suggestedWidgets.length; const showSnipingMode = hasResponse && hasWidgets; if (!hasConnections && !showSuggestedWidgets && !showSnipingMode) { @@ -276,12 +282,12 @@ function ActionSidebar({ entityDependencies={entityDependencies} /> )} - {hasResponse && Object.keys(widgets).length > 1 && ( + {canEditPage && hasResponse && Object.keys(widgets).length > 1 && ( {/*
Go to canvas and select widgets
*/}
)} + {brandColors && Object.keys(brandColors).length > 0 && ( +
+

Brand Colors

+
+ {Object.keys(brandColors).map( + (colorKey: string, colorIndex: number) => ( +
{ + setColor(brandColors[colorKey]); + setIsOpen(false); + changeColor(brandColors[colorKey], !e.isTrusted); + }} + style={{ backgroundColor: brandColors[colorKey] }} + tabIndex={colorIndex === 0 ? 0 : -1} + /> + ), + )} +
+
+ )} {showApplicationColors && applicationColors.length > 0 && (

Application Colors

@@ -204,7 +232,7 @@ function ColorPickerPopup(props: ColorPickerPopupProps) {

All Colors

-
+
{Object.keys(TAILWIND_COLORS).map((colorKey, rowIndex) => Object.keys(get(TAILWIND_COLORS, `${colorKey}`)).map( (singleColorKey, colIndex) => ( @@ -311,7 +339,7 @@ const POPOVER_MODFIER = { const ColorPickerComponent = React.forwardRef( (props: ColorPickerProps, containerRef: any) => { - const { isOpen: isOpenProp = false } = props; + const { isOpen: isOpenProp = false, placeholderText } = props; const popupRef = useRef(null); const inputGroupRef = useRef(null); // isClick is used to track whether the input field is in focus by mouse click or by keyboard @@ -517,6 +545,7 @@ const ColorPickerComponent = React.forwardRef( minimal modifiers={POPOVER_MODFIER} onInteraction={handleOnInteraction} + portalContainer={props.portalContainer} > diff --git a/app/client/src/components/propertyControls/ColumnActionSelectorControl.tsx b/app/client/src/components/propertyControls/ColumnActionSelectorControl.tsx index 57bc1e8359ea..9f19824ad3c4 100644 --- a/app/client/src/components/propertyControls/ColumnActionSelectorControl.tsx +++ b/app/client/src/components/propertyControls/ColumnActionSelectorControl.tsx @@ -81,7 +81,7 @@ class ColumnActionSelectorControl extends BaseControl< })} = { isDelete?: boolean; }; -type DroppableComponentProps = { +export type DroppableComponentProps = { className?: string; fixedHeight?: number | boolean; focusedIndex: number | null | undefined; diff --git a/app/client/src/components/propertyControls/FieldConfigurationControl.tsx b/app/client/src/components/propertyControls/FieldConfigurationControl.tsx index f3dc26c9fcca..c9c0186c0546 100644 --- a/app/client/src/components/propertyControls/FieldConfigurationControl.tsx +++ b/app/client/src/components/propertyControls/FieldConfigurationControl.tsx @@ -11,12 +11,13 @@ import SchemaParser, { import styled from "constants/DefaultTheme"; import { ARRAY_ITEM_KEY, Schema } from "widgets/JSONFormWidget/constants"; import { Category, Size } from "design-system"; -import { BaseItemProps, DroppableComponent } from "./DraggableListComponent"; +import { BaseItemProps } from "./DraggableListComponent"; import { DraggableListCard } from "components/propertyControls/DraggableListCard"; import { StyledPropertyPaneButton } from "./StyledControls"; import { getNextEntityName } from "utils/AppsmithUtils"; import { InputText } from "./InputTextControl"; import { JSONFormWidgetProps } from "widgets/JSONFormWidget/widget"; +import { DraggableListControl } from "pages/Editor/PropertyPane/DraggableListControl"; type DroppableItem = BaseItemProps & { index: number; @@ -132,6 +133,9 @@ class FieldConfigurationControl extends BaseControl { widgetName, isCustomField: true, skipDefaultValueProcessing: true, + baseSchemaPath: null, + removedSchemaItems: [], + modifiedSchemaItems: {}, identifier: nextFieldKey, fieldThemeStylesheets: childStylesheet, }); @@ -196,7 +200,7 @@ class FieldConfigurationControl extends BaseControl { const addNewFieldButton = ( { return ( - { + propertyPath={this.props.dataTreePath} + renderComponent={(props: any) => { const { id, isCustomField } = props.item; return DraggableListCard({ diff --git a/app/client/src/components/propertyControls/KeyValueComponent.tsx b/app/client/src/components/propertyControls/KeyValueComponent.tsx index 3894acfa5780..bcf3cf82ff22 100644 --- a/app/client/src/components/propertyControls/KeyValueComponent.tsx +++ b/app/client/src/components/propertyControls/KeyValueComponent.tsx @@ -235,7 +235,7 @@ export function KeyValueComponent(props: KeyValueComponentProps) { })} props.theme.colors.codeMirror.background.hoverState}; + background-color: #575757; + border-radius: 2px; + padding: 2px; + margin: 0px 2px; + font-size: 10px; +`; + +type InputTextProp = { + label: string; + value: string; + onChange: (event: React.ChangeEvent | string) => void; + evaluatedValue?: any; + expected?: CodeEditorExpected; + placeholder?: string; + dataTreePath?: string; + additionalDynamicData: AdditionalDynamicDataTree; + theme: EditorTheme; +}; + +function InputText(props: InputTextProp) { + const { + additionalDynamicData, + dataTreePath, + evaluatedValue, + expected, + onChange, + placeholder, + theme, + value, + } = props; + return ( + + + Access the current item using {"{{"} + currentItem + {"}}"} + + } + size={EditorSize.EXTENDED} + tabBehaviour={TabBehaviour.INDENT} + theme={theme} + /> + + ); +} + +class MenuButtonDynamicItemsControl extends BaseControl< + MenuButtonDynamicItemsControlProps +> { + render() { + const { + dataTreePath, + defaultValue, + expected, + label, + propertyValue, + theme, + } = this.props; + const menuButtonId = this.props.widgetProperties.widgetName; + const value = + propertyValue && isDynamicValue(propertyValue) + ? MenuButtonDynamicItemsControl.getInputComputedValue( + propertyValue, + menuButtonId, + ) + : propertyValue + ? propertyValue + : defaultValue; + const keys = this.props.widgetProperties.sourceDataKeys || []; + const currentItem: { [key: string]: any } = {}; + + Object.values(keys).forEach((key) => { + currentItem[key as keyof typeof currentItem] = undefined; + }); + + // Load default value in evaluated value + if (value && !propertyValue) { + this.onTextChange(value); + } + return ( + + ); + } + + static getBindingPrefix = (menuButtonId: string) => { + return `{{${menuButtonId}.sourceData.map((currentItem, currentIndex) => ( `; + }; + + static bindingSuffix = `))}}`; + + static getInputComputedValue = ( + propertyValue: string, + menuButtonId: string, + ) => { + if (!propertyValue.includes(this.getBindingPrefix(menuButtonId))) { + return propertyValue; + } + + const value = `${propertyValue.substring( + this.getBindingPrefix(menuButtonId).length, + propertyValue.length - this.bindingSuffix.length, + )}`; + const stringValue = JSToString(value); + + return stringValue; + }; + + getComputedValue = (value: string, menuButtonId: string) => { + if (!isDynamicValue(value)) { + return value; + } + + const stringToEvaluate = stringToJS(value); + + if (stringToEvaluate === "") { + return stringToEvaluate; + } + + return `${MenuButtonDynamicItemsControl.getBindingPrefix( + menuButtonId, + )}${stringToEvaluate}${MenuButtonDynamicItemsControl.bindingSuffix}`; + }; + + onTextChange = (event: React.ChangeEvent | string) => { + let value = ""; + if (typeof event !== "string") { + value = event.target?.value; + } else { + value = event; + } + if (isString(value)) { + const output = this.getComputedValue( + value, + this.props.widgetProperties.widgetName, + ); + + this.updateProperty(this.props.propertyName, output); + } else { + this.updateProperty(this.props.propertyName, value); + } + }; + + static getControlType() { + return "MENU_BUTTON_DYNAMIC_ITEMS"; + } +} + +export interface MenuButtonDynamicItemsControlProps extends ControlProps { + defaultValue?: string; +} + +export default MenuButtonDynamicItemsControl; diff --git a/app/client/src/components/propertyControls/MenuItemsControl.tsx b/app/client/src/components/propertyControls/MenuItemsControl.tsx index d3139ff8028a..b14b7b515b13 100644 --- a/app/client/src/components/propertyControls/MenuItemsControl.tsx +++ b/app/client/src/components/propertyControls/MenuItemsControl.tsx @@ -3,12 +3,12 @@ import BaseControl, { ControlProps } from "./BaseControl"; import { StyledPropertyPaneButton } from "./StyledControls"; import styled from "constants/DefaultTheme"; import { generateReactKey } from "utils/generators"; -import { DroppableComponent } from "./DraggableListComponent"; import { getNextEntityName } from "utils/AppsmithUtils"; import orderBy from "lodash/orderBy"; import isString from "lodash/isString"; import isUndefined from "lodash/isUndefined"; import { Category, Size } from "design-system"; +import { DraggableListControl } from "pages/Editor/PropertyPane/DraggableListControl"; import { DraggableListCard } from "components/propertyControls/DraggableListCard"; const StyledPropertyPaneButtonWrapper = styled.div` @@ -93,14 +93,15 @@ class MenuItemsControl extends BaseControl { render() { return ( - + propertyPath={this.props.dataTreePath} + renderComponent={(props: any) => DraggableListCard({ ...props, isDelete: true, @@ -114,7 +115,7 @@ class MenuItemsControl extends BaseControl { /> { max, min, minorStepSize, + onBlur, + onFocus, placeholderText, propertyValue, stepSize, @@ -75,6 +77,8 @@ class NumericInputControl extends BaseControl { max={max} min={min} minorStepSize={minorStepSize} + onBlur={onBlur} + onFocus={onFocus} onKeyDown={this.handleKeydown} onValueChange={this.handleValueChange} placeholder={placeholderText} @@ -106,6 +110,8 @@ export interface NumericInputControlProps extends ControlProps { majorStepSize?: number | null; placeholderText?: string; stepSize?: number; + onFocus?: () => void; + onBlur?: () => void; } export default NumericInputControl; diff --git a/app/client/src/components/propertyControls/OpenConfigPanelControl.tsx b/app/client/src/components/propertyControls/OpenConfigPanelControl.tsx new file mode 100644 index 000000000000..37705ca0dabb --- /dev/null +++ b/app/client/src/components/propertyControls/OpenConfigPanelControl.tsx @@ -0,0 +1,74 @@ +import React from "react"; +import BaseControl, { ControlProps } from "./BaseControl"; +import { StyledPropertyPaneButton } from "./StyledControls"; +import styled from "constants/DefaultTheme"; +import { Category, Size } from "design-system"; + +const StyledPropertyPaneButtonWrapper = styled.div` + display: flex; + width: 100%; + justify-content: center; + margin-top: 10px; +`; + +const Wrapper = styled.div` + width: 100%; + display: flex; + flex-direction: column; +`; + +const OpenNextPannelButton = styled(StyledPropertyPaneButton)` + justify-content: center; + flex-grow: 1; +`; + +class OpenConfigPanelControl extends BaseControl { + constructor(props: OpenConfigPanelControlProps) { + super(props); + } + + openConfigPanel = () => { + this.props.openNextPanel({ + index: 0, + ...this.props.propertyValue, + propPaneId: this.props.widgetProperties.widgetId, + }); + }; + + render() { + const { buttonConfig, widgetProperties } = this.props; + const { icon, label } = buttonConfig; + const { widgetName } = widgetProperties; + + return ( + + + + + + ); + } + + static getControlType() { + return "OPEN_CONFIG_PANEL"; + } +} + +export interface OpenConfigPanelControlProps extends ControlProps { + buttonConfig: { + icon: string; + label: string; + }; +} + +export default OpenConfigPanelControl; diff --git a/app/client/src/components/propertyControls/PrimaryColumnsControl.tsx b/app/client/src/components/propertyControls/PrimaryColumnsControl.tsx index 53744b19c4c5..f1a5c7ea3702 100644 --- a/app/client/src/components/propertyControls/PrimaryColumnsControl.tsx +++ b/app/client/src/components/propertyControls/PrimaryColumnsControl.tsx @@ -8,7 +8,6 @@ import BaseControl, { ControlProps } from "./BaseControl"; import { StyledPropertyPaneButton } from "./StyledControls"; import styled from "constants/DefaultTheme"; import { Indices } from "constants/Layers"; -import { DroppableComponent } from "./DraggableListComponent"; import { Size, Category } from "design-system"; import EmptyDataState from "components/utils/EmptyDataState"; import EvaluatedValuePopup from "components/editorComponents/CodeEditor/EvaluatedValuePopup"; @@ -28,6 +27,7 @@ import { getEvalValuePath, } from "utils/DynamicBindingUtils"; import { getNextEntityName } from "utils/AppsmithUtils"; +import { DraggableListControl } from "pages/Editor/PropertyPane/DraggableListControl"; import { DraggableListCard } from "components/propertyControls/DraggableListCard"; const TabsWrapper = styled.div` @@ -164,14 +164,15 @@ class PrimaryColumnsControl extends BaseControl { return ( - + propertyPath={this.props.dataTreePath} + renderComponent={(props: any) => DraggableListCard({ ...props, isDelete: false, @@ -186,7 +187,7 @@ class PrimaryColumnsControl extends BaseControl { {
- { itemHeight={45} items={draggableComponentColumns} onEdit={this.onEdit} - renderComponent={(props) => + propertyPath={this.props.dataTreePath} + renderComponent={(props: any) => DraggableListCard({ ...props, showCheckbox: true, @@ -248,7 +249,7 @@ class PrimaryColumnsControlV2 extends BaseControl { props.theme.colors.propertyPane.buttonText}; box-shadow: none; + + /* + We use this font family to show emoji flags + on windows devices + */ + .left-icon-wrapper { + font-family: "Twemoji Country Flags"; + } `; export const StyledMenu = styled(Menu)` @@ -303,20 +311,6 @@ export const StyledPropertyPaneButton = styled(Button)` margin-left: auto; display: flex; justify-content: flex-end; - border: 1px solid ${Colors.GREY_8}; - - &, - &:active { - border: 1px solid ${Colors.GREY_8}; - color: ${Colors.GREY_8}; - background-color: transparent; - } - - &:hover { - border: 1px solid ${Colors.GREY_8}; - color: ${Colors.GREY_8}; - background-color: ${Colors}; - } &&& svg { width: 14px; @@ -326,12 +320,6 @@ export const StyledPropertyPaneButton = styled(Button)` stroke: ${Colors.GREY_8}; } } - - &:disabled { - background-color: ${Colors.GREY_1}; - color: var(--appsmith-color-black-400); - border-color: ${Colors.MERCURY}; - } `; export const StyledOptionControlInputGroup = styled(StyledInputGroup)<{ diff --git a/app/client/src/components/propertyControls/TabControl.tsx b/app/client/src/components/propertyControls/TabControl.tsx index 51ae730daf92..be6036022623 100644 --- a/app/client/src/components/propertyControls/TabControl.tsx +++ b/app/client/src/components/propertyControls/TabControl.tsx @@ -2,11 +2,7 @@ import React from "react"; import BaseControl, { ControlProps } from "./BaseControl"; import { StyledPropertyPaneButton } from "./StyledControls"; import styled from "constants/DefaultTheme"; -import { - BaseItemProps, - DroppableComponent, - RenderComponentProps, -} from "./DraggableListComponent"; +import { BaseItemProps, RenderComponentProps } from "./DraggableListComponent"; import orderBy from "lodash/orderBy"; import isString from "lodash/isString"; import isUndefined from "lodash/isUndefined"; @@ -16,6 +12,7 @@ import * as Sentry from "@sentry/react"; import { Category, Size } from "design-system"; import { useDispatch } from "react-redux"; import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants"; +import { DraggableListControl } from "pages/Editor/PropertyPane/DraggableListControl"; import { DraggableListCard } from "components/propertyControls/DraggableListCard"; const StyledPropertyPaneButtonWrapper = styled.div` @@ -50,7 +47,7 @@ function AddTabButtonComponent({ widgetId }: any) { return ( { {tabs.length} tabs - ` - display: ${ - inline ? "inline-flex" : alignment === Alignment.RIGHT ? "block" : "flex" - }; + ${({ inline, optionCount }) => ` + display: ${inline ? "inline-flex" : "flex"}; flex-direction: ${inline ? "row" : "column"}; align-items: ${inline ? "center" : "flex-start"}; ${inline && "flex-wrap: wrap"}; justify-content: ${ optionCount > 1 ? `space-between` : inline ? `flex-start` : `center` }; + gap: 10px; + flex-grow: 1; `} ${BlueprintControlTransform}; @@ -342,42 +342,17 @@ export const BlueprintRadioSwitchGroupTransform = css<{ } return "flex"; }}; + width: ${({ alignment, inline }) => { + if (alignment === Alignment.RIGHT) { + return inline ? "auto" : "100%"; + } + return "auto"; + }}; align-items: center; border: 1px solid transparent; color: ${Colors.GREY_10}; line-height: 16px; - min-height: ${({ alignment }) => - alignment === Alignment.RIGHT ? 23 : 30}px; - margin-top: ${({ alignment }) => (alignment === Alignment.RIGHT ? 7 : 0)}px; - - margin-bottom: ${({ - alignment, - height, - inline, - labelPosition, - optionCount, - }) => { - if ( - alignment === Alignment.RIGHT && - !inline && - optionCount > 1 && - height - ) { - return Math.max( - (height - - (labelPosition === LabelPosition.Left ? 0 : 35) - - optionCount * 31) / - (optionCount - 1), - 8, - ); - } else { - return 0; - } - }}px; - &:last-child { - margin-bottom: 0; - } .bp3-control-indicator { margin-top: 0; border: 1px solid ${Colors.GREY_5}; @@ -1344,9 +1319,6 @@ type ColorType = { highlightTextColor: string; textColor: string; }; - pagesEditor: { - iconColor: string; - }; numberedStep: { line: string; }; @@ -1409,10 +1381,6 @@ const mentionSuggestion = { hover: "#EBEBEB", }; -const pagesEditor = { - iconColor: "#A2A6A8", -}; - const toggleMode = { activeModeBackground: "#EBEBEB", activeModeIcon: "#4B4848", @@ -2148,7 +2116,6 @@ export const dark: ColorType = { actionActiveBg: "#e1e1e1", }, actionSidePane, - pagesEditor, link: "#f86a2b", welcomePage: { text: lightShades[5], @@ -2782,7 +2749,6 @@ export const light: ColorType = { actionActiveBg: "#e1e1e1", }, actionSidePane, - pagesEditor, link: "#f86a2b", welcomePage: { text: lightShades[5], diff --git a/app/client/src/constants/Layers.tsx b/app/client/src/constants/Layers.tsx index 83b4df18a74e..8ffbd8540c42 100644 --- a/app/client/src/constants/Layers.tsx +++ b/app/client/src/constants/Layers.tsx @@ -57,6 +57,8 @@ export const Layers = { evaluationPopper: Indices.Layer3, concurrentEditorWarning: Indices.Layer2, manualUpgrade: Indices.Layer10, + + autoHeightWithLimitsOverlay: Indices.Layer3, }; export const tailwindLayers = { diff --git a/app/client/src/constants/PropertyControlConstants.tsx b/app/client/src/constants/PropertyControlConstants.tsx index 7323e05b6579..2dc61c2150ee 100644 --- a/app/client/src/constants/PropertyControlConstants.tsx +++ b/app/client/src/constants/PropertyControlConstants.tsx @@ -1,26 +1,28 @@ +import { ReduxActionType } from "@appsmith/constants/ReduxActionConstants"; +import { UpdateWidgetPropertyPayload } from "actions/controlActions"; +import { ReduxAction } from "ce/constants/ReduxActionConstants"; +import { CodeEditorExpected } from "components/editorComponents/CodeEditor"; import { getPropertyControlTypes } from "components/propertyControls"; import { ValidationResponse, ValidationTypes, } from "constants/WidgetValidation"; +import { Stylesheet } from "entities/AppTheming"; import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; -import { CodeEditorExpected } from "components/editorComponents/CodeEditor"; -import { UpdateWidgetPropertyPayload } from "actions/controlActions"; -import { AppTheme } from "entities/AppTheming"; -import { WidgetProps } from "widgets/BaseWidget"; -import { ReduxAction } from "ce/constants/ReduxActionConstants"; const ControlTypes = getPropertyControlTypes(); export type ControlType = typeof ControlTypes[keyof typeof ControlTypes]; export type PropertyPaneSectionConfig = { - sectionName: string | ((props: WidgetProps, propertyPath: string) => string); + sectionName: string; id?: string; children: PropertyPaneConfig[]; - collapsible?: boolean; + collapsible?: boolean; // Indicates whether the section could be collapsed or not + childrenId?: string; // A unique id generated by combining the ids of all the children hidden?: (props: any, propertyPath: string) => boolean; isDefaultOpen?: boolean; propertySectionPath?: string; + tag?: string; // Used to show a tag right after the section name (only in the search results) }; export type PropertyHookUpdates = { @@ -37,6 +39,7 @@ export type PanelConfig = { children?: PropertyPaneConfig[]; contentChildren?: PropertyPaneConfig[]; styleChildren?: PropertyPaneConfig[]; + searchConfig?: PropertyPaneConfig[]; // A combination of contentChildren and contentChildren which will be used to display search results updateHook: ( props: any, propertyPath: string, @@ -46,7 +49,7 @@ export type PanelConfig = { export type PropertyPaneControlConfig = { id?: string; - label: string | ((props: WidgetProps, propertyPath: string) => string); + label: string; propertyName: string; // Serves in the tooltip helpText?: string; @@ -85,8 +88,8 @@ export type PropertyPaneControlConfig = { getStylesheetValue?: ( props: any, propertyPath: string, - stylesheet?: AppTheme["stylesheet"][string], - ) => AppTheme["stylesheet"][string][string]; + stylesheet?: Stylesheet, + ) => Stylesheet[string]; // TODO(abhinav): To fix this, rename the options property of the controls which use this // Alternatively, create a new structure options?: any; @@ -95,6 +98,13 @@ export type PropertyPaneControlConfig = { propertyName?: string, propertyValue?: any, ) => ReduxAction; + // The following should ideally be used internally + postUpdateAction?: ReduxActionType; + onBlur?: () => void; + onFocus?: () => void; + isPanelProperty?: boolean; + // Numeric Input Control + min?: number; }; type ValidationConfigParams = { @@ -125,8 +135,8 @@ type ValidationConfigParams = { expected?: CodeEditorExpected; // FUNCTION type expected type and example strict?: boolean; //for strict string validation of TEXT type ignoreCase?: boolean; //to ignore the case of key - type?: ValidationTypes; // Used for ValidationType.TABLE_PROPERTY to define sub type - params?: ValidationConfigParams; // Used for ValidationType.TABLE_PROPERTY to define sub type params + type?: ValidationTypes; // Used for ValidationType.ARRAY_OF_TYPE_OR_TYPE to define sub type + params?: ValidationConfigParams; // Used for ValidationType.ARRAY_OF_TYPE_OR_TYPE to define sub type params passThroughOnZero?: boolean; // Used for ValidationType.NUMBER to allow 0 to be passed through. Deafults value is true limitLineBreaks?: boolean; // Used for ValidationType.TEXT to limit line breaks in a large json object. }; diff --git a/app/client/src/constants/WidgetConstants.tsx b/app/client/src/constants/WidgetConstants.tsx index 08138c6c5606..74f379589ffd 100644 --- a/app/client/src/constants/WidgetConstants.tsx +++ b/app/client/src/constants/WidgetConstants.tsx @@ -70,7 +70,7 @@ export const layoutConfigurations: LayoutConfigurations = { FLUID: { minWidth: -1, maxWidth: -1 }, }; -export const LATEST_PAGE_VERSION = 67; +export const LATEST_PAGE_VERSION = 71; export const GridDefaults = { DEFAULT_CELL_SIZE: 1, @@ -83,6 +83,8 @@ export const GridDefaults = { MAIN_CANVAS_EXTENSION_OFFSET: 8, }; +export const CANVAS_MIN_HEIGHT = 380; + // Note: Widget Padding + Container Padding === DEFAULT_GRID_ROW_HEIGHT to gracefully lose one row when a container is used, // which wud allow the user to place elements centered inside a container(columns are rendered proportionally so it take cares of itself). @@ -95,6 +97,7 @@ export const WIDGET_CLASSNAME_PREFIX = "WIDGET_"; export const MAIN_CONTAINER_WIDGET_ID = "0"; export const MAIN_CONTAINER_WIDGET_NAME = "MainContainer"; export const MODAL_PORTAL_CLASSNAME = "bp3-modal-widget"; +export const MODAL_PORTAL_OVERLAY_CLASSNAME = "bp3-overlay-zindex"; export const CANVAS_SELECTOR = "canvas"; export const widgetTypeClassname = (widgetType: string): string => `t--widget-${widgetType @@ -143,6 +146,7 @@ export const WIDGET_STATIC_PROPS = { renderMode: true, detachFromLayout: true, noContainerOffset: false, + height: false, }; export const WIDGET_DSL_STRUCTURE_PROPS = { @@ -157,3 +161,29 @@ export const WIDGET_DSL_STRUCTURE_PROPS = { export type TextSize = keyof typeof TextSizes; export const DEFAULT_FONT_SIZE = THEMEING_TEXT_SIZES.base; + +// The max and min height limits for widgets in rows. +// 9000 is an arbitrarily large value for the height of a widget +// In pixels this would be 90000px, which is a fairly large number. + +// 4 is the minimum for any widget, as we donot support zero height widgets today. +// This also makes sure that widgets have sufficient area in which users can interact. +export const WidgetHeightLimits = { + MAX_HEIGHT_IN_ROWS: 9000, + MIN_HEIGHT_IN_ROWS: 4, + MIN_CANVAS_HEIGHT_IN_ROWS: 10, +}; + +export const WIDGET_PROPS_TO_SKIP_FROM_EVAL = { + children: true, + parentId: true, + renderMode: true, + detachFromLayout: true, + noContainerOffset: false, + hideCard: true, + isDeprecated: true, + searchTags: true, + iconSVG: true, + version: true, + displayName: true, +}; diff --git a/app/client/src/constants/WidgetValidation.ts b/app/client/src/constants/WidgetValidation.ts index ba44cb357e89..ae62cebfc249 100644 --- a/app/client/src/constants/WidgetValidation.ts +++ b/app/client/src/constants/WidgetValidation.ts @@ -15,7 +15,7 @@ export enum ValidationTypes { IMAGE_URL = "IMAGE_URL", FUNCTION = "FUNCTION", SAFE_URL = "SAFE_URL", - TABLE_PROPERTY = "TABLE_PROPERTY", + ARRAY_OF_TYPE_OR_TYPE = "ARRAY_OF_TYPE_OR_TYPE", } export type ValidationResponse = { diff --git a/app/client/src/constants/defs/browser.json b/app/client/src/constants/defs/browser.json index 9b5942b7c0fd..984f7eec61ea 100644 --- a/app/client/src/constants/defs/browser.json +++ b/app/client/src/constants/defs/browser.json @@ -251,5 +251,123 @@ "!type": "fn(timeout: number)", "!url": "https://developer.mozilla.org/en/docs/DOM/window.clearTimeout", "!doc": "Clears the delay set by window.setTimeout()." + }, + "Response": { + "!type": "fn()", + "error": { + "!type": "fn() -> +Response" + }, + "redirect": { + "!type": "fn() -> +Response" + }, + "prototype": { + "status": { + "!type": "number" + }, + "statusText": { + "!type": "string" + }, + "type": { + "!type": "string" + }, + "url": { + "!type": "string" + }, + "ok": {}, + "body": { + "!type": "?" + }, + "bodyUsed": { + "!type": "bool" + }, + "headers": { + "!type": "?" + }, + "redirected": { + "!type": "bool" + }, + "json": { + "!type": "fn() -> +Promise[:t=!0..:t]" + }, + "text": { + "!type": "fn() -> +Promise[:t=!0..:t]" + }, + "clone": { + "!type": "fn() -> +Response" + }, + "formData": { + "!type": "fn() -> +Promise[:t=!0..:t]" + }, + "arrayBuffer": { + "!type": "fn() -> +Promise[:t=!0..:t]" + }, + "blob": { + "!type": "fn() -> +Promise[:t=!0..:t]" + } + } + }, + "Request": { + "!type": "fn(url: string, options?: ?)", + "prototype": { + "type": { + "!type": "string" + }, + "url": { + "!type": "string" + }, + "cache": { + "!type": "string" + }, + "credentials": { + "!type": "string" + }, + "destination": { + "!type": "string" + }, + "method": { + "!type": "string" + }, + "mode": { + "!type": "string" + }, + "priority": { + "!type": "string" + }, + "redirect": { + "!type": "string" + }, + "referrer": { + "!type": "string" + }, + "referrerPolicy": { + "!type": "string" + }, + "body": { + "!type": "?" + }, + "json": { + "!type": "fn() -> +Promise[:t=!0..:t]" + }, + "text": { + "!type": "fn() -> +Promise[:t=!0..:t]" + }, + "clone": { + "!type": "fn() -> +Request" + }, + "formData": { + "!type": "fn() -> +Promise[:t=!0..:t]" + }, + "arrayBuffer": { + "!type": "fn() -> +Promise[:t=!0..:t]" + }, + "blob": { + "!type": "fn() -> +Promise[:t=!0..:t]" + } + } + }, + "fetch": { + "!url": "https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API", + "!doc": "The Fetch API provides an interface for fetching resources (including across the network).", + "!type": "fn(url: string, options?: ?) -> +Promise[:t=+Response]" } } \ No newline at end of file diff --git a/app/client/src/constants/routes/appRoutes.ts b/app/client/src/constants/routes/appRoutes.ts index 8725f3ddd56a..f36db15b7add 100644 --- a/app/client/src/constants/routes/appRoutes.ts +++ b/app/client/src/constants/routes/appRoutes.ts @@ -19,7 +19,6 @@ export const QUERIES_EDITOR_ID_PATH = `${QUERIES_EDITOR_BASE_PATH}/:queryId`; export const JS_COLLECTION_EDITOR_PATH = `/jsObjects`; export const JS_COLLECTION_ID_PATH = `${JS_COLLECTION_EDITOR_PATH}/:collectionId`; export const CURL_IMPORT_PAGE_PATH = `/api/curl/curl-import`; -export const PAGE_LIST_EDITOR_PATH = `/pages`; export const DATA_SOURCES_EDITOR_ID_PATH = `/datasource/:datasourceId`; export const PROVIDER_TEMPLATE_PATH = `/provider/:providerId`; export const GEN_TEMPLATE_URL = "generate-page"; @@ -29,6 +28,8 @@ export const GENERATE_TEMPLATE_FORM_PATH = `${GENERATE_TEMPLATE_PATH}${GEN_TEMPL export const BUILDER_CHECKLIST_PATH = `/checklist`; export const ADMIN_SETTINGS_PATH = "/settings"; export const ADMIN_SETTINGS_CATEGORY_DEFAULT_PATH = "/settings/general"; +export const ADMIN_SETTINGS_CATEGORY_ACL_PATH = "/settings/groups"; +export const ADMIN_SETTINGS_CATEGORY_AUDIT_LOGS_PATH = "/settings/audit-logs"; export const ADMIN_SETTINGS_CATEGORY_PATH = "/settings/:category/:selected?"; export const BUILDER_PATCH_PATH = `/:applicationSlug/:pageSlug(.*\-):pageId/edit`; export const VIEWER_PATCH_PATH = `/:applicationSlug/:pageSlug(.*\-):pageId`; diff --git a/app/client/src/constants/userConstants.ts b/app/client/src/constants/userConstants.ts index 743a6043945c..3ac8756af6d0 100644 --- a/app/client/src/constants/userConstants.ts +++ b/app/client/src/constants/userConstants.ts @@ -15,6 +15,7 @@ export type User = { useCase?: string; isConfigurable: boolean; enableTelemetry: boolean; + adminSettingsVisible?: boolean; }; export interface UserApplication { @@ -35,6 +36,7 @@ export const DefaultCurrentUserDetails: User = { isSuperUser: false, isConfigurable: false, enableTelemetry: false, + adminSettingsVisible: false, }; // TODO keeping it here instead of the USER_API since it leads to cyclic deps errors during tests diff --git a/app/client/src/ee/pages/AdminSettings/config/branding/UpgradeBanner.tsx b/app/client/src/ee/pages/AdminSettings/config/branding/UpgradeBanner.tsx new file mode 100644 index 000000000000..77985ca24458 --- /dev/null +++ b/app/client/src/ee/pages/AdminSettings/config/branding/UpgradeBanner.tsx @@ -0,0 +1,3 @@ +export * from "ce/pages/AdminSettings/config/branding/UpgradeBanner"; +import { default as CE_UpgradeBanner } from "ce/pages/AdminSettings/config/branding/UpgradeBanner"; +export default CE_UpgradeBanner; diff --git a/app/client/src/ee/pages/AdminSettings/config/branding/index.tsx b/app/client/src/ee/pages/AdminSettings/config/branding/index.tsx new file mode 100644 index 000000000000..c843c95b78e5 --- /dev/null +++ b/app/client/src/ee/pages/AdminSettings/config/branding/index.tsx @@ -0,0 +1 @@ +export * from "ce/pages/AdminSettings/config/branding/index"; diff --git a/app/client/src/ee/pages/AdminSettings/config/branding/useBrandingForm.tsx b/app/client/src/ee/pages/AdminSettings/config/branding/useBrandingForm.tsx new file mode 100644 index 000000000000..0a683c3b0f42 --- /dev/null +++ b/app/client/src/ee/pages/AdminSettings/config/branding/useBrandingForm.tsx @@ -0,0 +1 @@ +export * from "ce/pages/AdminSettings/config/branding/useBrandingForm"; diff --git a/app/client/src/ee/pages/AppViewer/BackToHomeButton.tsx b/app/client/src/ee/pages/AppViewer/BackToHomeButton.tsx new file mode 100644 index 000000000000..647c96dda49b --- /dev/null +++ b/app/client/src/ee/pages/AppViewer/BackToHomeButton.tsx @@ -0,0 +1,3 @@ +export * from "ce/pages/AppViewer/BackToHomeButton"; +import { default as CE_BackToHomeButton } from "ce/pages/AppViewer/BackToHomeButton"; +export default CE_BackToHomeButton; diff --git a/app/client/src/entities/Action/index.ts b/app/client/src/entities/Action/index.ts index 277e10feba8c..a3376e3bd1f6 100644 --- a/app/client/src/entities/Action/index.ts +++ b/app/client/src/entities/Action/index.ts @@ -127,6 +127,7 @@ export interface BaseAction { confirmBeforeExecute?: boolean; eventData?: any; messages: string[]; + userPermissions?: string[]; errorReports?: Array; } diff --git a/app/client/src/entities/AppTheming/index.ts b/app/client/src/entities/AppTheming/index.ts index 10b477656dab..31727a53bd22 100644 --- a/app/client/src/entities/AppTheming/index.ts +++ b/app/client/src/entities/AppTheming/index.ts @@ -1,9 +1,30 @@ -type Stylesheet = { - [key: string]: { +type DefaultStylesheet = { + [key: string]: string | DefaultStylesheet; +} & { + childStylesheet?: AppThemeStylesheet; +}; + +export type Stylesheet = T extends void + ? DefaultStylesheet + : T & DefaultStylesheet; + +export type AppThemeStylesheet = { + [key: string]: Stylesheet; +}; + +export type ButtonStyles = { + resetButtonStyles: { + [key: string]: string; + }; + submitButtonStyles: { [key: string]: string; }; }; +export type ChildStylesheet = { + childStylesheet: AppThemeStylesheet; +}; + export type AppTheme = { id: string; name: string; @@ -35,23 +56,7 @@ export type AppTheme = { }; }; // styles for specific widgets - stylesheet: { - [key: string]: { - [key: string]: - | string - | Stylesheet - | { - [key: string]: string; - }; - childStylesheet: Stylesheet; - resetButtonStyles: { - [key: string]: string; - }; - submitButtonStyles: { - [key: string]: string; - }; - }; - }; + stylesheet: AppThemeStylesheet; // current values for the theme properties: { colors: { diff --git a/app/client/src/entities/AppTheming/utils.test.ts b/app/client/src/entities/AppTheming/utils.test.ts index c552602e841f..5321f4b343fc 100644 --- a/app/client/src/entities/AppTheming/utils.test.ts +++ b/app/client/src/entities/AppTheming/utils.test.ts @@ -1,7 +1,21 @@ import { RenderModes } from "constants/WidgetConstants"; import { getPropertiesToUpdateForReset } from "./utils"; +import { registerWidget } from "utils/WidgetRegisterHelpers"; +import ButtonWidget, { + CONFIG as ButtonWidgetConfig, +} from "widgets/ButtonWidget"; +import TableWidget, { CONFIG as TableWidgetConfig } from "widgets/TableWidget"; +import JSONFormWidget, { + CONFIG as JSONFormWidgetConfig, +} from "widgets/JSONFormWidget"; describe("AppThemingSaga test", () => { + beforeAll(() => { + registerWidget(ButtonWidget, ButtonWidgetConfig); + registerWidget(TableWidget, TableWidgetConfig); + registerWidget(JSONFormWidget, JSONFormWidgetConfig); + }); + it("Checks if button widget resets to correct value", () => { const input = [ { @@ -36,6 +50,7 @@ describe("AppThemingSaga test", () => { widgetId: "widget1", updates: { modify: { + borderRadius: "{{appsmith.theme.borderRadius.appBorderRadius}}", buttonColor: "{{appsmith.theme.colors.primaryColor}}", }, }, @@ -97,7 +112,9 @@ describe("AppThemingSaga test", () => { widgetId: "widget1", updates: { modify: { - buttonColor: "{{appsmith.theme.colors.primaryColor}}", + accentColor: "{{appsmith.theme.colors.primaryColor}}", + borderRadius: "{{appsmith.theme.borderRadius.appBorderRadius}}", + boxShadow: "{{appsmith.theme.boxShadow.appBoxShadow}}", "primaryColumns.customColumn1.buttonColor": "{{widget1.sanitizedTableData.map((currentRow) => ( appsmith.theme.colors.primaryColor))}}", }, @@ -157,6 +174,12 @@ describe("AppThemingSaga test", () => { boxShadow: "none", }, }, + resetButtonStyles: { + buttonColor: "{{appsmith.theme.colors.primaryColor}}", + }, + submitButtonStyles: { + buttonColor: "{{appsmith.theme.colors.primaryColor}}", + }, isLoading: false, parentColumnSpace: 1, parentRowSpace: 1, @@ -190,9 +213,17 @@ describe("AppThemingSaga test", () => { updates: { modify: { borderRadius: "{{appsmith.theme.borderRadius.appBorderRadius}}", - boxShadow: "{{appsmith.theme.borderRadius.appBorderRadius}}", - "schema.__root_schema__.children.name.accentColor": + "submitButtonStyles.borderRadius": + "{{appsmith.theme.borderRadius.appBorderRadius}}", + boxShadow: "{{appsmith.theme.boxShadow.appBoxShadow}}", + "resetButtonStyles.borderRadius": + "{{appsmith.theme.borderRadius.appBorderRadius}}", + "schema.__root_schema__.borderRadius": + "{{((sourceData, formData, fieldState) => (appsmith.theme.borderRadius.appBorderRadius))(JSONForm1.sourceData, JSONForm1.formData, JSONForm1.fieldState)}}", + "schema.__root_schema__.cellBorderRadius": "{{((sourceData, formData, fieldState) => (appsmith.theme.borderRadius.appBorderRadius))(JSONForm1.sourceData, JSONForm1.formData, JSONForm1.fieldState)}}", + "schema.__root_schema__.children.name.accentColor": + "{{((sourceData, formData, fieldState) => (appsmith.theme.colors.primaryColor))(JSONForm1.sourceData, JSONForm1.formData, JSONForm1.fieldState)}}", "schema.__root_schema__.children.name.borderRadius": "{{((sourceData, formData, fieldState) => (appsmith.theme.borderRadius.appBorderRadius))(JSONForm1.sourceData, JSONForm1.formData, JSONForm1.fieldState)}}", }, diff --git a/app/client/src/entities/AppTheming/utils.ts b/app/client/src/entities/AppTheming/utils.ts index aaeb42637744..633fad0cbf06 100644 --- a/app/client/src/entities/AppTheming/utils.ts +++ b/app/client/src/entities/AppTheming/utils.ts @@ -5,19 +5,18 @@ import { isDynamicValue, THEME_BINDING_REGEX, } from "utils/DynamicBindingUtils"; -import { ROOT_SCHEMA_KEY } from "widgets/JSONFormWidget/constants"; +import WidgetFactory from "utils/WidgetFactory"; import { parseSchemaItem } from "widgets/WidgetUtils"; +import { ROOT_SCHEMA_KEY } from "widgets/JSONFormWidget/constants"; import { getFieldStylesheet } from "widgets/JSONFormWidget/helper"; -import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; -import { AppTheme } from "entities/AppTheming"; import { UpdateWidgetPropertyPayload } from "actions/controlActions"; +import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; /** * get properties to update for reset */ export const getPropertiesToUpdateForReset = ( canvasWidgets: CanvasWidgetsReduxState, - themeStylesheet: AppTheme["stylesheet"], ) => { const propertiesToUpdate: UpdateWidgetPropertyPayload[] = []; @@ -36,7 +35,9 @@ export const getPropertiesToUpdateForReset = ( // in stylesheet Object.keys(canvasWidgets).map((widgetId) => { const widget = canvasWidgets[widgetId]; - const stylesheetValue = themeStylesheet[widget.type]; + const stylesheetValue = WidgetFactory.getWidgetStylesheetConfigMap( + widget.type, + ); const modifications: any = {}; if (stylesheetValue) { @@ -82,18 +83,19 @@ export const getPropertiesToUpdateForReset = ( Object.keys(widget.groupButtons).map((groupButtonName: string) => { const groupButton = widget.groupButtons[groupButtonName]; - const childStylesheetValue = stylesheetValue.childStylesheet.button; - - Object.keys(childStylesheetValue).map((childPropertyKey) => { - if ( - childStylesheetValue[childPropertyKey] !== - groupButton[childPropertyKey] - ) { - modifications[ - `groupButtons.${groupButtonName}.${childPropertyKey}` - ] = childStylesheetValue[childPropertyKey]; - } - }); + const childStylesheetValue = stylesheetValue?.childStylesheet?.button; + + childStylesheetValue && + Object.keys(childStylesheetValue).map((childPropertyKey) => { + if ( + get(childStylesheetValue, childPropertyKey) !== + groupButton[childPropertyKey] + ) { + modifications[ + `groupButtons.${groupButtonName}.${childPropertyKey}` + ] = get(childStylesheetValue, childPropertyKey); + } + }); }); } @@ -106,7 +108,8 @@ export const getPropertiesToUpdateForReset = ( const fieldStylesheet = getFieldStylesheet( widget.widgetName, schemaItem.fieldType, - themeStylesheet[widget.type].childStylesheet as any, + (WidgetFactory.getWidgetStylesheetConfigMap(widget.type) || {}) + .childStylesheet as any, ); Object.keys(fieldStylesheet).map((fieldPropertyKey) => { @@ -126,26 +129,31 @@ export const getPropertiesToUpdateForReset = ( } // reset submit button - ["submitButtonStyles", "resetButtonStyles"].map((buttonStyleKey) => { - Object.keys(stylesheetValue[buttonStyleKey]).map((propertyKey) => { - const buttonStylesheetValue = - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - stylesheetValue[buttonStyleKey][propertyKey]; + (["submitButtonStyles", "resetButtonStyles"] as const).map( + (buttonStyleKey) => { + Object.keys(get(stylesheetValue, buttonStyleKey, {})).map( + (propertyKey) => { + const buttonStylesheetValue = get( + stylesheetValue, + `${buttonStyleKey}.${propertyKey}`, + ) as string; - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - if ( - THEME_BINDING_REGEX.test(buttonStylesheetValue) && - buttonStylesheetValue !== widget[buttonStyleKey][propertyKey] && - buttonStylesheetValue !== widget[buttonStyleKey][propertyKey] - ) { - modifications[ - `${buttonStyleKey}.${propertyKey}` - ] = buttonStylesheetValue; - } - }); - }); + if ( + buttonStylesheetValue && + typeof buttonStylesheetValue === "string" && + THEME_BINDING_REGEX.test(buttonStylesheetValue) && + buttonStylesheetValue !== + widget[buttonStyleKey][propertyKey] && + buttonStylesheetValue !== widget[buttonStyleKey][propertyKey] + ) { + modifications[ + `${buttonStyleKey}.${propertyKey}` + ] = buttonStylesheetValue; + } + }, + ); + }, + ); } if (Object.keys(modifications).length > 0) { diff --git a/app/client/src/entities/DataTree/dataTreeAction.ts b/app/client/src/entities/DataTree/dataTreeAction.ts index d1f8dc985a9a..9f7d47fd5413 100644 --- a/app/client/src/entities/DataTree/dataTreeAction.ts +++ b/app/client/src/entities/DataTree/dataTreeAction.ts @@ -1,5 +1,8 @@ import { DependencyMap, DynamicPath } from "utils/DynamicBindingUtils"; -import { DataTreeAction, ENTITY_TYPE } from "entities/DataTree/dataTreeFactory"; +import { + ENTITY_TYPE, + UnEvalTreeAction, +} from "entities/DataTree/dataTreeFactory"; import { ActionData } from "reducers/entityReducers/actionsReducer"; import { getBindingAndReactivePathsOfAction, @@ -10,7 +13,7 @@ export const generateDataTreeAction = ( action: ActionData, editorConfig: any[], dependencyConfig: DependencyMap = {}, -): DataTreeAction => { +): UnEvalTreeAction => { let dynamicBindingPathList: DynamicPath[] = []; let datasourceUrl = ""; @@ -45,26 +48,30 @@ export const generateDataTreeAction = ( ); return { + actionId: action.config.id, run: {}, clear: {}, - actionId: action.config.id, - name: action.config.name, - pluginId: action.config.pluginId, - pluginType: action.config.pluginType, - config: action.config.actionConfiguration, - dynamicBindingPathList, data: action.data ? action.data.body : undefined, + isLoading: action.isLoading, responseMeta: { statusCode: action.data?.statusCode, isExecutionSuccess: action.data?.isExecutionSuccess || false, headers: action.data?.headers, }, + config: action.config.actionConfiguration, ENTITY_TYPE: ENTITY_TYPE.ACTION, - isLoading: action.isLoading, - bindingPaths, - reactivePaths, - dependencyMap, - logBlackList: {}, datasourceUrl, + __config__: { + actionId: action.config.id, + name: action.config.name, + pluginId: action.config.pluginId, + pluginType: action.config.pluginType, + dynamicBindingPathList, + ENTITY_TYPE: ENTITY_TYPE.ACTION, + bindingPaths, + reactivePaths, + dependencyMap, + logBlackList: {}, + }, }; }; diff --git a/app/client/src/entities/DataTree/dataTreeFactory.ts b/app/client/src/entities/DataTree/dataTreeFactory.ts index 2571d97e90a1..03300d50df34 100644 --- a/app/client/src/entities/DataTree/dataTreeFactory.ts +++ b/app/client/src/entities/DataTree/dataTreeFactory.ts @@ -1,125 +1,59 @@ -import { - ActionDataState, - ActionDataWithMeta, -} from "reducers/entityReducers/actionsReducer"; +import { ActionDataState } from "reducers/entityReducers/actionsReducer"; import { WidgetProps } from "widgets/BaseWidget"; -import { ActionResponse } from "api/ActionAPI"; import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; import { MetaState } from "reducers/entityReducers/metaReducer"; import { Page } from "@appsmith/constants/ReduxActionConstants"; -import { ActionConfig, PluginType } from "entities/Action"; import { AppDataState } from "reducers/entityReducers/appReducer"; -import { DependencyMap, DynamicPath } from "utils/DynamicBindingUtils"; +import { DependencyMap } from "utils/DynamicBindingUtils"; import { generateDataTreeAction } from "entities/DataTree/dataTreeAction"; import { generateDataTreeJSAction } from "entities/DataTree/dataTreeJSAction"; import { generateDataTreeWidget } from "entities/DataTree/dataTreeWidget"; import { JSCollectionDataState } from "reducers/entityReducers/jsActionsReducer"; -import { ValidationConfig } from "constants/PropertyControlConstants"; -import { Variable } from "entities/JSCollection"; -import { - ActionDescription, - ClearPluginActionDescription, - RunPluginActionDescription, -} from "entities/DataTree/actionTriggers"; import { AppTheme } from "entities/AppTheming"; -import { PluginId } from "api/PluginApi"; import log from "loglevel"; - -export type ActionDispatcher = ( - ...args: any[] -) => Promise | ActionDescription; - -export enum ENTITY_TYPE { - ACTION = "ACTION", - WIDGET = "WIDGET", - APPSMITH = "APPSMITH", - JSACTION = "JSACTION", +import { WidgetConfigProps } from "reducers/entityReducers/widgetConfigReducer"; +import { + ActionDispatcher, + ActionEntityConfig, + ActionEntityEvalTree, + ENTITY_TYPE, + JSActionEntityConfig, + JSActionEvalTree, + WidgetConfig, + EvaluationSubstitutionType, +} from "./types"; + +export interface UnEvalTreeAction extends ActionEntityEvalTree { + __config__: ActionEntityConfig; } +export interface DataTreeAction + extends ActionEntityEvalTree, + ActionEntityConfig {} -export enum EvaluationSubstitutionType { - TEMPLATE = "TEMPLATE", - PARAMETER = "PARAMETER", - SMART_SUBSTITUTE = "SMART_SUBSTITUTE", +export interface UnEvalTreeJSAction extends JSActionEvalTree { + __config__: JSActionEntityConfig; } -// Private widgets do not get evaluated -// For example, for widget Button1 in a List widget List1, List1.template.Button1.text gets evaluated, -// so there is no need to evaluate Button1.text -export type PrivateWidgets = Record; +export type DataTreeJSAction = JSActionEvalTree & JSActionEntityConfig; -export interface DataTreeAction - extends Omit { - data: ActionResponse["body"]; - actionId: string; - config: Partial; - pluginType: PluginType; - pluginId: PluginId; - name: string; - run: ActionDispatcher | RunPluginActionDescription | Record; - clear: - | ActionDispatcher - | ClearPluginActionDescription - | Record; - dynamicBindingPathList: DynamicPath[]; - bindingPaths: Record; - reactivePaths: Record; - ENTITY_TYPE: ENTITY_TYPE.ACTION; - dependencyMap: DependencyMap; - logBlackList: Record; - datasourceUrl: string; +export interface WidgetEntityConfig + extends Partial, + Omit, + WidgetConfig { + defaultMetaProps: Array; + type: string; } -export interface DataTreeJSAction { - pluginType: PluginType.JS; - name: string; - ENTITY_TYPE: ENTITY_TYPE.JSACTION; - body: string; - [propName: string]: any; - meta: Record; - dynamicBindingPathList: DynamicPath[]; - bindingPaths: Record; - reactivePaths: Record; - variables: Array; - dependencyMap: DependencyMap; +export interface WidgetEvalTree extends WidgetProps { + meta: Record; + ENTITY_TYPE: ENTITY_TYPE.WIDGET; } -export interface MetaArgs { - arguments: Variable[]; - isAsync: boolean; - confirmBeforeExecute: boolean; +export interface UnEvalTreeWidget extends WidgetEvalTree { + __config__: WidgetEntityConfig; } -/** - * Map of overriding property as key and overridden property as values - */ -export type OverridingPropertyPaths = Record; - -export enum OverridingPropertyType { - META = "META", - DEFAULT = "DEFAULT", -} -/** - * Map of property name as key and value as object with defaultPropertyName and metaPropertyName which it depends on. - */ -export type PropertyOverrideDependency = Record< - string, - { - DEFAULT: string | undefined; - META: string | undefined; - } ->; -export interface DataTreeWidget extends WidgetProps { - bindingPaths: Record; - reactivePaths: Record; - triggerPaths: Record; - validationPaths: Record; - ENTITY_TYPE: ENTITY_TYPE.WIDGET; - logBlackList: Record; - propertyOverrideDependency: PropertyOverrideDependency; - overridingPropertyPaths: OverridingPropertyPaths; - privateWidgets: PrivateWidgets; - meta: Record; -} +export interface DataTreeWidget extends WidgetEvalTree, WidgetConfig {} export interface DataTreeAppsmith extends Omit { ENTITY_TYPE: ENTITY_TYPE.APPSMITH; @@ -138,6 +72,20 @@ export type DataTree = { [entityName: string]: DataTreeEntity; }; +export type UnEvalTreeEntityObject = + | UnEvalTreeAction + | UnEvalTreeJSAction + | UnEvalTreeWidget; + +export type UnEvalTreeEntity = + | UnEvalTreeEntityObject + | DataTreeAppsmith + | Page[]; + +export type UnEvalTree = { + [entityName: string]: UnEvalTreeEntity; +}; + type DataTreeSeed = { actions: ActionDataState; editorConfigs: Record; @@ -150,6 +98,12 @@ type DataTreeSeed = { theme: AppTheme["properties"]; }; +export type DataTreeEntityConfig = + | WidgetEntityConfig + | ActionEntityConfig + | JSActionEntityConfig + | DataTreeAppsmith; + export class DataTreeFactory { static create({ actions, @@ -161,8 +115,8 @@ export class DataTreeFactory { theme, widgets, widgetsMeta, - }: DataTreeSeed): DataTree { - const dataTree: DataTree = {}; + }: DataTreeSeed): UnEvalTree { + const dataTree: UnEvalTree = {}; const start = performance.now(); const startActions = performance.now(); @@ -195,6 +149,7 @@ export class DataTreeFactory { const endWidgets = performance.now(); dataTree.pageList = pageList; + dataTree.appsmith = { ...appData, // combine both persistent and transient state with the transient state @@ -217,3 +172,5 @@ export class DataTreeFactory { return dataTree; } } + +export { ENTITY_TYPE, EvaluationSubstitutionType }; diff --git a/app/client/src/entities/DataTree/dataTreeJSAction.test.ts b/app/client/src/entities/DataTree/dataTreeJSAction.test.ts index a38d40da84ae..2f20eec82fc0 100644 --- a/app/client/src/entities/DataTree/dataTreeJSAction.test.ts +++ b/app/client/src/entities/DataTree/dataTreeJSAction.test.ts @@ -136,52 +136,9 @@ describe("generateDataTreeJSAction", () => { const expected = { myVar1: [], myVar2: {}, - name: "JSObject2", - actionId: "1234", - pluginType: "JS", ENTITY_TYPE: "JSACTION", body: "export default {\n\tmyVar1: [],\n\tmyVar2: {},\n\tmyFun1: () => {\n\t\t//write code here\n\t},\n\tmyFun2: async () => {\n\t\t//use async-await or promises\n\t}\n}", - meta: { - myFun2: { - arguments: [], - isAsync: true, - confirmBeforeExecute: false, - }, - myFun1: { - arguments: [], - isAsync: false, - confirmBeforeExecute: false, - }, - }, - bindingPaths: { - body: "SMART_SUBSTITUTE", - myFun2: "SMART_SUBSTITUTE", - myFun1: "SMART_SUBSTITUTE", - myVar1: "SMART_SUBSTITUTE", - myVar2: "SMART_SUBSTITUTE", - }, - dynamicBindingPathList: [ - { - key: "body", - }, - { - key: "myVar1", - }, - { - key: "myVar2", - }, - { - key: "myFun2", - }, - { - key: "myFun1", - }, - ], - variables: ["myVar1", "myVar2"], - dependencyMap: { - body: ["myFun2", "myFun1"], - }, myFun2: { data: { users: [{ id: 1, name: "John" }], @@ -190,12 +147,61 @@ describe("generateDataTreeJSAction", () => { myFun1: { data: {}, }, - reactivePaths: { - body: "SMART_SUBSTITUTE", - myFun1: "SMART_SUBSTITUTE", - myFun2: "SMART_SUBSTITUTE", - myVar1: "SMART_SUBSTITUTE", - myVar2: "SMART_SUBSTITUTE", + __config__: { + name: "JSObject2", + actionId: "1234", + pluginType: "JS", + ENTITY_TYPE: "JSACTION", + + meta: { + myFun2: { + arguments: [], + isAsync: true, + confirmBeforeExecute: false, + body: "async () => {\n\t\t//use async-await or promises\n\t}", + }, + myFun1: { + arguments: [], + isAsync: false, + confirmBeforeExecute: false, + body: "() => {\n\t\t//write code here\n\t}", + }, + }, + bindingPaths: { + body: "SMART_SUBSTITUTE", + myFun2: "SMART_SUBSTITUTE", + myFun1: "SMART_SUBSTITUTE", + myVar1: "SMART_SUBSTITUTE", + myVar2: "SMART_SUBSTITUTE", + }, + dynamicBindingPathList: [ + { + key: "body", + }, + { + key: "myVar1", + }, + { + key: "myVar2", + }, + { + key: "myFun2", + }, + { + key: "myFun1", + }, + ], + variables: ["myVar1", "myVar2"], + dependencyMap: { + body: ["myFun2", "myFun1"], + }, + reactivePaths: { + body: "SMART_SUBSTITUTE", + myFun1: "SMART_SUBSTITUTE", + myFun2: "SMART_SUBSTITUTE", + myVar1: "SMART_SUBSTITUTE", + myVar2: "SMART_SUBSTITUTE", + }, }, }; const result = generateDataTreeJSAction(jsCollection); @@ -335,52 +341,65 @@ describe("generateDataTreeJSAction", () => { const expected = { myVar1: [], myVar2: {}, - name: "JSObject2", - actionId: "1234", - pluginType: "JS", - ENTITY_TYPE: "JSACTION", body: "export default {\n\tmyVar1: [],\n\tmyVar2: {},\n\tmyFun1: () => {\n\t\t//write code here\n\t return JSObject2.myFun2},\n\tmyFun2: async () => {\n\t\t//use async-await or promises\n\t}\n}", - meta: { - myFun2: { - arguments: [], - isAsync: true, - confirmBeforeExecute: false, - }, - myFun1: { - arguments: [], - isAsync: false, - confirmBeforeExecute: false, - }, - }, - bindingPaths: { - body: "SMART_SUBSTITUTE", - myFun2: "SMART_SUBSTITUTE", - myFun1: "SMART_SUBSTITUTE", - myVar1: "SMART_SUBSTITUTE", - myVar2: "SMART_SUBSTITUTE", - }, - dynamicBindingPathList: [ - { - key: "body", - }, - { - key: "myVar1", + ENTITY_TYPE: "JSACTION", + __config__: { + ENTITY_TYPE: "JSACTION", + meta: { + myFun2: { + arguments: [], + isAsync: true, + confirmBeforeExecute: false, + body: "async () => {\n\t\t//use async-await or promises\n\t}", + }, + myFun1: { + arguments: [], + isAsync: false, + confirmBeforeExecute: false, + body: "() => {\n\t\t//write code here\n\t}", + }, }, - { - key: "myVar2", + bindingPaths: { + body: "SMART_SUBSTITUTE", + myFun2: "SMART_SUBSTITUTE", + myFun1: "SMART_SUBSTITUTE", + myVar1: "SMART_SUBSTITUTE", + myVar2: "SMART_SUBSTITUTE", }, - { - key: "myFun2", + dynamicBindingPathList: [ + { + key: "body", + }, + { + key: "myVar1", + }, + { + key: "myVar2", + }, + { + key: "myFun2", + }, + { + key: "myFun1", + }, + ], + variables: ["myVar1", "myVar2"], + dependencyMap: { + body: ["myFun2", "myFun1"], }, - { - key: "myFun1", + name: "JSObject2", + actionId: "1234", + pluginType: "JS", + reactivePaths: { + body: "SMART_SUBSTITUTE", + myFun1: "SMART_SUBSTITUTE", + myFun2: "SMART_SUBSTITUTE", + myVar1: "SMART_SUBSTITUTE", + myVar2: "SMART_SUBSTITUTE", }, - ], - variables: ["myVar1", "myVar2"], - dependencyMap: { - body: ["myFun2", "myFun1"], }, + myFun2: { data: { users: [{ id: 1, name: "John" }], @@ -389,13 +408,6 @@ describe("generateDataTreeJSAction", () => { myFun1: { data: {}, }, - reactivePaths: { - body: "SMART_SUBSTITUTE", - myFun1: "SMART_SUBSTITUTE", - myFun2: "SMART_SUBSTITUTE", - myVar1: "SMART_SUBSTITUTE", - myVar2: "SMART_SUBSTITUTE", - }, }; const result = generateDataTreeJSAction(jsCollection); diff --git a/app/client/src/entities/DataTree/dataTreeJSAction.ts b/app/client/src/entities/DataTree/dataTreeJSAction.ts index f8e7427d16ca..7e52773c95c3 100644 --- a/app/client/src/entities/DataTree/dataTreeJSAction.ts +++ b/app/client/src/entities/DataTree/dataTreeJSAction.ts @@ -1,17 +1,18 @@ import { - DataTreeJSAction, ENTITY_TYPE, - MetaArgs, + UnEvalTreeJSAction, } from "entities/DataTree/dataTreeFactory"; + import { JSCollectionData } from "reducers/entityReducers/jsActionsReducer"; import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory"; import { DependencyMap } from "utils/DynamicBindingUtils"; +import { MetaArgs } from "./types"; const reg = /this\./g; export const generateDataTreeJSAction = ( js: JSCollectionData, -): DataTreeJSAction => { +): UnEvalTreeJSAction => { const meta: Record = {}; const dynamicBindingPathList = []; const bindingPaths: Record = {}; @@ -43,6 +44,7 @@ export const generateDataTreeJSAction = ( arguments: action.actionConfiguration.jsArguments, isAsync: action.actionConfiguration.isAsync, confirmBeforeExecute: !!action.confirmBeforeExecute, + body: action.actionConfiguration.body, }; bindingPaths[action.name] = EvaluationSubstitutionType.SMART_SUBSTITUTE; dynamicBindingPathList.push({ key: action.name }); @@ -54,17 +56,20 @@ export const generateDataTreeJSAction = ( } return { ...variableList, - name: js.config.name, - actionId: js.config.id, - pluginType: js.config.pluginType, - ENTITY_TYPE: ENTITY_TYPE.JSACTION, - body: removeThisReference, - meta: meta, - bindingPaths: bindingPaths, // As all js object function referred to as action is user javascript code, we add them as binding paths. - reactivePaths: { ...bindingPaths }, - dynamicBindingPathList: dynamicBindingPathList, - variables: listVariables, - dependencyMap: dependencyMap, ...actionsData, + body: removeThisReference, + ENTITY_TYPE: ENTITY_TYPE.JSACTION, + __config__: { + meta: meta, + name: js.config.name, + actionId: js.config.id, + pluginType: js.config.pluginType, + ENTITY_TYPE: ENTITY_TYPE.JSACTION, + bindingPaths: bindingPaths, // As all js object function referred to as action is user javascript code, we add them as binding paths. + reactivePaths: { ...bindingPaths }, + dynamicBindingPathList: dynamicBindingPathList, + variables: listVariables, + dependencyMap: dependencyMap, + }, }; }; diff --git a/app/client/src/entities/DataTree/dataTreeWidget.test.ts b/app/client/src/entities/DataTree/dataTreeWidget.test.ts index f935ebadc480..79e3097cee5c 100644 --- a/app/client/src/entities/DataTree/dataTreeWidget.test.ts +++ b/app/client/src/entities/DataTree/dataTreeWidget.test.ts @@ -1,14 +1,13 @@ import { FlattenedWidgetProps } from "reducers/entityReducers/canvasWidgetsReducer"; import { generateDataTreeWidget } from "entities/DataTree/dataTreeWidget"; import { - DataTreeWidget, ENTITY_TYPE, EvaluationSubstitutionType, } from "entities/DataTree/dataTreeFactory"; -import { RenderModes } from "constants/WidgetConstants"; import WidgetFactory from "utils/WidgetFactory"; import { ValidationTypes } from "constants/WidgetValidation"; +import { RenderModes } from "constants/WidgetConstants"; // const WidgetTypes = WidgetFactory.widgetTypes; @@ -207,51 +206,62 @@ describe("generateDataTreeWidget", () => { errorMessage: EvaluationSubstitutionType.TEMPLATE, }; - const expected: DataTreeWidget = { - bindingPaths, - reactivePaths: { - ...bindingPaths, - isDirty: EvaluationSubstitutionType.TEMPLATE, - isFocused: EvaluationSubstitutionType.TEMPLATE, - isValid: EvaluationSubstitutionType.TEMPLATE, - text: EvaluationSubstitutionType.TEMPLATE, - value: EvaluationSubstitutionType.TEMPLATE, - "meta.text": EvaluationSubstitutionType.TEMPLATE, - }, - meta: { - text: "Tester", - isDirty: true, - deepObj: { - level1: { - metaValue: 10, + const expected = { + __config__: { + ENTITY_TYPE: ENTITY_TYPE.WIDGET, + bindingPaths, + reactivePaths: { + ...bindingPaths, + isDirty: EvaluationSubstitutionType.TEMPLATE, + isFocused: EvaluationSubstitutionType.TEMPLATE, + isValid: EvaluationSubstitutionType.TEMPLATE, + text: EvaluationSubstitutionType.TEMPLATE, + value: EvaluationSubstitutionType.TEMPLATE, + "meta.text": EvaluationSubstitutionType.TEMPLATE, + }, + + triggerPaths: { + onSubmit: true, + onTextChanged: true, + }, + type: "INPUT_WIDGET_V2", + validationPaths: { + defaultText: { type: ValidationTypes.TEXT }, + errorMessage: { type: ValidationTypes.TEXT }, + isDisabled: { type: ValidationTypes.BOOLEAN }, + isRequired: { type: ValidationTypes.BOOLEAN }, + isVisible: { type: ValidationTypes.BOOLEAN }, + placeholderText: { type: ValidationTypes.TEXT }, + regex: { type: ValidationTypes.REGEX }, + resetOnSubmit: { type: ValidationTypes.BOOLEAN }, + }, + dynamicBindingPathList: [ + { + key: "isValid", + }, + { + key: "value", }, + ], + logBlackList: { + isValid: true, + value: true, }, - }, - triggerPaths: { - onSubmit: true, - onTextChanged: true, - }, - validationPaths: { - defaultText: { type: ValidationTypes.TEXT }, - errorMessage: { type: ValidationTypes.TEXT }, - isDisabled: { type: ValidationTypes.BOOLEAN }, - isRequired: { type: ValidationTypes.BOOLEAN }, - isVisible: { type: ValidationTypes.BOOLEAN }, - placeholderText: { type: ValidationTypes.TEXT }, - regex: { type: ValidationTypes.REGEX }, - resetOnSubmit: { type: ValidationTypes.BOOLEAN }, - }, - dynamicBindingPathList: [ - { - key: "isValid", + propertyOverrideDependency: { + text: { + DEFAULT: "defaultText", + META: "meta.text", + }, + }, + defaultMetaProps: ["text", "isDirty", "isFocused"], + defaultProps: { + text: "defaultText", }, - { - key: "value", + overridingPropertyPaths: { + defaultText: ["text", "meta.text"], + "meta.text": ["text"], }, - ], - logBlackList: { - isValid: true, - value: true, + privateWidgets: {}, }, value: "{{Input1.text}}", isDirty: true, @@ -263,35 +273,28 @@ describe("generateDataTreeWidget", () => { leftColumn: 0, parentColumnSpace: 0, parentRowSpace: 0, - propertyOverrideDependency: { - text: { - DEFAULT: "defaultText", - META: "meta.text", - }, - }, - renderMode: RenderModes.CANVAS, rightColumn: 0, - topRow: 0, - type: "INPUT_WIDGET_V2", + renderMode: RenderModes.CANVAS, version: 0, + topRow: 0, widgetId: "123", widgetName: "Input1", ENTITY_TYPE: ENTITY_TYPE.WIDGET, defaultText: "", - defaultMetaProps: ["text", "isDirty", "isFocused"], - defaultProps: { - text: "defaultText", - }, - overridingPropertyPaths: { - defaultText: ["text", "meta.text"], - "meta.text": ["text"], - }, - privateWidgets: {}, deepObj: { level1: { metaValue: 10, }, }, + meta: { + text: "Tester", + isDirty: true, + deepObj: { + level1: { + metaValue: 10, + }, + }, + }, }; const result = generateDataTreeWidget(widget, widgetMetaProps); diff --git a/app/client/src/entities/DataTree/dataTreeWidget.ts b/app/client/src/entities/DataTree/dataTreeWidget.ts index 0df5871628a4..a2e41a7ec09a 100644 --- a/app/client/src/entities/DataTree/dataTreeWidget.ts +++ b/app/client/src/entities/DataTree/dataTreeWidget.ts @@ -2,15 +2,22 @@ import { getAllPathsFromPropertyConfig } from "entities/Widget/utils"; import _ from "lodash"; import memoize from "micro-memoize"; import { FlattenedWidgetProps } from "reducers/entityReducers/canvasWidgetsReducer"; -import { getEntityDynamicBindingPathList } from "utils/DynamicBindingUtils"; +import { + DynamicPath, + getEntityDynamicBindingPathList, +} from "utils/DynamicBindingUtils"; import WidgetFactory from "utils/WidgetFactory"; import { - DataTreeWidget, ENTITY_TYPE, + WidgetEntityConfig, + UnEvalTreeWidget, +} from "./dataTreeFactory"; +import { OverridingPropertyPaths, OverridingPropertyType, PropertyOverrideDependency, -} from "./dataTreeFactory"; +} from "./types"; + import { setOverridingProperty } from "./utils"; // We are splitting generateDataTreeWidget into two parts to memoize better as the widget doesn't change very often. @@ -19,9 +26,10 @@ import { setOverridingProperty } from "./utils"; const generateDataTreeWidgetWithoutMeta = ( widget: FlattenedWidgetProps, ): { - dataTreeWidgetWithoutMetaProps: DataTreeWidget; + dataTreeWidgetWithoutMetaProps: UnEvalTreeWidget; overridingMetaPropsMap: Record; defaultMetaProps: Record; + entityConfig: WidgetEntityConfig; } => { const derivedProps: any = {}; const blockedDerivedProps: Record = {}; @@ -130,14 +138,38 @@ const generateDataTreeWidgetWithoutMeta = ( * * Therefore spread is replaced with "merge" which merges objects recursively. */ + + const widgetPathsToOmit = [ + "dynamicBindingPathList", + "dynamicPropertyPathList", + "dynamicTriggerPathList", + "privateWidgets", + "type", + ]; + const dataTreeWidgetWithoutMetaProps = _.merge( - {}, - widget, + { + ENTITY_TYPE: ENTITY_TYPE.WIDGET, + }, + _.omit(widget, widgetPathsToOmit), unInitializedDefaultProps, - // defaultMetaProps, - // widgetMetaProps, derivedProps, - { + ); + + const dynamicPathsList: { + dynamicPropertyPathList?: DynamicPath[]; + dynamicTriggerPathList?: DynamicPath[]; + } = {}; + if (widget.dynamicPropertyPathList) + dynamicPathsList.dynamicPropertyPathList = widget.dynamicPropertyPathList; + if (widget.dynamicTriggerPathList) + dynamicPathsList.dynamicTriggerPathList = widget.dynamicTriggerPathList; + + return { + dataTreeWidgetWithoutMetaProps, + overridingMetaPropsMap, + defaultMetaProps, + entityConfig: { defaultProps, defaultMetaProps: Object.keys(defaultMetaProps), dynamicBindingPathList, @@ -145,9 +177,6 @@ const generateDataTreeWidgetWithoutMeta = ( ...widget.logBlackList, ...blockedDerivedProps, }, - meta: {}, // this will be overridden by meta value calculated in generateDataTreeWidget - propertyOverrideDependency, - overridingPropertyPaths, bindingPaths, reactivePaths, triggerPaths, @@ -156,31 +185,20 @@ const generateDataTreeWidgetWithoutMeta = ( privateWidgets: { ...widget.privateWidgets, }, + propertyOverrideDependency, + overridingPropertyPaths, + type: widget.type, + ...dynamicPathsList, }, - ); - return { - dataTreeWidgetWithoutMetaProps, - overridingMetaPropsMap, - defaultMetaProps, }; }; // @todo set the max size dynamically based on number of widgets. (widgets.length) -// Remove the debug statements in July 2022 + const generateDataTreeWidgetWithoutMetaMemoized = memoize( generateDataTreeWidgetWithoutMeta, { maxSize: 1000, - // onCacheHit: (cache, options) => { - // console.log("####### cache was hit: ", cache.keys.length); - // }, - // onCacheAdd: (cache, options) => { - // console.log( - // "####### cache was missed ", - // cache.keys.length, - // cache.keys[0][0].widgetName, - // ); - // }, }, ); @@ -191,6 +209,7 @@ export const generateDataTreeWidget = ( const { dataTreeWidgetWithoutMetaProps: dataTreeWidget, defaultMetaProps, + entityConfig, overridingMetaPropsMap, } = generateDataTreeWidgetWithoutMetaMemoized(widget); const overridingMetaProps: Record = {}; @@ -215,5 +234,7 @@ export const generateDataTreeWidget = ( }); dataTreeWidget["meta"] = meta; + dataTreeWidget["__config__"] = entityConfig; + return dataTreeWidget; }; diff --git a/app/client/src/entities/DataTree/types.ts b/app/client/src/entities/DataTree/types.ts new file mode 100644 index 000000000000..39547640bf0a --- /dev/null +++ b/app/client/src/entities/DataTree/types.ts @@ -0,0 +1,127 @@ +import { ActionResponse } from "api/ActionAPI"; +import { PluginId } from "api/PluginApi"; +import { ValidationConfig } from "constants/PropertyControlConstants"; +import { ActionConfig, PluginType } from "entities/Action"; +import { + ActionDescription, + ClearPluginActionDescription, + RunPluginActionDescription, +} from "entities/DataTree/actionTriggers"; +import { Variable } from "entities/JSCollection"; +import { DependencyMap, DynamicPath } from "utils/DynamicBindingUtils"; + +export type ActionDispatcher = ( + ...args: any[] +) => Promise | ActionDescription; + +export enum ENTITY_TYPE { + ACTION = "ACTION", + WIDGET = "WIDGET", + APPSMITH = "APPSMITH", + JSACTION = "JSACTION", +} + +export enum EvaluationSubstitutionType { + TEMPLATE = "TEMPLATE", + PARAMETER = "PARAMETER", + SMART_SUBSTITUTE = "SMART_SUBSTITUTE", +} + +// Action entity types +export interface ActionEntityEvalTree { + actionId: string; + isLoading: boolean; + data: ActionResponse["body"]; + run: ActionDispatcher | RunPluginActionDescription | Record; + clear: + | ActionDispatcher + | ClearPluginActionDescription + | Record; + responseMeta: { + statusCode?: string; + isExecutionSuccess: boolean; + headers?: unknown; + }; + ENTITY_TYPE: ENTITY_TYPE.ACTION; + config: Partial; + datasourceUrl: string; +} + +export interface ActionEntityConfig { + dynamicBindingPathList: DynamicPath[]; + bindingPaths: Record; + reactivePaths: Record; + ENTITY_TYPE: ENTITY_TYPE.ACTION; + dependencyMap: DependencyMap; + logBlackList: Record; + pluginType: PluginType; + pluginId: PluginId; + actionId: string; + name: string; +} + +// JSAction (JSObject) entity Types + +export interface MetaArgs { + arguments: Variable[]; + isAsync: boolean; + confirmBeforeExecute: boolean; + body: string; +} + +export interface JSActionEntityConfig { + meta: Record; + dynamicBindingPathList: DynamicPath[]; + bindingPaths: Record; + reactivePaths: Record; + variables: Array; + dependencyMap: DependencyMap; + pluginType: PluginType.JS; + name: string; + ENTITY_TYPE: ENTITY_TYPE.JSACTION; + actionId: string; +} + +export interface JSActionEvalTree { + [propName: string]: any; + body: string; +} + +// Widget entity Types + +// Private widgets do not get evaluated +// For example, for widget Button1 in a List widget List1, List1.template.Button1.text gets evaluated, +// so there is no need to evaluate Button1.text +export type PrivateWidgets = Record; + +/** + * Map of overriding property as key and overridden property as values + */ +export type OverridingPropertyPaths = Record; + +export enum OverridingPropertyType { + META = "META", + DEFAULT = "DEFAULT", +} +/** + * Map of property name as key and value as object with defaultPropertyName and metaPropertyName which it depends on. + */ +export type PropertyOverrideDependency = Record< + string, + { + DEFAULT: string | undefined; + META: string | undefined; + } +>; + +export type WidgetConfig = { + bindingPaths: Record; + reactivePaths: Record; + triggerPaths: Record; + validationPaths: Record; + ENTITY_TYPE: ENTITY_TYPE.WIDGET; + logBlackList: Record; + propertyOverrideDependency: PropertyOverrideDependency; + overridingPropertyPaths: OverridingPropertyPaths; + privateWidgets: PrivateWidgets; +}; diff --git a/app/client/src/entities/DataTree/utils.ts b/app/client/src/entities/DataTree/utils.ts index a0b96189fcef..4989849aaace 100644 --- a/app/client/src/entities/DataTree/utils.ts +++ b/app/client/src/entities/DataTree/utils.ts @@ -2,7 +2,7 @@ import { PropertyOverrideDependency, OverridingPropertyPaths, OverridingPropertyType, -} from "./dataTreeFactory"; +} from "./types"; type SetOverridingPropertyParams = { key: string; diff --git a/app/client/src/entities/Datasource/index.ts b/app/client/src/entities/Datasource/index.ts index 77c0ebd302ff..95bbea170c30 100644 --- a/app/client/src/entities/Datasource/index.ts +++ b/app/client/src/entities/Datasource/index.ts @@ -62,6 +62,7 @@ interface BaseDatasource { workspaceId: string; isValid: boolean; isConfigured?: boolean; + userPermissions?: string[]; isDeleting?: boolean; } diff --git a/app/client/src/entities/Engine/AppEditorEngine.ts b/app/client/src/entities/Engine/AppEditorEngine.ts index 68ca3c13c855..60cbf3a1aeed 100644 --- a/app/client/src/entities/Engine/AppEditorEngine.ts +++ b/app/client/src/entities/Engine/AppEditorEngine.ts @@ -32,7 +32,7 @@ import { } from "@appsmith/constants/ReduxActionConstants"; import { addBranchParam } from "constants/routes"; import { APP_MODE } from "entities/App"; -import { all, call, put, select } from "redux-saga/effects"; +import { call, put, select } from "redux-saga/effects"; import { failFastApiCalls } from "sagas/InitSagas"; import { getCurrentApplication } from "selectors/editorSelectors"; import { getCurrentGitBranch } from "selectors/gitSyncSelectors"; @@ -172,10 +172,8 @@ export default class AppEditorEngine extends AppEngine { } public *loadAppEntities(toLoadPageId: string, applicationId: string): any { - yield all([ - call(this.loadPageThemesAndActions, toLoadPageId, applicationId), - call(this.loadPluginsAndDatasources), - ]); + yield call(this.loadPageThemesAndActions, toLoadPageId, applicationId); + yield call(this.loadPluginsAndDatasources); } public *completeChore() { diff --git a/app/client/src/entities/JSCollection/index.ts b/app/client/src/entities/JSCollection/index.ts index eaca82280f21..36b9daec5540 100644 --- a/app/client/src/entities/JSCollection/index.ts +++ b/app/client/src/entities/JSCollection/index.ts @@ -17,6 +17,7 @@ export interface JSCollection { actions: Array; body: string; variables: Array; + userPermissions?: string[]; errorReports?: Array; } diff --git a/app/client/src/entities/Replay/ReplayEntity/ReplayCanvas.ts b/app/client/src/entities/Replay/ReplayEntity/ReplayCanvas.ts index fcac1a6062de..0dec5f7a5bd7 100644 --- a/app/client/src/entities/Replay/ReplayEntity/ReplayCanvas.ts +++ b/app/client/src/entities/Replay/ReplayEntity/ReplayCanvas.ts @@ -34,6 +34,7 @@ const positionProps = [ "detachFromLayout", "noContainerOffset", "isCanvas", + "height", ]; /** diff --git a/app/client/src/entities/URLRedirect/URLAssembly.ts b/app/client/src/entities/URLRedirect/URLAssembly.ts index 0f96db5ec076..69502949847f 100644 --- a/app/client/src/entities/URLRedirect/URLAssembly.ts +++ b/app/client/src/entities/URLRedirect/URLAssembly.ts @@ -47,11 +47,16 @@ export type PageURLParams = { customSlug?: string; }; -const fetchQueryParamsToPersist = () => { +const fetchQueryParamsToPersist = (persistExistingParams: boolean) => { const existingParams = getQueryParamsObject() || {}; // not persisting the entire query currently, since that's the current behavior const { branch, embed } = existingParams; - let params = { branch, embed } as any; + let params; + if (persistExistingParams) { + params = { ...existingParams }; + } else { + params = { branch, embed } as any; + } // test param to make sure a query param is present in the URL during dev and tests if ((window as any).Cypress) { params = { a: "b", ...params }; @@ -159,6 +164,29 @@ export class URLBuilder { return basePath; } + getCustomSlugPathPreview(pageId: string, customSlug: string) { + const urlPattern = + baseURLRegistry[URL_TYPE.CUSTOM_SLUG][APP_MODE.PUBLISHED]; + return generatePath(urlPattern, { + pageId, + customSlug: `${customSlug}-`, + }).toLowerCase(); + } + + getPagePathPreview(pageId: string, pageName: string) { + const { applicationVersion } = this.appParams; + + const urlType = this.getURLType(applicationVersion); + + const urlPattern = baseURLRegistry[urlType][APP_MODE.PUBLISHED]; + + const formattedParams = this.getFormattedParams(pageId); + + formattedParams.pageSlug = `${pageName}-`; + + return generatePath(urlPattern, formattedParams).toLowerCase(); + } + /** * @throws {URIError} * @param builderParams @@ -166,7 +194,13 @@ export class URLBuilder { * @returns URL string */ build(builderParams: URLBuilderParams, mode: APP_MODE = APP_MODE.EDIT) { - const { hash = "", params = {}, suffix, pageId } = builderParams; + const { + hash = "", + params = {}, + persistExistingParams = false, + suffix, + pageId, + } = builderParams; if (!pageId) { throw new URIError( @@ -176,7 +210,9 @@ export class URLBuilder { const basePath = this.generateBasePath(pageId, mode); - const queryParamsToPersist = fetchQueryParamsToPersist(); + const queryParamsToPersist = fetchQueryParamsToPersist( + persistExistingParams, + ); const modifiedQueryParams = { ...queryParamsToPersist, ...params }; diff --git a/app/client/src/entities/Widget/utils.test.ts b/app/client/src/entities/Widget/utils.test.ts index d564ea9f7023..0aeb272ad3fe 100644 --- a/app/client/src/entities/Widget/utils.test.ts +++ b/app/client/src/entities/Widget/utils.test.ts @@ -204,15 +204,15 @@ describe("getAllPathsFromPropertyConfig", () => { validationPaths: { tableData: { type: "OBJECT_ARRAY", params: { default: [] } }, "primaryColumns.status.boxShadow": { - type: ValidationTypes.TABLE_PROPERTY, + type: ValidationTypes.ARRAY_OF_TYPE_OR_TYPE, params: { type: ValidationTypes.TEXT }, }, "primaryColumns.status.borderRadius": { - type: ValidationTypes.TABLE_PROPERTY, + type: ValidationTypes.ARRAY_OF_TYPE_OR_TYPE, params: { type: ValidationTypes.TEXT }, }, "primaryColumns.status.buttonVariant": { - type: ValidationTypes.TABLE_PROPERTY, + type: ValidationTypes.ARRAY_OF_TYPE_OR_TYPE, params: { type: ValidationTypes.TEXT, params: { @@ -222,58 +222,58 @@ describe("getAllPathsFromPropertyConfig", () => { }, }, "primaryColumns.status.buttonColor": { - type: ValidationTypes.TABLE_PROPERTY, + type: ValidationTypes.ARRAY_OF_TYPE_OR_TYPE, params: { type: ValidationTypes.TEXT, params: { regex: /^(?![<|{{]).+/ }, }, }, "primaryColumns.status.isDisabled": { - type: ValidationTypes.TABLE_PROPERTY, + type: ValidationTypes.ARRAY_OF_TYPE_OR_TYPE, params: { type: "BOOLEAN" }, }, "primaryColumns.status.isCellVisible": { - type: ValidationTypes.TABLE_PROPERTY, + type: ValidationTypes.ARRAY_OF_TYPE_OR_TYPE, params: { type: "BOOLEAN" }, }, "primaryColumns.createdAt.cellBackground": { - type: ValidationTypes.TABLE_PROPERTY, + type: ValidationTypes.ARRAY_OF_TYPE_OR_TYPE, params: { type: ValidationTypes.TEXT, params: { regex: /^(?![<|{{]).+/ }, }, }, "primaryColumns.createdAt.textColor": { - type: ValidationTypes.TABLE_PROPERTY, + type: ValidationTypes.ARRAY_OF_TYPE_OR_TYPE, params: { type: ValidationTypes.TEXT, params: { regex: /^(?![<|{{]).+/ }, }, }, "primaryColumns.createdAt.verticalAlignment": { - type: ValidationTypes.TABLE_PROPERTY, + type: ValidationTypes.ARRAY_OF_TYPE_OR_TYPE, params: { type: ValidationTypes.TEXT, params: { allowedValues: ["TOP", "CENTER", "BOTTOM"] }, }, }, "primaryColumns.createdAt.fontStyle": { - type: ValidationTypes.TABLE_PROPERTY, + type: ValidationTypes.ARRAY_OF_TYPE_OR_TYPE, params: { type: ValidationTypes.TEXT }, }, "primaryColumns.createdAt.textSize": { - type: ValidationTypes.TABLE_PROPERTY, + type: ValidationTypes.ARRAY_OF_TYPE_OR_TYPE, params: { type: ValidationTypes.TEXT }, }, "primaryColumns.createdAt.horizontalAlignment": { - type: ValidationTypes.TABLE_PROPERTY, + type: ValidationTypes.ARRAY_OF_TYPE_OR_TYPE, params: { type: ValidationTypes.TEXT, params: { allowedValues: ["LEFT", "CENTER", "RIGHT"] }, }, }, "primaryColumns.createdAt.outputFormat": { - type: ValidationTypes.TABLE_PROPERTY, + type: ValidationTypes.ARRAY_OF_TYPE_OR_TYPE, params: { type: ValidationTypes.TEXT, params: { @@ -303,7 +303,7 @@ describe("getAllPathsFromPropertyConfig", () => { }, }, "primaryColumns.createdAt.inputFormat": { - type: ValidationTypes.TABLE_PROPERTY, + type: ValidationTypes.ARRAY_OF_TYPE_OR_TYPE, params: { type: ValidationTypes.TEXT, params: { @@ -333,47 +333,47 @@ describe("getAllPathsFromPropertyConfig", () => { }, }, "primaryColumns.createdAt.isCellVisible": { - type: ValidationTypes.TABLE_PROPERTY, + type: ValidationTypes.ARRAY_OF_TYPE_OR_TYPE, params: { type: "BOOLEAN" }, }, "primaryColumns.name.cellBackground": { - type: ValidationTypes.TABLE_PROPERTY, + type: ValidationTypes.ARRAY_OF_TYPE_OR_TYPE, params: { type: ValidationTypes.TEXT, params: { regex: /^(?![<|{{]).+/ }, }, }, "primaryColumns.name.textColor": { - type: ValidationTypes.TABLE_PROPERTY, + type: ValidationTypes.ARRAY_OF_TYPE_OR_TYPE, params: { type: ValidationTypes.TEXT, params: { regex: /^(?![<|{{]).+/ }, }, }, "primaryColumns.name.verticalAlignment": { - type: ValidationTypes.TABLE_PROPERTY, + type: ValidationTypes.ARRAY_OF_TYPE_OR_TYPE, params: { type: ValidationTypes.TEXT, params: { allowedValues: ["TOP", "CENTER", "BOTTOM"] }, }, }, "primaryColumns.name.fontStyle": { - type: ValidationTypes.TABLE_PROPERTY, + type: ValidationTypes.ARRAY_OF_TYPE_OR_TYPE, params: { type: ValidationTypes.TEXT }, }, "primaryColumns.name.textSize": { - type: ValidationTypes.TABLE_PROPERTY, + type: ValidationTypes.ARRAY_OF_TYPE_OR_TYPE, params: { type: ValidationTypes.TEXT }, }, "primaryColumns.name.horizontalAlignment": { - type: ValidationTypes.TABLE_PROPERTY, + type: ValidationTypes.ARRAY_OF_TYPE_OR_TYPE, params: { type: ValidationTypes.TEXT, params: { allowedValues: ["LEFT", "CENTER", "RIGHT"] }, }, }, "primaryColumns.name.isCellVisible": { - type: ValidationTypes.TABLE_PROPERTY, + type: ValidationTypes.ARRAY_OF_TYPE_OR_TYPE, params: { type: "BOOLEAN" }, }, primaryColumnId: { type: ValidationTypes.TEXT }, diff --git a/app/client/src/index.css b/app/client/src/index.css index 86b66c888675..f71bdca1d86f 100755 --- a/app/client/src/index.css +++ b/app/client/src/index.css @@ -114,3 +114,7 @@ div.bp3-popover-arrow { .bp3-overlay-backdrop { background-color: rgba(16, 22, 26, 0.7) !important; } + +.bp3-overlay-zindex { + z-index: 20 !important; +} \ No newline at end of file diff --git a/app/client/src/index.tsx b/app/client/src/index.tsx index 994d2b7d502a..0d88f36e57a2 100755 --- a/app/client/src/index.tsx +++ b/app/client/src/index.tsx @@ -6,16 +6,14 @@ import "./index.css"; import { ThemeProvider } from "constants/DefaultTheme"; import { appInitializer } from "utils/AppUtils"; import { Slide } from "react-toastify"; -import store from "./store"; +import store, { runSagaMiddleware } from "./store"; import { LayersContext, Layers } from "constants/Layers"; import AppRouter from "./AppRouter"; import * as Sentry from "@sentry/react"; -import { getCurrentThemeDetails, ThemeMode } from "selectors/themeSelectors"; +import { getCurrentThemeDetails } from "selectors/themeSelectors"; import { connect } from "react-redux"; import { AppState } from "@appsmith/reducers"; -import { setThemeMode } from "actions/themeActions"; import { StyledToastContainer } from "design-system"; -import localStorage from "utils/localStorage"; import "./assets/styles/index.css"; import "./polyfills/corejs-add-on"; import GlobalStyles from "globalStyles"; @@ -25,6 +23,8 @@ import AppErrorBoundary from "AppErrorBoundry"; const shouldAutoFreeze = process.env.NODE_ENV === "development"; setAutoFreeze(shouldAutoFreeze); +runSagaMiddleware(); + appInitializer(); function App() { @@ -41,13 +41,7 @@ function App() { class ThemedApp extends React.Component<{ currentTheme: any; - setTheme: (themeMode: ThemeMode) => void; }> { - componentDidMount() { - if (localStorage.getItem("THEME") === "LIGHT") { - this.props.setTheme(ThemeMode.LIGHT); - } - } render() { return ( @@ -70,16 +64,8 @@ class ThemedApp extends React.Component<{ const mapStateToProps = (state: AppState) => ({ currentTheme: getCurrentThemeDetails(state), }); -const mapDispatchToProps = (dispatch: any) => ({ - setTheme: (mode: ThemeMode) => { - dispatch(setThemeMode(mode)); - }, -}); -const ThemedAppWithProps = connect( - mapStateToProps, - mapDispatchToProps, -)(ThemedApp); +const ThemedAppWithProps = connect(mapStateToProps)(ThemedApp); ReactDOM.render(, document.getElementById("root")); diff --git a/app/client/src/navigation/FocusElements.ts b/app/client/src/navigation/FocusElements.ts index 890179843947..6fbd7ca80330 100644 --- a/app/client/src/navigation/FocusElements.ts +++ b/app/client/src/navigation/FocusElements.ts @@ -2,22 +2,41 @@ import { getApiPaneConfigSelectedTabIndex, getApiPaneResponsePaneHeight, getApiPaneResponseSelectedTab, + getApiRightPaneSelectedTab, } from "selectors/apiPaneSelectors"; import { setApiPaneResponseSelectedTab, setApiPaneConfigSelectedTabIndex, setApiPaneResponsePaneHeight, + setApiRightPaneSelectedTab, } from "actions/apiPaneActions"; import { AppState } from "@appsmith/reducers"; import { ReduxAction } from "@appsmith/constants/ReduxActionConstants"; import { + getAllEntityCollapsibleStates, getAllPropertySectionState, + getAllSubEntityCollapsibleStates, + getCodeEditorHistory, + getExplorerSwitchIndex, + getPropertyPanelState, getFocusableCodeEditorField, getSelectedCanvasDebuggerTab, - getSelectedPropertyTabIndex, + getWidgetSelectedPropertyTabIndex, } from "selectors/editorContextSelectors"; +import { + setAllEntityCollapsibleStates, + setAllSubEntityCollapsibleStates, + setCodeEditorHistory, + setExplorerSwitchIndex, + setPanelPropertiesState, + setWidgetSelectedPropertyTabIndex, +} from "actions/editorContextActions"; import { setFocusableCodeEditorField } from "actions/editorContextActions"; -import { getSelectedWidgets } from "selectors/ui"; +import { + getAllDatasourceCollapsibleState, + getSelectedWidgets, + isDatasourceInViewMode, +} from "selectors/ui"; import { selectMultipleWidgetsInitAction } from "actions/widgetSelectionActions"; import { FocusEntity } from "navigation/FocusEntity"; @@ -42,12 +61,29 @@ import { setJsPaneResponsePaneHeight, setJsPaneResponseSelectedTab, } from "actions/jsPaneActions"; +import { getExplorerWidth } from "selectors/explorerSelector"; +import { updateExplorerWidthAction } from "actions/explorerActions"; +import { + DEFAULT_ENTITY_EXPLORER_WIDTH, + DEFAULT_PROPERTY_PANE_WIDTH, +} from "constants/AppConstants"; +import { + getPropertyPaneWidth, + getSelectedPropertyPanel, +} from "selectors/propertyPaneSelectors"; +import { + setPropertyPaneWidthAction, + setSelectedPropertyPanels, +} from "actions/propertyPaneActions"; import { setAllPropertySectionState, setFocusablePropertyPaneField, - setSelectedPropertyTabIndex, } from "actions/propertyPaneActions"; import { setCanvasDebuggerSelectedTab } from "actions/debuggerActions"; +import { + setAllDatasourceCollapsible, + setDatasourceViewMode, +} from "actions/datasourceActions"; import { PluginPackageName } from "entities/Action"; import { getFocusablePropertyPaneField } from "selectors/propertyPaneSelectors"; @@ -55,6 +91,14 @@ export enum FocusElement { ApiPaneConfigTabs = "ApiPaneConfigTabs", ApiPaneResponseTabs = "ApiPaneResponseTabs", ApiPaneResponseHeight = "ApiPaneResponseHeight", + CanvasDebuggerTabs = "CanvasDebuggerTabs", + CodeEditorHistory = "CodeEditorHistory", + EntityCollapsibleState = "EntityCollapsibleState", + EntityExplorerWidth = "EntityExplorerWidth", + ExplorerSwitchIndex = "ExplorerSwitchIndex", + DatasourceViewMode = "DatasourceViewMode", + DatasourceAccordions = "DatasourceAccordions", + ApiRightPaneTabs = "ApiRightPaneTabs", QueryPaneConfigTabs = "QueryPaneConfigTabs", QueryPaneResponseTabs = "QueryPaneResponseTabs", QueryPaneResponseHeight = "QueryPaneResponseHeight", @@ -65,8 +109,11 @@ export enum FocusElement { PropertyField = "PropertyField", PropertySections = "PropertySections", PropertyTabs = "PropertyTabs", + PropertyPanelContext = "PropertyPanelContext", + PropertyPaneWidth = "PropertyPaneWidth", + SelectedPropertyPanel = "SelectedPropertyPanel", SelectedWidgets = "SelectedWidgets", - CanvasDebuggerTabs = "CanvasDebuggerTabs", + SubEntityCollapsibleState = "SubEntityCollapsibleState", } type Config = { @@ -79,6 +126,44 @@ type Config = { export const FocusElementsConfig: Record = { [FocusEntity.NONE]: [], + [FocusEntity.PAGE]: [ + { + name: FocusElement.CodeEditorHistory, + selector: getCodeEditorHistory, + setter: setCodeEditorHistory, + defaultValue: {}, + }, + { + name: FocusElement.EntityExplorerWidth, + selector: getExplorerWidth, + setter: updateExplorerWidthAction, + defaultValue: DEFAULT_ENTITY_EXPLORER_WIDTH, + }, + { + name: FocusElement.EntityCollapsibleState, + selector: getAllEntityCollapsibleStates, + setter: setAllEntityCollapsibleStates, + defaultValue: {}, + }, + { + name: FocusElement.SubEntityCollapsibleState, + selector: getAllSubEntityCollapsibleStates, + setter: setAllSubEntityCollapsibleStates, + defaultValue: {}, + }, + { + name: FocusElement.ExplorerSwitchIndex, + selector: getExplorerSwitchIndex, + setter: setExplorerSwitchIndex, + defaultValue: 0, + }, + { + name: FocusElement.PropertyPanelContext, + selector: getPropertyPanelState, + setter: setPanelPropertiesState, + defaultValue: {}, + }, + ], [FocusEntity.CANVAS]: [ { name: FocusElement.PropertySections, @@ -86,6 +171,12 @@ export const FocusElementsConfig: Record = { setter: setAllPropertySectionState, defaultValue: {}, }, + { + name: FocusElement.SelectedPropertyPanel, + selector: getSelectedPropertyPanel, + setter: setSelectedPropertyPanels, + defaultValue: {}, + }, { name: FocusElement.SelectedWidgets, selector: getSelectedWidgets, @@ -98,6 +189,25 @@ export const FocusElementsConfig: Record = { setter: setCanvasDebuggerSelectedTab, defaultValue: 0, }, + { + name: FocusElement.PropertyPaneWidth, + selector: getPropertyPaneWidth, + setter: setPropertyPaneWidthAction, + defaultValue: DEFAULT_PROPERTY_PANE_WIDTH, + }, + ], + [FocusEntity.DATASOURCE]: [ + { + name: FocusElement.DatasourceViewMode, + selector: isDatasourceInViewMode, + setter: setDatasourceViewMode, + defaultValue: true, + }, + { + name: FocusElement.DatasourceAccordions, + selector: getAllDatasourceCollapsibleState, + setter: setAllDatasourceCollapsible, + }, ], [FocusEntity.JS_OBJECT]: [ { @@ -152,14 +262,15 @@ export const FocusElementsConfig: Record = { [FocusEntity.PROPERTY_PANE]: [ { name: FocusElement.PropertyTabs, - selector: getSelectedPropertyTabIndex, - setter: setSelectedPropertyTabIndex, + selector: getWidgetSelectedPropertyTabIndex, + setter: setWidgetSelectedPropertyTabIndex, defaultValue: 0, }, { name: FocusElement.PropertyField, selector: getFocusablePropertyPaneField, setter: setFocusablePropertyPaneField, + defaultValue: "", }, ], [FocusEntity.API]: [ @@ -191,5 +302,10 @@ export const FocusElementsConfig: Record = { setter: setApiPaneResponsePaneHeight, defaultValue: ActionExecutionResizerHeight, }, + { + name: FocusElement.ApiRightPaneTabs, + selector: getApiRightPaneSelectedTab, + setter: setApiRightPaneSelectedTab, + }, ], }; diff --git a/app/client/src/navigation/FocusEntity.test.ts b/app/client/src/navigation/FocusEntity.test.ts index 4dab42c16efa..07775449eba1 100644 --- a/app/client/src/navigation/FocusEntity.test.ts +++ b/app/client/src/navigation/FocusEntity.test.ts @@ -5,66 +5,90 @@ describe("identifyEntityFromPath", () => { { path: "/applications/myAppId/pages/myPageId/edit", hash: "", - expected: { entity: FocusEntity.CANVAS, id: "" }, + expected: { entity: FocusEntity.CANVAS, id: "", pageId: "myPageId" }, }, { path: "/applications/myAppId/pages/myPageId/edit#ryvc8i7oja", hash: "#ryvc8i7oja", - expected: { entity: FocusEntity.PROPERTY_PANE, id: "#ryvc8i7oja" }, + expected: { + entity: FocusEntity.PROPERTY_PANE, + id: "#ryvc8i7oja", + pageId: "myPageId", + }, }, { path: "/applications/myAppId/pages/myPageId/edit/api/myApiId", hash: "", - expected: { entity: FocusEntity.API, id: "myApiId" }, + expected: { entity: FocusEntity.API, id: "myApiId", pageId: "myPageId" }, }, { path: "/applications/myAppId/pages/myPageId/edit/queries/myQueryId", hash: "", - expected: { entity: FocusEntity.QUERY, id: "myQueryId" }, + expected: { + entity: FocusEntity.QUERY, + id: "myQueryId", + pageId: "myPageId", + }, }, ]; const pageSlugCases = [ { path: "/app/eval/page1-myPageId/edit", hash: "", - expected: { entity: FocusEntity.CANVAS, id: "" }, + expected: { entity: FocusEntity.CANVAS, id: "", pageId: "myPageId" }, }, { path: "/app/myAppId/page1-myPageId/edit#ryvc8i7oja", hash: "#ryvc8i7oja", - expected: { entity: FocusEntity.PROPERTY_PANE, id: "#ryvc8i7oja" }, + expected: { + entity: FocusEntity.PROPERTY_PANE, + id: "#ryvc8i7oja", + pageId: "myPageId", + }, }, { path: "/app/eval/page1-myPageId/edit/api/myApiId", hash: "", - expected: { entity: FocusEntity.API, id: "myApiId" }, + expected: { entity: FocusEntity.API, id: "myApiId", pageId: "myPageId" }, }, { path: "/app/eval/page1-myPageId/edit/queries/myQueryId", hash: "", - expected: { entity: FocusEntity.QUERY, id: "myQueryId" }, + expected: { + entity: FocusEntity.QUERY, + id: "myQueryId", + pageId: "myPageId", + }, }, ]; const customSlugCases = [ { path: "/app/myCustomSlug-myPageId/edit", hash: "", - expected: { entity: FocusEntity.CANVAS, id: "" }, + expected: { entity: FocusEntity.CANVAS, id: "", pageId: "myPageId" }, }, { path: "/app/myCustomSlug-myPageId/edit#ryvc8i7oja", hash: "#ryvc8i7oja", - expected: { entity: FocusEntity.PROPERTY_PANE, id: "#ryvc8i7oja" }, + expected: { + entity: FocusEntity.PROPERTY_PANE, + id: "#ryvc8i7oja", + pageId: "myPageId", + }, }, { path: "/app/myCustomSlug-myPageId/edit/api/myApiId", hash: "", - expected: { entity: FocusEntity.API, id: "myApiId" }, + expected: { entity: FocusEntity.API, id: "myApiId", pageId: "myPageId" }, }, { path: "/app/myCustomSlug-myPageId/edit/queries/myQueryId", hash: "", - expected: { entity: FocusEntity.QUERY, id: "myQueryId" }, + expected: { + entity: FocusEntity.QUERY, + id: "myQueryId", + pageId: "myPageId", + }, }, ]; diff --git a/app/client/src/navigation/FocusEntity.ts b/app/client/src/navigation/FocusEntity.ts index 9dcba896e5be..aeecff2a0e50 100644 --- a/app/client/src/navigation/FocusEntity.ts +++ b/app/client/src/navigation/FocusEntity.ts @@ -4,14 +4,19 @@ import { BUILDER_CUSTOM_PATH, BUILDER_PATH, BUILDER_PATH_DEPRECATED, + DATA_SOURCES_EDITOR_ID_PATH, JS_COLLECTION_ID_PATH, QUERIES_EDITOR_ID_PATH, } from "constants/routes"; +import { SAAS_EDITOR_DATASOURCE_ID_PATH } from "pages/Editor/SaaSEditor/constants"; import { SAAS_EDITOR_API_ID_PATH } from "pages/Editor/SaaSEditor/constants"; +import { getQueryParamsFromString } from "utils/getQueryParamsObject"; export enum FocusEntity { + PAGE = "PAGE", API = "API", CANVAS = "CANVAS", + DATASOURCE = "DATASOURCE", QUERY = "QUERY", JS_OBJECT = "JS_OBJECT", PROPERTY_PANE = "PROPERTY_PANE", @@ -21,8 +26,56 @@ export enum FocusEntity { export type FocusEntityInfo = { entity: FocusEntity; id: string; + pageId?: string; }; +/** + * Method to indicate if the URL is of type API, Query etc, + * and not anything else + * @param path + * @returns + */ +export function shouldStoreURLforFocus(path: string) { + const entityTypesToStore = [ + FocusEntity.QUERY, + FocusEntity.API, + FocusEntity.JS_OBJECT, + FocusEntity.DATASOURCE, + ]; + + const entity = identifyEntityFromPath(path)?.entity; + + return entityTypesToStore.indexOf(entity) >= 0; +} + +/** + * parse search string and get branch + * @param searchString + * @returns + */ +const fetchGitBranch = (searchString: string | undefined) => { + const existingParams = + getQueryParamsFromString(searchString?.substring(1)) || {}; + + return existingParams.branch; +}; + +/** + * Compare if both the params are on same branch + * @param previousParamString + * @param currentParamStaring + * @returns + */ +export function isSameBranch( + previousParamString: string, + currentParamStaring: string, +) { + const previousBranch = fetchGitBranch(previousParamString); + const currentBranch = fetchGitBranch(currentParamStaring); + + return previousBranch === currentBranch; +} + export function identifyEntityFromPath( path: string, hash?: string, @@ -33,6 +86,7 @@ export function identifyEntityFromPath( } const match = matchPath<{ apiId?: string; + datasourceId?: string; pluginPackageName?: string; queryId?: string; appId?: string; @@ -46,6 +100,11 @@ export function identifyEntityFromPath( BUILDER_PATH_DEPRECATED + QUERIES_EDITOR_ID_PATH, BUILDER_PATH + QUERIES_EDITOR_ID_PATH, BUILDER_CUSTOM_PATH + QUERIES_EDITOR_ID_PATH, + BUILDER_PATH_DEPRECATED + DATA_SOURCES_EDITOR_ID_PATH, + BUILDER_PATH + DATA_SOURCES_EDITOR_ID_PATH, + BUILDER_CUSTOM_PATH + DATA_SOURCES_EDITOR_ID_PATH, + BUILDER_PATH + SAAS_EDITOR_DATASOURCE_ID_PATH, + BUILDER_CUSTOM_PATH + SAAS_EDITOR_DATASOURCE_ID_PATH, BUILDER_PATH_DEPRECATED + SAAS_EDITOR_API_ID_PATH, BUILDER_PATH + SAAS_EDITOR_API_ID_PATH, BUILDER_CUSTOM_PATH + SAAS_EDITOR_API_ID_PATH, @@ -56,24 +115,52 @@ export function identifyEntityFromPath( BUILDER_PATH, BUILDER_CUSTOM_PATH, ], + exact: true, }); if (!match) { return { entity: FocusEntity.NONE, id: "" }; } if (match.params.apiId) { if (match.params.pluginPackageName) { - return { entity: FocusEntity.QUERY, id: match.params.apiId }; + return { + entity: FocusEntity.QUERY, + id: match.params.apiId, + pageId: match.params.pageId, + }; } - return { entity: FocusEntity.API, id: match.params.apiId }; + return { + entity: FocusEntity.API, + id: match.params.apiId, + pageId: match.params.pageId, + }; + } + if (match.params.datasourceId) { + return { + entity: FocusEntity.DATASOURCE, + id: match.params.datasourceId, + pageId: match.params.pageId, + }; } if (match.params.queryId) { - return { entity: FocusEntity.QUERY, id: match.params.queryId }; + return { + entity: FocusEntity.QUERY, + id: match.params.queryId, + pageId: match.params.pageId, + }; } if (match.params.collectionId) { - return { entity: FocusEntity.JS_OBJECT, id: match.params.collectionId }; + return { + entity: FocusEntity.JS_OBJECT, + id: match.params.collectionId, + pageId: match.params.pageId, + }; } if (match.params.pageId && hash) { - return { entity: FocusEntity.PROPERTY_PANE, id: hash }; + return { + entity: FocusEntity.PROPERTY_PANE, + id: hash, + pageId: match.params.pageId, + }; } - return { entity: FocusEntity.CANVAS, id: "" }; + return { entity: FocusEntity.CANVAS, id: "", pageId: match.params.pageId }; } diff --git a/app/client/src/pages/AppViewer/AppViewerHeader.tsx b/app/client/src/pages/AppViewer/AppViewerHeader.tsx index 758793717cc4..9cde3e8c6e9c 100644 --- a/app/client/src/pages/AppViewer/AppViewerHeader.tsx +++ b/app/client/src/pages/AppViewer/AppViewerHeader.tsx @@ -30,7 +30,7 @@ import Button from "./AppViewerButton"; import MenuIcon from "remixicon-react/MenuFillIcon"; import CloseIcon from "remixicon-react/CloseFillIcon"; import PageMenu from "./PageMenu"; -import BackToHomeButton from "./BackToHomeButton"; +import BackToHomeButton from "@appsmith/pages/AppViewer/BackToHomeButton"; import TourCompletionMessage from "pages/Editor/GuidedTour/TourCompletionMessage"; import { useHref } from "pages/Editor/utils"; import { builderURL } from "RouteBuilder"; @@ -94,7 +94,7 @@ export function AppViewerHeader(props: AppViewerHeaderProps) { <>
{currentApplicationDetails && ( -
+
} diff --git a/app/client/src/pages/AppViewer/index.tsx b/app/client/src/pages/AppViewer/index.tsx index a770a6b90f66..3ccf9c8b351e 100644 --- a/app/client/src/pages/AppViewer/index.tsx +++ b/app/client/src/pages/AppViewer/index.tsx @@ -239,6 +239,11 @@ function AppViewer(props: Props) { [updateWidgetAutoHeightAction, dispatch], ); + const checkContainersForAutoHeightCallback = useCallback( + () => dispatch(checkContainersForAutoHeightAction()), + [checkContainersForAutoHeightAction], + ); + return ( ( svg { path { - fill: ${Colors.BLACK}; + fill: currentColor; } } } @@ -136,7 +137,7 @@ const NameWrapper = styled((props: HTMLDivProps & NameWrapperProps) => ( width: 16px; height: 16px; path { - fill: ${Colors.WHITE}; + fill: currentColor; } } } @@ -309,6 +310,7 @@ type ApplicationCardProps = { update?: (id: string, data: UpdateApplicationPayload) => void; enableImportExport?: boolean; isMobile?: boolean; + hasCreateNewApplicationPermission?: boolean; }; const EditButton = styled(Button)` @@ -468,7 +470,11 @@ export function ApplicationCard(props: ApplicationCardProps) { cypressSelector: "t--share", }); } - if (props.duplicate && hasEditPermission) { + if ( + props.duplicate && + props.hasCreateNewApplicationPermission && + hasEditPermission + ) { moreActionItems.push({ onSelect: duplicateApp, text: "Duplicate", @@ -512,6 +518,10 @@ export function ApplicationCard(props: ApplicationCardProps) { props.application?.userPermissions ?? [], PERMISSION_TYPE.EXPORT_APPLICATION, ); + const hasDeletePermission = hasDeleteApplicationPermission( + props.application?.userPermissions, + ); + const updateColor = (color: string) => { setSelectedColor(color); props.update && @@ -574,7 +584,7 @@ export function ApplicationCard(props: ApplicationCardProps) { setMoreActionItems(updatedActionItems); }; const addDeleteOption = () => { - if (props.delete && hasEditPermission) { + if (props.delete && hasDeletePermission) { const index = moreActionItems.findIndex( (el) => el.icon === "delete-blank", ); @@ -852,7 +862,7 @@ export function ApplicationCard(props: ApplicationCardProps) { )} {!isMenuOpen && (
- {/* {!props.skipThemeEditor && ( -
-

Positioning

- -
- )} */} - {!props.skipThemeEditor && }
); diff --git a/app/client/src/pages/Editor/DataSourceEditor/Collapsible.tsx b/app/client/src/pages/Editor/DataSourceEditor/Collapsible.tsx index 12de07709274..362ed518c9eb 100644 --- a/app/client/src/pages/Editor/DataSourceEditor/Collapsible.tsx +++ b/app/client/src/pages/Editor/DataSourceEditor/Collapsible.tsx @@ -1,7 +1,12 @@ -import React from "react"; +import React, { useCallback, useEffect } from "react"; import { Collapse, Icon } from "@blueprintjs/core"; import styled from "styled-components"; import { Icon as AdsIcon, IconName, IconSize } from "design-system"; +import { useDispatch, useSelector } from "react-redux"; +import { AppState } from "@appsmith/reducers"; +import { getDatasourceCollapsibleState } from "selectors/ui"; +import { setDatasourceCollapsible } from "actions/datasourceActions"; +import isUndefined from "lodash/isUndefined"; const SectionLabel = styled.div` font-weight: 500; @@ -32,10 +37,6 @@ const TopBorder = styled.div` margin-bottom: 24px; `; -interface ComponentState { - isOpen: boolean; -} - interface ComponentProps { children: any; title: string; @@ -49,51 +50,55 @@ interface ComponentProps { type Props = ComponentProps; -class Collapsible extends React.Component { - constructor(props: Props) { - super(props); +function Collapsible(props: Props) { + const { children, defaultIsOpen, headerIcon, title } = props; + const dispatch = useDispatch(); + const isOpen = useSelector((state: AppState) => + getDatasourceCollapsibleState(state, title), + ); - this.state = { - isOpen: props.defaultIsOpen || false, - }; - } + const setIsOpen = useCallback((open) => { + dispatch(setDatasourceCollapsible(title, open)); + }, []); - render() { - const { children, headerIcon, title } = this.props; - const { isOpen } = this.state; + useEffect(() => { + // We set the default value only when there is no state stored yet for the same + if (defaultIsOpen && isUndefined(isOpen)) { + setIsOpen(defaultIsOpen); + } + }, [defaultIsOpen, isOpen]); - return ( - <> - - this.setState({ isOpen: !this.state.isOpen })} - > - - {title} - {headerIcon && ( - - )} - - - + return ( + <> + + setIsOpen(!isOpen)} + > + + {title} + {headerIcon && ( + + )} + + + - - {children} - - - ); - } + + {children} + + + ); } export default Collapsible; diff --git a/app/client/src/pages/Editor/DataSourceEditor/Connected.tsx b/app/client/src/pages/Editor/DataSourceEditor/Connected.tsx index 12e97951f460..6e6b406dae05 100644 --- a/app/client/src/pages/Editor/DataSourceEditor/Connected.tsx +++ b/app/client/src/pages/Editor/DataSourceEditor/Connected.tsx @@ -7,9 +7,12 @@ import { getDatasource, getPlugin } from "selectors/entitiesSelector"; import { Colors } from "constants/Colors"; import { HeaderIcons } from "icons/HeaderIcons"; import styled from "styled-components"; -import { renderDatasourceSection } from "./DatasourceSection"; +import RenderDatasourceInformation from "./DatasourceSection"; import NewActionButton from "./NewActionButton"; +import { hasCreateDatasourceActionPermission } from "@appsmith/utils/permissionHelpers"; +import { getPagePermissions } from "selectors/editorSelectors"; + const ConnectedText = styled.div` color: ${Colors.OXFORD_BLUE}; font-size: 17px; @@ -37,6 +40,7 @@ const Wrapper = styled.div` function Connected() { const params = useParams<{ datasourceId: string }>(); + const datasource = useSelector((state: AppState) => getDatasource(state, params.datasourceId), ); @@ -49,6 +53,15 @@ function Connected() { getPlugin(state, datasource?.pluginId ?? ""), ); + const datasourcePermissions = datasource?.userPermissions || []; + + const pagePermissions = useSelector(getPagePermissions); + + const canCreateDatasourceActions = hasCreateDatasourceActionPermission([ + ...datasourcePermissions, + ...pagePermissions, + ]); + const currentFormConfig: Array = datasourceFormConfigs[datasource?.pluginId ?? ""]; @@ -67,14 +80,20 @@ function Connected() {
- {!isNil(currentFormConfig) && !isNil(datasource) - ? renderDatasourceSection(currentFormConfig[0], datasource) - : undefined} + {!isNil(currentFormConfig) && !isNil(datasource) ? ( + + ) : ( + undefined + )}
); diff --git a/app/client/src/pages/Editor/DataSourceEditor/DBForm.tsx b/app/client/src/pages/Editor/DataSourceEditor/DBForm.tsx index 01940873c275..3055c51175e2 100644 --- a/app/client/src/pages/Editor/DataSourceEditor/DBForm.tsx +++ b/app/client/src/pages/Editor/DataSourceEditor/DBForm.tsx @@ -26,11 +26,13 @@ import { } from "./JSONtoForm"; import DatasourceAuth from "pages/common/datasourceAuth"; import { getDatasourceFormButtonConfig } from "selectors/entitiesSelector"; +import { hasManageDatasourcePermission } from "@appsmith/utils/permissionHelpers"; +import { TEMP_DATASOURCE_ID } from "constants/Datasource"; const { cloudHosting } = getAppsmithConfigs(); interface DatasourceDBEditorProps extends JSONtoFormProps { - setDatasourceEditorMode: (id: string, viewMode: boolean) => void; + setDatasourceViewMode: (viewMode: boolean) => void; openOmnibarReadMore: (text: string) => void; datasourceId: string; applicationId: string; @@ -43,6 +45,11 @@ interface DatasourceDBEditorProps extends JSONtoFormProps { datasource: Datasource; datasourceButtonConfiguration: string[] | undefined; hiddenHeader?: boolean; + canManageDatasource?: boolean; + datasourceName?: string; + isDatasourceBeingSavedFromPopup: boolean; + isFormDirty: boolean; + datasourceDeleteTrigger: () => void; } type Props = DatasourceDBEditorProps & @@ -73,13 +80,14 @@ class DatasourceDBEditor extends JSONtoForm { componentDidUpdate(prevProps: Props) { if (prevProps.datasourceId !== this.props.datasourceId) { super.componentDidUpdate(prevProps); - if (!this.props.hiddenHeader) - this.props.setDatasourceEditorMode(this.props.datasourceId, true); } } // returns normalized and trimmed datasource form data getSanitizedData = () => { - return this.getTrimmedData(this.normalizeValues()); + return this.getTrimmedData({ + ...this.normalizeValues(), + name: this.props.datasourceName, + }); }; openOmnibarReadMore = () => { @@ -104,14 +112,19 @@ class DatasourceDBEditor extends JSONtoForm { renderDataSourceConfigForm = (sections: any) => { const { + canManageDatasource, datasource, datasourceButtonConfiguration, + datasourceDeleteTrigger, + datasourceId, formData, messages, pluginType, viewMode, } = this.props; + const createFlow = datasourceId === TEMP_DATASOURCE_ID; + return ( { @@ -122,17 +135,17 @@ class DatasourceDBEditor extends JSONtoForm {
- + {viewMode && ( { - this.props.setDatasourceEditorMode( - this.props.datasourceId, - false, - ); + this.props.setDatasourceViewMode(false); }} text="EDIT" /> @@ -159,31 +172,33 @@ class DatasourceDBEditor extends JSONtoForm { APPSMITH_IP_ADDRESSES, )} on your database instance to connect to it. `} - {"Read more "} + {"Learn more "} )} - {!viewMode ? ( + {(!viewMode || datasourceId === TEMP_DATASOURCE_ID) && ( <> {!_.isNil(sections) ? _.map(sections, this.renderMainSection) : undefined} {""} - ) : ( - )} + {viewMode && } {/* Render datasource form call-to-actions */} {datasource && ( )} @@ -203,11 +218,21 @@ const mapStateToProps = (state: AppState, props: any) => { props?.formData?.pluginId, ); + const datasourcePermissions = datasource.userPermissions || []; + + const canManageDatasource = hasManageDatasourcePermission( + datasourcePermissions, + ); + return { messages: hintMessages, datasource, datasourceButtonConfiguration, isReconnectingModalOpen: state.entities.datasources.isReconnectingModalOpen, + canManageDatasource: canManageDatasource, + datasourceName: datasource?.name ?? "", + isDatasourceBeingSavedFromPopup: + state.entities.datasources.isDatasourceBeingSavedFromPopup, }; }; diff --git a/app/client/src/pages/Editor/DataSourceEditor/DatasourceSection.tsx b/app/client/src/pages/Editor/DataSourceEditor/DatasourceSection.tsx index 5c8090a436b6..20590217d7d4 100644 --- a/app/client/src/pages/Editor/DataSourceEditor/DatasourceSection.tsx +++ b/app/client/src/pages/Editor/DataSourceEditor/DatasourceSection.tsx @@ -1,9 +1,9 @@ import { Datasource } from "entities/Datasource"; import React from "react"; -import { map, get, isEmpty, isArray } from "lodash"; +import { map, get, isArray } from "lodash"; import { Colors } from "constants/Colors"; import styled from "styled-components"; -import { isHidden } from "components/formControls/utils"; +import { isHidden, isKVArray } from "components/formControls/utils"; import log from "loglevel"; const Key = styled.div` @@ -30,91 +30,139 @@ const FieldWrapper = styled.div` } `; -export const renderDatasourceSection = ( - config: any, - datasource: Datasource, -): any => { - return ( - - {map(config.children, (section) => { - if (isHidden(datasource, section.hidden)) return null; - if ("children" in section) { - return renderDatasourceSection(section, datasource); - } else { - try { - const { configProperty, controlType, label } = section; - const reactKey = datasource.id + "_" + label; - let value = get(datasource, configProperty); +export default class RenderDatasourceInformation extends React.Component<{ + config: any; + datasource: Datasource; +}> { + renderKVArray = (children: Array) => { + try { + // setup config for each child + const firstConfigProperty = children[0].configProperty; + const configPropertyInfo = firstConfigProperty.split("[*]."); + const values = get(this.props.datasource, configPropertyInfo[0], null); + const renderValues: Array> = children.reduce( + ( + acc, + { configProperty, label }: { configProperty: string; label: string }, + ) => { + const configPropertyKey = configProperty.split("[*].")[1]; + values.forEach((value: any, index: number) => { + if (!acc[index]) { + acc[index] = []; + } + + acc[index].push({ + key: configPropertyKey, + label, + value: value[configPropertyKey], + }); + }); + return acc; + }, + [], + ); + return renderValues.map((renderValue, index: number) => ( + + {renderValue.map(({ key, label, value }) => ( + + {label}: + {value} + + ))} + + )); + } catch (e) { + return; + } + }; - if (!value || (isArray(value) && value.length < 1)) { - return; + renderDatasourceSection(section: any) { + const { datasource } = this.props; + return ( + + {map(section.children, (section) => { + if (isHidden(datasource, section.hidden)) return null; + if ("children" in section) { + if (isKVArray(section.children)) { + return this.renderKVArray(section.children); } - if (controlType === "KEYVALUE_ARRAY") { - const configPropertyInfo = configProperty.split("[*]."); - const values = get(datasource, configPropertyInfo[0], null); + return this.renderDatasourceSection(section); + } else { + try { + const { configProperty, controlType, label } = section; + const reactKey = datasource.id + "_" + label; - if (values && !isEmpty(values)) { - const keyValuePair = values[0]; - value = keyValuePair[configPropertyInfo[1]]; - } else { - value = ""; + if (controlType === "FIXED_KEY_INPUT") { + return ( + + {configProperty.key}: {" "} + {configProperty.value} + + ); } - } - if (controlType === "FIXED_KEY_INPUT") { - return ( - - {configProperty.key}: {" "} - {configProperty.value} - - ); - } + let value = get(datasource, configProperty); - if (controlType === "KEY_VAL_INPUT") { - return ( - - {label} - {value && - value.map((val: { key: string; value: string }) => { - return ( -
+ if (controlType === "DROP_DOWN") { + if (Array.isArray(section.options)) { + const option = section.options.find( + (el: any) => el.value === value, + ); + if (option && option.label) { + value = option.label; + } + } + } + + if (!value || (isArray(value) && value.length < 1)) { + return; + } + + if (isArray(value)) { + return ( + + {label}: + {value.map( + ( + { key, value }: { key: string; value: any }, + index: number, + ) => ( +
Key: - {val.key} + {key}
Value: - {val.value} + {value}
- ); - })} -
- ); - } - - if (controlType === "DROP_DOWN") { - if (Array.isArray(section.options)) { - const option = section.options.find( - (el: any) => el.value === value, + ), + )} + ); - if (option && option.label) { - value = option.label; - } } - } - return ( - - {label}: {value} - - ); - } catch (e) { - log.error(e); + return ( + + {label}: {value} + + ); + } catch (e) { + log.error(e); + } } - } - })} - - ); -}; + })} + + ); + } + + render() { + return this.renderDatasourceSection(this.props.config); + } +} diff --git a/app/client/src/pages/Editor/DataSourceEditor/FormTitle.tsx b/app/client/src/pages/Editor/DataSourceEditor/FormTitle.tsx index ae9b29d7ba8a..21333e6e6afa 100644 --- a/app/client/src/pages/Editor/DataSourceEditor/FormTitle.tsx +++ b/app/client/src/pages/Editor/DataSourceEditor/FormTitle.tsx @@ -10,8 +10,12 @@ import { getDatasource, getDatasources } from "selectors/entitiesSelector"; import { useSelector, useDispatch } from "react-redux"; import { Datasource } from "entities/Datasource"; import { isNameValid } from "utils/helpers"; -import { saveDatasourceName } from "actions/datasourceActions"; +import { + saveDatasourceName, + updateDatasourceName, +} from "actions/datasourceActions"; import { Spinner } from "@blueprintjs/core"; +import { TEMP_DATASOURCE_ID } from "constants/Datasource"; const Wrapper = styled.div` margin-left: 10px; @@ -23,6 +27,7 @@ const Wrapper = styled.div` interface ComponentProps { focusOnMount: boolean; + disabled?: boolean; } type FormTitleProps = ComponentProps; @@ -53,7 +58,22 @@ function FormTitle(props: FormTitleProps) { (name: string) => { const datasourcesNames: Record = {}; datasources - .filter((datasource) => datasource.id !== currentDatasource?.id) + // in case of REST API and Authenticated GraphQL API, when user clicks on save as datasource + // we first need to update the action and then redirect to action page, + // for that reason we need temporary datasource data to exist in store till action is updated, + // if temp datasource data is there, then duplicate name issue occurs + // hence added extra condition for REST and GraphQL. + .filter( + (datasource) => + datasource.id !== currentDatasource?.id && + !( + datasource.name === currentDatasource?.name && + ["REST API", "Authenticated GraphQL API"].includes( + (datasource as any).pluginName, + ) && + datasource.pluginId === currentDatasource?.pluginId + ), + ) .map((datasource) => { datasourcesNames[datasource.name] = datasource; }); @@ -77,12 +97,31 @@ function FormTitle(props: FormTitleProps) { const handleDatasourceNameChange = useCallback( (name: string) => { + // Check if the datasource name equals "Untitled Datasource ABC" if no , use the name passed. + const datsourceName = name || "Untitled Datasource ABC"; if ( !isInvalidDatasourceName(name) && currentDatasource && currentDatasource.name !== name ) { - dispatch(saveDatasourceName({ id: currentDatasource?.id ?? "", name })); + // if the currentDatasource id equals the temp datasource id, + // it means that you are about to create a new datasource hence + // saveDatasourceName would be dispatch + if (currentDatasource.id === TEMP_DATASOURCE_ID) { + dispatch( + saveDatasourceName({ + id: currentDatasource?.id ?? "", + name: datsourceName, + }), + ); + } else { + dispatch( + updateDatasourceName({ + id: currentDatasource?.id ?? "", + name: datsourceName, + }), + ); + } } }, [dispatch, isInvalidDatasourceName, currentDatasource], @@ -101,6 +140,7 @@ function FormTitle(props: FormTitleProps) { value); - - if (!isNotBlank) { - _.set(errors, fieldConfigProperty, "This field is required"); - } - } } else { const value = _.get(values, fieldConfigProperty); @@ -183,27 +172,6 @@ export class JSONtoForm< } else { formData = _.set(formData, properties[0], []); } - } else if (controlType === "KEY_VAL_INPUT") { - if (checked[configProperty]) continue; - - const values = _.get(formData, configProperty); - const newValues: ({ [s: string]: unknown } | ArrayLike)[] = []; - - values.forEach( - (object: { [s: string]: unknown } | ArrayLike) => { - const isEmpty = Object.values(object).every((x) => x === ""); - - if (!isEmpty) { - newValues.push(object); - } - }, - ); - - if (newValues.length) { - formData = _.set(formData, configProperty, newValues); - } else { - formData = _.set(formData, configProperty, []); - } } } @@ -221,7 +189,7 @@ export class JSONtoForm< if (isArrayorObject(valueType)) { this.getTrimmedData(formData[key]); } else if (valueType === Types.STRING) { - formData[key] = formData[key].trim(); + _.set(formData, key, formData[key].trim()); } }); } @@ -245,7 +213,7 @@ export class JSONtoForm< return ( {this.renderEachConfig(section)} @@ -284,13 +252,6 @@ export class JSONtoForm< } }; - isKVArray = (children: Array) => { - if (!Array.isArray(children) || children.length < 2) return false; - return ( - children[0].controlType && children[0].controlType === "KEYVALUE_ARRAY" - ); - }; - renderKVArray = (children: Array) => { try { // setup config for each child @@ -310,7 +271,7 @@ export class JSONtoForm< if (isHidden(this.props.formData, section.hidden)) return null; if ("children" in propertyControlOrSection) { const { children } = propertyControlOrSection as any; - if (this.isKVArray(children)) { + if (isKVArray(children)) { return this.renderKVArray(children); } return this.renderEachConfig(propertyControlOrSection); diff --git a/app/client/src/pages/Editor/DataSourceEditor/NewActionButton.tsx b/app/client/src/pages/Editor/DataSourceEditor/NewActionButton.tsx index d8d023596aa4..af6b46c69d12 100644 --- a/app/client/src/pages/Editor/DataSourceEditor/NewActionButton.tsx +++ b/app/client/src/pages/Editor/DataSourceEditor/NewActionButton.tsx @@ -38,13 +38,14 @@ const ActionButton = styled(Button)` type NewActionButtonProps = { datasource?: Datasource; + disabled?: boolean; packageName?: string; isLoading?: boolean; eventFrom?: string; // this is to track from where the new action is being generated plugin?: Plugin; }; function NewActionButton(props: NewActionButtonProps) { - const { datasource, plugin } = props; + const { datasource, disabled, plugin } = props; const pluginType = plugin?.type; const [isSelected, setIsSelected] = useState(false); @@ -87,10 +88,12 @@ function NewActionButton(props: NewActionButtonProps) { return ( ); diff --git a/app/client/src/pages/Editor/DataSourceEditor/RestAPIDatasourceForm.tsx b/app/client/src/pages/Editor/DataSourceEditor/RestAPIDatasourceForm.tsx index be202fa0193d..a180ddd1a82d 100644 --- a/app/client/src/pages/Editor/DataSourceEditor/RestAPIDatasourceForm.tsx +++ b/app/client/src/pages/Editor/DataSourceEditor/RestAPIDatasourceForm.tsx @@ -3,7 +3,6 @@ import styled from "styled-components"; import { createNewApiName } from "utils/AppsmithUtils"; import { DATASOURCE_REST_API_FORM } from "@appsmith/constants/forms"; import FormTitle from "./FormTitle"; -import Button from "components/editorComponents/Button"; import { Datasource } from "entities/Datasource"; import { getFormMeta, @@ -19,12 +18,14 @@ import { connect } from "react-redux"; import { AppState } from "@appsmith/reducers"; import { ApiActionConfig, PluginType } from "entities/Action"; import { ActionDataState } from "reducers/entityReducers/actionsReducer"; -import { Toaster, Variant } from "design-system"; +import { Button, Category, Toaster, Variant } from "design-system"; import { DEFAULT_API_ACTION_CONFIG } from "constants/ApiEditorConstants/ApiEditorConstants"; import { createActionRequest } from "actions/pluginActionActions"; import { + createDatasourceFromForm, deleteDatasource, redirectAuthorizationCode, + toggleSaveActionFlag, updateDatasource, } from "actions/datasourceActions"; import { ReduxAction } from "@appsmith/constants/ReduxActionConstants"; @@ -49,12 +50,15 @@ import Collapsible from "./Collapsible"; import _ from "lodash"; import FormLabel from "components/editorComponents/FormLabel"; import CopyToClipBoard from "components/designSystems/appsmith/CopyToClipBoard"; -import { BaseButton } from "components/designSystems/appsmith/BaseButton"; import { Callout } from "design-system"; import CloseEditor from "components/editorComponents/CloseEditor"; -import { ButtonVariantTypes } from "components/constants"; import { updateReplayEntity } from "actions/pageActions"; import { ENTITY_TYPE } from "entities/AppsmithConsole"; +import { TEMP_DATASOURCE_ID } from "constants/Datasource"; +import { + hasDeleteDatasourcePermission, + hasManageDatasourcePermission, +} from "@appsmith/utils/permissionHelpers"; interface DatasourceRestApiEditorProps { initializeReplayEntity: (id: string, data: any) => void; @@ -81,6 +85,15 @@ interface DatasourceRestApiEditorProps { hiddenHeader?: boolean; responseStatus?: string; responseMessage?: string; + datasourceName: string; + createDatasource: ( + data: Datasource, + onSuccess?: ReduxAction, + ) => void; + toggleSaveActionFlag: (flag: boolean) => void; + triggerSave?: boolean; + isFormDirty: boolean; + datasourceDeleteTrigger: () => void; } type Props = DatasourceRestApiEditorProps & @@ -135,7 +148,7 @@ const SaveButtonContainer = styled.div` justify-content: flex-end; `; -const ActionButton = styled(BaseButton)` +const ActionButton = styled(Button)` &&& { width: auto; min-width: 74px; @@ -177,7 +190,7 @@ class DatasourceRestAPIEditor extends React.Component< ); } - componentDidUpdate() { + componentDidUpdate(prevProps: Props) { if (!this.props.formData) return; if (this.state.confirmDelete) { @@ -195,6 +208,14 @@ class DatasourceRestAPIEditor extends React.Component< } else if (authType === AuthType.apiKey) { this.ensureAPIKeyDefaultsAreCorrect(); } + + // if trigger save changed, save datasource + if ( + prevProps.triggerSave !== this.props.triggerSave && + this.props.triggerSave + ) { + this.save(); + } } isDirty(prop: any) { @@ -269,12 +290,21 @@ class DatasourceRestAPIEditor extends React.Component< }; disableSave = (): boolean => { - const { formData } = this.props; + const { datasource, datasourceId, formData } = this.props; + const createMode = datasourceId === TEMP_DATASOURCE_ID; + const canManageDatasource = hasManageDatasourcePermission( + datasource?.userPermissions || [], + ); if (!formData) return true; - return !formData.url; + return ( + !formData.url || + !this.props.isFormDirty || + (!createMode && !canManageDatasource) + ); }; save = (onSuccess?: ReduxAction) => { + this.props.toggleSaveActionFlag(true); const normalizedValues = formValuesToDatasource( this.props.datasource, this.props.formData, @@ -284,7 +314,17 @@ class DatasourceRestAPIEditor extends React.Component< appId: this.props.applicationId, }); - this.props.updateDatasource(normalizedValues, onSuccess); + if (this.props.datasource.id !== TEMP_DATASOURCE_ID) { + return this.props.updateDatasource(normalizedValues, onSuccess); + } + + this.props.createDatasource( + { + ...normalizedValues, + name: this.props.datasourceName, + }, + onSuccess, + ); }; createApiAction = () => { @@ -347,6 +387,11 @@ class DatasourceRestAPIEditor extends React.Component< return { isValid: true, message: "" }; }; + handleDeleteDatasource = (datasourceId: string) => { + this.props.deleteDatasource(datasourceId); + this.props.datasourceDeleteTrigger(); + }; + render = () => { return ( <> @@ -368,56 +413,70 @@ class DatasourceRestAPIEditor extends React.Component< }; renderHeader = () => { - const { hiddenHeader, isNewDatasource, pluginImage } = this.props; + const { + datasource, + datasourceId, + hiddenHeader, + isNewDatasource, + pluginImage, + } = this.props; + const createMode = datasourceId === TEMP_DATASOURCE_ID; + const canManageDatasource = hasManageDatasourcePermission( + datasource?.userPermissions || [], + ); return !hiddenHeader ? (
- +
) : null; }; renderSave = () => { - const { - datasourceId, - deleteDatasource, - hiddenHeader, - isDeleting, - isSaving, - } = this.props; + const { datasourceId, hiddenHeader, isDeleting, isSaving } = this.props; + const createMode = datasourceId === TEMP_DATASOURCE_ID; + const canDeleteDatasource = hasDeleteDatasourcePermission( + this.props.datasource?.userPermissions || [], + ); + return ( {!hiddenHeader && ( { this.state.confirmDelete - ? deleteDatasource(datasourceId) + ? this.handleDeleteDatasource(datasourceId) : this.setState({ confirmDelete: true }); }} + size="medium" + tag="button" text={ this.state.confirmDelete ? createMessage(CONFIRM_CONTEXT_DELETE) : createMessage(CONTEXT_DELETE) } + variant={Variant.danger} /> )} - this.save()} - size="small" + size="medium" + tag="button" text="Save" + variant={Variant.success} /> ); @@ -458,7 +517,10 @@ class DatasourceRestAPIEditor extends React.Component< this.urlValidator, )} - + {this.renderKeyValueControlViaFormControl( "headers", "Headers", @@ -547,10 +609,10 @@ class DatasourceRestAPIEditor extends React.Component< GrantType.AuthorizationCode && ( this.save( redirectAuthorizationCode( @@ -560,10 +622,11 @@ class DatasourceRestAPIEditor extends React.Component< ), ) } - size="small" + tag="button" text={ isAuthorized ? "Save and Re-Authorize" : "Save and Authorize" } + variant={Variant.success} /> )} @@ -1201,6 +1264,7 @@ const mapStateToProps = (state: AppState, props: any) => { ) as ApiDatasourceForm, formMeta: getFormMeta(DATASOURCE_REST_API_FORM)(state), messages: hintMessages, + datasourceName: datasource?.name ?? "", }; }; @@ -1213,6 +1277,10 @@ const mapDispatchToProps = (dispatch: any) => { deleteDatasource: (id: string) => { dispatch(deleteDatasource({ id })); }, + createDatasource: (formData: any, onSuccess?: ReduxAction) => + dispatch(createDatasourceFromForm(formData, onSuccess)), + toggleSaveActionFlag: (flag: boolean) => + dispatch(toggleSaveActionFlag(flag)), }; }; diff --git a/app/client/src/pages/Editor/DataSourceEditor/SaveOrDiscardDatasourceModal.tsx b/app/client/src/pages/Editor/DataSourceEditor/SaveOrDiscardDatasourceModal.tsx new file mode 100644 index 000000000000..c6411ed26264 --- /dev/null +++ b/app/client/src/pages/Editor/DataSourceEditor/SaveOrDiscardDatasourceModal.tsx @@ -0,0 +1,76 @@ +import React from "react"; +import { + createMessage, + DELETE_CONFIRMATION_MODAL_TITLE, + SAVE_OR_DISCARD_DATASOURCE_WARNING, +} from "@appsmith/constants/messages"; +import { + Button, + Category, + DialogComponent as Dialog, + Size, +} from "design-system"; +import { TEMP_DATASOURCE_ID } from "constants/Datasource"; +import { hasManageDatasourcePermission } from "@appsmith/utils/permissionHelpers"; + +interface SaveOrDiscardModalProps { + isOpen: boolean; + onDiscard(): void; + onSave?(): void; + onClose(): void; + datasourceId: string; + datasourcePermissions: string[]; +} + +function SaveOrDiscardDatasourceModal(props: SaveOrDiscardModalProps) { + const { + datasourceId, + datasourcePermissions, + isOpen, + onClose, + onDiscard, + onSave, + } = props; + + const createMode = datasourceId === TEMP_DATASOURCE_ID; + const canManageDatasources = hasManageDatasourcePermission( + datasourcePermissions, + ); + const disableSaveButton = !createMode && !canManageDatasources; + + return ( + +
+

{createMessage(SAVE_OR_DISCARD_DATASOURCE_WARNING)}

+
+ +
+
+
+
+
+ ); +} + +export default SaveOrDiscardDatasourceModal; diff --git a/app/client/src/pages/Editor/DataSourceEditor/index.tsx b/app/client/src/pages/Editor/DataSourceEditor/index.tsx index f190399693ee..064ffb959377 100644 --- a/app/client/src/pages/Editor/DataSourceEditor/index.tsx +++ b/app/client/src/pages/Editor/DataSourceEditor/index.tsx @@ -1,6 +1,6 @@ import React from "react"; import { connect } from "react-redux"; -import { getFormValues } from "redux-form"; +import { getFormValues, isDirty } from "redux-form"; import { AppState } from "@appsmith/reducers"; import _ from "lodash"; import { @@ -10,9 +10,17 @@ import { } from "selectors/entitiesSelector"; import { switchDatasource, - setDatsourceEditorMode, + setDatasourceViewMode, + removeTempDatasource, + deleteTempDSFromDraft, + toggleSaveActionFlag, + toggleSaveActionFromPopupFlag, + createTempDatasourceFromForm, } from "actions/datasourceActions"; -import { DATASOURCE_DB_FORM } from "@appsmith/constants/forms"; +import { + DATASOURCE_DB_FORM, + DATASOURCE_REST_API_FORM, +} from "@appsmith/constants/forms"; import DataSourceEditorForm from "./DBForm"; import RestAPIDatasourceForm from "./RestAPIDatasourceForm"; import { Datasource } from "entities/Datasource"; @@ -35,6 +43,10 @@ import { REST_API_AUTHORIZATION_SUCCESSFUL, } from "@appsmith/constants/messages"; import { Toaster, Variant } from "design-system"; +import { isDatasourceInViewMode } from "selectors/ui"; +import { getQueryParams } from "utils/URLUtils"; +import { TEMP_DATASOURCE_ID } from "constants/Datasource"; +import SaveOrDiscardDatasourceModal from "./SaveOrDiscardDatasourceModal"; interface ReduxStateProps { datasourceId: string; @@ -55,15 +67,40 @@ interface ReduxStateProps { applicationSlug: string; pageSlug: string; fromImporting?: boolean; + isDatasourceBeingSaved: boolean; + triggerSave: boolean; + isFormDirty: boolean; + datasource: Datasource | undefined; +} + +interface DatasourcEditorProps { + datasourceDeleteTrigger: () => void; } type Props = ReduxStateProps & DatasourcePaneFunctions & + DatasourcEditorProps & RouteComponentProps<{ datasourceId: string; pageId: string; }>; +/* + **** State Variables Description **** + showDialog: flag used to show/hide the datasource discard popup + routesBlocked: flag used to identity if routes are blocked or not + unblock: on blocking routes using history.block, it returns a function which can be used to unblock the routes + navigation: function that navigates to path that we want to transition to, after discard action on datasource discard dialog popup +*/ +type State = { + showDialog: boolean; + routesBlocked: boolean; + readUrlParams: boolean; + + unblock(): void; + navigation(): void; +}; + class DataSourceEditor extends React.Component { componentDidUpdate(prevProps: Props) { //Fix to prevent restapi datasource from being set in DatasourceDBForm in view mode @@ -76,6 +113,7 @@ class DataSourceEditor extends React.Component { this.props.switchDatasource(this.props.datasourceId); } } + componentDidMount() { //Fix to prevent restapi datasource from being set in DatasourceDBForm in datasource view mode //TODO: Needs cleanup @@ -113,11 +151,13 @@ class DataSourceEditor extends React.Component { render() { const { + datasourceDeleteTrigger, datasourceId, formConfig, formData, fromImporting, isDeleting, + isFormDirty, isNewDatasource, isSaving, isTesting, @@ -126,19 +166,21 @@ class DataSourceEditor extends React.Component { pluginId, pluginImages, pluginType, - setDatasourceEditorMode, + setDatasourceViewMode, viewMode, } = this.props; return ( { pageId={pageId} pluginImage={pluginImages[pluginId]} pluginType={pluginType} - setDatasourceEditorMode={setDatasourceEditorMode} + setDatasourceViewMode={setDatasourceViewMode} viewMode={viewMode && !fromImporting} /> ); @@ -157,12 +199,29 @@ const mapStateToProps = (state: AppState, props: any): ReduxStateProps => { const datasourceId = props.datasourceId ?? props.match?.params?.datasourceId; const { datasourcePane } = state.ui; const { datasources, plugins } = state.entities; + const viewMode = isDatasourceInViewMode(state); const datasource = getDatasource(state, datasourceId); const { formConfigs } = plugins; const formData = getFormValues(DATASOURCE_DB_FORM)(state) as Datasource; const pluginId = _.get(datasource, "pluginId", ""); const plugin = getPlugin(state, pluginId); const { applicationSlug, pageSlug } = selectURLSlugs(state); + const formName = + plugin?.type === "API" ? DATASOURCE_REST_API_FORM : DATASOURCE_DB_FORM; + // for plugins, where 1 default endpoint is initialized, + // added this check so that form isnt considered dirty with default endpoint + const defaultEndpoints: Array<{ + host: string; + port: string; + }> = (formData?.datasourceConfiguration as any)?.endpoints || []; + const isDefaultEndpoint = + defaultEndpoints.length === 1 && + defaultEndpoints[0].host === "" && + defaultEndpoints[0].port === ""; + const isFormDirty = + datasourceId === TEMP_DATASOURCE_ID + ? true + : isDirty(formName)(state) && !isDefaultEndpoint; return { datasourceId, @@ -174,10 +233,9 @@ const mapStateToProps = (state: AppState, props: any): ReduxStateProps => { isDeleting: !!datasource?.isDeleting, isTesting: datasources.isTesting, formConfig: formConfigs[pluginId] || [], - isNewDatasource: datasourcePane.newDatasource === datasourceId, + isNewDatasource: datasourcePane.newDatasource === TEMP_DATASOURCE_ID, pageId: props.pageId ?? props.match?.params?.pageId, - viewMode: - datasourcePane.viewMode[datasource?.id ?? ""] ?? !props.fromImporting, + viewMode: viewMode ?? !props.fromImporting, pluginType: plugin?.type ?? "", pluginDatasourceForm: plugin?.datasourceComponent ?? DatasourceComponentTypes.AutoForm, @@ -185,6 +243,10 @@ const mapStateToProps = (state: AppState, props: any): ReduxStateProps => { applicationId: props.applicationId ?? getCurrentApplicationId(state), applicationSlug, pageSlug, + isDatasourceBeingSaved: datasources.isDatasourceBeingSaved, + triggerSave: datasources.isDatasourceBeingSavedFromPopup, + isFormDirty, + datasource, }; }; @@ -196,27 +258,198 @@ const mapDispatchToProps = ( // on reconnect data modal, it shouldn't be redirected to datasource edit page dispatch(switchDatasource(id, ownProps.fromImporting)); }, - setDatasourceEditorMode: (id: string, viewMode: boolean) => - dispatch(setDatsourceEditorMode({ id, viewMode })), + setDatasourceViewMode: (viewMode: boolean) => + dispatch(setDatasourceViewMode(viewMode)), openOmnibarReadMore: (text: string) => { dispatch(setGlobalSearchQuery(text)); dispatch(toggleShowGlobalSearchModal()); }, + discardTempDatasource: () => dispatch(removeTempDatasource()), + deleteTempDSFromDraft: () => dispatch(deleteTempDSFromDraft()), + toggleSaveActionFlag: (flag) => dispatch(toggleSaveActionFlag(flag)), + toggleSaveActionFromPopupFlag: (flag) => + dispatch(toggleSaveActionFromPopupFlag(flag)), + createTempDatasource: (data: any) => + dispatch(createTempDatasourceFromForm(data)), }); export interface DatasourcePaneFunctions { switchDatasource: (id: string) => void; - setDatasourceEditorMode: (id: string, viewMode: boolean) => void; + setDatasourceViewMode: (viewMode: boolean) => void; openOmnibarReadMore: (text: string) => void; + discardTempDatasource: () => void; + deleteTempDSFromDraft: () => void; + toggleSaveActionFlag: (flag: boolean) => void; + toggleSaveActionFromPopupFlag: (flag: boolean) => void; + createTempDatasource: (data: any) => void; } -class DatasourceEditorRouter extends React.Component { +class DatasourceEditorRouter extends React.Component { + constructor(props: Props) { + super(props); + this.state = { + showDialog: false, + routesBlocked: false, + readUrlParams: false, + unblock: () => { + return undefined; + }, + navigation: () => { + return undefined; + }, + }; + this.closeDialog = this.closeDialog.bind(this); + this.onSave = this.onSave.bind(this); + this.onDiscard = this.onDiscard.bind(this); + this.datasourceDeleteTrigger = this.datasourceDeleteTrigger.bind(this); + } + + componentDidUpdate(prevProps: Props) { + // update block state when form becomes dirty/view mode is switched on + if (prevProps.viewMode !== this.props.viewMode && !this.props.viewMode) { + this.blockRoutes(); + } + + // When save button is clicked in DS form, routes should be unblocked + if (this.props.isDatasourceBeingSaved) { + this.closeDialogAndUnblockRoutes(); + } + this.setViewModeFromQueryParams(); + } + + componentDidMount() { + // Create Temp Datasource on component mount, + // if user hasnt saved datasource for the first time and refreshed the page + if ( + !this.props.datasource && + this.props.match.params.datasourceId === TEMP_DATASOURCE_ID + ) { + const urlObject = new URL(window.location.href); + const pluginId = urlObject?.searchParams.get("pluginId"); + this.props.createTempDatasource({ + pluginId, + }); + } + if (!this.props.viewMode) { + this.blockRoutes(); + } + + if ( + this.props.pluginDatasourceForm === + DatasourceComponentTypes.RestAPIDatasourceForm + ) { + this.setViewModeFromQueryParams(); + } + } + + // To move to edit state for new datasources and when we want to move to edit state + // from outside the datasource route + setViewModeFromQueryParams() { + const params = getQueryParams(); + if (this.props.viewMode) { + if ( + (params.viewMode === "false" && !this.state.readUrlParams) || + this.props.isNewDatasource + ) { + // We just want to read the query params once. Cannot remove query params + // here as this triggers history.block + this.setState( + { + readUrlParams: true, + }, + () => { + this.props.setDatasourceViewMode(false); + }, + ); + } + } + } + + componentWillUnmount() { + this.props.discardTempDatasource(); + this.props.deleteTempDSFromDraft(); + !!this.state.unblock && this.state.unblock(); + } + + routesBlockFormChangeCallback() { + if (this.props.isFormDirty) { + if (!this.state.routesBlocked) { + this.blockRoutes(); + } + } else { + if (this.state.routesBlocked) { + this.closeDialogAndUnblockRoutes(true); + } + } + } + + blockRoutes() { + this.setState({ + unblock: this.props?.history?.block((tx: any) => { + this.setState( + { + // need to pass in query params as well as state, when user navigates away from ds form page + navigation: () => + this.props.history.push(tx.pathname + tx.search, tx.state), + showDialog: true, + routesBlocked: true, + }, + this.routesBlockFormChangeCallback.bind(this), + ); + return false; + }), + }); + } + + closeDialog() { + this.setState({ showDialog: false }); + } + + onSave() { + this.props.toggleSaveActionFromPopupFlag(true); + } + + onDiscard() { + this.closeDialogAndUnblockRoutes(); + this.props.discardTempDatasource(); + this.props.deleteTempDSFromDraft(); + this.state.navigation(); + } + + closeDialogAndUnblockRoutes(isNavigateBack?: boolean) { + this.closeDialog(); + !!this.state.unblock && this.state.unblock(); + this.props.toggleSaveActionFlag(false); + this.props.toggleSaveActionFromPopupFlag(false); + this.setState({ routesBlocked: false }); + if (isNavigateBack) { + this.state.navigation(); + } + } + + datasourceDeleteTrigger() { + !!this.state.unblock && this.state.unblock(); + } + + renderSaveDisacardModal() { + return ( + + ); + } render() { const { datasourceId, fromImporting, history, isDeleting, + isFormDirty, isNewDatasource, isSaving, location, @@ -237,17 +470,23 @@ class DatasourceEditorRouter extends React.Component { // Check for specific form types first if (pluginDatasourceForm === "RestAPIDatasourceForm" && !shouldViewMode) { return ( - + <> + + {this.renderSaveDisacardModal()} + ); } // for saas form @@ -276,11 +515,15 @@ class DatasourceEditorRouter extends React.Component { // Default to old flow // Todo: later refactor to make this "AutoForm" return ( - + <> + + {this.renderSaveDisacardModal()} + ); } } diff --git a/app/client/src/pages/Editor/EditorAppName/NavigationMenuData.ts b/app/client/src/pages/Editor/EditorAppName/NavigationMenuData.ts index ccdc40a49cb0..4e02a8bbbe1b 100644 --- a/app/client/src/pages/Editor/EditorAppName/NavigationMenuData.ts +++ b/app/client/src/pages/Editor/EditorAppName/NavigationMenuData.ts @@ -1,5 +1,5 @@ import { useDispatch, useSelector } from "react-redux"; -import { useHistory, useParams } from "react-router-dom"; +import { useHistory } from "react-router-dom"; import { noop } from "lodash"; import { Toaster, Variant } from "design-system"; @@ -8,60 +8,30 @@ import { APPLICATIONS_URL } from "constants/routes"; import { MenuItemData, MenuTypes } from "./NavigationMenuItem"; import { useCallback } from "react"; -import { ExplorerURLParams } from "../Explorer/helpers"; import { getExportAppAPIRoute } from "@appsmith/constants/ApiConstants"; import { + hasDeleteApplicationPermission, isPermitted, PERMISSION_TYPE, } from "@appsmith/utils/permissionHelpers"; import { getCurrentApplication } from "selectors/applicationSelectors"; import { Colors } from "constants/Colors"; -import { setIsGitSyncModalOpen } from "actions/gitSyncActions"; -import { GitSyncModalTab } from "entities/GitSync"; -import { getIsGitConnected } from "selectors/gitSyncSelectors"; -import { - createMessage, - DEPLOY_MENU_OPTION, - CONNECT_TO_GIT_OPTION, - CURRENT_DEPLOY_PREVIEW_OPTION, -} from "@appsmith/constants/messages"; import { getCurrentApplicationId } from "selectors/editorSelectors"; import { redoAction, undoAction } from "actions/pageActions"; import { redoShortCut, undoShortCut } from "utils/helpers"; -import { pageListEditorURL } from "RouteBuilder"; -import AnalyticsUtil from "utils/AnalyticsUtil"; +import { openAppSettingsPaneAction } from "actions/appSettingsPaneActions"; import { ThemeProp } from "widgets/constants"; type NavigationMenuDataProps = ThemeProp & { editMode: typeof noop; - deploy: typeof noop; - currentDeployLink: string; }; export const GetNavigationMenuData = ({ - currentDeployLink, - deploy, editMode, }: NavigationMenuDataProps): MenuItemData[] => { const dispatch = useDispatch(); const history = useHistory(); - const params = useParams(); - - const isGitConnected = useSelector(getIsGitConnected); - - const openGitConnectionPopup = () => { - AnalyticsUtil.logEvent("GS_CONNECT_GIT_CLICK", { - source: "Application name menu (top left)", - }); - - dispatch( - setIsGitSyncModalOpen({ - isOpen: true, - tab: GitSyncModalTab.GIT_CONNECTION, - }), - ); - }; const applicationId = useSelector(getCurrentApplicationId); @@ -78,6 +48,8 @@ export const GetNavigationMenuData = ({ } }, []); + const openAppSettingsPane = () => dispatch(openAppSettingsPaneAction()); + const deleteApplication = () => { if (applicationId && applicationId.length > 0) { dispatch({ @@ -95,37 +67,18 @@ export const GetNavigationMenuData = ({ } }; - const deployOptions = [ + return [ { - text: createMessage(DEPLOY_MENU_OPTION), - onClick: deploy, + text: "Home", + onClick: () => history.replace(APPLICATIONS_URL), type: MenuTypes.MENU, isVisible: true, - isOpensNewWindow: true, - className: "t--app-name-menu-deploy", }, { - text: createMessage(CURRENT_DEPLOY_PREVIEW_OPTION), - onClick: () => openExternalLink(currentDeployLink), - type: MenuTypes.MENU, + text: "divider_1", + type: MenuTypes.MENU_DIVIDER, isVisible: true, - isOpensNewWindow: true, - className: "t--app-name-menu-deploy-current-version", }, - ]; - - if (!isGitConnected) { - deployOptions.push({ - text: createMessage(CONNECT_TO_GIT_OPTION), - onClick: () => openGitConnectionPopup(), - type: MenuTypes.MENU, - isVisible: true, - isOpensNewWindow: false, - className: "t--app-name-menu-deploy-connect-to-git", - }); - } - - return [ { text: "Edit Name", onClick: editMode, @@ -154,20 +107,11 @@ export const GetNavigationMenuData = ({ ], }, { - text: "Pages", - onClick: () => { - history.push(pageListEditorURL({ pageId: params.pageId })); - }, + text: "Settings", + onClick: openAppSettingsPane, type: MenuTypes.MENU, isVisible: true, }, - { - text: "Deploy", - type: MenuTypes.PARENT, - isVisible: true, - children: deployOptions, - className: "t--app-name-menu-deploy-parent", - }, { text: "Help", type: MenuTypes.PARENT, @@ -211,7 +155,7 @@ export const GetNavigationMenuData = ({ type: MenuTypes.MENU, isVisible: isApplicationIdPresent && hasExportPermission, }, - { + hasDeleteApplicationPermission(currentApplication?.userPermissions) && { text: "Delete Application", confirmText: "Are you sure?", onClick: deleteApplication, @@ -219,5 +163,5 @@ export const GetNavigationMenuData = ({ isVisible: isApplicationIdPresent, style: { color: Colors.ERROR_RED }, }, - ]; + ].filter(Boolean) as MenuItemData[]; }; diff --git a/app/client/src/pages/Editor/EditorAppName/NavigationMenuItem.tsx b/app/client/src/pages/Editor/EditorAppName/NavigationMenuItem.tsx index b23e9b9ead97..6632f4dec289 100644 --- a/app/client/src/pages/Editor/EditorAppName/NavigationMenuItem.tsx +++ b/app/client/src/pages/Editor/EditorAppName/NavigationMenuItem.tsx @@ -7,6 +7,7 @@ import _, { noop } from "lodash"; import { getTypographyByKey, CommonComponentProps } from "design-system"; import AnalyticsUtil from "utils/AnalyticsUtil"; import { HeaderIcons } from "icons/HeaderIcons"; +import { MenuDivider } from "design-system"; const ShareIcon = HeaderIcons.SHARE; @@ -14,6 +15,7 @@ export enum MenuTypes { MENU = "menu", PARENT = "parent", RECONFIRM = "re-confirm", + MENU_DIVIDER = "menu divider", } export interface MenuItemData { @@ -175,7 +177,9 @@ export function NavigationMenuItem({ text={confirm.text} /> ); + case MenuTypes.MENU_DIVIDER: + return ; + default: + return null; } - - return null; } diff --git a/app/client/src/pages/Editor/EditorAppName/index.tsx b/app/client/src/pages/Editor/EditorAppName/index.tsx index f108ec78edce..feb56c78df16 100644 --- a/app/client/src/pages/Editor/EditorAppName/index.tsx +++ b/app/client/src/pages/Editor/EditorAppName/index.tsx @@ -27,7 +27,6 @@ type EditorAppNameProps = CommonComponentProps & placeholder?: string; editInteractionKind: EditInteractionKind; defaultSavingState: SavingState; - deploy: typeof noop; onBlur?: (value: string) => void; isEditingDefault?: boolean; inputValidation?: (value: string) => string | boolean; @@ -35,7 +34,6 @@ type EditorAppNameProps = CommonComponentProps & fill?: boolean; isError?: boolean; isNewApp: boolean; - currentDeployLink: string; isPopoverOpen: boolean; setIsPopoverOpen: typeof noop; }; @@ -132,10 +130,8 @@ const StyledMenu = styled(Menu)` export function EditorAppName(props: EditorAppNameProps) { const { - currentDeployLink, defaultSavingState, defaultValue, - deploy, isNewApp, isPopoverOpen, setIsPopoverOpen, @@ -190,9 +186,7 @@ export function EditorAppName(props: EditorAppNameProps) { }, []); const NavigationMenuData = GetNavigationMenuData({ - currentDeployLink, editMode, - deploy, theme, }); diff --git a/app/client/src/pages/Editor/EditorHeader.tsx b/app/client/src/pages/Editor/EditorHeader.tsx index ced823f7b007..2d0b88438235 100644 --- a/app/client/src/pages/Editor/EditorHeader.tsx +++ b/app/client/src/pages/Editor/EditorHeader.tsx @@ -65,6 +65,7 @@ import { CLOSE_ENTITY_EXPLORER_MESSAGE, createMessage, DEPLOY_BUTTON_TOOLTIP, + DEPLOY_MENU_OPTION, INVITE_USERS_MESSAGE, INVITE_USERS_PLACEHOLDER, LOCK_ENTITY_EXPLORER_MESSAGE, @@ -187,9 +188,9 @@ const StyledDeployIcon = styled(Icon)` height: 20px; width: 20px; align-self: center; - background: ${(props) => props.theme.colors.header.shareBtnHighlight}; + background: var(--ads-color-brand); &:hover { - background: rgb(191, 65, 9); + background: var(--ads-color-brand-hover); } `; @@ -402,12 +403,10 @@ export function EditorHeader(props: EditorHeaderProps) { handleClickDeploy(false)} editInteractionKind={EditInteractionKind.SINGLE} fill isError={isErroredSavingName} @@ -491,7 +490,7 @@ export function EditorHeader(props: EditorHeaderProps) { isLoading={isPublishing} onClick={() => handleClickDeploy(true)} size={Size.small} - text={"Deploy"} + text={DEPLOY_MENU_OPTION()} /> @@ -499,7 +498,7 @@ export function EditorHeader(props: EditorHeaderProps) { link={deployLink} trigger={ diff --git a/app/client/src/pages/Editor/Explorer/Actions/ActionEntity.tsx b/app/client/src/pages/Editor/Explorer/Actions/ActionEntity.tsx index 05d550eb714a..9ddcfe40fb20 100644 --- a/app/client/src/pages/Editor/Explorer/Actions/ActionEntity.tsx +++ b/app/client/src/pages/Editor/Explorer/Actions/ActionEntity.tsx @@ -1,12 +1,12 @@ -import React, { useCallback, memo, useMemo } from "react"; +import React, { memo, useCallback, useMemo } from "react"; +import { useSelector } from "react-redux"; import Entity, { EntityClassNames } from "../Entity"; import ActionEntityContextMenu from "./ActionEntityContextMenu"; -import history from "utils/history"; +import history, { NavigationMethod } from "utils/history"; import { saveActionName } from "actions/pluginActionActions"; import PerformanceTracker, { PerformanceTransactionName, } from "utils/PerformanceTracker"; -import { useSelector } from "store"; import { getCurrentPageId } from "selectors/editorSelectors"; import { getAction, getPlugins } from "selectors/entitiesSelector"; import { Action, PluginType } from "entities/Action"; @@ -14,6 +14,10 @@ import { keyBy } from "lodash"; import { getActionConfig } from "./helpers"; import AnalyticsUtil from "utils/AnalyticsUtil"; import { useLocation } from "react-router"; +import { + hasDeleteActionPermission, + hasManageActionPermission, +} from "@appsmith/utils/permissionHelpers"; const getUpdateActionNameReduxAction = (id: string, name: string) => { return saveActionName({ id, name }); @@ -47,7 +51,7 @@ export const ExplorerActionEntity = memo((props: ExplorerActionEntityProps) => { PerformanceTracker.startTracking(PerformanceTransactionName.OPEN_ACTION, { url, }); - url && history.push(url); + url && history.push(url, { invokedBy: NavigationMethod.EntityExplorer }); AnalyticsUtil.logEvent("ENTITY_EXPLORER_CLICK", { type: "QUERIES/APIs", fromUrl: location.pathname, @@ -56,8 +60,16 @@ export const ExplorerActionEntity = memo((props: ExplorerActionEntityProps) => { }); }, [url, location.pathname, action.name]); + const actionPermissions = action.userPermissions || []; + + const canDeleteAction = hasDeleteActionPermission(actionPermissions); + + const canManageAction = hasManageActionPermission(actionPermissions); + const contextMenu = ( { showBinding(props.id, props.name), + label: createMessage(CONTEXT_SHOW_BINDING), + }, + canManageAction && { + value: "copy", + onSelect: noop, + label: createMessage(CONTEXT_COPY), + children: menuPages.map((page) => { + return { + ...page, + onSelect: () => copyActionToPage(props.id, props.name, page.id), + }; + }), + }, + canManageAction && { + value: "move", + onSelect: noop, + label: createMessage(CONTEXT_MOVE), + children: + menuPages.length > 1 + ? menuPages + .filter((page) => page.id !== props.pageId) // Remove current page from the list + .map((page) => { + return { + ...page, + onSelect: () => + moveActionToPage(props.id, props.name, page.id), + }; + }) + : [ + { + value: "No Pages", + onSelect: noop, + label: createMessage(CONTEXT_NO_PAGE), + }, + ], + }, + canDeleteAction && { + confirmDelete: confirmDelete, + className: "t--apiFormDeleteBtn single-select", + value: "delete", + label: confirmDelete + ? createMessage(CONFIRM_CONTEXT_DELETE) + : createMessage(CONTEXT_DELETE), + intent: "danger", + onSelect: () => { + confirmDelete + ? deleteActionFromPage(props.id, props.name, () => { + history.push(builderURL({ pageId })); + setConfirmDelete(false); + }) + : setConfirmDelete(true); + }, + }, + ].filter(Boolean); + + return optionsTree.length > 0 ? ( showBinding(props.id, props.name), - label: createMessage(CONTEXT_SHOW_BINDING), - }, - { - value: "copy", - onSelect: noop, - label: createMessage(CONTEXT_COPY), - children: menuPages.map((page) => { - return { - ...page, - onSelect: () => copyActionToPage(props.id, props.name, page.id), - }; - }), - }, - { - value: "move", - onSelect: noop, - label: createMessage(CONTEXT_MOVE), - children: - menuPages.length > 1 - ? menuPages - .filter((page) => page.id !== props.pageId) // Remove current page from the list - .map((page) => { - return { - ...page, - onSelect: () => - moveActionToPage(props.id, props.name, page.id), - }; - }) - : [ - { - value: "No Pages", - onSelect: noop, - label: createMessage(CONTEXT_NO_PAGE), - }, - ], - }, - { - confirmDelete: confirmDelete, - className: "t--apiFormDeleteBtn single-select", - value: "delete", - label: confirmDelete - ? createMessage(CONFIRM_CONTEXT_DELETE) - : createMessage(CONTEXT_DELETE), - intent: "danger", - onSelect: () => { - confirmDelete - ? deleteActionFromPage(props.id, props.name, () => { - history.push(builderURL({ pageId })); - setConfirmDelete(false); - }) - : setConfirmDelete(true); - }, - }, - ]} + optionTree={optionsTree as TreeDropdownOption[]} selectedValue="" setConfirmDelete={setConfirmDelete} toggle={} /> - ); + ) : null; } export default ActionEntityContextMenu; diff --git a/app/client/src/pages/Editor/Explorer/Actions/MoreActionsMenu.tsx b/app/client/src/pages/Editor/Explorer/Actions/MoreActionsMenu.tsx index bbe0375f45d0..4fce6896918d 100644 --- a/app/client/src/pages/Editor/Explorer/Actions/MoreActionsMenu.tsx +++ b/app/client/src/pages/Editor/Explorer/Actions/MoreActionsMenu.tsx @@ -15,7 +15,7 @@ import TreeDropdown from "pages/Editor/Explorer/TreeDropdown"; import { useNewActionName } from "./helpers"; import styled from "styled-components"; import { Classes, Icon, IconSize } from "design-system"; -import { Position } from "@blueprintjs/core"; +import { Intent, Position } from "@blueprintjs/core"; import { inGuidedTour } from "selectors/onboardingSelectors"; import { toggleShowDeviationDialog } from "actions/onboardingActions"; import { @@ -25,12 +25,15 @@ import { CONTEXT_MOVE, createMessage, } from "@appsmith/constants/messages"; +import { IconName } from "@blueprintjs/icons"; type EntityContextMenuProps = { id: string; name: string; className?: string; pageId: string; + isChangePermitted?: boolean; + isDeletePermitted?: boolean; }; export const MoreActionablesContainer = styled.div<{ isOpen?: boolean }>` @@ -75,6 +78,7 @@ export function MoreActionsMenu(props: EntityContextMenuProps) { const nextEntityName = useNewActionName(); const guidedTourEnabled = useSelector(inGuidedTour); const [confirmDelete, setConfirmDelete] = useState(false); + const { isChangePermitted = false, isDeletePermitted = false } = props; const dispatch = useDispatch(); const copyActionToPage = useCallback( @@ -120,60 +124,80 @@ export function MoreActionsMenu(props: EntityContextMenuProps) { })); }); - return ( + const options = [ + ...(isChangePermitted + ? [ + { + icon: "duplicate" as IconName, + value: "copy", + onSelect: noop, + label: createMessage(CONTEXT_COPY), + children: menuPages.map((page) => { + return { + ...page, + onSelect: () => copyActionToPage(props.id, props.name, page.id), + }; + }), + }, + ] + : []), + ...(isChangePermitted + ? [ + { + icon: "swap-horizontal" as IconName, + value: "move", + onSelect: noop, + label: createMessage(CONTEXT_MOVE), + children: + menuPages.length > 1 + ? menuPages + .filter((page) => page.id !== props.pageId) // Remove current page from the list + .map((page) => { + return { + ...page, + onSelect: () => + moveActionToPage(props.id, props.name, page.id), + }; + }) + : [ + { + value: "No Pages", + onSelect: noop, + label: "No Pages", + }, + ], + }, + ] + : []), + ...(isDeletePermitted + ? [ + { + confirmDelete: confirmDelete, + icon: "trash" as IconName, + value: "delete", + onSelect: () => { + confirmDelete + ? deleteActionFromPage(props.id, props.name) + : setConfirmDelete(true); + }, + label: confirmDelete + ? createMessage(CONFIRM_CONTEXT_DELETE) + : createMessage(CONTEXT_DELETE), + intent: Intent.DANGER, + className: "t--apiFormDeleteBtn", + }, + ] + : []), + ]; + + return options.length > 0 ? ( setIsMenuOpen(isOpen)} onSelect={noop} - optionTree={[ - { - icon: "duplicate", - value: "copy", - onSelect: noop, - label: createMessage(CONTEXT_COPY), - children: menuPages.map((page) => { - return { - ...page, - onSelect: () => copyActionToPage(props.id, props.name, page.id), - }; - }), - }, - { - icon: "swap-horizontal", - value: "move", - onSelect: noop, - label: createMessage(CONTEXT_MOVE), - children: - menuPages.length > 1 - ? menuPages - .filter((page) => page.id !== props.pageId) // Remove current page from the list - .map((page) => { - return { - ...page, - onSelect: () => - moveActionToPage(props.id, props.name, page.id), - }; - }) - : [{ value: "No Pages", onSelect: noop, label: "No Pages" }], - }, - { - confirmDelete: confirmDelete, - icon: "trash", - value: "delete", - onSelect: () => { - confirmDelete - ? deleteActionFromPage(props.id, props.name) - : setConfirmDelete(true); - }, - label: confirmDelete - ? createMessage(CONFIRM_CONTEXT_DELETE) - : createMessage(CONTEXT_DELETE), - intent: "danger", - className: "t--apiFormDeleteBtn", - }, - ]} + optionTree={options} position={Position.LEFT_TOP} selectedValue="" setConfirmDelete={setConfirmDelete} @@ -186,7 +210,7 @@ export function MoreActionsMenu(props: EntityContextMenuProps) { } /> - ); + ) : null; } export default MoreActionsMenu; diff --git a/app/client/src/pages/Editor/Explorer/Datasources.tsx b/app/client/src/pages/Editor/Explorer/Datasources.tsx index 92c44bfdd59a..5544d19c48de 100644 --- a/app/client/src/pages/Editor/Explorer/Datasources.tsx +++ b/app/client/src/pages/Editor/Explorer/Datasources.tsx @@ -5,7 +5,7 @@ import { } from "./hooks"; import { Datasource } from "entities/Datasource"; import ExplorerDatasourceEntity from "./Datasources/DatasourceEntity"; -import { useSelector } from "store"; +import { useSelector } from "react-redux"; import { getCurrentApplicationId, getCurrentPageId, @@ -33,6 +33,13 @@ import { import { Icon } from "design-system"; import { AddEntity, EmptyComponent } from "./common"; import { integrationEditorURL } from "RouteBuilder"; +import { getCurrentAppWorkspace } from "@appsmith/selectors/workspaceSelectors"; + +import { AppState } from "@appsmith/reducers"; +import { + hasCreateDatasourcePermission, + hasManageDatasourcePermission, +} from "@appsmith/utils/permissionHelpers"; const ShowAll = styled.div` padding: 0.25rem 1.5rem; @@ -55,6 +62,15 @@ const Datasources = React.memo(() => { const applicationId = useSelector(getCurrentApplicationId); const isDatasourcesOpen = getExplorerStatus(applicationId, "datasource"); const pluginGroups = React.useMemo(() => keyBy(plugins, "id"), [plugins]); + + const userWorkspacePermissions = useSelector( + (state: AppState) => getCurrentAppWorkspace(state).userPermissions ?? [], + ); + + const canCreateDatasource = hasCreateDatasourcePermission( + userWorkspacePermissions, + ); + const addDatasource = useCallback(() => { history.push( integrationEditorURL({ @@ -78,8 +94,14 @@ const Datasources = React.memo(() => { const datasourceElements = React.useMemo( () => appWideDS.concat(datasourceSuggestions).map((datasource: Datasource) => { + const datasourcePermissions = datasource.userPermissions || []; + + const canManageDatasource = hasManageDatasourcePermission( + datasourcePermissions, + ); return ( { className={"datasources"} entityId="datasources_section" icon={null} - isDefaultExpanded={isDatasourcesOpen === null ? true : isDatasourcesOpen} + isDefaultExpanded={ + isDatasourcesOpen === null || isDatasourcesOpen === undefined + ? false + : isDatasourcesOpen + } isSticky name="Datasources" onCreate={addDatasource} onToggle={onDatasourcesToggle} searchKeyword={""} + showAddButton={canCreateDatasource} step={0} > {datasourceElements.length ? ( datasourceElements ) : ( )} - {datasourceElements.length > 0 && ( + {datasourceElements.length > 0 && canCreateDatasource && ( + getDatasource(state, props.datasourceId), + ); + + const datasourcePermissions = datasource?.userPermissions || []; + + const canDeleteDatasource = hasDeleteDatasourcePermission( + datasourcePermissions, + ); + + const canManageDatasource = hasManageDatasourcePermission( + datasourcePermissions, + ); + + const treeOptions = [ + canManageDatasource && { + value: "rename", + className: "single-select t--datasource-rename", + onSelect: editDatasourceName, + label: createMessage(CONTEXT_EDIT_NAME), + }, + { + value: "refresh", + className: "single-select t--datasource-refresh", + onSelect: dispatchRefresh, + label: createMessage(CONTEXT_REFRESH), + }, + canDeleteDatasource && { + confirmDelete: confirmDelete, + className: "t--apiFormDeleteBtn single-select t--datasource-delete", + value: "delete", + onSelect: () => { + confirmDelete ? dispatchDelete() : setConfirmDelete(true); + }, + label: confirmDelete + ? createMessage(CONFIRM_CONTEXT_DELETE) + : createMessage(CONTEXT_DELETE), + intent: "danger", + }, + ].filter(Boolean); + + return treeOptions.length > 0 ? ( { - confirmDelete ? dispatchDelete() : setConfirmDelete(true); - }, - label: confirmDelete - ? createMessage(CONFIRM_CONTEXT_DELETE) - : createMessage(CONTEXT_DELETE), - intent: "danger", - }, - ]} + optionTree={treeOptions && (treeOptions as TreeDropdownOption[])} selectedValue="" setConfirmDelete={setConfirmDelete} toggle={} /> - ); + ) : null; } export default DataSourceContextMenu; diff --git a/app/client/src/pages/Editor/Explorer/Datasources/DatasourceEntity.tsx b/app/client/src/pages/Editor/Explorer/Datasources/DatasourceEntity.tsx index 3d75b3d1a25e..b34e9d151115 100644 --- a/app/client/src/pages/Editor/Explorer/Datasources/DatasourceEntity.tsx +++ b/app/client/src/pages/Editor/Explorer/Datasources/DatasourceEntity.tsx @@ -5,18 +5,16 @@ import DataSourceContextMenu from "./DataSourceContextMenu"; import { getPluginIcon } from "../ExplorerIcons"; import { getQueryIdFromURL } from "../helpers"; import Entity, { EntityClassNames } from "../Entity"; -import history from "utils/history"; +import history, { NavigationMethod } from "utils/history"; import { - fetchDatasourceStructure, - saveDatasourceName, expandDatasourceEntity, - setDatsourceEditorMode, + fetchDatasourceStructure, + updateDatasourceName, } from "actions/datasourceActions"; import { useDispatch, useSelector } from "react-redux"; import { AppState } from "@appsmith/reducers"; import { DatasourceStructureContainer } from "./DatasourceStructureContainer"; import { isStoredDatasource, PluginType } from "entities/Action"; -import { getQueryParams } from "utils/URLUtils"; import { getAction } from "selectors/entitiesSelector"; import { datasourcesEditorIdURL, @@ -26,6 +24,8 @@ import { inGuidedTour } from "selectors/onboardingSelectors"; import { getCurrentPageId } from "selectors/editorSelectors"; import AnalyticsUtil from "utils/AnalyticsUtil"; import { useLocation } from "react-router"; +import omit from "lodash/omit"; +import { getQueryParams } from "utils/URLUtils"; type ExplorerDatasourceEntityProps = { plugin: Plugin; @@ -34,6 +34,7 @@ type ExplorerDatasourceEntityProps = { searchKeyword?: string; pageId: string; isActive: boolean; + canManageDatasource?: boolean; }; const ExplorerDatasourceEntity = React.memo( @@ -50,18 +51,12 @@ const ExplorerDatasourceEntity = React.memo( pageId, pluginPackageName: props.plugin.packageName, datasourceId: props.datasource.id, - params: { - viewMode: true, - }, }); } else { - dispatch( - setDatsourceEditorMode({ id: props.datasource.id, viewMode: true }), - ); url = datasourcesEditorIdURL({ pageId, datasourceId: props.datasource.id, - params: getQueryParams(), + params: omit(getQueryParams(), "viewMode"), }); } @@ -71,7 +66,7 @@ const ExplorerDatasourceEntity = React.memo( toUrl: url, name: props.datasource.name, }); - history.push(url); + history.push(url, { invokedBy: NavigationMethod.EntityExplorer }); }, [props.datasource.id, props.datasource.name, location.pathname]); const queryId = getQueryIdFromURL(); @@ -79,8 +74,8 @@ const ExplorerDatasourceEntity = React.memo( getAction(state, queryId || ""), ); - const updateDatasourceName = (id: string, name: string) => - saveDatasourceName({ id: props.datasource.id, name }); + const updateDatasourceNameCall = (id: string, name: string) => + updateDatasourceName({ id: props.datasource.id, name }); const datasourceStructure = useSelector((state: AppState) => { return state.entities.datasources.structure[props.datasource.id]; @@ -93,7 +88,7 @@ const ExplorerDatasourceEntity = React.memo( const getDatasourceStructure = useCallback( (isOpen: boolean) => { if (!datasourceStructure && isOpen) { - dispatch(fetchDatasourceStructure(props.datasource.id)); + dispatch(fetchDatasourceStructure(props.datasource.id, true)); } dispatch(expandDatasourceEntity(isOpen ? props.datasource.id : "")); @@ -121,6 +116,7 @@ const ExplorerDatasourceEntity = React.memo( setActive(false)); - const lightningMenu = ( + const datasource = useSelector((state: AppState) => + getDatasource(state, props.datasourceId), + ); + + const datasourcePermissions = datasource?.userPermissions || []; + const pagePermissions = useSelector(getPagePermissions); + + const canCreateDatasourceActions = hasCreateDatasourceActionPermission([ + ...datasourcePermissions, + ...pagePermissions, + ]); + + const lightningMenu = canCreateDatasourceActions ? ( setActive(!active)} @@ -61,7 +78,7 @@ export function DatasourceStructure(props: DatasourceStructureProps) { Add - ); + ) : null; if (dbStructure.templates) templateMenu = lightningMenu; const columnsAndKeys = dbStructure.columns.concat(dbStructure.keys); @@ -82,7 +99,7 @@ export function DatasourceStructure(props: DatasourceStructureProps) { position={Position.RIGHT_TOP} > setActive(!active)} + action={() => canCreateDatasourceActions && setActive(!active)} active={active} className={`datasourceStructure`} contextMenu={templateMenu} diff --git a/app/client/src/pages/Editor/Explorer/Entity/index.tsx b/app/client/src/pages/Editor/Explorer/Entity/index.tsx index f7031fccfca1..53c659dfac0b 100644 --- a/app/client/src/pages/Editor/Explorer/Entity/index.tsx +++ b/app/client/src/pages/Editor/Explorer/Entity/index.tsx @@ -1,11 +1,11 @@ import React, { ReactNode, - useState, useEffect, useRef, forwardRef, useCallback, RefObject, + useMemo, } from "react"; import styled, { css } from "styled-components"; import { Colors } from "constants/Colors"; @@ -26,6 +26,9 @@ import { inGuidedTour } from "selectors/onboardingSelectors"; import { toggleShowDeviationDialog } from "actions/onboardingActions"; import Boxed from "pages/Editor/GuidedTour/Boxed"; import { GUIDED_TOUR_STEPS } from "pages/Editor/GuidedTour/constants"; +import { getEntityCollapsibleState } from "selectors/editorContextSelectors"; +import { AppState } from "@appsmith/reducers"; +import { setEntityCollapsibleState } from "actions/editorContextActions"; export enum EntityClassNames { CONTEXT_MENU = "entity-context-menu", @@ -187,7 +190,9 @@ const IconWrapper = styled.span` export type EntityProps = { entityId: string; + showAddButton?: boolean; className?: string; + canEditEntityName?: boolean; name: string; children?: ReactNode; highlight?: boolean; @@ -214,40 +219,47 @@ export type EntityProps = { isSticky?: boolean; collapseRef?: RefObject | null; customAddButton?: ReactNode; + forceExpand?: boolean; }; export const Entity = forwardRef( (props: EntityProps, ref: React.Ref) => { - const [isOpen, open] = useState(!!props.isDefaultExpanded); + const isEntityOpen = useSelector((state: AppState) => + getEntityCollapsibleState(state, props.name), + ); + const isDefaultExpanded = useMemo(() => !!props.isDefaultExpanded, []); + const { canEditEntityName = false, showAddButton = false } = props; const isUpdating = useEntityUpdateState(props.entityId); const isEditing = useEntityEditState(props.entityId); const dispatch = useDispatch(); const guidedTourEnabled = useSelector(inGuidedTour); - /* eslint-disable react-hooks/exhaustive-deps */ - useEffect(() => { - if (props.isDefaultExpanded || props.searchKeyword) { - open(true); - props.onToggle && props.onToggle(true); + const isOpen = + (isEntityOpen === undefined ? isDefaultExpanded : isEntityOpen) || + !!props.searchKeyword; + + const open = (shouldOpen: boolean | undefined) => { + if (!!props.children && props.name && isOpen !== shouldOpen) { + dispatch(setEntityCollapsibleState(props.name, !!shouldOpen)); } - }, [props.isDefaultExpanded, props.searchKeyword]); + }; + useEffect(() => { - if (!props.searchKeyword && !props.isDefaultExpanded) { - open(false); - } - }, [props.searchKeyword]); - /* eslint-enable react-hooks/exhaustive-deps */ + if (isEntityOpen !== undefined) open(isOpen); + }, [props.name]); + + useEffect(() => { + if (!!props.forceExpand) open(true); + }, [props.forceExpand]); + /* eslint-enable react-hooks/exhaustive-deps */ const toggleChildren = (e: any) => { + props.onToggle && props.onToggle(!isOpen); // Make sure this entity is enabled before toggling the collpse of children. !props.disabled && open(!isOpen); if (props.runActionOnExpand && !isOpen) { props.action && props.action(e); } - - if (props.onToggle) { - props.onToggle(!isOpen); - } }; const updateNameCallback = useCallback( @@ -271,6 +283,7 @@ export const Entity = forwardRef( }, [dispatch]); const enterEditMode = useCallback(() => { + if (!canEditEntityName) return; if (guidedTourEnabled) { dispatch(toggleShowDeviationDialog(true)); return; @@ -331,7 +344,7 @@ export const Entity = forwardRef( @@ -365,7 +378,7 @@ export const Entity = forwardRef( {props.rightIcon} )} - {addButton} + {showAddButton && addButton} {props.contextMenu && ( {props.contextMenu} )} @@ -374,7 +387,7 @@ export const Entity = forwardRef( {props.children} diff --git a/app/client/src/pages/Editor/Explorer/EntityExplorer.test.tsx b/app/client/src/pages/Editor/Explorer/EntityExplorer.test.tsx index beffa21d370d..864c55f6ca2c 100644 --- a/app/client/src/pages/Editor/Explorer/EntityExplorer.test.tsx +++ b/app/client/src/pages/Editor/Explorer/EntityExplorer.test.tsx @@ -8,12 +8,15 @@ import { MockPageDSL } from "test/testCommon"; import Sidebar from "components/editorComponents/Sidebar"; import { generateReactKey } from "utils/generators"; import { DEFAULT_ENTITY_EXPLORER_WIDTH } from "constants/AppConstants"; -import store from "store"; +import store, { runSagaMiddleware } from "store"; import Datasources from "./Datasources"; import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants"; import { mockDatasources } from "./mockTestData"; import { updateCurrentPage } from "actions/pageActions"; import urlBuilder from "entities/URLRedirect/URLAssembly"; +import * as helpers from "./helpers"; +import * as permissionUtils from "@appsmith/utils/permissionHelpers"; +import userEvent from "@testing-library/user-event"; jest.useFakeTimers(); const pushState = jest.spyOn(window.history, "pushState"); @@ -22,7 +25,18 @@ pushState.mockImplementation((state: any, title: any, url: any) => { window.location.pathname = url; }); +jest.mock("@appsmith/utils/permissionHelpers", () => { + return { + __esModule: true, + ...jest.requireActual("@appsmith/utils/permissionHelpers"), + }; +}); + describe("Entity Explorer tests", () => { + beforeAll(() => { + runSagaMiddleware(); + }); + beforeEach(() => { urlBuilder.updateURLParams( { @@ -40,16 +54,78 @@ describe("Entity Explorer tests", () => { }); it("checks datasources section in explorer", () => { + const mockExplorerState = jest.spyOn(helpers, "getExplorerStatus"); + mockExplorerState.mockImplementationOnce( + (appId: string, entityName: keyof helpers.ExplorerStateType) => true, + ); store.dispatch({ type: ReduxActionTypes.FETCH_DATASOURCES_SUCCESS, payload: mockDatasources, }); + jest + .spyOn(permissionUtils, "hasCreateDatasourcePermission") + .mockReturnValue(true); store.dispatch(updateCurrentPage("pageId")); const component = render(); expect(component.container.getElementsByClassName("t--entity").length).toBe( 5, ); }); + it("should hide create datasources section in explorer if the user don't have valid permissions", () => { + store.dispatch({ + type: ReduxActionTypes.FETCH_DATASOURCES_SUCCESS, + payload: mockDatasources, + }); + jest + .spyOn(permissionUtils, "hasCreateDatasourcePermission") + .mockReturnValue(false); + const mockExplorerState = jest.spyOn(helpers, "getExplorerStatus"); + mockExplorerState.mockImplementationOnce( + (appId: string, entityName: keyof helpers.ExplorerStateType) => true, + ); + store.dispatch(updateCurrentPage("pageId")); + const component = render(); + expect(component.container.getElementsByClassName("t--entity").length).toBe( + 4, + ); + const addDatasourceEntity = document.getElementById( + "entity-add_new_datasource", + ); + expect(addDatasourceEntity).toBeNull(); + }); + it("should hide delete & edit of datasource if the user don't have valid permissions", async () => { + store.dispatch({ + type: ReduxActionTypes.FETCH_DATASOURCES_SUCCESS, + payload: mockDatasources, + }); + jest + .spyOn(permissionUtils, "hasCreateDatasourcePermission") + .mockReturnValue(true); + jest + .spyOn(permissionUtils, "hasManageDatasourcePermission") + .mockReturnValue(false); + jest + .spyOn(permissionUtils, "hasDeleteDatasourcePermission") + .mockReturnValue(false); + const mockExplorerState = jest.spyOn(helpers, "getExplorerStatus"); + mockExplorerState.mockImplementationOnce( + (appId: string, entityName: keyof helpers.ExplorerStateType) => true, + ); + store.dispatch(updateCurrentPage("pageId")); + const { container } = render(); + const target = container.getElementsByClassName("t--context-menu"); + await userEvent.click(target[2]); + const deleteOption = document.getElementsByClassName( + "t--datasource-delete", + ); + const editOption = document.getElementsByClassName("t--datasource-rename"); + const refreshOption = document.getElementsByClassName( + "t--datasource-refresh", + ); + expect(deleteOption.length).toBe(0); + expect(editOption.length).toBe(0); + expect(refreshOption.length).toBe(1); + }); it("Should render Widgets tree in entity explorer", () => { const children: any = buildChildren([{ type: "TABS_WIDGET" }]); const dsl: any = widgetCanvasFactory.build({ diff --git a/app/client/src/pages/Editor/Explorer/EntityExplorer.tsx b/app/client/src/pages/Editor/Explorer/EntityExplorer.tsx index 3c918f16e68f..923bd37dd72b 100644 --- a/app/client/src/pages/Editor/Explorer/EntityExplorer.tsx +++ b/app/client/src/pages/Editor/Explorer/EntityExplorer.tsx @@ -30,6 +30,8 @@ import { builderURL } from "RouteBuilder"; import history from "utils/history"; import { SEARCH_ENTITY } from "constants/Explorer"; import { getCurrentPageId } from "selectors/editorSelectors"; +import { fetchWorkspace } from "@appsmith/actions/workspaceActions"; +import { getCurrentWorkspaceId } from "@appsmith/selectors/workspaceSelectors"; const Wrapper = styled.div` height: 100%; @@ -96,6 +98,12 @@ function EntityExplorer({ isActive }: { isActive: boolean }) { } }, [isFirstTimeUserOnboardingEnabled, pageId]); + const currentWorkspaceId = useSelector(getCurrentWorkspaceId); + + useEffect(() => { + dispatch(fetchWorkspace(currentWorkspaceId)); + }, [currentWorkspaceId]); + /** * filter entitites */ diff --git a/app/client/src/pages/Editor/Explorer/Files/Submenu.tsx b/app/client/src/pages/Editor/Explorer/Files/Submenu.tsx index bef2f835fef2..1ad9923ef52d 100644 --- a/app/client/src/pages/Editor/Explorer/Files/Submenu.tsx +++ b/app/client/src/pages/Editor/Explorer/Files/Submenu.tsx @@ -8,7 +8,10 @@ import { import styled from "constants/DefaultTheme"; import React, { useCallback, useEffect, useMemo, useState } from "react"; import { useDispatch, useSelector } from "react-redux"; -import { getCurrentPageId } from "selectors/editorSelectors"; +import { + getCurrentPageId, + getPagePermissions, +} from "selectors/editorSelectors"; import EntityAddButton from "../Entity/AddButton"; import { ReactComponent as SearchIcon } from "assets/icons/ads/search.svg"; import { ReactComponent as CrossIcon } from "assets/icons/ads/cross.svg"; @@ -28,6 +31,7 @@ import { } from "@appsmith/constants/messages"; import { useCloseMenuOnScroll } from "../hooks"; import { SIDEBAR_ID } from "constants/Explorer"; +import { hasCreateActionPermission } from "@appsmith/utils/permissionHelpers"; const SubMenuContainer = styled.div` width: 250px; @@ -79,6 +83,10 @@ export default function ExplorerSubMenu({ useEffect(() => setShow(openMenu), [openMenu]); useCloseMenuOnScroll(SIDEBAR_ID, show, () => setShow(false)); + const pagePermissions = useSelector(getPagePermissions); + + const canCreateActions = hasCreateActionPermission(pagePermissions); + useEffect(() => { setQuery(""); }, [show]); @@ -209,24 +217,26 @@ export default function ExplorerSubMenu({ placement="right-start" transitionDuration={0} > - - {createMessage(ADD_QUERY_JS_BUTTON)} ( - {comboHelpText[SEARCH_CATEGORY_ID.ACTION_OPERATION]}) - - } - disabled={show} - hoverOpenDelay={TOOLTIP_HOVER_ON_DELAY} - position="right" - > - setShow(true)} - /> - + {canCreateActions && ( + + {createMessage(ADD_QUERY_JS_BUTTON)} ( + {comboHelpText[SEARCH_CATEGORY_ID.ACTION_OPERATION]}) + + } + disabled={show} + hoverOpenDelay={TOOLTIP_HOVER_ON_DELAY} + position="right" + > + setShow(true)} + /> + + )} ); } diff --git a/app/client/src/pages/Editor/Explorer/Files/index.tsx b/app/client/src/pages/Editor/Explorer/Files/index.tsx index 7a3531215f5e..337501bfed13 100644 --- a/app/client/src/pages/Editor/Explorer/Files/index.tsx +++ b/app/client/src/pages/Editor/Explorer/Files/index.tsx @@ -11,6 +11,7 @@ import { useDispatch, useSelector } from "react-redux"; import { getCurrentApplicationId, getCurrentPageId, + getPagePermissions, } from "selectors/editorSelectors"; import { ExplorerActionEntity } from "../Actions/ActionEntity"; import ExplorerJSCollectionEntity from "../JSActions/JSActionEntity"; @@ -20,6 +21,7 @@ import { getExplorerStatus, saveExplorerStatus } from "../helpers"; import { Icon } from "design-system"; import { AddEntity, EmptyComponent } from "../common"; import ExplorerSubMenu from "./Submenu"; +import { hasCreateActionPermission } from "@appsmith/utils/permissionHelpers"; function Files() { const applicationId = useSelector(getCurrentApplicationId); @@ -50,6 +52,10 @@ function Files() { [applicationId], ); + const pagePermissions = useSelector(getPagePermissions); + + const canCreateActions = hasCreateActionPermission(pagePermissions); + const onMenuClose = useCallback(() => openMenu(false), [openMenu]); const fileEntities = useMemo( @@ -59,7 +65,7 @@ function Files() { return (
{entity.name}
@@ -105,25 +111,30 @@ function Files() { disabled={false} entityId={pageId + "_widgets"} icon={null} - isDefaultExpanded={isFilesOpen ?? true} + isDefaultExpanded={ + isFilesOpen === null || isFilesOpen === undefined ? false : isFilesOpen + } isSticky key={pageId + "_widgets"} name="Queries/JS" onCreate={onCreate} onToggle={onFilesToggle} searchKeyword={""} + showAddButton={canCreateActions} step={0} > {fileEntities.length ? ( fileEntities ) : ( )} - {fileEntities.length > 0 && ( + {fileEntities.length > 0 && canCreateActions && ( showBinding(props.id, props.name), + label: createMessage(CONTEXT_SHOW_BINDING), + }, + canManage && { + value: "copy", + onSelect: noop, + label: createMessage(CONTEXT_COPY), + children: menuPages.map((page) => { + return { + ...page, + onSelect: () => copyJSCollectionToPage(props.id, props.name, page.id), + }; + }), + }, + canManage && { + value: "move", + onSelect: noop, + label: createMessage(CONTEXT_MOVE), + children: + menuPages.length > 1 + ? menuPages + .filter((page) => page.id !== props.pageId) // Remove current page from the list + .map((page) => { + return { + ...page, + onSelect: () => + moveJSCollectionToPage(props.id, props.name, page.id), + }; + }) + : [ + { + value: "No Pages", + onSelect: noop, + label: createMessage(CONTEXT_NO_PAGE), + }, + ], + }, + canDelete && { + confirmDelete: confirmDelete, + className: "t--apiFormDeleteBtn single-select", + value: "delete", + onSelect: () => { + confirmDelete + ? deleteJSCollectionFromPage(props.id, props.name) + : setConfirmDelete(true); + }, + label: confirmDelete + ? createMessage(CONFIRM_CONTEXT_DELETE) + : createMessage(CONTEXT_DELETE), + intent: "danger", + }, + ].filter(Boolean); + + return optionsTree.length > 0 ? ( showBinding(props.id, props.name), - label: createMessage(CONTEXT_SHOW_BINDING), - }, - { - value: "copy", - onSelect: noop, - label: createMessage(CONTEXT_COPY), - children: menuPages.map((page) => { - return { - ...page, - onSelect: () => - copyJSCollectionToPage(props.id, props.name, page.id), - }; - }), - }, - { - value: "move", - onSelect: noop, - label: createMessage(CONTEXT_MOVE), - children: - menuPages.length > 1 - ? menuPages - .filter((page) => page.id !== props.pageId) // Remove current page from the list - .map((page) => { - return { - ...page, - onSelect: () => - moveJSCollectionToPage(props.id, props.name, page.id), - }; - }) - : [ - { - value: "No Pages", - onSelect: noop, - label: createMessage(CONTEXT_NO_PAGE), - }, - ], - }, - { - confirmDelete: confirmDelete, - className: "t--apiFormDeleteBtn single-select", - value: "delete", - onSelect: () => { - confirmDelete - ? deleteJSCollectionFromPage(props.id, props.name) - : setConfirmDelete(true); - }, - label: confirmDelete - ? createMessage(CONFIRM_CONTEXT_DELETE) - : createMessage(CONTEXT_DELETE), - intent: "danger", - }, - ]} + optionTree={optionsTree as TreeDropdownOption[]} selectedValue="" setConfirmDelete={setConfirmDelete} toggle={} /> - ); + ) : null; } export default JSCollectionEntityContextMenu; diff --git a/app/client/src/pages/Editor/Explorer/JSActions/JSActionEntity.tsx b/app/client/src/pages/Editor/Explorer/JSActions/JSActionEntity.tsx index cb6c695328bd..aceaa6d44c2e 100644 --- a/app/client/src/pages/Editor/Explorer/JSActions/JSActionEntity.tsx +++ b/app/client/src/pages/Editor/Explorer/JSActions/JSActionEntity.tsx @@ -1,6 +1,6 @@ import React, { memo, useCallback } from "react"; import Entity, { EntityClassNames } from "../Entity"; -import history from "utils/history"; +import history, { NavigationMethod } from "utils/history"; import JSCollectionEntityContextMenu from "./JSActionContextMenu"; import { saveJSObjectName } from "actions/jsActionActions"; import { useSelector } from "react-redux"; @@ -13,6 +13,10 @@ import { PluginType } from "entities/Action"; import { jsCollectionIdURL } from "RouteBuilder"; import AnalyticsUtil from "utils/AnalyticsUtil"; import { useLocation } from "react-router"; +import { + hasDeleteActionPermission, + hasManageActionPermission, +} from "@appsmith/utils/permissionHelpers"; type ExplorerJSCollectionEntityProps = { step: number; @@ -46,11 +50,22 @@ export const ExplorerJSCollectionEntity = memo( toUrl: navigateToUrl, name: jsAction.name, }); - history.push(navigateToUrl); + history.push(navigateToUrl, { + invokedBy: NavigationMethod.EntityExplorer, + }); } }, [pageId, jsAction.id, jsAction.name, location.pathname]); + + const jsActionPermissions = jsAction.userPermissions || []; + + const canDeleteJSAction = hasDeleteActionPermission(jsActionPermissions); + + const canManageJSAction = hasManageActionPermission(jsActionPermissions); + const contextMenu = ( ` @@ -77,6 +80,7 @@ export function MoreJSCollectionsMenu(props: EntityContextMenuProps) { const [isMenuOpen, setIsMenuOpen] = useState(false); const [confirmDelete, setConfirmDelete] = useState(false); const dispatch = useDispatch(); + const { isChangePermitted = false, isDeletePermitted = false } = props; const copyJSCollectionToPage = useCallback( (actionId: string, actionName: string, pageId: string) => { @@ -113,7 +117,91 @@ export function MoreJSCollectionsMenu(props: EntityContextMenuProps) { const menuPages = useSelector(getPageListAsOptions); - return ( + const options = [ + ...(isChangePermitted + ? [ + { + icon: "duplicate" as IconName, + value: "copy", + onSelect: noop, + label: createMessage(CONTEXT_COPY), + children: menuPages.map((page) => { + return { + ...page, + onSelect: () => + copyJSCollectionToPage(props.id, props.name, page.id), + }; + }), + }, + ] + : []), + ...(isChangePermitted + ? [ + { + icon: "swap-horizontal" as IconName, + value: "move", + onSelect: noop, + label: createMessage(CONTEXT_MOVE), + children: + menuPages.length > 1 + ? menuPages + .filter((page) => page.id !== props.pageId) // Remove current page from the list + .map((page) => { + return { + ...page, + onSelect: () => + moveJSCollectionToPage(props.id, props.name, page.id), + }; + }) + : [{ value: "No Pages", onSelect: noop, label: "No Pages" }], + }, + ] + : []), + ...(isChangePermitted + ? [ + { + value: "prettify", + icon: "code" as IconName, + subText: prettifyCodeKeyboardShortCut, + onSelect: () => { + /* + PS: Please do not remove ts-ignore from here, TS keeps suggesting that + the object is null, but that is not the case, and we need an + instance of the editor to pass to autoIndentCode function + */ + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + const editor = document.querySelector(".CodeMirror").CodeMirror; + autoIndentCode(editor); + dispatch(updateJSCollectionBody(editor.getValue(), props.id)); + AnalyticsUtil.logEvent("PRETTIFY_CODE_MANUAL_TRIGGER"); + }, + label: "Prettify Code", + }, + ] + : []), + ...(isDeletePermitted + ? [ + { + confirmDelete: confirmDelete, + icon: "trash" as IconName, + value: "delete", + onSelect: () => { + confirmDelete + ? deleteJSCollectionFromPage(props.id, props.name) + : setConfirmDelete(true); + }, + label: confirmDelete + ? createMessage(CONFIRM_CONTEXT_DELETE) + : createMessage(CONTEXT_DELETE), + intent: Intent.DANGER, + className: "t--apiFormDeleteBtn", + }, + ] + : []), + ]; + + return options.length > 0 ? ( setIsMenuOpen(isOpen)} onSelect={noop} - optionTree={[ - { - icon: "duplicate", - value: "copy", - onSelect: noop, - label: createMessage(CONTEXT_COPY), - children: menuPages.map((page) => { - return { - ...page, - onSelect: () => - copyJSCollectionToPage(props.id, props.name, page.id), - }; - }), - }, - { - icon: "swap-horizontal", - value: "move", - onSelect: noop, - label: createMessage(CONTEXT_MOVE), - children: - menuPages.length > 1 - ? menuPages - .filter((page) => page.id !== props.pageId) // Remove current page from the list - .map((page) => { - return { - ...page, - onSelect: () => - moveJSCollectionToPage(props.id, props.name, page.id), - }; - }) - : [{ value: "No Pages", onSelect: noop, label: "No Pages" }], - }, - { - value: "prettify", - icon: "code", - subText: prettifyCodeKeyboardShortCut, - onSelect: () => { - /* - PS: Please do not remove ts-ignore from here, TS keeps suggesting that - the object is null, but that is not the case, and we need an - instance of the editor to pass to autoIndentCode function - */ - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - const editor = document.querySelector(".CodeMirror").CodeMirror; - autoIndentCode(editor); - dispatch(updateJSCollectionBody(editor.getValue(), props.id)); - AnalyticsUtil.logEvent("PRETTIFY_CODE_MANUAL_TRIGGER"); - }, - label: "Prettify Code", - }, - { - confirmDelete: confirmDelete, - icon: "trash", - value: "delete", - onSelect: () => { - confirmDelete - ? deleteJSCollectionFromPage(props.id, props.name) - : setConfirmDelete(true); - }, - label: confirmDelete - ? createMessage(CONFIRM_CONTEXT_DELETE) - : createMessage(CONTEXT_DELETE), - intent: "danger", - className: "t--apiFormDeleteBtn", - }, - ]} + optionTree={options} position={Position.LEFT_TOP} selectedValue="" setConfirmDelete={setConfirmDelete} @@ -200,7 +222,7 @@ export function MoreJSCollectionsMenu(props: EntityContextMenuProps) { } /> - ); + ) : null; } export default MoreJSCollectionsMenu; diff --git a/app/client/src/pages/Editor/Explorer/Pages/AddPageContextMenu.tsx b/app/client/src/pages/Editor/Explorer/Pages/AddPageContextMenu.tsx index 0053914abaa3..7abbc495ee96 100644 --- a/app/client/src/pages/Editor/Explorer/Pages/AddPageContextMenu.tsx +++ b/app/client/src/pages/Editor/Explorer/Pages/AddPageContextMenu.tsx @@ -32,6 +32,7 @@ import { } from "@appsmith/constants/messages"; import HotKeys from "../Files/SubmenuHotkeys"; import { selectFeatureFlags } from "selectors/usersSelectors"; +import AnalyticsUtil from "utils/AnalyticsUtil"; const MenuItem = styled.div<{ active: boolean }>` display: flex; @@ -91,12 +92,14 @@ function AddPageContextMenu({ icon: FileAddIcon, onClick: createPageCallback, "data-cy": "add-page", + key: "CREATE_PAGE", }, { title: createMessage(GENERATE_PAGE_ACTION_TITLE), icon: Database2LineIcon, onClick: () => history.push(generateTemplateFormURL({ pageId })), "data-cy": "generate-page", + key: "GENERATE_PAGE", }, ]; @@ -106,6 +109,7 @@ function AddPageContextMenu({ icon: Layout2LineIcon, onClick: () => dispatch(showTemplatesModal(true)), "data-cy": "add-page-from-template", + key: "ADD_PAGE_FROM_TEMPLATE", }); } @@ -134,6 +138,9 @@ function AddPageContextMenu({ const onMenuItemClick = (item: typeof ContextMenuItems[number]) => { setShow(false); item.onClick(); + AnalyticsUtil.logEvent("ENTITY_EXPLORER_ADD_PAGE_CLICK", { + item: item.key, + }); }; return ( diff --git a/app/client/src/pages/Editor/Explorer/Pages/PageContextMenu.tsx b/app/client/src/pages/Editor/Explorer/Pages/PageContextMenu.tsx index 18756d92e0ae..ea1bc3e3edd7 100644 --- a/app/client/src/pages/Editor/Explorer/Pages/PageContextMenu.tsx +++ b/app/client/src/pages/Editor/Explorer/Pages/PageContextMenu.tsx @@ -1,5 +1,5 @@ import React, { ReactNode, useCallback, useState } from "react"; -import { useDispatch } from "react-redux"; +import { useDispatch, useSelector } from "react-redux"; import TreeDropdown, { TreeDropdownOption, } from "pages/Editor/Explorer/TreeDropdown"; @@ -23,7 +23,18 @@ import { CONTEXT_DELETE, CONFIRM_CONTEXT_DELETE, createMessage, + CONTEXT_SETTINGS, } from "@appsmith/constants/messages"; +import { openAppSettingsPaneAction } from "actions/appSettingsPaneActions"; +import { AppSettingsTabs } from "pages/Editor/AppSettingsPane/AppSettings"; +import { + hasCreatePagePermission, + hasDeletePagePermission, + hasManagePagePermission, +} from "@appsmith/utils/permissionHelpers"; +import { getPageById } from "selectors/editorSelectors"; +import { getCurrentApplication } from "selectors/applicationSelectors"; +import { AppState } from "@appsmith/reducers"; const CustomLabel = styled.div` display: flex; @@ -89,22 +100,51 @@ export function PageContextMenu(props: { * @return void */ const setHiddenField = useCallback( - () => dispatch(updatePage(props.pageId, props.name, !props.isHidden)), + () => + dispatch( + updatePage({ + id: props.pageId, + name: props.name, + isHidden: !props.isHidden, + }), + ), [dispatch, props.pageId, props.name, props.isHidden], ); - const optionTree: TreeDropdownOption[] = [ - { + const openAppSettingsPane = () => + dispatch( + openAppSettingsPaneAction({ + type: AppSettingsTabs.Page, + pageId: props.pageId, + }), + ); + + const pagePermissions = + useSelector(getPageById(props.pageId))?.userPermissions || []; + + const userAppPermissions = useSelector( + (state: AppState) => getCurrentApplication(state)?.userPermissions ?? [], + ); + + const canCreatePages = hasCreatePagePermission(userAppPermissions); + + const canManagePages = hasManagePagePermission(pagePermissions); + + const canDeletePages = hasDeletePagePermission(pagePermissions); + + const optionsTree = [ + canManagePages && { value: "rename", onSelect: editPageName, label: createMessage(CONTEXT_EDIT_NAME), }, - { - value: "clone", - onSelect: clonePage, - label: createMessage(CONTEXT_CLONE), - }, - { + canCreatePages && + canManagePages && { + value: "clone", + onSelect: clonePage, + label: createMessage(CONTEXT_CLONE), + }, + canManagePages && { value: "visibility", onSelect: setHiddenField, // Possibly support ReactNode in TreeOption @@ -115,41 +155,51 @@ export function PageContextMenu(props: { ) as ReactNode) as string, }, - ]; - if (!props.isDefaultPage) { - optionTree.push({ - value: "setdefault", - onSelect: setPageAsDefaultCallback, - label: createMessage(CONTEXT_SET_AS_HOME_PAGE), - }); - } - - if (!props.isDefaultPage) { - optionTree.push({ - className: "t--apiFormDeleteBtn single-select", - confirmDelete: confirmDelete, - value: "delete", - onSelect: () => { - confirmDelete ? deletePageCallback() : setConfirmDelete(true); + !props.isDefaultPage && + canManagePages && { + value: "setdefault", + onSelect: setPageAsDefaultCallback, + label: createMessage(CONTEXT_SET_AS_HOME_PAGE), }, - label: confirmDelete - ? createMessage(CONFIRM_CONTEXT_DELETE) - : createMessage(CONTEXT_DELETE), - intent: "danger", - }); - } - return ( + props.isDefaultPage && + canManagePages && { + className: "!text-[color:var(--appsmith-color-black-500)]", + disabled: true, + value: "setdefault", + label: createMessage(CONTEXT_SET_AS_HOME_PAGE), + }, + { + value: "settings", + onSelect: openAppSettingsPane, + label: createMessage(CONTEXT_SETTINGS), + }, + !props.isDefaultPage && + canDeletePages && { + className: "t--apiFormDeleteBtn single-select", + confirmDelete: confirmDelete, + value: "delete", + onSelect: () => { + confirmDelete ? deletePageCallback() : setConfirmDelete(true); + }, + label: confirmDelete + ? createMessage(CONFIRM_CONTEXT_DELETE) + : createMessage(CONTEXT_DELETE), + intent: "danger", + }, + ].filter(Boolean); + + return optionsTree?.length > 0 ? ( } /> - ); + ) : null; } export default PageContextMenu; diff --git a/app/client/src/pages/Editor/Explorer/Pages/index.tsx b/app/client/src/pages/Editor/Explorer/Pages/index.tsx index f5238fbaac2a..2b5861f5fe48 100644 --- a/app/client/src/pages/Editor/Explorer/Pages/index.tsx +++ b/app/client/src/pages/Editor/Explorer/Pages/index.tsx @@ -1,52 +1,52 @@ import React, { useCallback, - useMemo, useEffect, + useMemo, useRef, useState, } from "react"; import { useDispatch, useSelector } from "react-redux"; import { + getCurrentApplication, getCurrentApplicationId, getCurrentPageId, } from "selectors/editorSelectors"; import Entity, { EntityClassNames } from "../Entity"; -import history from "utils/history"; +import history, { NavigationMethod } from "utils/history"; import { createPage, updatePage } from "actions/pageActions"; import { + currentPageIcon, + defaultPageIcon, hiddenPageIcon, pageIcon, - defaultPageIcon, - settingsIcon, - currentPageIcon, } from "../ExplorerIcons"; -import { - createMessage, - ADD_PAGE_TOOLTIP, - PAGE_PROPERTIES_TOOLTIP, -} from "@appsmith/constants/messages"; +import { ADD_PAGE_TOOLTIP, createMessage } from "@appsmith/constants/messages"; import { Page } from "@appsmith/constants/ReduxActionConstants"; import { getNextEntityName } from "utils/AppsmithUtils"; import { extractCurrentDSL } from "utils/WidgetPropsUtils"; -import { TooltipComponent } from "design-system"; -import { TOOLTIP_HOVER_ON_DELAY } from "constants/AppConstants"; import styled from "styled-components"; import PageContextMenu from "./PageContextMenu"; import { resolveAsSpaceChar } from "utils/helpers"; import { getExplorerPinned } from "selectors/explorerSelector"; import { setExplorerPinnedAction } from "actions/explorerActions"; import { selectAllPages } from "selectors/entitiesSelector"; -import { builderURL, pageListEditorURL } from "RouteBuilder"; -import { saveExplorerStatus, getExplorerStatus } from "../helpers"; +import { builderURL } from "RouteBuilder"; +import { getExplorerStatus, saveExplorerStatus } from "../helpers"; import { tailwindLayers } from "constants/Layers"; import useResize, { - DIRECTION, CallbackResponseType, + DIRECTION, } from "utils/hooks/useResize"; import AddPageContextMenu from "./AddPageContextMenu"; import AnalyticsUtil from "utils/AnalyticsUtil"; import { useLocation } from "react-router"; import { toggleInOnboardingWidgetSelection } from "actions/onboardingActions"; +import { + hasCreatePagePermission, + hasManagePagePermission, +} from "@appsmith/utils/permissionHelpers"; +import { AppState } from "@appsmith/reducers"; +import { pageChanged } from "actions/focusHistoryActions"; const ENTITY_HEIGHT = 36; const MIN_PAGES_HEIGHT = 60; @@ -123,7 +123,19 @@ function Pages() { toUrl: navigateToUrl, }); dispatch(toggleInOnboardingWidgetSelection(true)); - history.push(navigateToUrl); + history.push(navigateToUrl, { + invokedBy: NavigationMethod.EntityExplorer, + }); + const currentURL = navigateToUrl.split(/(?=\?)/g); + dispatch( + pageChanged( + page.pageId, + currentURL[0], + currentURL[1], + location.pathname, + location.search, + ), + ); }, [location.pathname], ); @@ -144,20 +156,6 @@ function Pages() { const onMenuClose = useCallback(() => openMenu(false), [openMenu]); - const settingsIconWithTooltip = React.useMemo( - () => ( - - {settingsIcon} - - ), - [], - ); - /** * toggles the pinned state of sidebar */ @@ -165,15 +163,6 @@ function Pages() { dispatch(setExplorerPinnedAction(!pinned)); }, [pinned, dispatch, setExplorerPinnedAction]); - const onClickRightIcon = useCallback(() => { - history.push(pageListEditorURL({ pageId: currentPageId })); - }, [currentPageId]); - - const onPageListSelection = React.useCallback( - () => history.push(pageListEditorURL({ pageId: currentPageId })), - [currentPageId], - ); - const onPageToggle = useCallback( (isOpen: boolean) => { saveExplorerStatus(applicationId, "pages", isOpen); @@ -181,12 +170,20 @@ function Pages() { [applicationId], ); + const userAppPermissions = useSelector( + (state: AppState) => getCurrentApplication(state)?.userPermissions ?? [], + ); + + const canCreatePages = hasCreatePagePermission(userAppPermissions); + const pageElements = useMemo( () => pages.map((page) => { const icon = page.isDefault ? defaultPageIcon : pageIcon; const rightIcon = !!page.isHidden ? hiddenPageIcon : null; const isCurrentPage = currentPageId === page.pageId; + const pagePermissions = page.userPermissions; + const canManagePages = hasManagePagePermission(pagePermissions); const contextMenu = ( switchPage(page)} + canEditEntityName={canManagePages} className={`page ${isCurrentPage && "activePage"}`} contextMenu={contextMenu} entityId={page.pageId} @@ -215,18 +213,17 @@ function Pages() { searchKeyword={""} step={1} updateEntityName={(id, name) => - updatePage(id, name, !!page.isHidden) + updatePage({ id, name, isHidden: !!page.isHidden }) } /> ); }), - [pages, currentPageId, applicationId], + [pages, currentPageId, applicationId, location.pathname], ); return ( {pageElements} diff --git a/app/client/src/pages/Editor/Explorer/TreeDropdown.tsx b/app/client/src/pages/Editor/Explorer/TreeDropdown.tsx index 7dadadf0db99..a013d8fe6adb 100644 --- a/app/client/src/pages/Editor/Explorer/TreeDropdown.tsx +++ b/app/client/src/pages/Editor/Explorer/TreeDropdown.tsx @@ -26,6 +26,7 @@ export type TreeDropdownOption = DropdownOption & { className?: string; type?: string; confirmDelete?: boolean; + disabled?: boolean; }; type Setter = (value: TreeDropdownOption, defaultVal?: string) => void; @@ -155,6 +156,7 @@ export default function TreeDropdown(props: TreeDropdownProps) { { @@ -84,11 +85,6 @@ export function WidgetContextMenu(props: { }, [dispatch, widgetId, guidedTourEnabled]); const optionTree: TreeDropdownOption[] = [ - { - value: "rename", - onSelect: editWidgetName, - label: "Edit Name", - }, { value: "showBinding", onSelect: () => showBinding(props.widgetId, widget.widgetName), @@ -96,7 +92,16 @@ export function WidgetContextMenu(props: { }, ]; - if (widget.isDeletable !== false) { + if (props.canManagePages) { + const option: TreeDropdownOption = { + value: "rename", + onSelect: editWidgetName, + label: "Edit Name", + }; + optionTree.push(option); + } + + if (widget.isDeletable !== false && props.canManagePages) { const option: TreeDropdownOption = { value: "delete", onSelect: dispatchDelete, @@ -106,17 +111,17 @@ export function WidgetContextMenu(props: { optionTree.push(option); } - return ( + return optionTree.length > 0 ? ( } /> - ); + ) : null; } WidgetContextMenu.displayName = "WidgetContextMenu"; diff --git a/app/client/src/pages/Editor/Explorer/Widgets/WidgetEntity.tsx b/app/client/src/pages/Editor/Explorer/Widgets/WidgetEntity.tsx index 43cb82e1e967..041d24cabbd0 100644 --- a/app/client/src/pages/Editor/Explorer/Widgets/WidgetEntity.tsx +++ b/app/client/src/pages/Editor/Explorer/Widgets/WidgetEntity.tsx @@ -1,4 +1,4 @@ -import React, { useMemo, useCallback, memo } from "react"; +import React, { memo, useCallback, useMemo } from "react"; import Entity, { EntityClassNames } from "../Entity"; import { WidgetProps } from "widgets/BaseWidget"; import { WidgetType } from "constants/WidgetConstants"; @@ -13,6 +13,9 @@ import WidgetIcon from "./WidgetIcon"; import AnalyticsUtil from "utils/AnalyticsUtil"; import { builderURL } from "RouteBuilder"; import { useLocation } from "react-router"; +import { hasManagePagePermission } from "@appsmith/utils/permissionHelpers"; +import { getPagePermissions } from "selectors/editorSelectors"; +import { NavigationMethod } from "utils/history"; export type WidgetTree = WidgetProps & { children?: WidgetTree[] }; @@ -23,7 +26,6 @@ const useWidget = ( widgetType: WidgetType, pageId: string, widgetsInStep: string[], - parentModalId?: string, ) => { const selectedWidgets = useSelector(getSelectedWidgets); const lastSelectedWidget = useSelector(getLastSelectedWidget); @@ -40,8 +42,8 @@ const useWidget = ( widgetId, widgetType, pageId, + NavigationMethod.EntityExplorer, isWidgetSelected, - parentModalId, isMultiSelect, isShiftSelect, widgetsInStep, @@ -52,7 +54,6 @@ const useWidget = ( widgetType, pageId, isWidgetSelected, - parentModalId, widgetsInStep, navigateToWidget, ], @@ -86,7 +87,11 @@ export const WidgetEntity = memo((props: WidgetEntityProps) => { const icon = ; const location = useLocation(); - const shouldExpand = widgetsToExpand.includes(props.widgetId); + const forceExpand = widgetsToExpand.includes(props.widgetId); + + const pagePermissions = useSelector(getPagePermissions); + + const canManagePages = hasManagePagePermission(pagePermissions); const { isWidgetSelected, @@ -98,7 +103,6 @@ export const WidgetEntity = memo((props: WidgetEntityProps) => { props.widgetType, props.pageId, props.widgetsInStep, - props.parentModalId, ); const { parentModalId, widgetId, widgetType } = props; @@ -131,6 +135,7 @@ export const WidgetEntity = memo((props: WidgetEntityProps) => { const contextMenu = ( { diff --git a/app/client/src/pages/Editor/Explorer/Widgets/WidgetGroup.tsx b/app/client/src/pages/Editor/Explorer/Widgets/WidgetGroup.tsx index b8ef8179a769..abb1f216eebe 100644 --- a/app/client/src/pages/Editor/Explorer/Widgets/WidgetGroup.tsx +++ b/app/client/src/pages/Editor/Explorer/Widgets/WidgetGroup.tsx @@ -5,6 +5,7 @@ import WidgetEntity from "./WidgetEntity"; import { getCurrentApplicationId, getCurrentPageId, + getPagePermissions, } from "selectors/editorSelectors"; import { ADD_WIDGET_BUTTON, @@ -19,6 +20,7 @@ import { getExplorerStatus, saveExplorerStatus } from "../helpers"; import { Icon } from "design-system"; import { AddEntity, EmptyComponent } from "../common"; import { noop } from "lodash"; +import { hasManagePagePermission } from "@appsmith/utils/permissionHelpers"; type ExplorerWidgetGroupProps = { step: number; @@ -32,7 +34,7 @@ export const ExplorerWidgetGroup = memo((props: ExplorerWidgetGroupProps) => { const widgets = useSelector(selectWidgetsForCurrentPage); const guidedTour = useSelector(inGuidedTour); let isWidgetsOpen = getExplorerStatus(applicationId, "widgets"); - if (isWidgetsOpen === null) { + if (isWidgetsOpen === null || isWidgetsOpen === undefined) { isWidgetsOpen = widgets?.children?.length === 0 || guidedTour; saveExplorerStatus(applicationId, "widgets", isWidgetsOpen); } else if (guidedTour) { @@ -51,9 +53,14 @@ export const ExplorerWidgetGroup = memo((props: ExplorerWidgetGroupProps) => { [applicationId], ); + const pagePermissions = useSelector(getPagePermissions); + + const canManagePages = hasManagePagePermission(pagePermissions); + return ( { onCreate={props.addWidgetsFn} onToggle={onWidgetToggle} searchKeyword={props.searchKeyword} + showAddButton={canManagePages} step={props.step} > {widgets?.children?.map((child) => ( @@ -88,7 +96,7 @@ export const ExplorerWidgetGroup = memo((props: ExplorerWidgetGroupProps) => { mainText={createMessage(EMPTY_WIDGET_MAIN_TEXT)} /> )} - {widgets?.children && widgets?.children?.length > 0 && ( + {widgets?.children && widgets?.children?.length > 0 && canManagePages && ( { const params = useParams(); @@ -33,16 +30,10 @@ export const useNavigateToWidget = () => { widgetId: string, widgetType: WidgetType, pageId: string, - parentModalId?: string, + navigationMethod?: NavigationMethod, ) => { - if (widgetType === WidgetTypes.MODAL_WIDGET) { - dispatch(showModal(widgetId)); - return; - } - if (parentModalId) dispatch(showModal(parentModalId)); - else dispatch(closeAllModals()); selectWidget(widgetId, false); - navigateToCanvas(pageId, widgetId); + navigateToCanvas(pageId, widgetId, navigationMethod); quickScrollToWidget(widgetId); // Navigating to a widget from query pane seems to make the property pane // appear below the entity explorer hence adding a timeout here @@ -61,8 +52,8 @@ export const useNavigateToWidget = () => { widgetId: string, widgetType: WidgetType, pageId: string, + navigationMethod: NavigationMethod, isWidgetSelected?: boolean, - parentModalId?: string, isMultiSelect?: boolean, isShiftSelect?: boolean, widgetsInStep?: string[], @@ -77,7 +68,7 @@ export const useNavigateToWidget = () => { } else if (isMultiSelect) { multiSelectWidgets(widgetId, pageId); } else { - selectSingleWidget(widgetId, widgetType, pageId, parentModalId); + selectSingleWidget(widgetId, widgetType, pageId, navigationMethod); } }, [dispatch, params, selectWidget], diff --git a/app/client/src/pages/Editor/Explorer/Widgets/utils.ts b/app/client/src/pages/Editor/Explorer/Widgets/utils.ts index be3e36e7c3c1..f00479b7e8c0 100644 --- a/app/client/src/pages/Editor/Explorer/Widgets/utils.ts +++ b/app/client/src/pages/Editor/Explorer/Widgets/utils.ts @@ -1,13 +1,18 @@ import { builderURL } from "RouteBuilder"; -import history from "utils/history"; +import history, { NavigationMethod } from "utils/history"; -export const navigateToCanvas = (pageId: string, widgetId?: string) => { +export const navigateToCanvas = ( + pageId: string, + widgetId?: string, + invokedBy?: NavigationMethod, +) => { const currentPath = window.location.pathname; const canvasEditorURL = `${builderURL({ pageId, hash: widgetId, + persistExistingParams: true, })}`; if (currentPath !== canvasEditorURL) { - history.push(canvasEditorURL); + history.push(canvasEditorURL, { invokedBy }); } }; diff --git a/app/client/src/pages/Editor/Explorer/common.tsx b/app/client/src/pages/Editor/Explorer/common.tsx index 1cbd46eb880c..c1f4c218f0a3 100644 --- a/app/client/src/pages/Editor/Explorer/common.tsx +++ b/app/client/src/pages/Editor/Explorer/common.tsx @@ -39,16 +39,19 @@ const ECAddButton = styled.div` export function EmptyComponent(props: { mainText: string; - addBtnText: string; - addFunction: () => void; + addBtnText?: string; + addFunction?: () => void; }) { + const showAddCta = props.addFunction && props.addBtnText; return ( {props.mainText} - - - {props.addBtnText} - + {showAddCta && ( + + + {props.addBtnText && props.addBtnText} + + )} ); } diff --git a/app/client/src/pages/Editor/Explorer/helpers.tsx b/app/client/src/pages/Editor/Explorer/helpers.tsx index 13538db18211..5ada11a86d7a 100644 --- a/app/client/src/pages/Editor/Explorer/helpers.tsx +++ b/app/client/src/pages/Editor/Explorer/helpers.tsx @@ -153,12 +153,7 @@ export const saveExplorerStatus = ( ): void => { const storageItemName = EXPLORER_STORAGE_PREFIX + appId; const state = localStorage.getItem(storageItemName); - let data: ExplorerStateType = { - pages: false, - widgets: false, - queriesAndJs: false, - datasource: false, - }; + let data = {} as ExplorerStateType; if (state !== null) { data = JSON.parse(state); } diff --git a/app/client/src/pages/Editor/Explorer/hooks.ts b/app/client/src/pages/Editor/Explorer/hooks.ts index b8a4188315a4..c6b2d71e11ac 100644 --- a/app/client/src/pages/Editor/Explorer/hooks.ts +++ b/app/client/src/pages/Editor/Explorer/hooks.ts @@ -26,6 +26,7 @@ import { QUERIES_EDITOR_ID_PATH, } from "constants/routes"; import { SAAS_EDITOR_API_ID_PATH } from "../SaaSEditor/constants"; +import { TEMP_DATASOURCE_ID } from "constants/Datasource"; const findWidgets = (widgets: CanvasStructure, keyword: string) => { if (!widgets || !widgets.widgetName) return widgets; @@ -113,7 +114,9 @@ export const useOtherDatasourcesInWorkspace = () => { new Set(), ); return allDatasources.filter( - (ds) => !datasourceIdsUsedInCurrentApplication.has(ds.id), + (ds) => + !datasourceIdsUsedInCurrentApplication.has(ds.id) && + ds.id !== TEMP_DATASOURCE_ID, ); }; diff --git a/app/client/src/pages/Editor/Explorer/index.tsx b/app/client/src/pages/Editor/Explorer/index.tsx index b49fbbc872d0..a1500b2022e4 100644 --- a/app/client/src/pages/Editor/Explorer/index.tsx +++ b/app/client/src/pages/Editor/Explorer/index.tsx @@ -3,7 +3,7 @@ import { forceOpenWidgetPanel } from "actions/widgetSidebarActions"; import { Switcher } from "design-system"; import { Colors } from "constants/Colors"; import { tailwindLayers } from "constants/Layers"; -import React, { useEffect, useMemo, useState } from "react"; +import React, { useEffect, useMemo } from "react"; import { useDispatch, useSelector } from "react-redux"; import { useLocation } from "react-router"; import { AppState } from "@appsmith/reducers"; @@ -15,6 +15,8 @@ import { trimQueryString } from "utils/helpers"; import history from "utils/history"; import WidgetSidebar from "../WidgetSidebar"; import EntityExplorer from "./EntityExplorer"; +import { getExplorerSwitchIndex } from "selectors/editorContextSelectors"; +import { setExplorerSwitchIndex } from "actions/editorContextActions"; const selectForceOpenWidgetPanel = (state: AppState) => state.ui.onBoarding.forceOpenWidgetPanel; @@ -48,6 +50,7 @@ function ExplorerContent() { }); } dispatch(forceOpenWidgetPanel(true)); + dispatch(setExplorerSwitchIndex(1)); if (isFirstTimeUserOnboardingEnabled) { dispatch(toggleInOnboardingWidgetSelection(true)); } @@ -63,11 +66,18 @@ function ExplorerContent() { pageId, ], ); - const [activeSwitch, setActiveSwitch] = useState(switches[0]); + const activeSwitchIndex = useSelector(getExplorerSwitchIndex); + + const setActiveSwitchIndex = (index: number) => { + dispatch(setExplorerSwitchIndex(index)); + }; const openWidgetPanel = useSelector(selectForceOpenWidgetPanel); useEffect(() => { - setActiveSwitch(switches[openWidgetPanel ? 1 : 0]); + const currentIndex = openWidgetPanel ? 1 : 0; + if (currentIndex !== activeSwitchIndex) { + setActiveSwitchIndex(currentIndex); + } }, [openWidgetPanel]); return ( @@ -77,10 +87,12 @@ function ExplorerContent() {
- +
- - + +
); } diff --git a/app/client/src/pages/Editor/Explorer/mockTestData.ts b/app/client/src/pages/Editor/Explorer/mockTestData.ts index 0f91c3fd3445..9a2e89ff9684 100644 --- a/app/client/src/pages/Editor/Explorer/mockTestData.ts +++ b/app/client/src/pages/Editor/Explorer/mockTestData.ts @@ -189,3 +189,227 @@ export const mockDatasources = [ }, }, ]; + +export const mockApiDatas = [ + { + id: "634929568f35a90ce8a428dc", + workspaceId: "607d2c9c1a5f642a171ebd9b", + pluginType: "API", + pluginId: "5c9f512f96c1a50004819786", + name: "Api1", + datasource: { + userPermissions: [], + name: "poiuyt09", + pluginId: "5c9f512f96c1a50004819786", + workspaceId: "607d2c9c1a5f642a171ebd9b", + datasourceConfiguration: { + connection: { + mode: "READ_WRITE", + ssl: { + authType: "DEFAULT", + }, + }, + endpoints: [ + { + host: "localhost", + port: 5432, + }, + ], + authentication: { + authenticationType: "dbAuth", + username: "postgres", + databaseName: "fakeapi", + }, + sshProxyEnabled: false, + }, + }, + actionConfiguration: { + timeoutInMillisecond: 10000, + paginationType: "NONE", + headers: [], + encodeParamsToggle: true, + queryParameters: [], + bodyFormData: [], + httpMethod: "GET", + selfReferencingDataPaths: [], + pluginSpecifiedTemplates: [ + { + value: true, + }, + ], + formData: { + apiContentType: "none", + }, + }, + executeOnLoad: false, + dynamicBindingPathList: [], + isValid: true, + invalids: [], + messages: [], + jsonPathKeys: [], + confirmBeforeExecute: false, + userPermissions: [ + "read:actions", + "delete:actions", + "execute:actions", + "manage:actions", + ], + validName: "Api1", + }, +]; + +export const mockJsActions = [ + { + id: "634926dc8f35a90ce8a428d2", + workspaceId: "607d2c9c1a5f642a171ebd9b", + name: "JSObject1", + pluginId: "634925cd8f35a90ce8a42841", + pluginType: "JS", + actionIds: [], + archivedActionIds: [], + actions: [ + { + id: "634926dc8f35a90ce8a428d0", + workspaceId: "607d2c9c1a5f642a171ebd9b", + pluginType: "JS", + pluginId: "634925cd8f35a90ce8a42841", + name: "myFun1", + fullyQualifiedName: "JSObject1.myFun1", + datasource: { + userPermissions: [], + name: "UNUSED_DATASOURCE", + pluginId: "634925cd8f35a90ce8a42841", + workspaceId: "607d2c9c1a5f642a171ebd9b", + messages: [], + isValid: true, + new: true, + }, + collectionId: "634926dc8f35a90ce8a428d2", + actionConfiguration: { + timeoutInMillisecond: 10000, + paginationType: "NONE", + encodeParamsToggle: true, + body: "() => {}", + selfReferencingDataPaths: [], + jsArguments: [], + isAsync: false, + }, + executeOnLoad: false, + clientSideExecution: true, + dynamicBindingPathList: [ + { + key: "body", + }, + ], + isValid: true, + invalids: [], + messages: [], + jsonPathKeys: ["() => {}"], + confirmBeforeExecute: false, + userPermissions: [ + "read:actions", + "delete:actions", + "execute:actions", + "manage:actions", + ], + validName: "JSObject1.myFun1", + }, + { + id: "634926dc8f35a90ce8a428cf", + workspaceId: "607d2c9c1a5f642a171ebd9b", + pluginType: "JS", + pluginId: "634925cd8f35a90ce8a42841", + name: "myFun2", + fullyQualifiedName: "JSObject1.myFun2", + datasource: { + userPermissions: [], + name: "UNUSED_DATASOURCE", + pluginId: "634925cd8f35a90ce8a42841", + workspaceId: "607d2c9c1a5f642a171ebd9b", + messages: [], + isValid: true, + new: true, + }, + collectionId: "634926dc8f35a90ce8a428d2", + actionConfiguration: { + timeoutInMillisecond: 10000, + paginationType: "NONE", + encodeParamsToggle: true, + body: "async () => {}", + selfReferencingDataPaths: [], + jsArguments: [], + isAsync: true, + }, + executeOnLoad: false, + clientSideExecution: true, + dynamicBindingPathList: [ + { + key: "body", + }, + ], + isValid: true, + invalids: [], + messages: [], + jsonPathKeys: ["async () => {}"], + confirmBeforeExecute: false, + userPermissions: [ + "read:actions", + "delete:actions", + "execute:actions", + "manage:actions", + ], + validName: "JSObject1.myFun2", + }, + ], + archivedActions: [], + body: + "export default {sad\n\tmyVar1: [],\n\tmyVar2: {},\n\tmyFun1: () => {\n\t\t//write code here\n\t},\n\tmyFun2: async () => {\n\t\t//use async-await or promises\n\t}\n}", + variables: [ + { + name: "myVar1", + value: "[]", + }, + { + name: "myVar2", + value: "{}", + }, + ], + userPermissions: [ + "read:actions", + "delete:actions", + "execute:actions", + "manage:actions", + ], + }, +]; + +export const mockEntitiesFilesSelector = () => [ + { + type: "group", + entity: { + name: "APIs", + }, + }, + { + type: "API", + entity: { + id: "634929568f35a90ce8a428dc", + name: "Api1", + }, + group: "APIs", + }, + { + type: "group", + entity: { + name: "JS Objects", + }, + }, + { + type: "JS", + entity: { + id: "634926dc8f35a90ce8a428d2", + name: "JSObject1", + }, + group: "JS Objects", + }, +]; diff --git a/app/client/src/pages/Editor/FirstTimeUserOnboarding/Checklist.tsx b/app/client/src/pages/Editor/FirstTimeUserOnboarding/Checklist.tsx index 126e2feffb92..6378f3be0dbc 100644 --- a/app/client/src/pages/Editor/FirstTimeUserOnboarding/Checklist.tsx +++ b/app/client/src/pages/Editor/FirstTimeUserOnboarding/Checklist.tsx @@ -2,8 +2,7 @@ import React from "react"; import { Icon } from "@blueprintjs/core"; import { Button, Category, Text, TextType } from "design-system"; import styled from "styled-components"; -import { useDispatch } from "react-redux"; -import { useSelector } from "store"; +import { useDispatch, useSelector } from "react-redux"; import { getCanvasWidgets, getDatasources, diff --git a/app/client/src/pages/Editor/FirstTimeUserOnboarding/IntroductionModal.tsx b/app/client/src/pages/Editor/FirstTimeUserOnboarding/IntroductionModal.tsx index 4db8df2f7753..8ca39de18b07 100644 --- a/app/client/src/pages/Editor/FirstTimeUserOnboarding/IntroductionModal.tsx +++ b/app/client/src/pages/Editor/FirstTimeUserOnboarding/IntroductionModal.tsx @@ -213,7 +213,7 @@ export default function IntroductionModal({ close }: IntroductionModalProps) {
{ (error: string) => { setIsFetchingColumnHeaderList(false); setErrorFetchingColumnHeaderList(error); + setColumnHeaderList([]); }, [setErrorFetchingColumnHeaderList, setIsFetchingColumnHeaderList], ); @@ -396,6 +397,7 @@ export const useSheetColumnHeaders = () => { parameters: { sheetName: params.sheetName, sheetUrl: params.selectedSpreadsheetUrl, + tableHeaderIndex: params.tableHeaderIndex, }, pluginId: params.pluginId, requestType: "COLUMNS_SELECTOR", diff --git a/app/client/src/pages/Editor/GlobalHotKeys/GlobalHotKeys.test.tsx b/app/client/src/pages/Editor/GlobalHotKeys/GlobalHotKeys.test.tsx index 2c50c215cbf9..e14b13011cc6 100644 --- a/app/client/src/pages/Editor/GlobalHotKeys/GlobalHotKeys.test.tsx +++ b/app/client/src/pages/Editor/GlobalHotKeys/GlobalHotKeys.test.tsx @@ -13,7 +13,7 @@ import { MemoryRouter } from "react-router-dom"; import * as widgetRenderUtils from "utils/widgetRenderUtils"; import * as utilities from "selectors/editorSelectors"; import * as dataTreeSelectors from "selectors/dataTreeSelectors"; -import store from "store"; +import store, { runSagaMiddleware } from "store"; import { buildChildren, widgetCanvasFactory, @@ -43,6 +43,10 @@ jest.mock("constants/routes", () => { }); describe("Canvas Hot Keys", () => { + beforeAll(() => { + runSagaMiddleware(); + }); + const mockGetIsFetchingPage = jest.spyOn(utilities, "getIsFetchingPage"); const spyGetCanvasWidgetDsl = jest.spyOn(utilities, "getCanvasWidgetDsl"); const spyGetChildWidgets = jest.spyOn(utilities, "getChildWidgets"); diff --git a/app/client/src/pages/Editor/GuidedTour/Boxed.tsx b/app/client/src/pages/Editor/GuidedTour/Boxed.tsx index 74974601c2ac..b5284381df9f 100644 --- a/app/client/src/pages/Editor/GuidedTour/Boxed.tsx +++ b/app/client/src/pages/Editor/GuidedTour/Boxed.tsx @@ -1,11 +1,11 @@ import React from "react"; import { ReactNode } from "react"; +import { useSelector } from "react-redux"; import { forceShowContentSelector, getCurrentStep, inGuidedTour, } from "selectors/onboardingSelectors"; -import { useSelector } from "store"; type BoxedProps = { alternative?: JSX.Element; diff --git a/app/client/src/pages/Editor/GuidedTour/DeviationModal.tsx b/app/client/src/pages/Editor/GuidedTour/DeviationModal.tsx index 6e7b4526e40a..d32a4403fb0f 100644 --- a/app/client/src/pages/Editor/GuidedTour/DeviationModal.tsx +++ b/app/client/src/pages/Editor/GuidedTour/DeviationModal.tsx @@ -1,3 +1,4 @@ +import { useSelector } from "react-redux"; import { enableGuidedTour, toggleShowDeviationDialog, @@ -16,7 +17,6 @@ import { showDeviatingDialogSelector, showEndTourDialogSelector, } from "selectors/onboardingSelectors"; -import { useSelector } from "store"; import styled from "styled-components"; import AnalyticsUtil from "utils/AnalyticsUtil"; diff --git a/app/client/src/pages/Editor/GuidedTour/app.json b/app/client/src/pages/Editor/GuidedTour/app.json index 64d5968431de..9ae605c207c7 100644 --- a/app/client/src/pages/Editor/GuidedTour/app.json +++ b/app/client/src/pages/Editor/GuidedTour/app.json @@ -1,6 +1,6 @@ { "clientSchemaVersion": 1.0, - "serverSchemaVersion": 5.0, + "serverSchemaVersion": 6.0, "exportedApplication": { "name": "Customer Support Dashboard", "isPublic": false, @@ -63,7 +63,7 @@ "parentRowSpace": 1.0, "type": "CANVAS_WIDGET", "canExtend": true, - "version": 59.0, + "version": 67.0, "minHeight": 740.0, "parentColumnSpace": 1.0, "dynamicBindingPathList": [], @@ -90,301 +90,12 @@ "widgetId": "uhe8hpkvld", "isVisible": true, "fontStyle": "BOLD", - "version": 1.0, - "textColor": "#231F20", - "parentId": "0", - "isLoading": false, - "borderRadius": "0px", - "fontSize": "1.5rem" - }, - { - "boxShadow": "none", - "widgetName": "CustomersTable", - "defaultPageSize": 0.0, - "columnOrder": [ - "id", - "name", - "email", - "country", - "gender", - "latitude", - "longitude", - "dob", - "phone", - "image" - ], - "isVisibleDownload": true, - "dynamicPropertyPathList": [], - "displayName": "Table", - "iconSVG": "/static/media/icon.db8a9cbd.svg", - "topRow": 7.0, - "bottomRow": 67.0, - "parentRowSpace": 10.0, - "isSortable": true, - "type": "TABLE_WIDGET", - "defaultSelectedRow": "0", - "hideCard": false, - "parentColumnSpace": 17.28125, - "dynamicTriggerPathList": [ - { "key": "onRowSelected" }, - { "key": "onSearchTextChanged" } - ], - "dynamicBindingPathList": [ - { "key": "primaryColumns.id.computedValue" }, - { "key": "primaryColumns.name.computedValue" }, - { "key": "primaryColumns.gender.computedValue" }, - { "key": "primaryColumns.latitude.computedValue" }, - { "key": "primaryColumns.longitude.computedValue" }, - { "key": "primaryColumns.dob.computedValue" }, - { "key": "primaryColumns.phone.computedValue" }, - { "key": "primaryColumns.email.computedValue" }, - { "key": "primaryColumns.image.computedValue" }, - { "key": "primaryColumns.country.computedValue" }, - { "key": "accentColor" }, - { "key": "tableData" } - ], - "leftColumn": 0.0, - "primaryColumns": { - "id": { - "index": 0.0, - "width": 150.0, - "id": "id", - "horizontalAlignment": "LEFT", - "verticalAlignment": "CENTER", - "columnType": "text", - "textSize": "0.875rem", - "enableFilter": true, - "enableSort": true, - "isVisible": true, - "isDisabled": false, - "isCellVisible": true, - "isDerived": false, - "label": "id", - "computedValue": "{{CustomersTable.sanitizedTableData.map((currentRow) => ( currentRow.id))}}", - "borderRadius": "0px", - "boxShadow": "none" - }, - "name": { - "index": 1.0, - "width": 150.0, - "id": "name", - "horizontalAlignment": "LEFT", - "verticalAlignment": "CENTER", - "columnType": "text", - "textSize": "0.875rem", - "enableFilter": true, - "enableSort": true, - "isVisible": true, - "isDisabled": false, - "isCellVisible": true, - "isDerived": false, - "label": "name", - "computedValue": "{{CustomersTable.sanitizedTableData.map((currentRow) => ( currentRow.name))}}", - "borderRadius": "0px", - "boxShadow": "none" - }, - "gender": { - "index": 1.0, - "width": 150.0, - "id": "gender", - "horizontalAlignment": "LEFT", - "verticalAlignment": "CENTER", - "columnType": "text", - "textSize": "0.875rem", - "enableFilter": true, - "enableSort": true, - "isVisible": false, - "isDisabled": false, - "isCellVisible": true, - "isDerived": false, - "label": "gender", - "computedValue": "{{CustomersTable.sanitizedTableData.map((currentRow) => ( currentRow.gender))}}", - "borderRadius": "0px", - "boxShadow": "none" - }, - "latitude": { - "index": 2.0, - "width": 150.0, - "id": "latitude", - "horizontalAlignment": "LEFT", - "verticalAlignment": "CENTER", - "columnType": "text", - "textSize": "0.875rem", - "enableFilter": true, - "enableSort": true, - "isVisible": false, - "isDisabled": false, - "isCellVisible": true, - "isDerived": false, - "label": "latitude", - "computedValue": "{{CustomersTable.sanitizedTableData.map((currentRow) => ( currentRow.latitude))}}", - "borderRadius": "0px", - "boxShadow": "none" - }, - "longitude": { - "index": 3.0, - "width": 150.0, - "id": "longitude", - "horizontalAlignment": "LEFT", - "verticalAlignment": "CENTER", - "columnType": "text", - "textSize": "0.875rem", - "enableFilter": true, - "enableSort": true, - "isVisible": false, - "isDisabled": false, - "isCellVisible": true, - "isDerived": false, - "label": "longitude", - "computedValue": "{{CustomersTable.sanitizedTableData.map((currentRow) => ( currentRow.longitude))}}", - "borderRadius": "0px", - "boxShadow": "none" - }, - "dob": { - "index": 4.0, - "width": 150.0, - "id": "dob", - "horizontalAlignment": "LEFT", - "verticalAlignment": "CENTER", - "columnType": "text", - "textSize": "0.875rem", - "enableFilter": true, - "enableSort": true, - "isVisible": false, - "isDisabled": false, - "isCellVisible": true, - "isDerived": false, - "label": "dob", - "computedValue": "{{CustomersTable.sanitizedTableData.map((currentRow) => ( currentRow.dob))}}", - "borderRadius": "0px", - "boxShadow": "none" - }, - "phone": { - "index": 5.0, - "width": 150.0, - "id": "phone", - "horizontalAlignment": "LEFT", - "verticalAlignment": "CENTER", - "columnType": "text", - "textSize": "0.875rem", - "enableFilter": true, - "enableSort": true, - "isVisible": false, - "isDisabled": false, - "isCellVisible": true, - "isDerived": false, - "label": "phone", - "computedValue": "{{CustomersTable.sanitizedTableData.map((currentRow) => ( currentRow.phone))}}", - "borderRadius": "0px", - "boxShadow": "none" - }, - "email": { - "index": 6.0, - "width": 150.0, - "id": "email", - "horizontalAlignment": "LEFT", - "verticalAlignment": "CENTER", - "columnType": "text", - "textSize": "0.875rem", - "enableFilter": true, - "enableSort": true, - "isVisible": true, - "isDisabled": false, - "isCellVisible": true, - "isDerived": false, - "label": "email", - "computedValue": "{{CustomersTable.sanitizedTableData.map((currentRow) => ( currentRow.email))}}", - "borderRadius": "0px", - "boxShadow": "none" - }, - "image": { - "index": 7.0, - "width": 150.0, - "id": "image", - "horizontalAlignment": "LEFT", - "verticalAlignment": "CENTER", - "columnType": "text", - "textSize": "0.875rem", - "enableFilter": true, - "enableSort": true, - "isVisible": false, - "isDisabled": false, - "isCellVisible": true, - "isDerived": false, - "label": "image", - "computedValue": "{{CustomersTable.sanitizedTableData.map((currentRow) => ( currentRow.image))}}", - "borderRadius": "0px", - "boxShadow": "none" - }, - "country": { - "index": 8.0, - "width": 150.0, - "id": "country", - "horizontalAlignment": "LEFT", - "verticalAlignment": "CENTER", - "columnType": "text", - "textSize": "0.875rem", - "enableFilter": true, - "enableSort": true, - "isVisible": true, - "isDisabled": false, - "isCellVisible": true, - "isDerived": false, - "label": "country", - "computedValue": "{{CustomersTable.sanitizedTableData.map((currentRow) => ( currentRow.country))}}", - "borderRadius": "0px", - "boxShadow": "none" - } - }, - "delimiter": ",", - "onRowSelected": "", - "key": "gsiv88orej", - "derivedColumns": {}, - "labelTextSize": "0.875rem", - "rightColumn": 34.0, - "textSize": "0.875rem", - "widgetId": "rsrxzwk730", - "accentColor": "{{appsmith.theme.colors.primaryColor}}", - "isVisibleFilters": true, - "tableData": "{{getCustomers.data}}", - "isVisible": true, - "label": "Data", - "searchKey": "", - "version": 3.0, - "totalRecordsCount": 0.0, - "parentId": "0", - "renderMode": "CANVAS", - "isLoading": false, - "onSearchTextChanged": "", - "horizontalAlignment": "LEFT", - "isVisibleSearch": true, - "childStylesheet": { - "button": { - "buttonColor": "{{appsmith.theme.colors.primaryColor}}", - "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", - "boxShadow": "none" - }, - "menuButton": { - "menuColor": "{{appsmith.theme.colors.primaryColor}}", - "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", - "boxShadow": "none" - }, - "iconButton": { - "menuColor": "{{appsmith.theme.colors.primaryColor}}", - "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", - "boxShadow": "none" - } - }, + "version": 1.0, + "textColor": "#231F20", + "parentId": "0", + "isLoading": false, "borderRadius": "0px", - "isVisiblePagination": true, - "verticalAlignment": "CENTER", - "columnSizeMap": { - "task": 245.0, - "step": 62.0, - "status": 75.0, - "id": 60.0, - "email": 247.0 - } + "fontSize": "1.5rem" }, { "boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}", @@ -806,6 +517,375 @@ "renderMode": "CANVAS", "isLoading": false, "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}" + }, + { + "boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}", + "borderColor": "#E0DEDE", + "isVisibleDownload": true, + "iconSVG": "/static/media/icon.db8a9cbd2acd22a31ea91cc37ea2a46c.svg", + "topRow": 7.0, + "isSortable": true, + "type": "TABLE_WIDGET_V2", + "inlineEditingSaveOption": "ROW_LEVEL", + "animateLoading": true, + "dynamicBindingPathList": [ + { "key": "accentColor" }, + { "key": "borderRadius" }, + { "key": "boxShadow" }, + { "key": "childStylesheet.button.buttonColor" }, + { "key": "childStylesheet.button.borderRadius" }, + { "key": "childStylesheet.menuButton.menuColor" }, + { "key": "childStylesheet.menuButton.borderRadius" }, + { "key": "childStylesheet.iconButton.buttonColor" }, + { "key": "childStylesheet.iconButton.borderRadius" }, + { "key": "childStylesheet.editActions.saveButtonColor" }, + { "key": "childStylesheet.editActions.saveBorderRadius" }, + { "key": "childStylesheet.editActions.discardButtonColor" }, + { + "key": "childStylesheet.editActions.discardBorderRadius" + }, + { "key": "tableData" }, + { "key": "primaryColumns.id.computedValue" }, + { "key": "primaryColumns.gender.computedValue" }, + { "key": "primaryColumns.latitude.computedValue" }, + { "key": "primaryColumns.longitude.computedValue" }, + { "key": "primaryColumns.dob.computedValue" }, + { "key": "primaryColumns.phone.computedValue" }, + { "key": "primaryColumns.email.computedValue" }, + { "key": "primaryColumns.image.computedValue" }, + { "key": "primaryColumns.country.computedValue" }, + { "key": "primaryColumns.name.computedValue" } + ], + "leftColumn": 0.0, + "delimiter": ",", + "defaultSelectedRowIndex": 0.0, + "accentColor": "{{appsmith.theme.colors.primaryColor}}", + "isVisibleFilters": true, + "isVisible": true, + "enableClientSideSearch": true, + "version": 1.0, + "totalRecordsCount": 0.0, + "isLoading": false, + "childStylesheet": { + "button": { + "buttonColor": "{{appsmith.theme.colors.primaryColor}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "boxShadow": "none" + }, + "menuButton": { + "menuColor": "{{appsmith.theme.colors.primaryColor}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "boxShadow": "none" + }, + "iconButton": { + "buttonColor": "{{appsmith.theme.colors.primaryColor}}", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "boxShadow": "none" + }, + "editActions": { + "saveButtonColor": "{{appsmith.theme.colors.primaryColor}}", + "saveBorderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "discardButtonColor": "{{appsmith.theme.colors.primaryColor}}", + "discardBorderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}" + } + }, + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "defaultSelectedRowIndices": [0.0], + "widgetName": "CustomersTable", + "defaultPageSize": 0.0, + "columnOrder": [ + "id", + "name", + "email", + "country", + "gender", + "latitude", + "longitude", + "dob", + "phone", + "image" + ], + "dynamicPropertyPathList": [], + "displayName": "Table", + "bottomRow": 68.0, + "columnWidthMap": { + "task": 245.0, + "step": 62.0, + "status": 75.0, + "id": 60.0, + "email": 248.0 + }, + "parentRowSpace": 10.0, + "hideCard": false, + "parentColumnSpace": 17.9375, + "dynamicTriggerPathList": [], + "borderWidth": "1", + "primaryColumns": { + "id": { + "allowCellWrapping": false, + "index": 0.0, + "width": 150.0, + "originalId": "id", + "id": "id", + "alias": "id", + "horizontalAlignment": "LEFT", + "verticalAlignment": "CENTER", + "columnType": "number", + "textSize": "0.875rem", + "enableFilter": true, + "enableSort": true, + "isVisible": true, + "isDisabled": false, + "isCellEditable": false, + "isEditable": false, + "isCellVisible": true, + "isDerived": false, + "label": "id", + "isSaveVisible": true, + "isDiscardVisible": true, + "computedValue": "{{CustomersTable.processedTableData.map((currentRow, currentIndex) => ( currentRow[\"id\"]))}}", + "validation": {} + }, + "gender": { + "allowCellWrapping": false, + "index": 1.0, + "width": 150.0, + "originalId": "gender", + "id": "gender", + "alias": "gender", + "horizontalAlignment": "LEFT", + "verticalAlignment": "CENTER", + "columnType": "text", + "textSize": "0.875rem", + "enableFilter": true, + "enableSort": true, + "isVisible": false, + "isDisabled": false, + "isCellEditable": false, + "isEditable": false, + "isCellVisible": true, + "isDerived": false, + "label": "gender", + "isSaveVisible": true, + "isDiscardVisible": true, + "computedValue": "{{CustomersTable.processedTableData.map((currentRow, currentIndex) => ( currentRow[\"gender\"]))}}", + "validation": {} + }, + "latitude": { + "allowCellWrapping": false, + "index": 2.0, + "width": 150.0, + "originalId": "latitude", + "id": "latitude", + "alias": "latitude", + "horizontalAlignment": "LEFT", + "verticalAlignment": "CENTER", + "columnType": "text", + "textSize": "0.875rem", + "enableFilter": true, + "enableSort": true, + "isVisible": false, + "isDisabled": false, + "isCellEditable": false, + "isEditable": false, + "isCellVisible": true, + "isDerived": false, + "label": "latitude", + "isSaveVisible": true, + "isDiscardVisible": true, + "computedValue": "{{CustomersTable.processedTableData.map((currentRow, currentIndex) => ( currentRow[\"latitude\"]))}}", + "validation": {} + }, + "longitude": { + "allowCellWrapping": false, + "index": 3.0, + "width": 150.0, + "originalId": "longitude", + "id": "longitude", + "alias": "longitude", + "horizontalAlignment": "LEFT", + "verticalAlignment": "CENTER", + "columnType": "text", + "textSize": "0.875rem", + "enableFilter": true, + "enableSort": true, + "isVisible": false, + "isDisabled": false, + "isCellEditable": false, + "isEditable": false, + "isCellVisible": true, + "isDerived": false, + "label": "longitude", + "isSaveVisible": true, + "isDiscardVisible": true, + "computedValue": "{{CustomersTable.processedTableData.map((currentRow, currentIndex) => ( currentRow[\"longitude\"]))}}", + "validation": {} + }, + "dob": { + "allowCellWrapping": false, + "index": 4.0, + "width": 150.0, + "originalId": "dob", + "id": "dob", + "alias": "dob", + "horizontalAlignment": "LEFT", + "verticalAlignment": "CENTER", + "columnType": "date", + "textSize": "0.875rem", + "enableFilter": true, + "enableSort": true, + "isVisible": false, + "isDisabled": false, + "isCellEditable": false, + "isEditable": false, + "isCellVisible": true, + "isDerived": false, + "label": "dob", + "isSaveVisible": true, + "isDiscardVisible": true, + "computedValue": "{{CustomersTable.processedTableData.map((currentRow, currentIndex) => ( currentRow[\"dob\"]))}}", + "validation": {} + }, + "phone": { + "allowCellWrapping": false, + "index": 5.0, + "width": 150.0, + "originalId": "phone", + "id": "phone", + "alias": "phone", + "horizontalAlignment": "LEFT", + "verticalAlignment": "CENTER", + "columnType": "text", + "textSize": "0.875rem", + "enableFilter": true, + "enableSort": true, + "isVisible": false, + "isDisabled": false, + "isCellEditable": false, + "isEditable": false, + "isCellVisible": true, + "isDerived": false, + "label": "phone", + "isSaveVisible": true, + "isDiscardVisible": true, + "computedValue": "{{CustomersTable.processedTableData.map((currentRow, currentIndex) => ( currentRow[\"phone\"]))}}", + "validation": {} + }, + "email": { + "allowCellWrapping": false, + "index": 6.0, + "width": 150.0, + "originalId": "email", + "id": "email", + "alias": "email", + "horizontalAlignment": "LEFT", + "verticalAlignment": "CENTER", + "columnType": "text", + "textSize": "0.875rem", + "enableFilter": true, + "enableSort": true, + "isVisible": true, + "isDisabled": false, + "isCellEditable": false, + "isEditable": false, + "isCellVisible": true, + "isDerived": false, + "label": "email", + "isSaveVisible": true, + "isDiscardVisible": true, + "computedValue": "{{CustomersTable.processedTableData.map((currentRow, currentIndex) => ( currentRow[\"email\"]))}}", + "validation": {} + }, + "image": { + "allowCellWrapping": false, + "index": 7.0, + "width": 150.0, + "originalId": "image", + "id": "image", + "alias": "image", + "horizontalAlignment": "LEFT", + "verticalAlignment": "CENTER", + "columnType": "text", + "textSize": "0.875rem", + "enableFilter": true, + "enableSort": true, + "isVisible": false, + "isDisabled": false, + "isCellEditable": false, + "isEditable": false, + "isCellVisible": true, + "isDerived": false, + "label": "image", + "isSaveVisible": true, + "isDiscardVisible": true, + "computedValue": "{{CustomersTable.processedTableData.map((currentRow, currentIndex) => ( currentRow[\"image\"]))}}", + "validation": {} + }, + "country": { + "allowCellWrapping": false, + "index": 8.0, + "width": 150.0, + "originalId": "country", + "id": "country", + "alias": "country", + "horizontalAlignment": "LEFT", + "verticalAlignment": "CENTER", + "columnType": "text", + "textSize": "0.875rem", + "enableFilter": true, + "enableSort": true, + "isVisible": true, + "isDisabled": false, + "isCellEditable": false, + "isEditable": false, + "isCellVisible": true, + "isDerived": false, + "label": "country", + "isSaveVisible": true, + "isDiscardVisible": true, + "computedValue": "{{CustomersTable.processedTableData.map((currentRow, currentIndex) => ( currentRow[\"country\"]))}}", + "validation": {} + }, + "name": { + "allowCellWrapping": false, + "index": 9.0, + "width": 150.0, + "originalId": "name", + "id": "name", + "alias": "name", + "horizontalAlignment": "LEFT", + "verticalAlignment": "CENTER", + "columnType": "text", + "textSize": "0.875rem", + "enableFilter": true, + "enableSort": true, + "isVisible": true, + "isDisabled": false, + "isCellEditable": false, + "isEditable": false, + "isCellVisible": true, + "isDerived": false, + "label": "name", + "isSaveVisible": true, + "isDiscardVisible": true, + "computedValue": "{{CustomersTable.processedTableData.map((currentRow, currentIndex) => ( currentRow[\"name\"]))}}", + "validation": {} + } + }, + "key": "hlupn299rl", + "isDeprecated": false, + "rightColumn": 34.0, + "textSize": "0.875rem", + "widgetId": "aqpqbznlzl", + "tableData": "{{getCustomers.data}}", + "label": "Data", + "searchKey": "", + "parentId": "0", + "renderMode": "CANVAS", + "horizontalAlignment": "LEFT", + "isVisibleSearch": true, + "isVisiblePagination": true, + "verticalAlignment": "CENTER" } ] }, @@ -821,6 +901,7 @@ } ] ], + "layoutOnLoadActionErrors": [], "validOnPageLoadActions": true, "id": "Page1", "deleted": false, @@ -1646,6 +1727,7 @@ "paginationType": "NONE", "encodeParamsToggle": true, "body": "SELECT * FROM users ORDER BY id LIMIT 20;", + "selfReferencingDataPaths": [], "pluginSpecifiedTemplates": [{ "value": false }] }, "executeOnLoad": true, @@ -1677,6 +1759,7 @@ "paginationType": "NONE", "encodeParamsToggle": true, "body": "SELECT * FROM users ORDER BY id LIMIT 20;", + "selfReferencingDataPaths": [], "pluginSpecifiedTemplates": [{ "value": false }] }, "executeOnLoad": true, @@ -1715,6 +1798,7 @@ "paginationType": "NONE", "encodeParamsToggle": true, "body": "UPDATE users SET name = '{{NameInput.text}}', email = '{{EmailInput.text}}', country = '{{CountryInput.text}}' WHERE id = {{CustomersTable.selectedRow.id}}", + "selfReferencingDataPaths": [], "pluginSpecifiedTemplates": [{ "value": false }] }, "executeOnLoad": false, @@ -1751,6 +1835,7 @@ "paginationType": "NONE", "encodeParamsToggle": true, "body": "UPDATE users SET name = '{{NameInput.text}}', email = '{{EmailInput.text}}', country = '{{CountryInput.text}}' WHERE id = {{CustomersTable.selectedRow.id}}", + "selfReferencingDataPaths": [], "pluginSpecifiedTemplates": [{ "value": false }] }, "executeOnLoad": false, diff --git a/app/client/src/pages/Editor/IntegrationEditor/ActiveDataSources.tsx b/app/client/src/pages/Editor/IntegrationEditor/ActiveDataSources.tsx index 29913ca8279a..7f5a04739ac4 100644 --- a/app/client/src/pages/Editor/IntegrationEditor/ActiveDataSources.tsx +++ b/app/client/src/pages/Editor/IntegrationEditor/ActiveDataSources.tsx @@ -11,6 +11,8 @@ import { createMessage, EMPTY_ACTIVE_DATA_SOURCES, } from "@appsmith/constants/messages"; +import { hasCreateDatasourcePermission } from "@appsmith/utils/permissionHelpers"; +import { getCurrentAppWorkspace } from "@appsmith/selectors/workspaceSelectors"; const QueryHomePage = styled.div` ${thinScrollbar}; @@ -58,6 +60,14 @@ function ActiveDataSources(props: ActiveDataSourcesProps) { }); const pluginGroups = useMemo(() => keyBy(plugins, "id"), [plugins]); + const userWorkspacePermissions = useSelector( + (state: AppState) => getCurrentAppWorkspace(state).userPermissions ?? [], + ); + + const canCreateDatasource = hasCreateDatasourcePermission( + userWorkspacePermissions, + ); + if (dataSources.length === 0) { return ( @@ -65,6 +75,7 @@ function ActiveDataSources(props: ActiveDataSourcesProps) { {createMessage(EMPTY_ACTIVE_DATA_SOURCES)}  {(!datasource.isConfigured || supportTemplateGeneration) && ( )} - { - e.stopPropagation(); - }} - > - - - - } + {(canDeleteDatasource || canEditDatasource) && ( + { + e.stopPropagation(); + }} > - - { - if (!isDeletingDatasource) { - confirmDelete ? deleteAction() : setConfirmDelete(true); - } - }} - text={ - isDeletingDatasource - ? createMessage(CONFIRM_CONTEXT_DELETING) - : confirmDelete - ? createMessage(CONFIRM_CONTEXT_DELETE) - : createMessage(CONTEXT_DELETE) + + + } - /> - - + > + {canEditDatasource && ( + + )} + {canDeleteDatasource && ( + { + if (!isDeletingDatasource) { + confirmDelete + ? deleteAction() + : setConfirmDelete(true); + } + }} + text={ + isDeletingDatasource + ? createMessage(CONFIRM_CONTEXT_DELETING) + : confirmDelete + ? createMessage(CONFIRM_CONTEXT_DELETE) + : createMessage(CONTEXT_DELETE) + } + /> + )} + + + )} @@ -375,7 +408,10 @@ function DatasourceCard(props: DatasourceCardProps) { > - {renderDatasourceSection(currentFormConfig[0], datasource)} + diff --git a/app/client/src/pages/Editor/IntegrationEditor/DatasourceHome.tsx b/app/client/src/pages/Editor/IntegrationEditor/DatasourceHome.tsx index 1ef78008694a..c3b050b82e45 100644 --- a/app/client/src/pages/Editor/IntegrationEditor/DatasourceHome.tsx +++ b/app/client/src/pages/Editor/IntegrationEditor/DatasourceHome.tsx @@ -5,7 +5,10 @@ import { initialize } from "redux-form"; import { getDBPlugins, getPluginImages } from "selectors/entitiesSelector"; import { Plugin } from "api/PluginApi"; import { DATASOURCE_DB_FORM } from "@appsmith/constants/forms"; -import { createDatasourceFromForm } from "actions/datasourceActions"; +import { + createDatasourceFromForm, + createTempDatasourceFromForm, +} from "actions/datasourceActions"; import { AppState } from "@appsmith/reducers"; import AnalyticsUtil from "utils/AnalyticsUtil"; import { getCurrentApplication } from "selectors/applicationSelectors"; @@ -119,6 +122,7 @@ interface DatasourceHomeScreenProps { interface ReduxDispatchProps { initializeForm: (data: Record) => void; createDatasource: (data: any) => void; + createTempDatasource: (data: any) => void; } interface ReduxStateProps { @@ -176,7 +180,7 @@ class DatasourceHomeScreen extends React.Component { } } - this.props.createDatasource({ + this.props.createTempDatasource({ pluginId, }); }; @@ -239,6 +243,8 @@ const mapDispatchToProps = (dispatch: any) => { initializeForm: (data: Record) => dispatch(initialize(DATASOURCE_DB_FORM, data)), createDatasource: (data: any) => dispatch(createDatasourceFromForm(data)), + createTempDatasource: (data: any) => + dispatch(createTempDatasourceFromForm(data)), }; }; diff --git a/app/client/src/pages/Editor/IntegrationEditor/IntegrationsHomeScreen.tsx b/app/client/src/pages/Editor/IntegrationEditor/IntegrationsHomeScreen.tsx index d4cb6b676118..807408cb0f5e 100644 --- a/app/client/src/pages/Editor/IntegrationEditor/IntegrationsHomeScreen.tsx +++ b/app/client/src/pages/Editor/IntegrationEditor/IntegrationsHomeScreen.tsx @@ -22,6 +22,9 @@ import { getQueryParams } from "utils/URLUtils"; import { getIsGeneratePageInitiator } from "utils/GenerateCrudUtil"; import { getCurrentApplicationId } from "selectors/editorSelectors"; import { integrationEditorURL } from "RouteBuilder"; +import { getCurrentAppWorkspace } from "@appsmith/selectors/workspaceSelectors"; + +import { hasCreateDatasourcePermission } from "@appsmith/utils/permissionHelpers"; const HeaderFlex = styled.div` display: flex; @@ -95,6 +98,7 @@ type IntegrationsHomeScreenProps = { dataSources: Datasource[]; mockDatasources: MockDatasource[]; applicationId: string; + canCreateDatasource?: boolean; }; type IntegrationsHomeScreenState = { @@ -107,21 +111,6 @@ type IntegrationsHomeScreenState = { type Props = IntegrationsHomeScreenProps & InjectedFormProps<{ category: string }, IntegrationsHomeScreenProps>; -const PRIMARY_MENU: TabProp[] = [ - { - key: "ACTIVE", - title: "Active", - panelComponent:
, - }, - { - key: "CREATE_NEW", - title: "Create New", - panelComponent:
, - icon: "plus", - iconSize: IconSize.XS, - }, -]; - const PRIMARY_MENU_IDS = { ACTIVE: 0, CREATE_NEW: 1, @@ -215,6 +204,7 @@ function CreateNewAPI({ }: any) { const newAPIRef = useRef(null); const isMounted = useRef(false); + useEffect(() => { if (active && newAPIRef.current) { isMounted.current && @@ -406,11 +396,37 @@ class IntegrationsHomeScreen extends React.Component< }; render() { - const { dataSources, history, isCreating, location, pageId } = this.props; + const { + canCreateDatasource = false, + dataSources, + history, + isCreating, + location, + pageId, + } = this.props; const { unsupportedPluginDialogVisible } = this.state; let currentScreen; const { activePrimaryMenuId, activeSecondaryMenuId } = this.state; + const PRIMARY_MENU: TabProp[] = [ + { + key: "ACTIVE", + title: "Active", + panelComponent:
, + }, + ...(canCreateDatasource + ? [ + { + key: "CREATE_NEW", + title: "Create New", + panelComponent:
, + icon: "plus", + iconSize: IconSize.XS, + }, + ] + : []), + ].filter(Boolean); + const isGeneratePageInitiator = getIsGeneratePageInitiator(); // Avoid user to switch tabs when in generate page flow by hiding the tabs itself. const showTabs = !isGeneratePageInitiator; @@ -530,11 +546,18 @@ class IntegrationsHomeScreen extends React.Component< } const mapStateToProps = (state: AppState) => { + const userWorkspacePermissions = + getCurrentAppWorkspace(state).userPermissions ?? []; + + const canCreateDatasource = hasCreateDatasourcePermission( + userWorkspacePermissions, + ); return { dataSources: getDatasources(state), mockDatasources: getMockDatasources(state), isCreating: state.ui.apiPane.isCreating, applicationId: getCurrentApplicationId(state), + canCreateDatasource, }; }; diff --git a/app/client/src/pages/Editor/IntegrationEditor/NewApi.tsx b/app/client/src/pages/Editor/IntegrationEditor/NewApi.tsx index f6a707504214..de9402f7e070 100644 --- a/app/client/src/pages/Editor/IntegrationEditor/NewApi.tsx +++ b/app/client/src/pages/Editor/IntegrationEditor/NewApi.tsx @@ -1,7 +1,10 @@ import React, { useCallback, useEffect, useState } from "react"; import { connect, useSelector } from "react-redux"; import styled from "styled-components"; -import { createDatasourceFromForm } from "actions/datasourceActions"; +import { + createDatasourceFromForm, + createTempDatasourceFromForm, +} from "actions/datasourceActions"; import { AppState } from "@appsmith/reducers"; import { Colors } from "constants/Colors"; import CurlLogo from "assets/images/Curl-logo.svg"; @@ -138,6 +141,7 @@ type ApiHomeScreenProps = { createDatasourceFromForm: (data: any) => void; isCreating: boolean; showUnsupportedPluginDialog: (callback: any) => void; + createTempDatasourceFromForm: (data: any) => void; }; type Props = ApiHomeScreenProps; @@ -172,11 +176,11 @@ function NewApiScreen(props: Props) { pluginName: authApiPlugin.name, pluginPackageName: authApiPlugin.packageName, }); - props.createDatasourceFromForm({ + props.createTempDatasourceFromForm({ pluginId: authApiPlugin.id, }); } - }, [authApiPlugin, props.createDatasourceFromForm]); + }, [authApiPlugin, props.createTempDatasourceFromForm]); const handleCreateNew = (source: string) => { AnalyticsUtil.logEvent("CREATE_DATA_SOURCE_CLICK", { @@ -237,7 +241,10 @@ function NewApiScreen(props: Props) { break; } case API_ACTION.CREATE_DATASOURCE_FORM: { - props.createDatasourceFromForm({ pluginId: params.pluginId }); + props.createTempDatasourceFromForm({ + pluginId: params.pluginId, + type: params.type, + }); break; } case API_ACTION.AUTH_API: { @@ -274,7 +281,7 @@ function NewApiScreen(props: Props) { src={PlusLogo} />
-

Create new API

+

REST API

{isCreating && } @@ -322,7 +329,7 @@ function NewApiScreen(props: Props) { src={PlusLogo} />
-

Create new GraphQL API

+

GraphQL API

{API_PLUGINS.map((p) => ( @@ -365,6 +372,7 @@ const mapStateToProps = (state: AppState) => ({ const mapDispatchToProps = { createNewApiAction, createDatasourceFromForm, + createTempDatasourceFromForm, }; export default connect(mapStateToProps, mapDispatchToProps)(NewApiScreen); diff --git a/app/client/src/pages/Editor/IntegrationEditor/UnsupportedPluginDialog.tsx b/app/client/src/pages/Editor/IntegrationEditor/UnsupportedPluginDialog.tsx index 69b337463214..bc9b2957709f 100644 --- a/app/client/src/pages/Editor/IntegrationEditor/UnsupportedPluginDialog.tsx +++ b/app/client/src/pages/Editor/IntegrationEditor/UnsupportedPluginDialog.tsx @@ -155,7 +155,7 @@ function UnsupportedPluginDialog(props: Props) { { AnalyticsUtil.logEvent("UNSUPPORTED_PLUGIN_DIALOG_BACK_ACTION"); handleClose(); diff --git a/app/client/src/pages/Editor/JSEditor/Form.tsx b/app/client/src/pages/Editor/JSEditor/Form.tsx index 56a2185fd938..306b7b53c586 100644 --- a/app/client/src/pages/Editor/JSEditor/Form.tsx +++ b/app/client/src/pages/Editor/JSEditor/Form.tsx @@ -60,6 +60,11 @@ import { } from "./styledComponents"; import { getJSPaneConfigSelectedTabIndex } from "selectors/jsPaneSelectors"; import { EventLocation } from "utils/AnalyticsUtil"; +import { + hasDeleteActionPermission, + hasExecuteActionPermission, + hasManageActionPermission, +} from "@appsmith/utils/permissionHelpers"; import { executeCommandAction } from "../../../actions/apiPaneActions"; import { SlashCommand } from "../../../entities/Action"; @@ -215,6 +220,16 @@ function JSEditorForm({ jsCollection: currentJSCollection }: Props) { return []; }, [selectedJSActionOption.label, currentJSCollection.name]); + const isChangePermitted = hasManageActionPermission( + currentJSCollection?.userPermissions || [], + ); + const isExecutePermitted = hasExecuteActionPermission( + currentJSCollection?.userPermissions || [], + ); + const isDeletePermitted = hasDeleteActionPermission( + currentJSCollection?.userPermissions || [], + ); + const selectedConfigTab = useSelector(getJSPaneConfigSelectedTabIndex); const setSelectedConfigTab = useCallback((selectedIndex: number) => { @@ -234,12 +249,17 @@ function JSEditorForm({ jsCollection: currentJSCollection }: Props) {
- + @@ -259,7 +279,7 @@ function JSEditorForm({ jsCollection: currentJSCollection }: Props) { }} /> + ), }, ]} @@ -321,7 +345,7 @@ function JSEditorForm({ jsCollection: currentJSCollection }: Props) { ` @@ -120,7 +122,7 @@ function SettingsHeading({ grow, hasInfo, info, text }: SettingsHeadingProps) { ); } -function SettingsItem({ action }: SettingsItemProps) { +function SettingsItem({ action, disabled = false }: SettingsItemProps) { const dispatch = useDispatch(); const [executeOnPageLoad, setExecuteOnPageLoad] = useState( String(!!action.executeOnLoad), @@ -169,6 +171,7 @@ function SettingsItem({ action }: SettingsItemProps) { action.actionConfiguration.isAsync, ); @@ -208,7 +215,7 @@ function JSFunctionSettingsView({ actions }: JSFunctionSettingsProps) { {asyncActions && asyncActions.length ? ( asyncActions.map((action) => ( - + )) ) : ( diff --git a/app/client/src/pages/Editor/JSEditor/JSObjectNameEditor.tsx b/app/client/src/pages/Editor/JSEditor/JSObjectNameEditor.tsx index 3c3d82f982a5..6beff19c5bd0 100644 --- a/app/client/src/pages/Editor/JSEditor/JSObjectNameEditor.tsx +++ b/app/client/src/pages/Editor/JSEditor/JSObjectNameEditor.tsx @@ -54,6 +54,7 @@ type JSObjectNameEditorProps = { In future, when default component will be ads editable-text, then we can remove this prop. */ page?: string; + disabled?: boolean; }; const JSIconWrapper = styled.img` @@ -108,6 +109,7 @@ export function JSObjectNameEditor(props: JSObjectNameEditorProps) { defaultValue={ currentJSObjectConfig ? currentJSObjectConfig.name : "" } + disabled={props.disabled} editInteractionKind={EditInteractionKind.SINGLE} errorTooltipClass="t--action-name-edit-error" forceDefault={forceUpdate} diff --git a/app/client/src/pages/Editor/MainContainer.test.tsx b/app/client/src/pages/Editor/MainContainer.test.tsx index 064cda8fe3a6..0ada84e3a557 100644 --- a/app/client/src/pages/Editor/MainContainer.test.tsx +++ b/app/client/src/pages/Editor/MainContainer.test.tsx @@ -15,6 +15,7 @@ import { MockApplication, mockCreateCanvasWidget, mockGetCanvasWidgetDsl, + mockGetPagePermissions, mockGetWidgetEvalValues, syntheticTestMouseEvent, } from "test/testCommon"; @@ -573,7 +574,9 @@ describe("Drag and Drop widgets into Main container", () => { }); spyGetCanvasWidgetDsl.mockImplementation(mockGetCanvasWidgetDsl); mockGetIsFetchingPage.mockImplementation(() => false); - + jest + .spyOn(utilities, "getPagePermissions") + .mockImplementation(mockGetPagePermissions); const component = render( { - setSidebarWidth(newWidth); + dispatch(updateExplorerWidthAction(newWidth)); }, []); /** @@ -56,15 +55,15 @@ function MainContainer() { const isPreviewMode = useSelector(previewModeSelector); - const location = useLocation(); + const location = useLocation(); useEffect(() => { - dispatch(routeChanged(location.pathname, location.hash)); + dispatch(routeChanged(location)); }, [location.pathname, location.hash]); return ( <> - + +
setFocusedIndex(selectedIndex)} diff --git a/app/client/src/pages/Editor/PageListSidebar/PageListItem.tsx b/app/client/src/pages/Editor/PageListSidebar/PageListItem.tsx deleted file mode 100644 index 34e95c5bfdb0..000000000000 --- a/app/client/src/pages/Editor/PageListSidebar/PageListItem.tsx +++ /dev/null @@ -1,120 +0,0 @@ -import React from "react"; -import styled, { css, withTheme } from "styled-components"; -import LetterIcon from "components/editorComponents/LetterIcon"; -import ContextDropdown, { - ContextDropdownOption, -} from "components/editorComponents/ContextDropdown"; -import { MenuIcons } from "icons/MenuIcons"; -import { Theme } from "constants/DefaultTheme"; -import EditableText, { - EditInteractionKind, -} from "components/editorComponents/EditableText"; -import AnalyticsUtil from "utils/AnalyticsUtil"; -import { Tooltip } from "@blueprintjs/core"; - -/** Page List Item */ -export const PageListItemCSS = css` - font-size: ${(props) => props.theme.fontSizes[3]}px; - height: 40px; - cursor: pointer; - margin: 0px; - padding: 0px 30px; - display: flex; - align-items: center; -`; - -const PageListItemActiveCSS = css` - background: ${(props) => props.theme.colors.paneInputBG}; -`; - -const PageListItemWrapper = styled.div<{ active: boolean }>` - && { - position: relative; - ${PageListItemCSS} - ${(props) => (props.active ? PageListItemActiveCSS : "")} - &:hover { - ${PageListItemActiveCSS} - } - & > div { - width: 100%; - display: flex; - justify-content: flex-start; - align-items: center; - & > div:first-of-type { - margin-right: 10px; - } - & > div:last-of-type { - margin: 0px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - & div > svg > path:first-of-type { - fill: ${(props) => props.theme.colors.primaryOld}; - } - } - & .more { - position: absolute; - right: 10px; - top: 10px; - } - } -`; -type PageListItemProps = { - name: string; - id: string; - isDefault: boolean; - updatePage: (pageId: string, name: string) => void; - switchPage: (pageId: string) => void; - active: boolean; - contextActions: ContextDropdownOption[]; - theme: Theme; -}; -const PageListItem = withTheme((props: PageListItemProps) => { - const onEditPageName = (name: string) => { - props.updatePage(props.id, name); - AnalyticsUtil.logEvent("PAGE_RENAME", { - pageName: props.name, - newName: name, - pageId: props.id, - }); - }; - const pageIcon = props.isDefault ? ( - MenuIcons.HOMEPAGE_ICON({ width: 28, height: 28 }) - ) : ( - - ); - return ( - props.switchPage(props.id)} - > -
- {pageIcon} - - - -
- -
- ); -}); - -export default PageListItem; diff --git a/app/client/src/pages/Editor/PagesEditor/ContextMenu.tsx b/app/client/src/pages/Editor/PagesEditor/ContextMenu.tsx deleted file mode 100644 index 543525272b48..000000000000 --- a/app/client/src/pages/Editor/PagesEditor/ContextMenu.tsx +++ /dev/null @@ -1,222 +0,0 @@ -import { get, noop } from "lodash"; -import React, { useCallback, useState } from "react"; -import styled, { useTheme } from "styled-components"; - -import "@blueprintjs/popover2/lib/css/blueprint-popover2.css"; -import { Popover2 } from "@blueprintjs/popover2"; - -import { ControlIcons } from "icons/ControlIcons"; -import { FormIcons } from "icons/FormIcons"; -import { Page } from "@appsmith/constants/ReduxActionConstants"; -import { Toggle } from "design-system"; -import { Action } from "./PageListItem"; -import EditName from "./EditName"; -import { useSelector } from "react-redux"; - -import { getCurrentApplicationId } from "selectors/editorSelectors"; -import { Colors } from "constants/Colors"; -import { TooltipComponent } from "design-system"; -import { createMessage, SETTINGS_TOOLTIP } from "@appsmith/constants/messages"; -import { TOOLTIP_HOVER_ON_DELAY } from "constants/AppConstants"; - -// render over popover portals -const Container = styled.div` - padding: 12px; - padding-top: 6px; - - /* min width to be 280px i.e. 17.5rem to wrap long page names */ - max-width: inherit; - min-width: 17.5rem; - - background-color: ${Colors.GREY_1}; - - h4 { - margin: 0; - margin-top: 8px; - margin-bottom: 10px; - font-weight: normal; - font-size: 16px; - } - - main { - padding: 4px; - } - - & .ContextMenuPopOver .ContextMenu { - width: 10rem; - } - - & .editing { - width: 10rem; - } -`; - -const Header = styled.div` - display: flex; - align-items: center; -`; - -const MenuItem = styled.div` - display: flex; - margin-top: 14px; - align-items: center; - - & > div { - flex-grow: 1; - font-size: 14px; - } -`; - -const MenuItemToggle = styled(Toggle)` - flex-basis: 48px; - height: 23px; - transform: scale(0.85); - - input:checked + .slider { - background-color: ${Colors.GREY_10}; - } -`; - -const Actions = styled.div` - display: flex; - align-items: center; - - & > button { - margin-left: 0px; - } -`; - -const PageName = styled.div` - flex-grow: 1; - - & > h1 { - font-weight: normal; - margin: 0; - font-size: 14px; - width: 150px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } -`; - -const DeleteIcon = FormIcons.DELETE_ICON; -const CloseIcon = ControlIcons.CLOSE_CONTROL; -const CopyIcon = ControlIcons.COPY_CONTROL; -const SettingsIcon = ControlIcons.SETTINGS_CONTROL; - -type Props = { - page: Page; - onSetPageHidden: () => void; - onCopy: (pageId: string) => void; - onDelete: (pageId: string, pageName: string) => void; - onSetPageDefault: (pageId: string, applicationId?: string) => void; -}; - -function ContextMenu(props: Props) { - const { onCopy, onDelete, onSetPageDefault, onSetPageHidden, page } = props; - const theme = useTheme(); - const [isOpen, setIsOpen] = useState(false); - - const applicationId = useSelector(getCurrentApplicationId); - - /** - * opens the context menu on interaction ( on click ) - */ - const handleInteraction = useCallback((isOpen) => { - setIsOpen(isOpen); - }, []); - - return ( - -
- -
- -
-
- - - onCopy(page.pageId)} - width={16} - /> - - - onDelete(page.pageId, page.pageName)} - width={16} - /> - - - setIsOpen(false)} - width={16} - /> - - -
-
-

General

- {!page.isDefault && ( - -
Set Homepage
- onSetPageDefault(page.pageId, applicationId)} - value={page.isDefault} - /> -
- )} - - -
Visible
- -
-
- - } - isOpen={isOpen} - minimal - onInteraction={handleInteraction} - placement="bottom-start" - portalClassName="pages-editor-context-menu" - > - - - - - -
- ); -} - -export default ContextMenu; diff --git a/app/client/src/pages/Editor/PagesEditor/EditName.tsx b/app/client/src/pages/Editor/PagesEditor/EditName.tsx deleted file mode 100644 index e8fd17c9dac3..000000000000 --- a/app/client/src/pages/Editor/PagesEditor/EditName.tsx +++ /dev/null @@ -1,128 +0,0 @@ -import { get, noop } from "lodash"; -import { useDispatch, useSelector } from "react-redux"; -import styled, { useTheme } from "styled-components"; -import { useHistory } from "react-router"; -import React, { useCallback, useState, useRef } from "react"; - -import useClick from "utils/hooks/useClick"; -import { updatePage } from "actions/pageActions"; -import { MenuIcons } from "icons/MenuIcons"; -import { resolveAsSpaceChar } from "utils/helpers"; -import { Page } from "@appsmith/constants/ReduxActionConstants"; -import EditNameInput from "pages/Editor/Explorer/Entity/Name"; -import { getCurrentApplicationId } from "selectors/editorSelectors"; -import { TooltipComponent } from "design-system"; -import { createMessage, GO_TO_PAGE } from "@appsmith/constants/messages"; -import { TOOLTIP_HOVER_ON_DELAY } from "constants/AppConstants"; -import { builderURL } from "RouteBuilder"; - -const LinkIcon = MenuIcons.LINK_ICON; - -export const EditNameContainer = styled.div` - flex-grow: 1; - display: flex; - align-items: center; - padding-left: 4px; - - & > .page-list-item-edit-icon { - visibility: hidden; /* Hide the visiblity when not hovered */ - margin-left: 8px; - align-items: center; - } - - &:hover .page-list-item-edit-icon { - visibility: visible; /* on Hover display the element */ - } - - & > div { - display: flex; - min-height: 36px; - align-items: center; - } - - & > div:hover { - text-decoration: underline; - } - - & > div:first-child { - &:hover { - cursor: pointer; - } - } -`; - -type Props = { - page: Page; -}; - -function EditName(props: Props) { - const { page } = props; - const theme = useTheme(); - const dispatch = useDispatch(); - const history = useHistory(); - const [isEditing, setIsEditing] = useState(false); - const applicationId = useSelector(getCurrentApplicationId); - - const updateNameCallback = useCallback( - (name: string) => { - return updatePage(page.pageId, name, !!page.isHidden); - }, - [dispatch], - ); - - const exitEditMode = useCallback(() => { - setIsEditing(false); - }, []); - - const enterEditMode = useCallback(() => setIsEditing(true), []); - - const switchPage = useCallback(() => { - if (!!applicationId && !isEditing) { - history.push( - builderURL({ - pageId: props.page.pageId, - }), - ); - } - }, [props.page.pageId, applicationId]); - - const handleClick = () => { - if (!isEditing) enterEditMode(); - }; - - const itemRef = useRef(null); - useClick(itemRef, handleClick, noop); - - return ( - - - {!isEditing && ( -
- - - -
- )} -
- ); -} - -export default EditName; diff --git a/app/client/src/pages/Editor/PagesEditor/PageListItem.tsx b/app/client/src/pages/Editor/PagesEditor/PageListItem.tsx deleted file mode 100644 index 2f22455d6545..000000000000 --- a/app/client/src/pages/Editor/PagesEditor/PageListItem.tsx +++ /dev/null @@ -1,325 +0,0 @@ -import { get } from "lodash"; -import { useDispatch, useSelector } from "react-redux"; -import styled, { useTheme } from "styled-components"; -import React, { useCallback, DragEvent, useState, useEffect } from "react"; - -import { - updatePage, - clonePageInit, - deletePage, - setPageAsDefault, - setPageSlug, -} from "actions/pageActions"; -import EditName from "./EditName"; -import ContextMenu from "./ContextMenu"; -import { FormIcons } from "icons/FormIcons"; -import { ControlIcons } from "icons/ControlIcons"; - -import { Page } from "@appsmith/constants/ReduxActionConstants"; -import AnalyticsUtil from "utils/AnalyticsUtil"; -import { Colors } from "constants/Colors"; -import { MenuIcons } from "icons/MenuIcons"; -import { Button, Category, TextInput, TooltipComponent } from "design-system"; -import { - CLONE_TOOLTIP, - createMessage, - DEFAULT_PAGE_TOOLTIP, - DELETE_TOOLTIP, - HIDDEN_TOOLTIP, -} from "@appsmith/constants/messages"; -import { TOOLTIP_HOVER_ON_DELAY } from "constants/AppConstants"; - -import { - getCurrentApplicationId, - selectApplicationVersion, -} from "selectors/editorSelectors"; -import { ApplicationVersion } from "actions/applicationActions"; -import { AppState } from "@appsmith/reducers"; - -export const Container = styled.div` - display: flex; - width: 100%; - align-items: center; - cursor: grab; - - &:focus, - &:active { - cursor: grabbing; - } -`; - -export const ListItem = styled.div` - display: flex; - width: 100%; - align-items: center; - padding: 10px; - background-color: ${Colors.GREY_1}; - .url { - background-color: ${Colors.GREY_3}; - padding: 2px 4px; - } -`; - -const Actions = styled.div` - display: flex; - align-items: center; -`; - -export const Action = styled.button` - cursor: pointer; - height: 28px; - width: 28px; - display: flex; - align-items: center; - justify-content: center; - outline: none; - border: none; - background: transparent; - margin-left: 4px; - - &:focus { - outline: none; - } -`; - -const DefaultPageIcon = MenuIcons.DEFAULT_HOMEPAGE_ICON; -const DeleteIcon = FormIcons.DELETE_ICON; -const CopyIcon = ControlIcons.COPY_CONTROL; -const DragIcon = ControlIcons.DRAG_CONTROL; -const HideIcon = ControlIcons.HIDE_COLUMN; - -export interface PageListItemProps { - item: Page; -} - -const disableDrag = { - // Draggable true is required to invoke onDragStart - draggable: true, - onDragStart: (e: DragEvent) => { - // Stop drag event propagation to prevent click events - e.stopPropagation(); - }, -}; - -function PageListItem(props: PageListItemProps) { - const theme = useTheme(); - const { item } = props; - const dispatch = useDispatch(); - const applicationId = useSelector(getCurrentApplicationId); - /** - * clones the page - * - * @return void - */ - const clonePageCallback = useCallback((): void => { - dispatch(clonePageInit(item.pageId, true)); - }, [dispatch, item]); - - /** - * delete the page - * - * @return void - */ - const deletePageCallback = useCallback((): void => { - dispatch(deletePage(item.pageId)); - - AnalyticsUtil.logEvent("DELETE_PAGE", { - pageName: item.pageName, - }); - }, [dispatch, item]); - - /** - * sets the page as default - * - * @return void - */ - const setPageAsDefaultCallback = useCallback((): void => { - dispatch(setPageAsDefault(item.pageId, applicationId)); - }, [dispatch, item, applicationId]); - - /** - * sets the page hidden - * - * @return void - */ - const setPageHidden = useCallback(() => { - return dispatch(updatePage(item.pageId, item.pageName, !item.isHidden)); - }, [dispatch, item]); - - return ( - - - -
-
- - - {item.isDefault && ( - - - - - - )} - {item.isHidden && ( - - - - - - )} - - - - - - - - - - - - -
- {/* */} -
- {/* Disabling drag on action items as attempting to drag also invokes the click event. - Clicks events in child elements could be disabled once we upgrade react-use-gesture to - the latest version */} -
-
- ); -} - -type CustomURLSlugProp = { - page: Page; -}; - -const isPageLoading = (pageId: string) => (state: AppState) => - state.entities.pageList.loading[pageId]; - -export function CustomURLSlug(props: CustomURLSlugProp) { - const { page } = props; - const applicationVersion = useSelector(selectApplicationVersion); - const [customSlug, setCustomSlug] = useState(page.customSlug || ""); - const [isSlugValid, setIsSlugValid] = useState(true); - const dispatch = useDispatch(); - const isLoading = useSelector(isPageLoading(page.pageId)); - - useEffect(() => { - setCustomSlug(page.customSlug || ""); - }, [page.customSlug]); - - const noSpecialCharactersValidator = useCallback( - (text: string) => { - const noSpecialCharacters = /^[A-Za-z0-9\-]+$/; - const isValid = !text || noSpecialCharacters.test(text); - setIsSlugValid(isValid); - return { - isValid, - message: isValid ? "" : "No special character allowed", - }; - }, - [setIsSlugValid], - ); - - const saveSlug = useCallback(() => { - dispatch(setPageSlug({ customSlug, pageId: page.pageId })); - }, [page.pageId, customSlug]); - - const resetCustomSlug = useCallback(() => { - dispatch(setPageSlug({ customSlug: "", pageId: page.pageId })); - }, [page.pageId]); - - const onChange = useCallback( - (value: string) => { - setCustomSlug(value); - }, - [setCustomSlug], - ); - - if (applicationVersion < ApplicationVersion.SLUG_URL) return null; - return ( -
-
- {`${window.location.origin}/app/`} - - -{page.pageId} -
-
-
-
- ); -} - -export default PageListItem; diff --git a/app/client/src/pages/Editor/PagesEditor/index.tsx b/app/client/src/pages/Editor/PagesEditor/index.tsx deleted file mode 100644 index 5f341ab8bb4f..000000000000 --- a/app/client/src/pages/Editor/PagesEditor/index.tsx +++ /dev/null @@ -1,174 +0,0 @@ -import { get } from "lodash"; -import styled, { useTheme } from "styled-components"; -import { useHistory } from "react-router"; -import React, { useEffect, useCallback, useMemo } from "react"; -import { useDispatch, useSelector } from "react-redux"; - -import AnalyticsUtil from "utils/AnalyticsUtil"; -import { ControlIcons } from "icons/ControlIcons"; -import { Button, IconWrapper, Size } from "design-system"; -import PageListItem, { Action } from "./PageListItem"; -import { Page } from "@appsmith/constants/ReduxActionConstants"; -import { - getCurrentApplicationId, - getCurrentPageId, - getPageList, -} from "selectors/editorSelectors"; -import { getNextEntityName } from "utils/AppsmithUtils"; -import { DraggableList } from "design-system"; -import { extractCurrentDSL } from "utils/WidgetPropsUtils"; -import { createPage, setPageOrder } from "actions/pageActions"; -import { getCurrentApplication } from "selectors/applicationSelectors"; -import { builderURL } from "RouteBuilder"; - -const Wrapper = styled.div` - padding: 20px; - height: 100%; - overflow: auto; -`; - -const Header = styled.div` - display: flex; - padding-bottom: 20px; - button { - margin-left: auto; - } - - & > div { - display: flex; - align-items: center; - - h1 { - margin: 0; - font-size: 18px; - color: ${(props) => props.theme.colors.text.heading}; - margin-left: 10px; - } - } -`; - -const NewPageButton = styled(Button)` - & > ${IconWrapper} svg { - margin-right: 4px; - height: 11px; - width: 11px; - } - - & > ${IconWrapper} path { - stroke: white !important; - } -`; - -const CloseIcon = ControlIcons.CLOSE_CONTROL; - -type PageListPayloadWithId = Page[] & { id?: string }; - -function PagesEditor() { - const theme = useTheme(); - const dispatch = useDispatch(); - const history = useHistory(); - const pages: PageListPayloadWithId = useSelector( - getPageList, - )?.map((page) => ({ ...page, id: page.pageId })); - const currentApp = useSelector(getCurrentApplication); - const applicationId = useSelector(getCurrentApplicationId) as string; - const pageId = useSelector(getCurrentPageId); - - useEffect(() => { - AnalyticsUtil.logEvent("PAGES_LIST_LOAD", { - appName: currentApp?.name, - mode: "EDIT", - }); - }, []); - - /** - * creates the page - * - * @return void - */ - const createPageCallback = useCallback(() => { - const name = getNextEntityName( - "Page", - pages.map((page: Page) => page.pageName), - ); - // Default layout is extracted by adding dynamically computed properties like min-height. - const defaultPageLayouts = [ - { dsl: extractCurrentDSL(), layoutOnLoadActions: [] }, - ]; - dispatch(createPage(applicationId, name, defaultPageLayouts, true)); - }, [dispatch, pages, applicationId]); - - /** - * updates the order of page - * - * @return void - */ - const setPageOrderCallback = useCallback( - (pageId: string, newOrder: number) => { - dispatch(setPageOrder(applicationId, pageId, newOrder)); - }, - [dispatch, applicationId], - ); - - /** - * closes the page properties onc lick - * - * @return void - */ - const onClose = useCallback(() => { - history.push(builderURL({ pageId })); - }, [pageId]); - - /** - * Draggable List Render item - * - * - * @return JSX.Element - */ - const draggableListRenderItem = useMemo( - () => - function renderer({ item }: any) { - return ; - }, - [], - ); - - return ( - -
-
- - - -

Page Properties

-
- -
- - { - setPageOrderCallback(pages[originalIndex].pageId, newIndex); - }} - shouldReRender={false} - /> -
- ); -} - -export default PagesEditor; diff --git a/app/client/src/pages/Editor/PropertyPane/ConnectDataCTA.tsx b/app/client/src/pages/Editor/PropertyPane/ConnectDataCTA.tsx index c5b446525c23..78299b68060d 100644 --- a/app/client/src/pages/Editor/PropertyPane/ConnectDataCTA.tsx +++ b/app/client/src/pages/Editor/PropertyPane/ConnectDataCTA.tsx @@ -91,7 +91,7 @@ function ConnectDataCTA(props: ConnectDataCTAProps) { text="CONNECT DATA" />
diff --git a/app/client/src/pages/Editor/ThemePropertyPane/SaveThemeModal.tsx b/app/client/src/pages/Editor/ThemePropertyPane/SaveThemeModal.tsx index b52c9fc7ab94..eaf3d6094a73 100644 --- a/app/client/src/pages/Editor/ThemePropertyPane/SaveThemeModal.tsx +++ b/app/client/src/pages/Editor/ThemePropertyPane/SaveThemeModal.tsx @@ -120,43 +120,45 @@ function SaveThemeModal(props: SaveThemeModalProps) { onClose={onClose} title="Save Theme" > -
-
-

- You can save your custom themes to use across applications and use - them when you need. -

-
-

Your theme name

- +
+ +
+

+ You can save your custom themes to use across applications and use + them when you need. +

+
+

Your theme name

+ +
-
-
-
-
-
- + +
); } diff --git a/app/client/src/pages/Editor/ThemePropertyPane/ThemeBetaCard.tsx b/app/client/src/pages/Editor/ThemePropertyPane/ThemeBetaCard.tsx index c7b1b9b3e7e6..7994444b92fe 100644 --- a/app/client/src/pages/Editor/ThemePropertyPane/ThemeBetaCard.tsx +++ b/app/client/src/pages/Editor/ThemePropertyPane/ThemeBetaCard.tsx @@ -42,7 +42,7 @@ export function ThemeBetaCard() {
{createMessage(APP_THEME_BETA_CARD_CONTENT)}
- - - } - onClick={onOpenSaveModal} - text="Save theme" - /> - } - onClick={onResetTheme} - text="Reset widget styles" - /> - - -
+
+
+
+ + Theme Properties + +
- - - - -
-
- {/* FONT */} - - {Object.keys(selectedTheme.config.fontFamily).map( - (fontFamilySectionName: string, index: number) => { - return ( -
-

{startCase(fontFamilySectionName)}

- -
- ); - }, - )} -
- {/* COLORS */} - -
- -
-
+ + + + + } + onClick={onOpenSaveModal} + text="Save theme" + /> + } + onClick={onResetTheme} + text="Reset widget styles" + /> + + +
+
- {/* BORDER RADIUS */} - + + +
+
+ {/* FONT */} + + {Object.keys(selectedTheme.config.fontFamily).map( + (fontFamilySectionName: string, index: number) => { + return ( +
+

{startCase(fontFamilySectionName)}

+ +
+ ); + }, + )} +
+ {/* COLORS */} + +
+ +
+
- {/* BOX SHADOW */} - - {Object.keys(selectedTheme.config.boxShadow).map( - (boxShadowSectionName: string, index: number) => { - return ( -
-

{startCase(boxShadowSectionName)}

- -
- ); - }, - )} -
-
-
+ {/* BORDER RADIUS */} + + {Object.keys(selectedTheme.config.borderRadius).map( + (borderRadiusSectionName: string, index: number) => { + return ( +
+

{startCase(borderRadiusSectionName)}

+ +
+ ); + }, + )} +
+ + {/* BOX SHADOW */} + + {Object.keys(selectedTheme.config.boxShadow).map( + (boxShadowSectionName: string, index: number) => { + return ( +
+

{startCase(boxShadowSectionName)}

+ +
+ ); + }, + )} +
+ diff --git a/app/client/src/pages/Editor/ThemePropertyPane/controls/ThemeColorControl.tsx b/app/client/src/pages/Editor/ThemePropertyPane/controls/ThemeColorControl.tsx index 5cd35a997b07..0e53128c6f0d 100644 --- a/app/client/src/pages/Editor/ThemePropertyPane/controls/ThemeColorControl.tsx +++ b/app/client/src/pages/Editor/ThemePropertyPane/controls/ThemeColorControl.tsx @@ -69,6 +69,9 @@ function ThemeColorControl(props: ThemeColorControlProps) { color={userDefinedColors[selectedColor]} isOpen={autoFocus} key={selectedColor} + portalContainer={ + document.getElementById("app-settings-portal") || undefined + } />
)} diff --git a/app/client/src/pages/Editor/ThemePropertyPane/controls/ThemeFontControl.tsx b/app/client/src/pages/Editor/ThemePropertyPane/controls/ThemeFontControl.tsx index a955c1748008..d19fc3ca7e88 100644 --- a/app/client/src/pages/Editor/ThemePropertyPane/controls/ThemeFontControl.tsx +++ b/app/client/src/pages/Editor/ThemePropertyPane/controls/ThemeFontControl.tsx @@ -59,6 +59,9 @@ function ThemeFontControl(props: ThemeFontControlProps) { value: option, label: option, }))} + portalContainer={ + document.getElementById("app-settings-portal") || undefined + } renderOption={renderOption} selected={{ label: selectedOption, diff --git a/app/client/src/pages/Editor/WidgetsEditor/EmptyCanvasSection.tsx b/app/client/src/pages/Editor/WidgetsEditor/EmptyCanvasSection.tsx index 3200a9553872..b8981487320f 100644 --- a/app/client/src/pages/Editor/WidgetsEditor/EmptyCanvasSection.tsx +++ b/app/client/src/pages/Editor/WidgetsEditor/EmptyCanvasSection.tsx @@ -96,6 +96,16 @@ function CanvasTopSection() { const showTemplatesModal = () => { dispatch(showTemplatesModalAction(true)); + AnalyticsUtil.logEvent("CANVAS_BLANK_PAGE_CTA_CLICK", { + item: "ADD_PAGE_FROM_TEMPLATE", + }); + }; + + const onGeneratePageClick = () => { + goToGenPageForm({ applicationSlug, pageSlug, pageId }); + AnalyticsUtil.logEvent("CANVAS_BLANK_PAGE_CTA_CLICK", { + item: "GENERATE_PAGE", + }); }; return ( @@ -116,7 +126,7 @@ function CanvasTopSection() { goToGenPageForm({ applicationSlug, pageSlug, pageId })} + onClick={onGeneratePageClick} > diff --git a/app/client/src/pages/Editor/WidgetsEditor/PropertyPaneContainer.tsx b/app/client/src/pages/Editor/WidgetsEditor/PropertyPaneContainer.tsx index 84e91ea38841..5124be032c4c 100644 --- a/app/client/src/pages/Editor/WidgetsEditor/PropertyPaneContainer.tsx +++ b/app/client/src/pages/Editor/WidgetsEditor/PropertyPaneContainer.tsx @@ -1,29 +1,27 @@ -import { updateExplorerWidthAction } from "actions/explorerActions"; +import { setPropertyPaneWidthAction } from "actions/propertyPaneActions"; import PropertyPaneSidebar from "components/editorComponents/PropertyPaneSidebar"; -import { DEFAULT_PROPERTY_PANE_WIDTH } from "constants/AppConstants"; import React, { useCallback } from "react"; -import { useDispatch } from "react-redux"; +import { useDispatch, useSelector } from "react-redux"; +import { getPropertyPaneWidth } from "selectors/propertyPaneSelectors"; function PropertyPaneContainer() { const dispatch = useDispatch(); + const propertyPaneWidth = useSelector(getPropertyPaneWidth); - const [propertyPaneWidth, setPropertyPaneWidth] = React.useState( - DEFAULT_PROPERTY_PANE_WIDTH, - ); /** * on property pane sidebar drag end * * @return void */ const onRightSidebarDragEnd = useCallback(() => { - dispatch(updateExplorerWidthAction(propertyPaneWidth)); + dispatch(setPropertyPaneWidthAction(propertyPaneWidth)); }, [propertyPaneWidth]); /** * on property pane sidebar width change */ const onRightSidebarWidthChange = useCallback((newWidth) => { - setPropertyPaneWidth(newWidth); + dispatch(setPropertyPaneWidthAction(newWidth)); }, []); return ( diff --git a/app/client/src/pages/Editor/WidgetsEditor/index.tsx b/app/client/src/pages/Editor/WidgetsEditor/index.tsx index 80946cc3fb5c..29b465c61d2e 100644 --- a/app/client/src/pages/Editor/WidgetsEditor/index.tsx +++ b/app/client/src/pages/Editor/WidgetsEditor/index.tsx @@ -17,6 +17,7 @@ import { } from "selectors/onboardingSelectors"; import AnalyticsUtil from "utils/AnalyticsUtil"; import { quickScrollToWidget } from "utils/helpers"; +import { useAutoHeightUIState } from "utils/hooks/autoHeightUIHooks"; import { useAllowEditorDragToSelect } from "utils/hooks/useAllowEditorDragToSelect"; import { useWidgetSelection } from "utils/hooks/useWidgetSelection"; import PerformanceTracker, { @@ -71,16 +72,24 @@ function WidgetsEditor() { }, [isFetchingPage, selectWidget, guidedTourEnabled]); const allowDragToSelect = useAllowEditorDragToSelect(); + const { isAutoHeightWithLimitsChanging } = useAutoHeightUIState(); const handleWrapperClick = useCallback(() => { - if (allowDragToSelect) { + // Making sure that we don't deselect the widget + // after we are done dragging the limits in auto height with limits + if (allowDragToSelect && !isAutoHeightWithLimitsChanging) { focusWidget && focusWidget(); deselectAll && deselectAll(); dispatch(closePropertyPane()); dispatch(closeTableFilterPane()); dispatch(setCanvasSelectionFromEditor(false)); } - }, [allowDragToSelect, focusWidget, deselectAll]); + }, [ + allowDragToSelect, + focusWidget, + deselectAll, + isAutoHeightWithLimitsChanging, + ]); /** * drag event handler for selection drawing diff --git a/app/client/src/pages/Editor/__tests__/QueryEditorTable.test.tsx b/app/client/src/pages/Editor/__tests__/QueryEditorTable.test.tsx index fb3671f277bb..fe4114721340 100644 --- a/app/client/src/pages/Editor/__tests__/QueryEditorTable.test.tsx +++ b/app/client/src/pages/Editor/__tests__/QueryEditorTable.test.tsx @@ -12,6 +12,20 @@ function createEle() { } const scrollW = 6; +const reponsePaneHeight = 307; + +const tableData = [ + { "": "Jan 1 1970 10:15AM" }, + { "": "Jan 2 1970 10:15AM" }, + { "": "Jan 3 1970 10:15AM" }, + { "": "Jan 4 1970 10:15AM" }, + { "": "Jan 5 1970 10:15AM" }, + { "": "Jan 1 1970 10:15AM" }, + { "": "Jan 2 1970 10:15AM" }, + { "": "Jan 3 1970 10:15AM" }, + { "": "Jan 4 1970 10:15AM" }, + { "": "Jan 5 1970 10:15AM" }, +]; describe("Query Editor Table", () => { it("it should render table with missing key", () => { @@ -43,4 +57,10 @@ describe("Query Editor Table", () => { const scrollWidth = getScrollBarWidth(ele, scrollW); expect(scrollWidth).toBeGreaterThan(0); }); + + it("17653: Scroll bar in table doesnt appear", () => { + render(); + const tbodyEle = document.querySelectorAll(".tbody > div"); + expect(tbodyEle[0]).toHaveStyle("height: 171px"); + }); }); diff --git a/app/client/src/pages/Editor/gitSync/QuickGitActions/index.tsx b/app/client/src/pages/Editor/gitSync/QuickGitActions/index.tsx index 3a32ce4d749b..a58c0b40c2a7 100644 --- a/app/client/src/pages/Editor/gitSync/QuickGitActions/index.tsx +++ b/app/client/src/pages/Editor/gitSync/QuickGitActions/index.tsx @@ -263,7 +263,7 @@ function ConnectGitPlaceholder() { {isGitConnectionEnabled ? ( + + + + + ); +}; + +function FieldImageInput() { + return function FieldCheckbox( + componentProps: FormTextFieldProps & { + meta: Partial; + input: Partial; + }, + ) { + return ; + }; +} +export function ImageInputComponent({ setting }: SettingComponentProps) { + return ( + + + + ); +} + +export default memo(ImageInputComponent); diff --git a/app/client/src/pages/Settings/FormGroup/Link.test.tsx b/app/client/src/pages/Settings/FormGroup/Link.test.tsx index d63b2b20185f..c1cf740dc831 100644 --- a/app/client/src/pages/Settings/FormGroup/Link.test.tsx +++ b/app/client/src/pages/Settings/FormGroup/Link.test.tsx @@ -1,11 +1,14 @@ import { render, screen } from "test/testUtils"; import React from "react"; -import { SettingTypes } from "@appsmith/pages/AdminSettings/config/types"; +import { + Setting, + SettingTypes, +} from "@appsmith/pages/AdminSettings/config/types"; import Link from "./Link"; let container: any = null; const linkClickHandler = jest.fn(); -const setting = { +const setting: Setting = { id: "SETTING_ID", isHidden: false, label: "setting label", diff --git a/app/client/src/pages/Settings/FormGroup/Link.tsx b/app/client/src/pages/Settings/FormGroup/Link.tsx index 2965a41e9d64..fdeb2a4f286e 100644 --- a/app/client/src/pages/Settings/FormGroup/Link.tsx +++ b/app/client/src/pages/Settings/FormGroup/Link.tsx @@ -1,7 +1,7 @@ import { Icon } from "@blueprintjs/core"; import { Text, TextType } from "design-system"; import { Colors } from "constants/Colors"; -import { createMessage } from "@appsmith/constants/messages"; +import { createMessage, LEARN_MORE } from "@appsmith/constants/messages"; import React from "react"; import { useDispatch } from "react-redux"; import styled from "styled-components"; @@ -18,6 +18,7 @@ const LinkWrapper = styled.div` const StyledLink = styled.a` cursor: pointer; + text-transform: uppercase; &&, &:hover { color: ${(props) => props.theme.colors.settings.link}; @@ -69,7 +70,7 @@ export default function Link({ setting }: SettingComponentProps) { {createMessage(() => setting.label || "")}   - READ MORE + {createMessage(LEARN_MORE)}   diff --git a/app/client/src/pages/Settings/FormGroup/Radio.test.tsx b/app/client/src/pages/Settings/FormGroup/Radio.test.tsx new file mode 100644 index 000000000000..08eee1dba76d --- /dev/null +++ b/app/client/src/pages/Settings/FormGroup/Radio.test.tsx @@ -0,0 +1,80 @@ +import { render } from "test/testUtils"; +import React from "react"; +import { + Setting, + SettingTypes, +} from "@appsmith/pages/AdminSettings/config/types"; +import Radio from "./Radio"; +import { SETTINGS_FORM_NAME } from "@appsmith/constants/forms"; +import { reduxForm } from "redux-form"; + +let container: any = null; +const setting: Setting = { + id: "SETTING_RADIO", + name: "SETTING_RADIO", + category: "test category", + controlType: SettingTypes.RADIO, + label: "test label", + controlTypeProps: { + options: [ + { + label: "Label one", + value: "ONE", + }, + { + label: "Label two", + value: "TWO", + }, + ], + }, + format: (value) => { + return { value }; + }, + parse: (value) => { + return value.value; + }, +}; + +function renderComponent() { + function RadioFieldComponent() { + return ; + } + const Parent = reduxForm({ + validate: () => { + return {}; + }, + form: SETTINGS_FORM_NAME, + touchOnBlur: true, + })(RadioFieldComponent); + + render(, { + initialState: { + form: { + [SETTINGS_FORM_NAME]: { + values: { + [setting.id]: "TWO", + }, + }, + }, + }, + }); +} + +describe("Radio", () => { + beforeEach(() => { + container = document.createElement("div"); + document.body.appendChild(container); + }); + + it("is rendered", () => { + renderComponent(); + const radioOptions: NodeListOf = document.querySelectorAll( + "input[type=radio]", + ); + const numberOfCheckboxes = radioOptions.length; + expect(numberOfCheckboxes).toEqual( + setting.controlTypeProps?.options.length, + ); + expect(radioOptions[1].checked).toBeTruthy(); + }); +}); diff --git a/app/client/src/pages/Settings/FormGroup/Radio.tsx b/app/client/src/pages/Settings/FormGroup/Radio.tsx new file mode 100644 index 000000000000..ebfc31f8ec08 --- /dev/null +++ b/app/client/src/pages/Settings/FormGroup/Radio.tsx @@ -0,0 +1,216 @@ +import React, { ReactElement } from "react"; +import { + IconWrapper, + OptionProps, + Radio, + Text, + TextType, + Button, + Size, + IconSize, +} from "design-system"; +import { Popover2 } from "@blueprintjs/popover2"; +import { FormGroup, SettingComponentProps } from "./Common"; +import { + Field, + WrappedFieldInputProps, + WrappedFieldMetaProps, +} from "redux-form"; +import { FieldError } from "design-system"; +import { Colors } from "constants/Colors"; +import styled from "styled-components"; +import { Position } from "@blueprintjs/core"; + +type RadioOption = { + node?: ReactElement; + nodeLabel?: string; + nodeInputPath?: string; + nodeParentClass?: string; + badge?: string; + tooltip?: { + icon: any; + text: string; + linkText: string; + link: string; + }; +} & OptionProps; +export type RadioProps = { + options: RadioOption[]; +}; + +const Badge = styled(Text)<{ selected?: boolean }>` + background-color: ${(props) => + props.selected ? Colors.WARNING_ORANGE : Colors.SEA_SHELL}; + padding: 1.5px 5px; + margin-left: 4px; +`; + +const TooltipContent = styled.div` + width: 300px; + padding: 12px; + + a { + justify-content: flex-start; + padding: 0; + margin-top: 4px; + text-decoration: underline; + } + + .tooltip-text { + line-height: 1.17; + } +`; + +const RadioWrapper = styled.div<{ index: number }>` + ${(props) => + props.index > 0 && + ` + margin-top: 12.5px; + `} + + .icon { + margin-left: 4px; + } +`; + +const SuffixWrapper = styled.div` + display: inline-flex; + align-items: center; +`; + +const NodeWrapper = styled.div` + margin-left: 27px; + margin-top: 8px; +`; + +type RadioGroupProps = SettingComponentProps; + +function RadioFieldWrapper( + componentProps: { + meta: Partial; + input: Partial; + } & RadioProps, +) { + function onChangeHandler(e?: any) { + componentProps.input.onChange && + componentProps.input.onChange({ + value: e.target.value, + additionalData: componentProps.input.value.additionalData, + }); + } + + function onInputNodeChangeHandler(value?: any) { + componentProps.input.onChange && + componentProps.input.onChange({ + value: componentProps.input.value.value, + additionalData: value, + }); + componentProps.input.onBlur && + componentProps.input.onBlur({ + value: componentProps.input.value.value, + additionalData: value, + }); + } + + return ( +
+ {componentProps.options.map((item, index) => { + const isSelected = componentProps.input.value.value === item.value; + + return ( + + + {item.label} + + + + {item.badge && ( + + {item.badge} + + )} + {item.tooltip && ( + + + {item.tooltip.text} + +
+ ); +} + +export default function RadioField({ setting }: RadioGroupProps) { + const controlTypeProps = setting.controlTypeProps as RadioProps; + + return ( + + + + ); +} diff --git a/app/client/src/pages/Settings/FormGroup/TagInputField.test.tsx b/app/client/src/pages/Settings/FormGroup/TagInputField.test.tsx index 37daf2d70b79..b8a134bcce4c 100644 --- a/app/client/src/pages/Settings/FormGroup/TagInputField.test.tsx +++ b/app/client/src/pages/Settings/FormGroup/TagInputField.test.tsx @@ -1,12 +1,15 @@ import { render, screen } from "test/testUtils"; import React from "react"; -import { SettingTypes } from "@appsmith/pages/AdminSettings/config/types"; +import { + Setting, + SettingTypes, +} from "@appsmith/pages/AdminSettings/config/types"; import TagInputField from "./TagInputField"; import { SETTINGS_FORM_NAME } from "@appsmith/constants/forms"; import { reduxForm } from "redux-form"; let container: any = null; -const setting = { +const setting: Setting = { id: "SETTING_TAG_INPUT_ID", name: "SETTING_TAG_INPUT_ID", category: "test category", diff --git a/app/client/src/pages/Settings/FormGroup/TagInputField.tsx b/app/client/src/pages/Settings/FormGroup/TagInputField.tsx index 4e4f61a916db..5e29c04b96ea 100644 --- a/app/client/src/pages/Settings/FormGroup/TagInputField.tsx +++ b/app/client/src/pages/Settings/FormGroup/TagInputField.tsx @@ -18,7 +18,7 @@ const renderComponent = ( const setting = componentProps.setting; return ( diff --git a/app/client/src/pages/Settings/FormGroup/Text.test.tsx b/app/client/src/pages/Settings/FormGroup/Text.test.tsx index c747cb1b0c61..bf30469bd191 100644 --- a/app/client/src/pages/Settings/FormGroup/Text.test.tsx +++ b/app/client/src/pages/Settings/FormGroup/Text.test.tsx @@ -1,11 +1,14 @@ import { render, screen } from "test/testUtils"; import React from "react"; -import { SettingTypes } from "@appsmith/pages/AdminSettings/config/types"; +import { + Setting, + SettingTypes, +} from "@appsmith/pages/AdminSettings/config/types"; import TextComponent from "./Text"; let container: any = null; const buttonClickHandler = jest.fn(); -const setting = { +const setting: Setting = { id: "SETTING_ID", name: "textType", text: "download", diff --git a/app/client/src/pages/Settings/FormGroup/Text.tsx b/app/client/src/pages/Settings/FormGroup/Text.tsx index 2bad64f91f76..019c2a3756b3 100644 --- a/app/client/src/pages/Settings/FormGroup/Text.tsx +++ b/app/client/src/pages/Settings/FormGroup/Text.tsx @@ -1,7 +1,7 @@ import { Text, TextType } from "design-system"; import React from "react"; import { getSettings } from "selectors/settingsSelectors"; -import { useSelector } from "store"; +import { useSelector } from "react-redux"; import styled from "styled-components"; import { FormGroup, SettingComponentProps } from "./Common"; diff --git a/app/client/src/pages/Settings/FormGroup/TextInput.test.tsx b/app/client/src/pages/Settings/FormGroup/TextInput.test.tsx index bb5bd5e5b877..df5730692ac2 100644 --- a/app/client/src/pages/Settings/FormGroup/TextInput.test.tsx +++ b/app/client/src/pages/Settings/FormGroup/TextInput.test.tsx @@ -3,13 +3,14 @@ import React from "react"; import { SettingTypes, SettingSubtype, + Setting, } from "@appsmith/pages/AdminSettings/config/types"; import TextInput from "./TextInput"; import { SETTINGS_FORM_NAME } from "@appsmith/constants/forms"; import { reduxForm } from "redux-form"; let container: any = null; -const setting = { +const setting: Setting = { id: "SETTING_TEXT_INPUT_ID", name: "SETTING_TEXT_INPUT_ID", category: "test category", diff --git a/app/client/src/pages/Settings/FormGroup/Toggle.test.tsx b/app/client/src/pages/Settings/FormGroup/Toggle.test.tsx index 78df982e6f25..e096c845acbb 100644 --- a/app/client/src/pages/Settings/FormGroup/Toggle.test.tsx +++ b/app/client/src/pages/Settings/FormGroup/Toggle.test.tsx @@ -1,12 +1,15 @@ import { render } from "test/testUtils"; import React from "react"; -import { SettingTypes } from "@appsmith/pages/AdminSettings/config/types"; +import { + Setting, + SettingTypes, +} from "@appsmith/pages/AdminSettings/config/types"; import Toggle from "./Toggle"; import { SETTINGS_FORM_NAME } from "@appsmith/constants/forms"; import { reduxForm } from "redux-form"; let container: any = null; -const setting = { +const setting: Setting = { id: "SETTING_TOGGLE_ID", name: "SETTING_TOGGLE_ID", category: "test category", diff --git a/app/client/src/pages/Settings/FormGroup/common.test.tsx b/app/client/src/pages/Settings/FormGroup/common.test.tsx index 424a99f0c832..bddfe2c16231 100644 --- a/app/client/src/pages/Settings/FormGroup/common.test.tsx +++ b/app/client/src/pages/Settings/FormGroup/common.test.tsx @@ -1,10 +1,13 @@ import { render, screen } from "test/testUtils"; import React from "react"; -import { SettingTypes } from "@appsmith/pages/AdminSettings/config/types"; +import { + Setting, + SettingTypes, +} from "@appsmith/pages/AdminSettings/config/types"; import { FormGroup } from "./Common"; let container: any = null; -const setting = { +const setting: Setting = { id: "SETTING_ID", label: "formGroup", helpText: "", diff --git a/app/client/src/pages/Settings/FormGroup/group.tsx b/app/client/src/pages/Settings/FormGroup/group.tsx index 2a8549e4328c..73b9dcfb2857 100644 --- a/app/client/src/pages/Settings/FormGroup/group.tsx +++ b/app/client/src/pages/Settings/FormGroup/group.tsx @@ -11,9 +11,10 @@ import Text from "./Text"; import Button from "./Button"; import { getFormValues } from "redux-form"; import { SETTINGS_FORM_NAME } from "@appsmith/constants/forms"; -import { useSelector } from "store"; +import { useSelector } from "react-redux"; import { createMessage, + LEARN_MORE, REDIRECT_URL_TOOLTIP, } from "@appsmith/constants/messages"; import { CalloutV2 } from "design-system"; @@ -24,7 +25,9 @@ import Dropdown from "./Dropdown"; import { Classes } from "@blueprintjs/core"; import { Colors } from "constants/Colors"; import Checkbox from "./Checkbox"; +import Radio from "./Radio"; import { useDispatch } from "react-redux"; +import { getTypographyByKey } from "constants/DefaultTheme"; type GroupProps = { name?: string; @@ -58,29 +61,29 @@ const GroupBody = styled.div` margin-top: 0px; } } - & .tag-input { - .t--admin-settings-tag-input { - > div { - margin: 0; - .${Classes.TAG_INPUT}, .${Classes.TAG_INPUT}.${Classes.ACTIVE} { - border: 1.2px solid var(--appsmith-color-black-250); - box-shadow: none; - .bp3-tag { - background: var(--appsmith-color-black-50); - color: ${Colors.BLACK}; - svg:hover { - cursor: pointer; - path { - fill: currentColor; - } - } - } - } - .${Classes.TAG_INPUT}.${Classes.ACTIVE} { - border: 1.2px solid var(--appsmith-input-focus-border-color); - } + &&&& { + // TagInput in design system has a right margin + .tag-input > div { + margin: 0; + } + + .tag-input .${Classes.TAG_INPUT} { + box-shadow: none; + } + + .tag-input .${Classes.TAG} { + color: ${Colors.GRAY_700}; + background-color: ${Colors.GRAY_200}; + ${(props) => getTypographyByKey(props, "h5")} + // Cursor on close icon need to be a pointer + svg:hover { + cursor: pointer; } } + + .tag-input .${Classes.TAG_INPUT}.${Classes.ACTIVE} { + border: 1.2px solid var(--appsmith-color-black-900); + } } `; @@ -110,6 +113,16 @@ export default function Group({ return null; } switch (setting.controlType) { + case SettingTypes.RADIO: + return ( +
+ +
+ ); case SettingTypes.TEXTINPUT: return (
{setting.action ? ( setting.label || "")} onClick={ ((() => { @@ -165,7 +178,7 @@ export default function Group({ /> ) : ( setting.label || "")} type={setting.calloutType || "Notify"} url={setting.url} diff --git a/app/client/src/pages/Settings/RestartBanner.tsx b/app/client/src/pages/Settings/RestartBanner.tsx index 8c268417b188..1ab53d4d5206 100644 --- a/app/client/src/pages/Settings/RestartBanner.tsx +++ b/app/client/src/pages/Settings/RestartBanner.tsx @@ -11,7 +11,7 @@ import { getIsRestartFailed, getRestartingState, } from "selectors/settingsSelectors"; -import { useSelector } from "store"; +import { useSelector } from "react-redux"; import styled from "styled-components"; import { createMessage, diff --git a/app/client/src/pages/Settings/SaveSettings.tsx b/app/client/src/pages/Settings/SaveSettings.tsx index 85e21d3cfb07..ba7044f113fa 100644 --- a/app/client/src/pages/Settings/SaveSettings.tsx +++ b/app/client/src/pages/Settings/SaveSettings.tsx @@ -62,7 +62,7 @@ const saveAdminSettings = (props: SaveAdminSettingsProps) => { text={createMessage(() => "Save & Restart")} /> ; } return ; diff --git a/app/client/src/pages/Settings/components.tsx b/app/client/src/pages/Settings/components.tsx index 6dd7389ec727..b730fd61c6b6 100644 --- a/app/client/src/pages/Settings/components.tsx +++ b/app/client/src/pages/Settings/components.tsx @@ -3,7 +3,7 @@ import { Classes } from "@blueprintjs/core"; export const Wrapper = styled.div` flex-basis: calc(100% - ${(props) => props.theme.homePage.leftPane.width}px); - padding-top: 40px; + padding: 40px 0 0 24px; `; export const HeaderWrapper = styled.div<{ margin?: string }>` diff --git a/app/client/src/pages/Settings/config/branding/BrandingPage.tsx b/app/client/src/pages/Settings/config/branding/BrandingPage.tsx new file mode 100644 index 000000000000..f0dd65664c84 --- /dev/null +++ b/app/client/src/pages/Settings/config/branding/BrandingPage.tsx @@ -0,0 +1,93 @@ +import React, { useEffect } from "react"; +import { useSelector } from "react-redux"; +import { useForm } from "react-hook-form"; + +import Previews from "./previews"; +import SettingsForm from "./SettingsForm"; +import { getTenantConfig } from "@appsmith/selectors/tenantSelectors"; +import { AdminConfigType } from "@appsmith/pages/AdminSettings/config/types"; +import { Wrapper } from "@appsmith/pages/AdminSettings/config/authentication/AuthPage"; +import UpgradeBanner from "@appsmith/pages/AdminSettings/config/branding/UpgradeBanner"; + +export type brandColorsKeys = + | "primary" + | "background" + | "font" + | "hover" + | "disabled"; + +export type Inputs = { + brandColors: Record; + APPSMITH_BRAND_LOGO: string; + APPSMITH_BRAND_FAVICON: string; +}; + +type BrandingPageProps = { + category: AdminConfigType; +}; + +function BrandingPage(props: BrandingPageProps) { + const { category } = props; + const { needsUpgrade = true } = category; + const tentantConfig = useSelector(getTenantConfig); + const defaultValues = { + brandColors: tentantConfig.brandColors, + APPSMITH_BRAND_LOGO: tentantConfig.brandLogoUrl, + APPSMITH_BRAND_FAVICON: tentantConfig.brandFaviconUrl, + }; + const { + control, + formState, + getValues, + handleSubmit, + reset, + resetField, + setValue, + watch, + } = useForm({ + defaultValues, + }); + + const values = getValues(); + + /** + * reset the form when the tenant config changes + */ + useEffect(() => { + reset({ + brandColors: tentantConfig.brandColors, + APPSMITH_BRAND_LOGO: tentantConfig.brandLogoUrl, + APPSMITH_BRAND_FAVICON: tentantConfig.brandFaviconUrl, + }); + }, [tentantConfig, reset]); + + watch(); + + return ( + + +
+ +
+ +
+
+
+ ); +} + +export default BrandingPage; diff --git a/app/client/src/pages/Settings/config/branding/SettingsForm.tsx b/app/client/src/pages/Settings/config/branding/SettingsForm.tsx new file mode 100644 index 000000000000..5ec463b157fe --- /dev/null +++ b/app/client/src/pages/Settings/config/branding/SettingsForm.tsx @@ -0,0 +1,171 @@ +import React from "react"; +import { + Control, + Controller, + FormState, + UseFormReset, + UseFormHandleSubmit, + UseFormSetValue, + UseFormResetField, +} from "react-hook-form"; +import QuestionIcon from "remixicon-react/QuestionFillIcon"; +import { Button, Size, TooltipComponent } from "design-system"; + +import { Inputs } from "./BrandingPage"; +import { + ADMIN_BRANDING_LOGO_REQUIREMENT, + ADMIN_BRANDING_FAVICON_REQUIREMENT, + ADMIN_BRANDING_COLOR_TOOLTIP_BACKGROUND, + ADMIN_BRANDING_COLOR_TOOLTIP_FONT, + ADMIN_BRANDING_COLOR_TOOLTIP_PRIMARY, + ADMIN_BRANDING_COLOR_TOOLTIP_HOVER, + ADMIN_BRANDING_COLOR_TOOLTIP_DISABLED, + ADMIN_BRANDING_COLOR_TOOLTIP, + createMessage, +} from "@appsmith/constants/messages"; +import { ColorInput } from "pages/Settings/FormGroup/ColorInput"; +import { ImageInput } from "pages/Settings/FormGroup/ImageInput"; +import { logoImageValidator, faivconImageValidator } from "utils/BrandingUtils"; +import { useBrandingForm } from "@appsmith/pages/AdminSettings/config/branding/useBrandingForm"; +import AnalyticsUtil from "utils/AnalyticsUtil"; + +type SettingsFormProps = { + disabled?: boolean; + control: Control; + formState: FormState; + handleSubmit: UseFormHandleSubmit; + reset: UseFormReset; + defaultValues: Inputs; + setValue: UseFormSetValue; + resetField: UseFormResetField; + values: Inputs; +}; + +function SettingsForm(props: SettingsFormProps) { + const { control, defaultValues, disabled, formState, handleSubmit } = props; + const hasDirtyFields = Object.keys(formState.dirtyFields).length > 0; + const { onSubmit } = useBrandingForm({ + dirtyFields: formState.dirtyFields, + }); + + return ( +
+
+ {/* LOGO */} +
+ + ( + { + onChange && onChange(e); + + AnalyticsUtil.logEvent("BRANDING_PROPERTY_UPDATE", { + propertyName: "logo", + }); + }} + validate={logoImageValidator} + value={value} + /> + )} + /> +

+ {createMessage(ADMIN_BRANDING_LOGO_REQUIREMENT)} +

+
+ + {/* FAVICON */} +
+ + ( + { + onChange && onChange(e); + + AnalyticsUtil.logEvent("BRANDING_PROPERTY_UPDATE", { + propertyName: "favicon", + }); + }} + validate={faivconImageValidator} + value={value} + /> + )} + /> +

+ {createMessage(ADMIN_BRANDING_FAVICON_REQUIREMENT)} +

+
+ + {/* COLOR */} +
+
+ + + + +
+ + ( + !["disabled", "hover"].includes(key)} + logEvent={(property: string) => { + AnalyticsUtil.logEvent("BRANDING_PROPERTY_UPDATE", { + propertyName: property, + }); + }} + onChange={onChange} + tooltips={{ + primary: createMessage(ADMIN_BRANDING_COLOR_TOOLTIP_PRIMARY), + background: createMessage( + ADMIN_BRANDING_COLOR_TOOLTIP_BACKGROUND, + ), + hover: createMessage(ADMIN_BRANDING_COLOR_TOOLTIP_HOVER), + font: createMessage(ADMIN_BRANDING_COLOR_TOOLTIP_FONT), + disabled: createMessage( + ADMIN_BRANDING_COLOR_TOOLTIP_DISABLED, + ), + }} + value={value} + /> + )} + /> +
+ +
+ + ); +} + +export default SettingsForm; diff --git a/app/client/src/pages/Settings/config/branding/previews/AppPreview.tsx b/app/client/src/pages/Settings/config/branding/previews/AppPreview.tsx new file mode 100644 index 000000000000..00a8994af184 --- /dev/null +++ b/app/client/src/pages/Settings/config/branding/previews/AppPreview.tsx @@ -0,0 +1,28 @@ +import React from "react"; +import AppsIcon from "remixicon-react/AppsLineIcon"; + +import { PreviewsProps } from "."; +import PreviewBox from "./PreviewBox"; + +const AppPreview = (props: PreviewsProps) => { + const { logo } = props; + + return ( + +
+
+
+ + Branding Logo +
+
+
+
+ ); +}; + +export default AppPreview; diff --git a/app/client/src/pages/Settings/config/branding/previews/DashboardPreview.tsx b/app/client/src/pages/Settings/config/branding/previews/DashboardPreview.tsx new file mode 100644 index 000000000000..f3226af352d3 --- /dev/null +++ b/app/client/src/pages/Settings/config/branding/previews/DashboardPreview.tsx @@ -0,0 +1,35 @@ +import React from "react"; + +import { PreviewsProps } from "."; +import PreviewBox from "./PreviewBox"; + +const DashboardPreview = (props: PreviewsProps) => { + const { logo, shades } = props; + + return ( + +
+
+
+ Branding Logo +
+
+
+
+
+
+
+
+ ); +}; + +export default DashboardPreview; diff --git a/app/client/src/pages/Settings/config/branding/previews/DashboardThumbnail.tsx b/app/client/src/pages/Settings/config/branding/previews/DashboardThumbnail.tsx new file mode 100644 index 000000000000..df22162adcbb --- /dev/null +++ b/app/client/src/pages/Settings/config/branding/previews/DashboardThumbnail.tsx @@ -0,0 +1,22 @@ +import React from "react"; +import UserIcon from "remixicon-react/User3FillIcon"; + +import PreviewBox from "./PreviewBox"; + +const DashboardThumbnail = () => { + return ( + +
+

Application name

+
+ +
+
+
+ ); +}; + +export default DashboardThumbnail; diff --git a/app/client/src/pages/Settings/config/branding/previews/EmailPreview.tsx b/app/client/src/pages/Settings/config/branding/previews/EmailPreview.tsx new file mode 100644 index 000000000000..5f07525f6c6d --- /dev/null +++ b/app/client/src/pages/Settings/config/branding/previews/EmailPreview.tsx @@ -0,0 +1,56 @@ +import React from "react"; + +import { PreviewsProps } from "."; +import PreviewBox from "./PreviewBox"; + +const EmailPreview = (props: PreviewsProps) => { + const { logo, shades } = props; + + return ( + +
+
+ Branding Logo +

+ Lorem ipsum dolor sit amet consectetur, adipisicing elit. Nostrum + quas quos cumque sit hic unde deserunt +

+
+
+
+
+
+ + ); +}; + +export default EmailPreview; diff --git a/app/client/src/pages/Settings/config/branding/previews/FaviconPreview.tsx b/app/client/src/pages/Settings/config/branding/previews/FaviconPreview.tsx new file mode 100644 index 000000000000..f03100515ba6 --- /dev/null +++ b/app/client/src/pages/Settings/config/branding/previews/FaviconPreview.tsx @@ -0,0 +1,33 @@ +import React from "react"; +import PreviewBox from "./PreviewBox"; + +import AddIcon from "remixicon-react/AddFillIcon"; + +import { PreviewsProps } from "."; + +const FaviconPreview = (props: PreviewsProps) => { + const { favicon } = props; + + return ( + +
+
+
+ Branding Logo + Application Name +
+ +
+
+
+
+
+ + ); +}; + +export default FaviconPreview; diff --git a/app/client/src/pages/Settings/config/branding/previews/LinkPreview.tsx b/app/client/src/pages/Settings/config/branding/previews/LinkPreview.tsx new file mode 100644 index 000000000000..345e47a9e847 --- /dev/null +++ b/app/client/src/pages/Settings/config/branding/previews/LinkPreview.tsx @@ -0,0 +1,23 @@ +import React from "react"; + +import { PreviewsProps } from "."; +import PreviewBox from "./PreviewBox"; + +const LinkPreview = (props: PreviewsProps) => { + const { logo } = props; + + return ( + +
+ Branding Logo +

Application name

+
+
+ ); +}; + +export default LinkPreview; diff --git a/app/client/src/pages/Settings/config/branding/previews/LoginPreview.tsx b/app/client/src/pages/Settings/config/branding/previews/LoginPreview.tsx new file mode 100644 index 000000000000..f2ae57b86f87 --- /dev/null +++ b/app/client/src/pages/Settings/config/branding/previews/LoginPreview.tsx @@ -0,0 +1,50 @@ +import React from "react"; + +import { PreviewsProps } from "."; +import PreviewBox from "./PreviewBox"; + +const LoginPreview = (props: PreviewsProps) => { + const { logo, shades } = props; + + return ( + +
+
+ Logo +
+
+
+
+
+
+
+ + ); +}; + +export default LoginPreview; diff --git a/app/client/src/pages/Settings/config/branding/previews/NotFoundPreview.tsx b/app/client/src/pages/Settings/config/branding/previews/NotFoundPreview.tsx new file mode 100644 index 000000000000..0ada0e201985 --- /dev/null +++ b/app/client/src/pages/Settings/config/branding/previews/NotFoundPreview.tsx @@ -0,0 +1,37 @@ +import React from "react"; +import PreviewBox from "./PreviewBox"; + +import { PreviewsProps } from "."; + +const NotFoundPreview = (props: PreviewsProps) => { + const { shades } = props; + + return ( + +
+
+

+ 404 +

+
+
+
+ + ); +}; + +export default NotFoundPreview; diff --git a/app/client/src/pages/Settings/config/branding/previews/PreviewBox.tsx b/app/client/src/pages/Settings/config/branding/previews/PreviewBox.tsx new file mode 100644 index 000000000000..842c6e55cd3b --- /dev/null +++ b/app/client/src/pages/Settings/config/branding/previews/PreviewBox.tsx @@ -0,0 +1,29 @@ +import React from "react"; + +type PreviewBoxProps = { + title?: string; + children: React.ReactNode; + className?: string; + style?: React.CSSProperties; +}; + +const PreviewBox = (props: PreviewBoxProps) => { + const { children, className, title, ...rest } = props; + + return ( +
+ {children} + {title && ( +

+ {title} +

+ )} +
+ ); +}; + +export default PreviewBox; diff --git a/app/client/src/pages/Settings/config/branding/previews/index.tsx b/app/client/src/pages/Settings/config/branding/previews/index.tsx new file mode 100644 index 000000000000..57c61af98bac --- /dev/null +++ b/app/client/src/pages/Settings/config/branding/previews/index.tsx @@ -0,0 +1,74 @@ +import React, { useEffect, useState } from "react"; + +import AppPreview from "./AppPreview"; +import EmailPreview from "./EmailPreview"; +import LoginPreview from "./LoginPreview"; +import FaviconPreview from "./FaviconPreview"; +import NotFoundPreview from "./NotFoundPreview"; +import DashboardPreview from "./DashboardPreview"; +import { brandColorsKeys } from "../BrandingPage"; + +export type PreviewsProps = { + shades: Record; + logo: string | Blob; + favicon: string; +}; + +const Previews = (props: PreviewsProps) => { + const { favicon, logo } = props; + + const [logoPreview, setLogoPreview] = useState(null); + const [faviconPreview, setFaviconPreview] = useState(null); + + useEffect(() => { + if (!logo) return; + + if (typeof logo === "string") { + setLogoPreview(logo); + + return; + } + + if (typeof logo !== "string") { + const reader = new FileReader(); + reader.readAsDataURL(logo); + + reader.onloadend = function() { + setLogoPreview(reader.result); + }; + } + }, [logo]); + + useEffect(() => { + if (!favicon) return; + + if (typeof favicon === "string") { + setFaviconPreview(favicon); + + return; + } + + if (typeof favicon !== "string") { + const reader = new FileReader(); + reader.readAsDataURL(favicon); + + reader.onloadend = function() { + setFaviconPreview(reader.result); + }; + } + }, [favicon]); + + return ( +
+ {/* login */} + + + + + + +
+ ); +}; + +export default Previews; diff --git a/app/client/src/pages/Templates/ForkTemplate.tsx b/app/client/src/pages/Templates/ForkTemplate.tsx index 57d4b7f1bf94..49361c328435 100644 --- a/app/client/src/pages/Templates/ForkTemplate.tsx +++ b/app/client/src/pages/Templates/ForkTemplate.tsx @@ -92,7 +92,7 @@ function ForkTemplate({ />
+ ); +} + +export default Container; diff --git a/app/client/src/pages/UserAuth/FooterLinks.tsx b/app/client/src/pages/UserAuth/FooterLinks.tsx index 8db32af5e5d8..b0a5b461c4e3 100644 --- a/app/client/src/pages/UserAuth/FooterLinks.tsx +++ b/app/client/src/pages/UserAuth/FooterLinks.tsx @@ -1,40 +1,23 @@ import React from "react"; -import styled from "styled-components"; - -const FooterLink = styled.a` - cursor: pointer; - text-decoration: none; - :hover { - text-decoration: underline; - color: ${(props) => props.theme.colors.text.normal}; - } - font-weight: ${(props) => props.theme.typography.releaseList.fontWeight}; - font-size: ${(props) => props.theme.typography.releaseList.fontSize}px; - line-height: ${(props) => props.theme.typography.releaseList.lineHeight}px; - letter-spacing: ${(props) => - props.theme.typography.releaseList.letterSpacing}px; - color: #000 !important; -`; - -const FooterLinksContainer = styled.div` - padding: ${(props) => props.theme.spaces[9]}px; - display: flex; - align-items: center; - justify-content: space-around; - width: 100%; - max-width: ${(props) => props.theme.authCard.width}px; -`; function FooterLinks() { return ( - - + ); } diff --git a/app/client/src/pages/UserAuth/ForgotPassword.tsx b/app/client/src/pages/UserAuth/ForgotPassword.tsx index 0f1660592d8d..5d3ed91290b4 100644 --- a/app/client/src/pages/UserAuth/ForgotPassword.tsx +++ b/app/client/src/pages/UserAuth/ForgotPassword.tsx @@ -1,6 +1,6 @@ import React, { useEffect } from "react"; import { connect, useDispatch } from "react-redux"; -import { withRouter, RouteComponentProps } from "react-router-dom"; +import { withRouter, RouteComponentProps, Link } from "react-router-dom"; import { change, reduxForm, @@ -9,7 +9,6 @@ import { } from "redux-form"; import StyledForm from "components/editorComponents/Form"; import { - AuthCardHeader, FormActions, BlackAuthCardNavLink, FormMessagesContainer, @@ -38,6 +37,7 @@ import { forgotPasswordSubmitHandler, } from "./helpers"; import { getAppsmithConfigs } from "@appsmith/configs"; +import Container from "./Container"; const { mailEnabled } = getAppsmithConfigs(); @@ -76,19 +76,18 @@ export const ForgotPassword = withTheme( }, [props.emailValue]); return ( - <> - -

{createMessage(FORGOT_PASSWORD_PAGE_TITLE)}

-
-
- + {createMessage(FORGOT_PASSWORD_PAGE_LOGIN_LINK)} -
+ } + title={createMessage(FORGOT_PASSWORD_PAGE_TITLE)} + > {submitSucceeded && ( + Configure Email service + + ), text: "Configure Email service", intent: "primary", }, ]} intent="warning" + linkAs={Link} message={ "You haven’t setup any email service yet. Please configure your email service to receive a reset link" } @@ -143,7 +151,7 @@ export const ForgotPassword = withTheme( /> - + ); }, ); diff --git a/app/client/src/pages/UserAuth/ResetPassword.tsx b/app/client/src/pages/UserAuth/ResetPassword.tsx index 0fc79d3a54e8..3de5ce209e65 100644 --- a/app/client/src/pages/UserAuth/ResetPassword.tsx +++ b/app/client/src/pages/UserAuth/ResetPassword.tsx @@ -1,6 +1,6 @@ import React, { useLayoutEffect } from "react"; import { AppState } from "@appsmith/reducers"; -import { withRouter, RouteComponentProps } from "react-router-dom"; +import { Link, withRouter, RouteComponentProps } from "react-router-dom"; import { connect } from "react-redux"; import { InjectedFormProps, reduxForm, Field } from "redux-form"; import { RESET_PASSWORD_FORM_NAME } from "@appsmith/constants/forms"; @@ -21,11 +21,7 @@ import Spinner from "components/editorComponents/Spinner"; import StyledForm from "components/editorComponents/Form"; import { isEmptyString, isStrongPassword } from "utils/formhelpers"; import { ResetPasswordFormValues, resetPasswordSubmitHandler } from "./helpers"; -import { - AuthCardHeader, - BlackAuthCardNavLink, - FormActions, -} from "./StyledComponents"; +import { BlackAuthCardNavLink, FormActions } from "./StyledComponents"; import { AUTH_LOGIN_URL, FORGOT_PASSWORD_URL } from "constants/routes"; import { withTheme } from "styled-components"; import { Theme } from "constants/DefaultTheme"; @@ -45,6 +41,7 @@ import { RESET_PASSWORD_RESET_SUCCESS_LOGIN_LINK, createMessage, } from "@appsmith/constants/messages"; +import Container from "./Container"; const validate = (values: ResetPasswordFormValues) => { const errors: ResetPasswordFormValues = {}; @@ -96,10 +93,13 @@ export function ResetPassword(props: ResetPasswordProps) { let message = ""; let messageActions: MessageAction[] | undefined = undefined; if (showExpiredMessage || showInvalidMessage) { + const messageActionText = createMessage( + RESET_PASSWORD_FORGOT_PASSWORD_LINK, + ); messageActions = [ { - url: FORGOT_PASSWORD_URL, - text: createMessage(RESET_PASSWORD_FORGOT_PASSWORD_LINK), + linkElement: {messageActionText}, + text: messageActionText, intent: "primary", }, ]; @@ -112,11 +112,14 @@ export function ResetPassword(props: ResetPasswordProps) { } if (showSuccessMessage) { + const messageActionText = createMessage( + RESET_PASSWORD_RESET_SUCCESS_LOGIN_LINK, + ); message = createMessage(RESET_PASSWORD_RESET_SUCCESS); messageActions = [ { - url: AUTH_LOGIN_URL, - text: createMessage(RESET_PASSWORD_RESET_SUCCESS_LOGIN_LINK), + linkElement: {messageActionText}, + text: messageActionText, intent: "success", }, ]; @@ -130,10 +133,15 @@ export function ResetPassword(props: ResetPasswordProps) { createMessage(RESET_PASSWORD_FORGOT_PASSWORD_LINK).toLowerCase(), ) ) { + const messageActionText = createMessage( + RESET_PASSWORD_FORGOT_PASSWORD_LINK, + ); messageActions = [ { - url: FORGOT_PASSWORD_URL, - text: createMessage(RESET_PASSWORD_FORGOT_PASSWORD_LINK), + linkElement: ( + {messageActionText} + ), + text: messageActionText, intent: "primary", }, ]; @@ -157,19 +165,18 @@ export function ResetPassword(props: ResetPasswordProps) { return ; } return ( - <> - -

{createMessage(RESET_PASSWORD_PAGE_TITLE)}

-
-
- + {createMessage(RESET_PASSWORD_LOGIN_LINK_TEXT)} -
+ } + title={createMessage(RESET_PASSWORD_PAGE_TITLE)} + > {(showSuccessMessage || showFailureMessage) && ( )} @@ -201,7 +208,7 @@ export function ResetPassword(props: ResetPasswordProps) { /> - + ); } diff --git a/app/client/src/pages/UserAuth/StyledComponents.tsx b/app/client/src/pages/UserAuth/StyledComponents.tsx index fd0de317ce04..ae84959b5c4a 100644 --- a/app/client/src/pages/UserAuth/StyledComponents.tsx +++ b/app/client/src/pages/UserAuth/StyledComponents.tsx @@ -129,7 +129,6 @@ export const ForgotPasswordLink = styled.div` ${getTypographyByKey("cardSubheader")} color: ${(props) => props.theme.colors.auth.text}; text-align: center; - margin-top: ${(props) => props.theme.spaces[11]}px; & a { color: ${(props) => props.theme.colors.auth.text}; } diff --git a/app/client/src/pages/UserAuth/index.tsx b/app/client/src/pages/UserAuth/index.tsx index 923110f50c05..ba5c4cf151b6 100644 --- a/app/client/src/pages/UserAuth/index.tsx +++ b/app/client/src/pages/UserAuth/index.tsx @@ -1,12 +1,10 @@ import React from "react"; import { Route, Switch, useLocation, useRouteMatch } from "react-router-dom"; import Login from "@appsmith/pages/UserAuth/Login"; -import { AuthCard, AuthCardContainer, AuthContainer } from "./StyledComponents"; import SignUp from "@appsmith/pages/UserAuth/SignUp"; import ForgotPassword from "./ForgotPassword"; import ResetPassword from "./ResetPassword"; -import PageNotFound from "pages/common/PageNotFound"; -import FooterLinks from "./FooterLinks"; +import PageNotFound from "pages/common/ErrorPages/PageNotFound"; import * as Sentry from "@sentry/react"; import { requiresUnauth } from "./requiresAuthHOC"; import { useSelector } from "react-redux"; @@ -25,28 +23,23 @@ export function UserAuth() { return ( - - - - - - - - - - - - - - +
+ + + + + + + +
); } diff --git a/app/client/src/pages/common/AppHeader.tsx b/app/client/src/pages/common/AppHeader.tsx index c82b3302f343..cb21ea4d9b7a 100644 --- a/app/client/src/pages/common/AppHeader.tsx +++ b/app/client/src/pages/common/AppHeader.tsx @@ -1,7 +1,6 @@ import React from "react"; import ReactDOM from "react-dom"; import PageHeader from "pages/common/PageHeader"; -import LoginHeader from "pages/common/LoginHeader"; import { Route, Switch } from "react-router"; import { VIEWER_PATH, @@ -37,7 +36,7 @@ class AppHeader extends React.Component { return ( - + diff --git a/app/client/src/pages/common/AppRoute.tsx b/app/client/src/pages/common/AppRoute.tsx deleted file mode 100644 index f0f78e2d90ab..000000000000 --- a/app/client/src/pages/common/AppRoute.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import React from "react"; -import { Route, RouteComponentProps } from "react-router-dom"; -import * as Sentry from "@sentry/react"; -import { connect } from "react-redux"; -import { getCurrentThemeDetails, ThemeMode } from "selectors/themeSelectors"; -import { AppState } from "@appsmith/reducers"; -import { setThemeMode } from "actions/themeActions"; -import equal from "fast-deep-equal/es6"; -const SentryRoute = Sentry.withSentryRouting(Route); - -interface AppRouteProps { - currentTheme: any; - path?: string; - component: - | React.ComponentType> - | React.ComponentType; - exact?: boolean; - logDisable?: boolean; - name: string; - location?: any; - setTheme: (themeMode: ThemeMode) => void; -} - -class AppRouteWithoutProps extends React.Component { - shouldComponentUpdate(prevProps: AppRouteProps, nextProps: AppRouteProps) { - return !equal(prevProps?.location, nextProps?.location); - } - - render() { - const { currentTheme, ...rest } = this.props; - if ( - window.location.pathname === "/applications" || - window.location.pathname.indexOf("/settings/") !== -1 - ) { - document.body.style.backgroundColor = - currentTheme.colors.homepageBackground; - } else { - document.body.style.backgroundColor = currentTheme.colors.appBackground; - } - return ; - } -} -const mapStateToProps = (state: AppState) => ({ - currentTheme: getCurrentThemeDetails(state), -}); -const mapDispatchToProps = (dispatch: any) => ({ - setTheme: (mode: ThemeMode) => { - dispatch(setThemeMode(mode)); - }, -}); - -const AppRoute = connect( - mapStateToProps, - mapDispatchToProps, -)(AppRouteWithoutProps); - -(AppRoute as any).whyDidYouRender = { - logOnDifferentValues: false, -}; - -export default AppRoute; diff --git a/app/client/src/pages/common/CanvasArenas/StickyCanvasArena.tsx b/app/client/src/pages/common/CanvasArenas/StickyCanvasArena.tsx index a57341c755a3..f6d08ded5620 100644 --- a/app/client/src/pages/common/CanvasArenas/StickyCanvasArena.tsx +++ b/app/client/src/pages/common/CanvasArenas/StickyCanvasArena.tsx @@ -33,10 +33,6 @@ const StyledCanvasSlider = styled.div<{ paddingBottom: number }>` overflow-y: auto; `; -const StickyCanvas = styled.canvas` - position: absolute; -`; - export const StickyCanvasArena = forwardRef( (props: StickyCanvasArenaProps, ref: any) => { const { @@ -126,7 +122,8 @@ export const StickyCanvasArena = forwardRef( {/* Canvas will always be sticky to its scrollable parent's view port. i.e, it will only be as big as its viewable area so maximum size would be less than screen width and height in all cases. */} - props.theme.fontWeights[3]}; - font-size: 24px; - } - .page-unavailable-img { - width: 35%; - } - .button-position { - margin: auto; - } -`; - -interface Props { - flushErrors?: any; -} - -function ClientError(props: Props) { - const { flushErrors } = props; - - return ( - - Page Unavailable -
-

Whoops something went wrong!

-

This is embarrassing, please contact Appsmith support for help

-
-
- ); -} - -export default connect(null, { - flushErrors, -})(ClientError); diff --git a/app/client/src/pages/common/ErrorPage.tsx b/app/client/src/pages/common/ErrorPage.tsx index 40b765b9c52b..3921deee8cb2 100644 --- a/app/client/src/pages/common/ErrorPage.tsx +++ b/app/client/src/pages/common/ErrorPage.tsx @@ -1,10 +1,10 @@ import React from "react"; import { ERROR_CODES } from "@appsmith/constants/ApiConstants"; -import PageNotFound from "pages/common/PageNotFound"; -import ServerTimeout from "pages/common/ServerTimeout"; -import ServerUnavailable from "pages/common/ServerUnavailable"; -import ClientError from "pages/common/ClientError"; +import PageNotFound from "pages/common/ErrorPages/PageNotFound"; +import ServerTimeout from "pages/common/ErrorPages/ServerTimeout"; +import ServerUnavailable from "pages/common/ErrorPages/ServerUnavailable"; +import ClientError from "pages/common/ErrorPages/ClientError"; interface ErrorPageProps { code: ERROR_CODES; diff --git a/app/client/src/pages/common/ErrorPageHeader.tsx b/app/client/src/pages/common/ErrorPageHeader.tsx index f89fd05ff299..5373dbf25497 100644 --- a/app/client/src/pages/common/ErrorPageHeader.tsx +++ b/app/client/src/pages/common/ErrorPageHeader.tsx @@ -1,10 +1,9 @@ import React from "react"; import { Link, useLocation } from "react-router-dom"; -import { connect } from "react-redux"; +import { connect, useSelector } from "react-redux"; import { getCurrentUser } from "selectors/usersSelectors"; import styled from "styled-components"; import StyledHeader from "components/designSystems/appsmith/StyledHeader"; -import { ReactComponent as AppsmithLogo } from "assets/svg/appsmith_logo_primary.svg"; import { AppState } from "@appsmith/reducers"; import { User, ANONYMOUS_USERNAME } from "constants/userConstants"; import { AUTH_LOGIN_URL, APPLICATIONS_URL } from "constants/routes"; @@ -13,16 +12,19 @@ import { Colors } from "constants/Colors"; import ProfileDropdown from "./ProfileDropdown"; import { flushErrorsAndRedirect, flushErrors } from "actions/errorActions"; import { getSafeCrash } from "selectors/errorSelectors"; +import { Indices } from "constants/Layers"; +import { getTenantConfig } from "@appsmith/selectors/tenantSelectors"; const StyledPageHeader = styled(StyledHeader)` - background: ${Colors.ALABASTER_ALT}; + box-shadow: none; + justify-content: normal; + background: white; height: 48px; color: white; - flex-direction: row; position: fixed; top: 0; - z-index: 10; - box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.05); + z-index: ${Indices.Layer9}; + box-shadow: 0px 1px 0px ${Colors.GALLERY_2}; `; const HeaderSection = styled.div` @@ -33,11 +35,6 @@ const HeaderSection = styled.div` const StyledDropDownContainer = styled.div``; -const AppsmithLogoImg = styled(AppsmithLogo)` - max-width: 110px; - width: 110px; -`; - type ErrorPageHeaderProps = { user?: User; flushErrors?: any; @@ -48,6 +45,7 @@ type ErrorPageHeaderProps = { export function ErrorPageHeader(props: ErrorPageHeaderProps) { const { flushErrors, flushErrorsAndRedirect, safeCrash, user } = props; const location = useLocation(); + const tenantConfig = useSelector(getTenantConfig); const queryParams = new URLSearchParams(location.search); let loginUrl = AUTH_LOGIN_URL; const redirectUrl = queryParams.get("redirectUrl"); @@ -58,15 +56,17 @@ export function ErrorPageHeader(props: ErrorPageHeaderProps) { return ( - { - if (safeCrash) flushErrors(); - }} - to={APPLICATIONS_URL} - > - - + {tenantConfig.brandLogoUrl && ( + { + if (safeCrash) flushErrors(); + }} + to={APPLICATIONS_URL} + > + Logo + + )} {user && ( diff --git a/app/client/src/pages/common/ErrorPages/ClientError.tsx b/app/client/src/pages/common/ErrorPages/ClientError.tsx new file mode 100644 index 000000000000..5a2c2d560d49 --- /dev/null +++ b/app/client/src/pages/common/ErrorPages/ClientError.tsx @@ -0,0 +1,44 @@ +import React from "react"; +import { connect } from "react-redux"; +import { Button, Size } from "design-system"; +import { flushErrors } from "actions/errorActions"; + +import Page from "./Page"; +import { + createMessage, + PAGE_CLIENT_ERROR_DESCRIPTION, + PAGE_CLIENT_ERROR_TITLE, +} from "@appsmith/constants/messages"; + +interface Props { + flushErrors?: any; +} + +function ClientError(props: Props) { + const { flushErrors } = props; + + return ( + { + flushErrors(); + window.open("https://discord.gg/rBTTVJp", "_blank"); + }} + size={Size.large} + text="Contact us on discord" + /> + } + description={createMessage(PAGE_CLIENT_ERROR_DESCRIPTION)} + title={createMessage(PAGE_CLIENT_ERROR_TITLE)} + /> + ); +} + +export default connect(null, { + flushErrors, +})(ClientError); diff --git a/app/client/src/pages/common/ErrorPages/Page.tsx b/app/client/src/pages/common/ErrorPages/Page.tsx new file mode 100644 index 000000000000..ab46b405a51d --- /dev/null +++ b/app/client/src/pages/common/ErrorPages/Page.tsx @@ -0,0 +1,31 @@ +import React from "react"; + +type PageProps = { + children?: React.ReactNode; + errorCode?: string | number; + title?: string; + description: string; + cta?: React.ReactNode; + flushErrorsAndRedirect?: any; +}; + +function Page(props: PageProps) { + const { cta, description, errorCode, title } = props; + + return ( +
+ {errorCode && ( +
+ {errorCode} +
+ )} + {title && ( +

{title}

+ )} + {description &&

{description}

} + {cta} +
+ ); +} + +export default Page; diff --git a/app/client/src/pages/common/PageNotFound.tsx b/app/client/src/pages/common/ErrorPages/PageNotFound.tsx similarity index 52% rename from app/client/src/pages/common/PageNotFound.tsx rename to app/client/src/pages/common/ErrorPages/PageNotFound.tsx index 172d93bbf737..29bc019a178c 100644 --- a/app/client/src/pages/common/PageNotFound.tsx +++ b/app/client/src/pages/common/ErrorPages/PageNotFound.tsx @@ -1,60 +1,34 @@ import React, { useEffect } from "react"; import { connect } from "react-redux"; -import styled from "styled-components"; import { APPLICATIONS_URL } from "constants/routes"; import { Button, IconPositions, Size } from "design-system"; import { flushErrorsAndRedirect } from "actions/errorActions"; -import PageUnavailableImage from "assets/images/404-image.png"; import { BACK_TO_HOMEPAGE, createMessage, PAGE_NOT_FOUND, + PAGE_NOT_FOUND_TITLE, } from "@appsmith/constants/messages"; import AnalyticsUtil from "utils/AnalyticsUtil"; -const Wrapper = styled.div` - text-align: center; - margin-top: 5%; - .bold-text { - font-weight: ${(props) => props.theme.fontWeights[3]}; - font-size: 24px; - } - .page-unavailable-img { - width: 35%; - } - .button-position { - margin: auto; - } -`; +import Page from "./Page"; -interface Props { - flushErrorsAndRedirect?: any; -} +type Props = { + flushErrorsAndRedirect: any; +}; function PageNotFound(props: Props) { const { flushErrorsAndRedirect } = props; - useEffect(() => { AnalyticsUtil.logEvent("PAGE_NOT_FOUND"); }, []); return ( - - Page Unavailable -
-

{createMessage(PAGE_NOT_FOUND)}

-

- Either this page doesn't exist, or you don't have access to{" "} -
- this page -

+ -
-
+ } + description="Either this page doesn't exist, or you don't have access to this page" + errorCode={createMessage(PAGE_NOT_FOUND_TITLE)} + title={createMessage(PAGE_NOT_FOUND)} + /> ); } diff --git a/app/client/src/pages/common/ErrorPages/ServerTimeout.tsx b/app/client/src/pages/common/ErrorPages/ServerTimeout.tsx new file mode 100644 index 000000000000..0bb9a1d68c0f --- /dev/null +++ b/app/client/src/pages/common/ErrorPages/ServerTimeout.tsx @@ -0,0 +1,34 @@ +import React from "react"; +import { Button, Size } from "design-system"; + +import Page from "./Page"; +import { + createMessage, + PAGE_SERVER_TIMEOUT_DESCRIPTION, + PAGE_SERVER_TIMEOUT_ERROR_CODE, + PAGE_SERVER_TIMEOUT_TITLE, +} from "@appsmith/constants/messages"; + +function ServerTimeout() { + return ( + window.location.reload()} + size={Size.large} + tag="button" + text={"Retry"} + variant="info" + /> + } + description={createMessage(PAGE_SERVER_TIMEOUT_DESCRIPTION)} + errorCode={createMessage(PAGE_SERVER_TIMEOUT_ERROR_CODE)} + title={createMessage(PAGE_SERVER_TIMEOUT_TITLE)} + /> + ); +} + +export default ServerTimeout; diff --git a/app/client/src/pages/common/ErrorPages/ServerUnavailable.tsx b/app/client/src/pages/common/ErrorPages/ServerUnavailable.tsx new file mode 100644 index 000000000000..20adf37c185e --- /dev/null +++ b/app/client/src/pages/common/ErrorPages/ServerUnavailable.tsx @@ -0,0 +1,34 @@ +import React from "react"; +import { Button, Size } from "design-system"; + +import Page from "./Page"; +import { + createMessage, + PAGE_SERVER_UNAVAILABLE_DESCRIPTION, + PAGE_SERVER_UNAVAILABLE_ERROR_CODE, + PAGE_SERVER_UNAVAILABLE_TITLE, +} from "@appsmith/constants/messages"; + +function ServerUnavailable() { + return ( + window.location.reload()} + size={Size.large} + tag="button" + text={"Retry"} + variant="info" + /> + } + description={createMessage(PAGE_SERVER_UNAVAILABLE_DESCRIPTION)} + errorCode={createMessage(PAGE_SERVER_UNAVAILABLE_ERROR_CODE)} + title={createMessage(PAGE_SERVER_UNAVAILABLE_TITLE)} + /> + ); +} + +export default ServerUnavailable; diff --git a/app/client/src/pages/common/MobileSidebar.tsx b/app/client/src/pages/common/MobileSidebar.tsx index 759bdef6e955..8f344b34f967 100644 --- a/app/client/src/pages/common/MobileSidebar.tsx +++ b/app/client/src/pages/common/MobileSidebar.tsx @@ -3,7 +3,6 @@ import styled from "styled-components"; import { Colors } from "constants/Colors"; import ProfileImage from "pages/common/ProfileImage"; import { MenuItem } from "design-system"; -import { ADMIN_SETTINGS_CATEGORY_DEFAULT_PATH } from "constants/routes"; import { DropdownOnSelectActions, getOnSelectAction, @@ -19,6 +18,8 @@ import { } from "@appsmith/constants/messages"; import { getAppsmithConfigs } from "@appsmith/configs"; import { howMuchTimeBeforeText } from "utils/helpers"; +import { getDefaultAdminSettingsPath } from "@appsmith/utils/adminSettingsHelpers"; +import { getTenantPermissions } from "@appsmith/selectors/tenantSelectors"; type MobileSideBarProps = { name: string; @@ -97,6 +98,7 @@ const LeftPaneVersionData = styled.div` export default function MobileSideBar(props: MobileSideBarProps) { const user = useSelector(getCurrentUser); + const tenantPermissions = useSelector(getTenantPermissions); const { appVersion, cloudHosting } = getAppsmithConfigs(); const howMuchTimeBefore = howMuchTimeBeforeText(appVersion.releaseDate); @@ -122,7 +124,10 @@ export default function MobileSideBar(props: MobileSideBarProps) { icon="setting" onSelect={() => { getOnSelectAction(DropdownOnSelectActions.REDIRECT, { - path: ADMIN_SETTINGS_CATEGORY_DEFAULT_PATH, + path: getDefaultAdminSettingsPath({ + isSuperUser: user?.isSuperUser, + tenantPermissions, + }), }); }} text={createMessage(ADMIN_SETTINGS)} diff --git a/app/client/src/pages/common/PageHeader.tsx b/app/client/src/pages/common/PageHeader.tsx index 86458e8038d6..49b4ba94c6ac 100644 --- a/app/client/src/pages/common/PageHeader.tsx +++ b/app/client/src/pages/common/PageHeader.tsx @@ -4,7 +4,6 @@ import { connect, useDispatch, useSelector } from "react-redux"; import { getCurrentUser, selectFeatureFlags } from "selectors/usersSelectors"; import styled from "styled-components"; import StyledHeader from "components/designSystems/appsmith/StyledHeader"; -import { ReactComponent as AppsmithLogo } from "assets/svg/appsmith_logo_primary.svg"; import { AppState } from "@appsmith/reducers"; import { User, ANONYMOUS_USERNAME } from "constants/userConstants"; import { @@ -26,6 +25,7 @@ import MobileSideBar from "./MobileSidebar"; import { Indices } from "constants/Layers"; import { Icon, IconSize } from "design-system"; import { getTemplateNotificationSeenAction } from "actions/templateActions"; +import { getTenantConfig } from "@appsmith/selectors/tenantSelectors"; const StyledPageHeader = styled(StyledHeader)<{ hideShadow?: boolean; @@ -33,7 +33,7 @@ const StyledPageHeader = styled(StyledHeader)<{ showSeparator?: boolean; showingTabs: boolean; }>` - box-shadow: 0px 1px 0px ${Colors.GALLERY_2}; + box-shadow: none; justify-content: normal; background: white; height: 48px; @@ -41,21 +41,7 @@ const StyledPageHeader = styled(StyledHeader)<{ position: fixed; top: 0; z-index: ${Indices.Layer9}; - box-shadow: ${(props) => { - if (props.isMobile) { - return `0px 4px 4px rgba(0, 0, 0, 0.05)`; - } - if (props.hideShadow) { - // solid line - return `0px 1px 0px ${Colors.GALLERY_2}`; - } else { - return `0px 4px 4px rgba(0, 0, 0, 0.05)`; - } - }}; - ${(props) => - props.showingTabs && - !props.isMobile && - `box-shadow: 0px 1px 0px ${Colors.GALLERY_2};`} + box-shadow: 0px 1px 0px ${Colors.GALLERY_2}; ${({ isMobile }) => isMobile && ` @@ -104,7 +90,7 @@ const TabName = styled.div<{ isSelected: boolean }>` align-items: center; ${(props) => props.isSelected && - `border-bottom: 2px solid ${Colors.CRUSTA}; + `border-bottom: 2px solid var(--ads-color-brand); color: ${Colors.COD_GRAY};`} cursor: pointer; `; @@ -122,6 +108,7 @@ export function PageHeader(props: PageHeaderProps) { const queryParams = new URLSearchParams(location.search); const isMobile = useIsMobileDevice(); const [isMobileSidebarOpen, setIsMobileSidebarOpen] = useState(false); + const tenantConfig = useSelector(getTenantConfig); let loginUrl = AUTH_LOGIN_URL; if (queryParams.has("redirectUrl")) { loginUrl += `?redirectUrl @@ -165,15 +152,18 @@ export function PageHeader(props: PageHeaderProps) { showingTabs={showTabs} > - - - + {tenantConfig.brandLogoUrl && ( + + Logo + + )} {showTabs && !isMobile && ( <> history.push(APPLICATIONS_URL)} > diff --git a/app/client/src/pages/common/PageWrapper.tsx b/app/client/src/pages/common/PageWrapper.tsx index bf9d3d2b8055..7f5e07879176 100644 --- a/app/client/src/pages/common/PageWrapper.tsx +++ b/app/client/src/pages/common/PageWrapper.tsx @@ -31,9 +31,10 @@ const Wrapper = styled.section<{ isFixed?: boolean }>` } `; -const PageBody = styled.div` +const PageBody = styled.div<{ isSavable?: boolean }>` height: calc( - 100vh - ${(props) => props.theme.homePage.header}px + 100vh - ${(props) => props.theme.homePage.header}px - ${(props) => + props.isSavable ? "84px" : "0px"} ); display: flex; flex-direction: column; @@ -50,10 +51,11 @@ type PageWrapperProps = { children?: ReactNode; displayName?: string; isFixed?: boolean; + isSavable?: boolean; }; export function PageWrapper(props: PageWrapperProps) { - const { isFixed = false } = props; + const { isFixed = false, isSavable = false } = props; return ( @@ -61,7 +63,7 @@ export function PageWrapper(props: PageWrapperProps) { props.displayName ? `${props.displayName} | ` : "" }Appsmith`} - {props.children} + {props.children} ); } diff --git a/app/client/src/pages/common/ServerTimeout.tsx b/app/client/src/pages/common/ServerTimeout.tsx deleted file mode 100644 index a8bb85b67124..000000000000 --- a/app/client/src/pages/common/ServerTimeout.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import React from "react"; -import styled from "styled-components"; -import AppTimeoutImage from "assets/images/timeout-image.png"; -import { Button, Size } from "design-system"; - -const Wrapper = styled.div` - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - text-align: center; - height: calc(100vh - ${(props) => props.theme.headerHeight}); - .bold-text { - font-weight: ${(props) => props.theme.fontWeights[3]}; - font-size: 24px; - } - .page-unavailable-img { - width: 35%; - } - .button-position { - margin: auto; - } -`; - -function ServerTimeout() { - return ( - - Page Unavailable -
-

- Appsmith server is taking too long to respond -

-

Please retry after some time

-
-
- ); -} - -export default ServerTimeout; diff --git a/app/client/src/pages/common/ServerUnavailable.tsx b/app/client/src/pages/common/ServerUnavailable.tsx deleted file mode 100644 index e16b9ca8a7a5..000000000000 --- a/app/client/src/pages/common/ServerUnavailable.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import React from "react"; -import styled from "styled-components"; -import PageUnavailableImage from "assets/images/404-image.png"; -import { Button, Size } from "design-system"; - -const Wrapper = styled.div` - height: calc(100vh - ${(props) => props.theme.headerHeight}); - background-color: #fafafa; - text-align: center; - padding-top: calc(${(props) => props.theme.headerHeight} + 50px); - .bold-text { - font-weight: ${(props) => props.theme.fontWeights[3]}; - font-size: 24px; - } - .page-unavailable-img { - width: 35%; - margin: auto; - } - .button-position { - margin: auto; - } -`; - -function ServerUnavailable() { - return ( - - Page Unavailable -
-

Appsmith server is unavailable

-

Please try again after some time

-
-
- ); -} - -export default ServerUnavailable; diff --git a/app/client/src/pages/common/SharedUserList.tsx b/app/client/src/pages/common/SharedUserList.tsx index 214cabd549f4..8e9c42c4b7d9 100644 --- a/app/client/src/pages/common/SharedUserList.tsx +++ b/app/client/src/pages/common/SharedUserList.tsx @@ -2,7 +2,7 @@ import { Popover, PopoverInteractionKind, Position } from "@blueprintjs/core"; import UserApi from "@appsmith/api/UserApi"; import React, { useMemo } from "react"; import { getCurrentUser } from "selectors/usersSelectors"; -import { useSelector } from "store"; +import { useSelector } from "react-redux"; import styled from "styled-components"; import ProfileImage from "./ProfileImage"; import { ScrollIndicator } from "design-system"; diff --git a/app/client/src/pages/common/datasourceAuth/index.tsx b/app/client/src/pages/common/datasourceAuth/index.tsx index 10b4e0c886fb..a60d733643cd 100644 --- a/app/client/src/pages/common/datasourceAuth/index.tsx +++ b/app/client/src/pages/common/datasourceAuth/index.tsx @@ -4,12 +4,10 @@ import { ActionButton, SaveButtonContainer, } from "pages/Editor/DataSourceEditor/JSONtoForm"; -import EditButton from "components/editorComponents/Button"; import { useDispatch, useSelector } from "react-redux"; import { getEntities, getPluginTypeFromDatasourceId, - getIsReconnectingDatasourcesModalOpen, } from "selectors/entitiesSelector"; import { testDatasource, @@ -17,15 +15,14 @@ import { updateDatasource, redirectAuthorizationCode, getOAuthAccessToken, + setDatasourceViewMode, + createDatasourceFromForm, + toggleSaveActionFlag, } from "actions/datasourceActions"; import AnalyticsUtil from "utils/AnalyticsUtil"; -import { redirectToNewIntegrations } from "actions/apiPaneActions"; -import { getQueryParams } from "utils/URLUtils"; import { getCurrentApplicationId } from "selectors/editorSelectors"; import { useParams, useLocation } from "react-router"; import { ExplorerURLParams } from "pages/Editor/Explorer/helpers"; -import { getIsGeneratePageInitiator } from "utils/GenerateCrudUtil"; -import { ButtonVariantTypes } from "components/constants"; import { AppState } from "@appsmith/reducers"; import { AuthType, @@ -36,13 +33,19 @@ import { OAUTH_AUTHORIZATION_APPSMITH_ERROR, OAUTH_AUTHORIZATION_FAILED, } from "@appsmith/constants/messages"; -import { Toaster, Variant } from "design-system"; +import { Category, Toaster, Variant } from "design-system"; import { CONTEXT_DELETE, CONFIRM_CONTEXT_DELETE, createMessage, } from "@appsmith/constants/messages"; import { debounce } from "lodash"; +import { TEMP_DATASOURCE_ID } from "constants/Datasource"; + +import { + hasDeleteDatasourcePermission, + hasManageDatasourcePermission, +} from "@appsmith/utils/permissionHelpers"; interface Props { datasource: Datasource; @@ -52,6 +55,9 @@ interface Props { pageId?: string; shouldRender: boolean; datasourceButtonConfiguration: string[] | undefined; + triggerSave?: boolean; + isFormDirty?: boolean; + datasourceDeleteTrigger: () => void; } export type DatasourceFormButtonTypes = Record; @@ -78,7 +84,7 @@ export const DatasourceButtonType: Record< SAVE_AND_AUTHORIZE: "SAVE_AND_AUTHORIZE", }; -const StyledButton = styled(EditButton)<{ fluidWidth?: boolean }>` +const StyledButton = styled(ActionButton)<{ fluidWidth?: boolean }>` &&&& { height: 32px; width: ${(props) => (props.fluidWidth ? "" : "87px")}; @@ -97,11 +103,14 @@ const StyledAuthMessage = styled.div` function DatasourceAuth({ datasource, datasourceButtonConfiguration = ["DELETE", "SAVE"], + datasourceDeleteTrigger, formData, getSanitizedFormData, isInvalid, pageId: pageIdProp, shouldRender, + triggerSave, + isFormDirty, }: Props) { const authType = formData && @@ -110,6 +119,16 @@ function DatasourceAuth({ const { id: datasourceId, isDeleting } = datasource; const applicationId = useSelector(getCurrentApplicationId); + const datasourcePermissions = datasource.userPermissions || []; + + const canManageDatasource = hasManageDatasourcePermission( + datasourcePermissions, + ); + + const canDeleteDatasource = hasDeleteDatasourcePermission( + datasourcePermissions, + ); + // hooks const dispatch = useDispatch(); const location = useLocation(); @@ -171,10 +190,15 @@ function DatasourceAuth({ getPluginTypeFromDatasourceId(state, datasourceId), ); - // to check if saving during import flow - const isReconnectModelOpen: boolean = useSelector( - getIsReconnectingDatasourcesModalOpen, - ); + useEffect(() => { + if (triggerSave) { + if (pluginType === "SAAS") { + handleOauthDatasourceSave(); + } else { + handleDefaultAuthDatasourceSave(); + } + } + }, [triggerSave]); const isAuthorized = datasource?.datasourceConfiguration?.authentication @@ -185,6 +209,7 @@ function DatasourceAuth({ // Handles datasource deletion const handleDatasourceDelete = () => { dispatch(deleteDatasource({ id: datasourceId })); + datasourceDeleteTrigger(); }; // Handles datasource testing @@ -198,88 +223,115 @@ function DatasourceAuth({ // Handles default auth datasource saving const handleDefaultAuthDatasourceSave = () => { - const isGeneratePageInitiator = getIsGeneratePageInitiator(); + dispatch(toggleSaveActionFlag(true)); AnalyticsUtil.logEvent("SAVE_DATA_SOURCE_CLICK", { pageId: pageId, appId: applicationId, }); // After saving datasource, only redirect to the 'new integrations' page // if datasource is not used to generate a page - dispatch( - updateDatasource( - getSanitizedFormData(), - !isGeneratePageInitiator && !isReconnectModelOpen - ? dispatch(redirectToNewIntegrations(pageId, getQueryParams())) - : undefined, - ), - ); + if (datasource.id === TEMP_DATASOURCE_ID) { + dispatch(createDatasourceFromForm(getSanitizedFormData())); + } else { + dispatch(setDatasourceViewMode(true)); + // we dont need to redirect it to active ds list instead ds would be shown in view only mode + dispatch(updateDatasource(getSanitizedFormData())); + } }; // Handles Oauth datasource saving const handleOauthDatasourceSave = () => { - dispatch( - updateDatasource( - getSanitizedFormData(), - pluginType - ? redirectAuthorizationCode(pageId, datasourceId, pluginType) - : undefined, - ), - ); + dispatch(toggleSaveActionFlag(true)); + if (datasource.id === TEMP_DATASOURCE_ID) { + dispatch( + createDatasourceFromForm( + getSanitizedFormData(), + pluginType + ? redirectAuthorizationCode(pageId, datasourceId, pluginType) + : undefined, + ), + ); + } else { + dispatch(setDatasourceViewMode(true)); + dispatch( + updateDatasource( + getSanitizedFormData(), + pluginType + ? redirectAuthorizationCode(pageId, datasourceId, pluginType) + : undefined, + ), + ); + } }; + const createMode = datasourceId === TEMP_DATASOURCE_ID; + const datasourceButtonsComponentMap = (buttonType: string): JSX.Element => { return { [DatasourceButtonType.DELETE]: ( { confirmDelete ? handleDatasourceDelete() : setConfirmDelete(true); }} + size="medium" + tag="button" text={ confirmDelete && !isDeleting ? createMessage(CONFIRM_CONTEXT_DELETE) : createMessage(CONTEXT_DELETE) } + variant={Variant.danger} /> ), [DatasourceButtonType.TEST]: ( ), [DatasourceButtonType.SAVE]: ( - ), [DatasourceButtonType.SAVE_AND_AUTHORIZE]: ( ), }[buttonType]; diff --git a/app/client/src/pages/setup/DetailsForm.tsx b/app/client/src/pages/setup/DetailsForm.tsx index 9e32d2be4c5b..bb1595efdf73 100644 --- a/app/client/src/pages/setup/DetailsForm.tsx +++ b/app/client/src/pages/setup/DetailsForm.tsx @@ -141,7 +141,7 @@ export default function DetailsForm( )}
- - - - - -
- - - - - - -
- - - - - - -
- - - - - - - -
- - - - - - - - - - - - - -
- -
- - - - - - -
-
-
- Hello, -
-
-
-
- Forgot the password to your Appsmith account? No worries, we've got you covered. -
-
-
-
- - - - - - -
- - - - - - -
- Reset Password -
-
- - - - - - -
-
-
- The link will expire in 48 hours. If you didn't request a password reset, you can safely ignore this email. -
-
-
-
- Cheers -
-
- Devs at Appsmith -
-
-
-
-
- -
-
-
-