fix: added container wrapper component to handle styles (#6476)

* FIX #2587 : added container wrapper component to handle styles, updated test cases

* FIX #2587 : abstract widget style component for common use and wraped on text widget

* updated style boundary and css

* updated cypress test

* fix: accommodate prop pane height change for the test

* removed properties from text widget

* updated container wrapper style to handle border corners, updated migrations for container and form widget

* fixed container overflow

* added testcases for container and text widget

Co-authored-by: Rishabh Saxena <rishabh.robben@gmail.com>
This commit is contained in:
Yash Vibhandik 2021-09-16 09:51:31 +05:30 committed by GitHub
parent c4de8fc720
commit aea55014ec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 386 additions and 84 deletions

View File

@ -106,4 +106,25 @@ describe("Text Widget color/font/alignment Functionality", function() {
});
cy.get(commonlocators.headingTextStyle).scrollIntoView({ duration: 2000 });
});
it("Test border width, color and verity", function() {
cy.openPropertyPane("textwidget");
cy.testJsontext("borderwidth", "10");
cy.get(
`div[data-testid='container-wrapper-${dsl.dsl.children[0].widgetId}'] div`,
)
.should("have.css", "border-width")
.and("eq", "10px");
cy.get(widgetsPage.boadercolorPicker)
.first()
.click({ force: true });
cy.xpath(widgetsPage.yellowColor).click();
cy.get(
`div[data-testid='container-wrapper-${dsl.dsl.children[0].widgetId}'] div`,
)
.should("have.css", "border-color")
.and("eq", "rgb(255, 193, 61)");
});
});

View File

@ -1,4 +1,5 @@
const commonlocators = require("../../../../locators/commonlocators.json");
const publish = require("../../../../locators/publishWidgetspage.json");
const widgetsPage = require("../../../../locators/Widgets.json");
const dsl = require("../../../../fixtures/containerdsl.json");
@ -20,7 +21,19 @@ describe("Container Widget Functionality", function() {
commonlocators.containerInnerText,
);
/**
* @param{Text} Random Colour
* @param{Text} Random Border Colour
*/
cy.get(widgetsPage.boadercolorPicker)
.first()
.click({ force: true });
cy.xpath(widgetsPage.yellowColor).click();
cy.get(
`div[data-testid='container-wrapper-${dsl.dsl.children[0].widgetId}'] div`,
)
.should("have.css", "border-color")
.and("eq", "rgb(255, 193, 61)");
/**
* @param{Text} Random Background Colour
*/
cy.get(widgetsPage.backgroundcolorPicker)
.first()
@ -46,6 +59,73 @@ describe("Container Widget Functionality", function() {
.should("have.css", "background-color")
.and("eq", "rgb(3, 179, 101)");
});
it("Test border width and verity", function() {
cy.get(publish.backToEditor).click();
cy.openPropertyPane("containerwidget");
cy.testJsontext("borderwidth", "10");
cy.get(
`div[data-testid='container-wrapper-${dsl.dsl.children[0].widgetId}'] div`,
)
.should("have.css", "border-width")
.and("eq", "10px");
});
it("Test border radius and verity", function() {
cy.testJsontext("borderradius", "10");
cy.get(
`div[data-testid='container-wrapper-${dsl.dsl.children[0].widgetId}'] div`,
)
.should("have.css", "border-radius")
.and("eq", "10px");
// should have overflow : hidden to show border edges
cy.get(
`div[data-testid='container-wrapper-${dsl.dsl.children[0].widgetId}'] div`,
)
.should("have.css", "overflow")
.and("eq", "hidden");
// wrapper should have same border radius
cy.get(
`div[data-testid='container-wrapper-${dsl.dsl.children[0].widgetId}']`,
)
.should("have.css", "border-radius")
.and("eq", "10px");
});
it("Test Box shadow and verity", function() {
cy.get(widgetsPage.boxShadow)
.children()
.eq(3)
.click({ force: true });
cy.get(
`div[data-testid='container-wrapper-${dsl.dsl.children[0].widgetId}']`,
)
.should("have.css", "box-shadow")
.and("eq", "rgba(0, 0, 0, 0.5) 0px 1px 3px 0px");
// change shadow color and check box-shadow again
cy.get(widgetsPage.boxShadowColorPicker)
.first()
.click({ force: true });
cy.xpath(widgetsPage.blueColor).click();
cy.get(
`div[data-testid='container-wrapper-${dsl.dsl.children[0].widgetId}']`,
)
.should("have.css", "box-shadow")
.and("eq", "rgb(51, 102, 255) 0px 1px 3px 0px");
});
it("Test overflow of widget boundaries", function() {
cy.testJsontext("borderwidth", "500");
// prevent overflow of widget boundaries
cy.get(
`div[data-testid='container-wrapper-${dsl.dsl.children[0].widgetId}']`,
)
.should("have.css", "overflow")
.and("eq", "hidden");
});
afterEach(() => {
// put your clean up code if any
});

View File

@ -49,7 +49,7 @@ describe("Widget property pane draggable", function() {
cy.openPropertyPane(testWidget);
cy.get("[data-cy=t--property-pane-drag-handle]")
.trigger("mousedown", { which: 1 })
.trigger("mousemove", { clientX: 400, clientY: 500 })
.trigger("mousemove", { clientX: 0, clientY: 0 })
.trigger("mouseup", { force: true });
cy.get("[data-cy=t--property-pane-drag-handle]").then((oldPorpPane) => {
const oldPropPanePosition = oldPorpPane[0].getBoundingClientRect();

View File

@ -39,6 +39,7 @@
"checkboxInput": ".t--draggable-checkboxwidget span.t--widget-name",
"checkboxLabel": ".t--draggable-checkboxwidget label",
"containerD": "div[type='CONTAINER_WIDGET']",
"containerWrapper": "div[data-testid='container-wrapper']",
"defaultInput": ".t--property-control-defaulttext .CodeMirror-code",
"placeholder": ".t--property-control-placeholder .CodeMirror-code",
"inputLabelControl": ".t--property-control-label .CodeMirror-code",
@ -81,8 +82,13 @@
"verticalCenter": ".t--icon-tab-CENTER",
"verticalBottom": ".t--icon-tab-BOTTOM",
"textColor": ".t--property-control-textcolor input",
"backgroundcolorPicker": ".t--property-control-backgroundcolor input",
"boadercolorPicker": ".t--property-control-bordercolour input",
"boxShadowColorPicker": ".t--property-control-shadowcolor input",
"boxShadow": ".t--property-control-boxshadow .bp3-button-group",
"backgroundcolorPicker": ".t--property-control-backgroundcolour input",
"greenColor": "//div[@color='#03b365']",
"yellowColor": "//div[@color='#FFC13D']",
"blueColor": "//div[@color='#3366FF']",
"toggleJsColor": ".t--property-control-textcolor .t--js-toggle",
"backgroundColor": ".t--property-control-cellbackground input",
"toggleJsBcgColor": ".t--property-control-cellbackground .t--js-toggle",

View File

@ -0,0 +1,90 @@
import React, { ReactNode } from "react";
import styled from "styled-components";
import { ContainerStyle } from "widgets/ContainerWidget/component";
import { Color } from "constants/Colors";
import { Theme } from "constants/DefaultTheme";
export enum BoxShadowTypes {
NONE = "NONE",
VARIANT1 = "VARIANT1",
VARIANT2 = "VARIANT2",
VARIANT3 = "VARIANT3",
VARIANT4 = "VARIANT4",
VARIANT5 = "VARIANT5",
}
export type BoxShadow = keyof typeof BoxShadowTypes;
export interface WidgetStyleContainerProps {
widgetId: string;
containerStyle?: ContainerStyle;
children?: ReactNode;
borderColor?: Color;
borderWidth?: number;
borderRadius?: number;
boxShadow?: BoxShadow;
boxShadowColor?: string;
}
// get box shadow style string based on boxShadow and boxShadowColor
const getBoxShadow = ({
boxShadow,
boxShadowColor,
theme,
}: {
boxShadow?: BoxShadow;
boxShadowColor?: string;
theme: Theme;
}) => {
switch (boxShadow) {
case BoxShadowTypes.VARIANT1:
return `0px 0px 4px 3px ${boxShadowColor ||
theme.colors.button.boxShadow.default.variant1}`;
case BoxShadowTypes.VARIANT2:
return `3px 3px 4px ${boxShadowColor ||
theme.colors.button.boxShadow.default.variant2}`;
case BoxShadowTypes.VARIANT3:
return `0px 1px 3px ${boxShadowColor ||
theme.colors.button.boxShadow.default.variant3}`;
case BoxShadowTypes.VARIANT4:
return `2px 2px 0px ${boxShadowColor ||
theme.colors.button.boxShadow.default.variant4}`;
case BoxShadowTypes.VARIANT5:
return `-2px -2px 0px ${boxShadowColor ||
theme.colors.button.boxShadow.default.variant5}`;
default:
return "none";
}
};
const WidgetStyle = styled.div<WidgetStyleContainerProps>`
height: 100%;
width: 100%;
overflow: hidden;
border-radius: ${(props) => props.borderRadius}px;
box-shadow: ${(props) => getBoxShadow(props)} !important;
& > div {
${(props) =>
props.containerStyle !== "none"
? `
border-width: ${props.borderWidth}px;
border-radius: ${props.borderRadius}px;
border-color: ${props.borderColor || "transparent"};
border-style: solid;`
: ""}
height: 100%;
width: 100%;
overflow: hidden;
}
}`;
// wrapper component for apply styles on any widget boundary
function WidgetStyleContainer(props: WidgetStyleContainerProps) {
return (
<WidgetStyle {...props} data-testid={`container-wrapper-${props.widgetId}`}>
<div>{props.children}</div>
</WidgetStyle>
);
}
export default WidgetStyleContainer;

View File

@ -0,0 +1,25 @@
import { WidgetProps } from "widgets/BaseWidget";
import { ContainerWidgetProps } from "widgets/ContainerWidget/widget";
import WidgetFactory from "utils/WidgetFactory";
const WidgetTypes = WidgetFactory.widgetTypes;
export const migrateContainerAndFormWidgetStyleProperties = (
currentDSL: ContainerWidgetProps<WidgetProps>,
) => {
currentDSL.children = currentDSL.children?.map((child: WidgetProps) => {
if (
child.type === WidgetTypes.CONTAINER_WIDGET ||
child.type === WidgetTypes.FORM_WIDGET
) {
if (!("borderWidth" in child)) {
child.borderWidth = "0";
child.borderRadius = "0";
}
} else if (child.children && child.children.length > 0) {
child = migrateContainerAndFormWidgetStyleProperties(child);
}
return child;
});
return currentDSL;
};

View File

@ -5,6 +5,10 @@ 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";
const scrollContents = css`
@ -16,12 +20,6 @@ const StyledContainerComponent = styled.div<
ref: RefObject<HTMLDivElement>;
}
>`
${(props) =>
props.containerStyle !== "none"
? `
box-shadow: 0px 0px 0px 1px #E7E7E7;
border-radius: 0;`
: ""}
height: 100%;
width: 100%;
background: ${(props) => props.backgroundColor};
@ -70,25 +68,38 @@ function ContainerComponent(props: ContainerComponentProps) {
}
}, [props.shouldScrollContents]);
return (
<StyledContainerComponent
{...props}
className={`${
props.shouldScrollContents ? getCanvasClassName() : ""
} ${generateClassName(props.widgetId)}`}
containerStyle={containerStyle}
// Before you remove: generateClassName is used for bounding the resizables within this canvas
// getCanvasClassName is used to add a scrollable parent.
ref={containerRef}
<WidgetStyleContainer
{...pick(props, [
"widgetId",
"containerStyle",
"borderColor",
"borderWidth",
"borderRadius",
"boxShadow",
"boxShadowColor",
])}
>
{props.children}
</StyledContainerComponent>
<StyledContainerComponent
{...props}
className={`${
props.shouldScrollContents ? getCanvasClassName() : ""
} ${generateClassName(props.widgetId)}`}
containerStyle={containerStyle}
// Before you remove: generateClassName is used for bounding the resizables within this canvas
// getCanvasClassName is used to add a scrollable parent.
ref={containerRef}
>
{props.children}
</StyledContainerComponent>
</WidgetStyleContainer>
);
}
export type ContainerStyle = "border" | "card" | "rounded-border" | "none";
export interface ContainerComponentProps extends ComponentProps {
containerStyle?: ContainerStyle;
export interface ContainerComponentProps
extends ComponentProps,
WidgetStyleContainerProps {
children?: ReactNode;
className?: string;
backgroundColor?: Color;

View File

@ -1,6 +1,7 @@
import Widget from "./widget";
import IconSVG from "./icon.svg";
import { GRID_DENSITY_MIGRATION_V1 } from "widgets/constants";
import { ButtonBoxShadowTypes } from "components/constants";
export const CONFIG = {
type: Widget.getWidgetType(),
@ -13,6 +14,10 @@ export const CONFIG = {
columns: 8 * GRID_DENSITY_MIGRATION_V1,
widgetName: "Container",
containerStyle: "card",
borderColor: "transparent",
borderWidth: "0",
borderRadius: "0",
boxShadow: ButtonBoxShadowTypes.NONE,
children: [],
blueprint: {
view: [

View File

@ -33,17 +33,6 @@ class ContainerWidget extends BaseWidget<
{
sectionName: "General",
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: "Controls the visibility of the widget",
propertyName: "isVisible",
@ -63,6 +52,82 @@ class ContainerWidget extends BaseWidget<
},
],
},
{
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 Colour",
controlType: "COLOR_PICKER",
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 Colour",
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 },
},
{
helpText: "Enter value for border radius",
propertyName: "borderRadius",
label: "Border Radius",
placeholderText: "Enter value in px",
controlType: "INPUT_TEXT",
isBindProperty: true,
isTriggerProperty: false,
validation: { type: ValidationTypes.NUMBER },
},
{
propertyName: "boxShadow",
label: "Box Shadow",
helpText:
"Enables you to cast a drop shadow from the frame of the widget",
controlType: "BOX_SHADOW_OPTIONS",
isBindProperty: false,
isTriggerProperty: false,
validation: {
type: ValidationTypes.TEXT,
params: {
allowedValues: [
"NONE",
"VARIANT1",
"VARIANT2",
"VARIANT3",
"VARIANT4",
"VARIANT5",
],
},
},
},
{
propertyName: "boxShadowColor",
helpText: "Sets the shadow color of the widget",
label: "Shadow Color",
controlType: "COLOR_PICKER",
isBindProperty: false,
isTriggerProperty: false,
validation: { type: ValidationTypes.TEXT },
},
],
},
];
}

View File

@ -6,46 +6,8 @@ import ContainerWidget, {
ContainerWidgetProps,
} from "widgets/ContainerWidget/widget";
import { ContainerComponentProps } from "widgets/ContainerWidget/component";
import { ValidationTypes } from "constants/WidgetValidation";
class FormWidget extends ContainerWidget {
static getPropertyPaneConfig() {
return [
{
sectionName: "General",
children: [
{
propertyName: "backgroundColor",
label: "Background Color",
helpText: "Use a html color name, HEX, RGB or RGBA value",
placeholderText: "#FFFFFF / Gray / rgb(255, 99, 71)",
controlType: "COLOR_PICKER",
isBindProperty: true,
isJSConvertible: true,
isTriggerProperty: false,
validation: { type: ValidationTypes.TEXT },
},
{
helpText: "Controls the visibility of the widget",
propertyName: "isVisible",
label: "Visible",
controlType: "SWITCH",
isJSConvertible: true,
isBindProperty: true,
isTriggerProperty: false,
validation: { type: ValidationTypes.BOOLEAN },
},
{
propertyName: "shouldScrollContents",
label: "Scroll Contents",
controlType: "SWITCH",
isBindProperty: false,
isTriggerProperty: false,
},
],
},
];
}
checkInvalidChildren = (children: WidgetProps[]): boolean => {
return some(children, (child) => {
if ("children" in child) {

View File

@ -1,4 +1,8 @@
import React from "react";
import { pick } from "lodash";
import WidgetStyleContainer, {
WidgetStyleContainerProps,
} from "components/designSystems/appsmith/WidgetStyleContainer";
import { TextSize } from "constants/WidgetConstants";
@ -68,6 +72,27 @@ class TextWidget extends BaseWidget<TextWidgetProps, WidgetState> {
},
},
},
{
helpText: "Use a html color name, HEX, RGB or RGBA value",
placeholderText: "#FFFFFF / Gray / rgb(255, 99, 71)",
propertyName: "borderColor",
label: "Border Colour",
controlType: "COLOR_PICKER",
isBindProperty: true,
isTriggerProperty: false,
validation: { type: ValidationTypes.TEXT },
},
{
helpText:
"Enter value for border width which can also use as margin",
propertyName: "borderWidth",
label: "Border Width",
placeholderText: "Enter value in px",
controlType: "INPUT_TEXT",
isBindProperty: true,
isTriggerProperty: false,
validation: { type: ValidationTypes.NUMBER },
},
{
propertyName: "fontSize",
label: "Text Size",
@ -157,18 +182,27 @@ class TextWidget extends BaseWidget<TextWidgetProps, WidgetState> {
getPageView() {
return (
<TextComponent
backgroundColor={this.props.backgroundColor}
fontSize={this.props.fontSize}
fontStyle={this.props.fontStyle}
isLoading={this.props.isLoading}
key={this.props.widgetId}
shouldScroll={this.props.shouldScroll}
text={this.props.text}
textAlign={this.props.textAlign ? this.props.textAlign : "LEFT"}
textColor={this.props.textColor}
widgetId={this.props.widgetId}
/>
<WidgetStyleContainer
{...pick(this.props, [
"widgetId",
"containerStyle",
"borderColor",
"borderWidth",
])}
>
<TextComponent
backgroundColor={this.props.backgroundColor}
fontSize={this.props.fontSize}
fontStyle={this.props.fontStyle}
isLoading={this.props.isLoading}
key={this.props.widgetId}
shouldScroll={this.props.shouldScroll}
text={this.props.text}
textAlign={this.props.textAlign ? this.props.textAlign : "LEFT"}
textColor={this.props.textColor}
widgetId={this.props.widgetId}
/>
</WidgetStyleContainer>
);
}
@ -191,7 +225,10 @@ export interface TextStyles {
textAlign?: TextAlign;
}
export interface TextWidgetProps extends WidgetProps, TextStyles {
export interface TextWidgetProps
extends WidgetProps,
TextStyles,
WidgetStyleContainerProps {
text?: string;
isLoading: boolean;
shouldScroll: boolean;