Merge branch 'release' into 'master'

Release

See merge request theappsmith/internal-tools-client!383
This commit is contained in:
Hetu Nandu 2020-03-17 07:37:52 +00:00
commit 393f7fb33f
52 changed files with 946 additions and 2390 deletions

View File

@ -29,4 +29,5 @@ yarn-error.log*
.idea
.storybook-out/
cypress/videos
results/
cypress/screenshots
results/

View File

@ -48,6 +48,7 @@ cypress-test:
- echo "127.0.0.1 dev.appsmith.com" >> /etc/hosts
- yarn test
artifacts:
when: on_failure
expire_in: 1 week
paths:
- build/
@ -61,7 +62,7 @@ cypress-test:
docker-package-release:
image: docker:dind
services:
- docker:dind
- docker:dind
stage: package
script:
- docker build --build-arg REACT_APP_ENVIRONMENT=STAGING --build-arg GIT_SHA=$CI_COMMIT_SHORT_SHA -t appsmith/appsmith-editor:release .
@ -75,7 +76,7 @@ docker-package-prod:
services:
- docker:dind
stage: package
script:
script:
- docker build --build-arg REACT_APP_ENVIRONMENT=PRODUCTION --build-arg GIT_SHA=$CI_COMMIT_SHORT_SHA -t appsmith/appsmith-editor:latest .
- docker login -u $DOCKER_HUB_USERNAME -p $DOCKER_HUB_ACCESS_TOKEN
- docker push appsmith/appsmith-editor:latest

View File

@ -44,6 +44,7 @@
"flow-bin": "^0.91.0",
"fontfaceobserver": "^2.1.0",
"fuse.js": "^3.4.5",
"fusioncharts": "^3.15.0-sr.1",
"history": "^4.10.1",
"husky": "^3.0.5",
"interweave": "^12.1.1",
@ -67,6 +68,7 @@
"react-dnd-html5-backend": "^9.3.4",
"react-dnd-touch-backend": "^9.4.0",
"react-dom": "^16.7.0",
"react-fusioncharts": "^3.1.2",
"react-helmet": "^5.2.1",
"react-redux": "^7.1.3",
"react-router": "^5.1.2",
@ -157,8 +159,7 @@
},
"husky": {
"hooks": {
"pre-commit": "lint-staged",
"pre-push": "yarn run test"
"pre-commit": "lint-staged"
}
}
}

View File

@ -62,12 +62,12 @@ export interface RestAction {
cacheResponse?: string;
}
export type PaginationField = "PREV" | "NEXT" | undefined;
export type PaginationField = "PREV" | "NEXT";
export interface ExecuteActionRequest extends APIRequest {
action: Pick<RestAction, "id"> | Omit<RestAction, "id">;
params?: Property[];
paginationField: PaginationField;
paginationField?: PaginationField;
}
export interface ExecuteActionResponse extends ApiResponse {
@ -80,7 +80,7 @@ export interface ActionApiResponse {
data: {
body: object;
headers: Record<string, string[]>;
statusCode: string | number;
statusCode: string;
};
clientMeta: {
duration: string;
@ -91,7 +91,7 @@ export interface ActionApiResponse {
export interface ActionResponse {
body: object;
headers: Record<string, string[]>;
statusCode: string | number;
statusCode: string;
duration: string;
size: string;
}

View File

@ -0,0 +1,3 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16.7494 13.8508L13.069 10.0279L4.21712 19.024L3 17.8274L13.0818 7.58082L16.798 11.4404L23.1909 5.31898L21.3593 3.48817H26.1416V8.26875L24.3978 6.52586L16.7494 13.8508ZM7.23518 26.3199H12.9965V18.2114H7.23518V26.3199ZM16.237 26.3199H20.9982V15.6509H16.237V26.3199ZM24.2387 10.3164V26.3199H29V10.3164H24.2387Z" fill="#F1F7FC"/>
</svg>

After

Width:  |  Height:  |  Size: 439 B

View File

@ -0,0 +1,83 @@
import React from "react";
import ReactFC from "react-fusioncharts";
import FusionCharts from "fusioncharts";
import Column2D from "fusioncharts/fusioncharts.charts";
import FusionTheme from "fusioncharts/themes/fusioncharts.theme.fusion";
import { ChartType, ChartData } from "widgets/ChartWidget";
import styled from "styled-components";
import { invisible } from "constants/DefaultTheme";
ReactFC.fcRoot(FusionCharts, Column2D, FusionTheme);
export interface ChartComponentProps {
chartType: ChartType;
chartData: ChartData[];
xAxisName: string;
yAxisName: string;
chartName: string;
componentWidth: number;
componentHeight: number;
isVisible?: boolean;
}
const CanvasContainer = styled.div<ChartComponentProps>`
border: none;
border-radius: ${props => `${props.theme.radii[1]}px`};
height: 100%;
width: 100%;
background: white;
box-shadow: 0 1px 1px 0 rgba(60,75,100,.14),0 2px 1px -1px rgba(60,75,100,.12),0 1px 3px 0 rgba(60,75,100,.2);
position: relative;
${props => (!props.isVisible ? invisible : "")};
}`;
/* eslint-disable react/display-name */
const ChartComponent = (props: ChartComponentProps) => {
const getChartType = (chartType: ChartType) => {
switch (chartType) {
case "LINE_CHART":
return "line";
case "BAR_CHART":
return "bar2d";
case "PIE_CHART":
return "pie2d";
case "COLUMN_CHART":
return "column2d";
case "AREA_CHART":
return "area2d";
default:
return "column2d";
}
};
const getChartData = (chartData: ChartData[]) => {
return chartData.map(item => {
return {
label: item.x,
value: item.y,
};
});
};
return (
<CanvasContainer {...props}>
<ReactFC
type={getChartType(props.chartType)}
width={props.componentWidth.toString()}
height={props.componentHeight.toString()}
dataForma="json"
dataSource={{
chart: {
xAxisName: props.xAxisName,
yAxisName: props.yAxisName,
theme: "fusion",
caption: props.chartName,
},
data: getChartData(props.chartData),
}}
/>
</CanvasContainer>
);
};
export default ChartComponent;

View File

@ -32,7 +32,7 @@ class FilePickerComponent extends React.Component<
<BaseButton
accent="primary"
filled
className={this.props.isLoading ? "bp3-skeleton" : ""}
loading={this.props.isLoading}
text={label}
onClick={this.openModal}
/>

View File

@ -26,22 +26,22 @@ class CheckboxComponent extends React.Component<CheckboxComponentProps> {
className={
this.props.isLoading ? "bp3-skeleton" : Classes.RUNNING_TEXT
}
defaultChecked={this.props.defaultCheckedState}
onChange={this.onCheckChange}
disabled={this.props.isDisabled}
checked={this.props.isChecked}
/>
</CheckboxContainer>
);
}
onCheckChange = (event: React.ChangeEvent<HTMLInputElement>) => {
this.props.onCheckChange(event.target.value === "true");
onCheckChange = () => {
this.props.onCheckChange(!this.props.isChecked);
};
}
export interface CheckboxComponentProps extends ComponentProps {
label: string;
defaultCheckedState: boolean;
isChecked: boolean;
onCheckChange: (isChecked: boolean) => void;
isLoading: boolean;
}

View File

@ -8,15 +8,39 @@ import moment from "moment-timezone";
import "../../../../node_modules/@blueprintjs/datetime/lib/css/blueprint-datetime.css";
import { DatePickerType } from "widgets/DatePickerWidget";
import { WIDGET_PADDING } from "constants/WidgetConstants";
import { Colors } from "constants/Colors";
const StyledControlGroup = styled(ControlGroup)`
&&& {
.${Classes.INPUT} {
box-shadow: none;
border: 1px solid;
border-color: ${Colors.GEYSER_LIGHT};
border-radius: ${props => props.theme.radii[1]}px;
width: 100%;
height: inherit;
align-items: center;
&:active {
border-color: ${Colors.HIT_GRAY};
}
&:focus {
border-color: ${Colors.MYSTIC};
}
}
.${Classes.INPUT_GROUP} {
display: block;
margin: 0;
}
.${Classes.CONTROL_GROUP} {
justify-content: flex-start;
}
label {
${labelStyle}
flex: 0 1 30%;
margin: 7px ${WIDGET_PADDING * 2}px 0 0;
text-align: right;
margin: 0 ${WIDGET_PADDING * 2}px 0 0;
align-self: center;
align-self: flex-start;
max-width: calc(30% - ${WIDGET_PADDING}px);
}
}
`;
@ -47,14 +71,14 @@ class DatePickerComponent extends React.Component<DatePickerComponentProps> {
this.props.enableTimePicker
? {
useAmPm: true,
value: this.props.selectedDate || this.props.defaultDate,
value: this.props.selectedDate,
showArrowButtons: true,
}
: undefined
}
closeOnSelection={true}
onChange={this.onDateSelected}
value={this.props.selectedDate || this.props.defaultDate}
value={this.props.selectedDate}
/>
) : (
<DateRangeInput
@ -97,7 +121,6 @@ class DatePickerComponent extends React.Component<DatePickerComponentProps> {
export interface DatePickerComponentProps extends ComponentProps {
label: string;
defaultDate?: Date;
dateFormat: string;
enableTimePicker?: boolean;
selectedDate?: Date;

View File

@ -21,6 +21,7 @@ import _ from "lodash";
import { WIDGET_PADDING } from "constants/WidgetConstants";
import "../../../../node_modules/@blueprintjs/select/lib/css/blueprint-select.css";
import styled, {
createGlobalStyle,
labelStyle,
BlueprintCSSTransform,
BlueprintInputTransform,
@ -79,53 +80,61 @@ const StyledControlGroup = styled(ControlGroup)<{ haslabel: string }>`
}
`;
const DropdownContainer = styled.div`
${BlueprintCSSTransform}
&&&& .${Classes.MENU_ITEM} {
border-radius: ${props => props.theme.radii[1]}px;
&:hover{
background: ${Colors.POLAR};
}
&.${Classes.ACTIVE} {
background: ${Colors.POLAR};
color: ${props => props.theme.colors.textDefault};
position:relative;
&.single-select{
&:before{
left: 0;
top: -2px;
position: absolute;
content: "";
background: ${props => props.theme.colors.primary};
border-radius: 4px 0 0 4px;
width: 4px;
height:100%;
}
}
}
}
&& .${Classes.POPOVER} {
const DropdownStyles = createGlobalStyle`
.select-popover-wrapper {
width: 100%;
border-radius: ${props => props.theme.radii[1]}px;
box-shadow: 0px 2px 4px rgba(67, 70, 74, 0.14);
padding: ${props => props.theme.spaces[3]}px;
background: white;
}
&& .${Classes.POPOVER_WRAPPER} {
position:relative;
.${Classes.OVERLAY} {
position: absolute;
.${Classes.TRANSITION_CONTAINER} {
width: 100%;
&& .${Classes.MENU} {
max-width: 100%;
max-height: auto;
}
&&&& .${Classes.MENU_ITEM} {
border-radius: ${props => props.theme.radii[1]}px;
&:hover{
background: ${Colors.POLAR};
}
&.${Classes.ACTIVE} {
background: ${Colors.POLAR};
color: ${props => props.theme.colors.textDefault};
position:relative;
&.single-select{
&:before{
left: 0;
top: -2px;
position: absolute;
content: "";
background: ${props => props.theme.colors.primary};
border-radius: 4px 0 0 4px;
width: 4px;
height:100%;
}
}
}
.${Classes.CONTROL} .${Classes.CONTROL_INDICATOR} {
background: white;
box-shadow: none;
border-width: 2px;
border-style: solid;
border-color: ${Colors.GEYSER};
&::before {
width: auto;
height: 1em;
}&
}
.${Classes.CONTROL} input:checked ~ .${Classes.CONTROL_INDICATOR} {
background: ${props => props.theme.colors.primary};
color: ${props => props.theme.colors.textOnDarkBG};
border-color: ${props => props.theme.colors.primary};
}
}
}
&& .${Classes.MENU} {
max-width: 100%;
max-height: auto;
}
width: 100%;
`;
const DropdownContainer = styled.div`
${BlueprintCSSTransform}
`;
const StyledMultiDropDown = styled(MultiDropDown)`
@ -169,24 +178,6 @@ const StyledMultiDropDown = styled(MultiDropDown)`
}
}
}
&&&& {
.${Classes.CONTROL} .${Classes.CONTROL_INDICATOR} {
background: white;
box-shadow: none;
border-width: 2px;
border-style: solid;
border-color: ${Colors.GEYSER};
&::before {
width: auto;
height: 1em;
}
}
.${Classes.CONTROL} input:checked ~ .${Classes.CONTROL_INDICATOR} {
background: ${props => props.theme.colors.primary};
color: ${props => props.theme.colors.textOnDarkBG};
border-color: ${props => props.theme.colors.primary};
}
}
`;
const StyledCheckbox = styled(Checkbox)`
@ -204,6 +195,7 @@ class DropDownComponent extends React.Component<DropDownComponentProps> {
: [];
return (
<DropdownContainer>
<DropdownStyles />
<StyledControlGroup
fill
haslabel={!!this.props.label ? "true" : "false"}
@ -228,14 +220,16 @@ class DropDownComponent extends React.Component<DropDownComponentProps> {
onItemSelect={this.onItemSelect}
popoverProps={{
minimal: true,
usePortal: false,
usePortal: true,
popoverClassName: "select-popover-wrapper",
}}
>
<Button
rightIcon={IconNames.CHEVRON_DOWN}
text={
!_.isEmpty(this.props.options) &&
this.props.selectedIndex !== undefined
this.props.selectedIndex !== undefined &&
this.props.selectedIndex > -1
? this.props.options[this.props.selectedIndex].label
: "-- Empty --"
}
@ -258,9 +252,10 @@ class DropDownComponent extends React.Component<DropDownComponentProps> {
onItemSelect={this.onItemSelect}
popoverProps={{
minimal: true,
usePortal: false,
usePortal: true,
popoverClassName: "select-popover-wrapper",
}}
></StyledMultiDropDown>
/>
)}
</StyledControlGroup>
</DropdownContainer>

View File

@ -27,8 +27,9 @@ import { INPUT_WIDGET_DEFAULT_VALIDATION_ERROR } from "constants/messages";
*/
const InputComponentWrapper = styled(props => (
<ControlGroup {..._.omit(props, ["hasError"])} />
<ControlGroup {..._.omit(props, ["hasError", "numeric"])} />
))<{
numeric: boolean;
multiline: string;
hasError: boolean;
}>`
@ -41,6 +42,13 @@ const InputComponentWrapper = styled(props => (
border-radius: ${props => props.theme.radii[1]}px;
height: ${props => (props.multiline === "true" ? "100%" : "inherit")};
width: 100%;
${props =>
props.numeric &&
`
border-top-right-radius: 0px;
border-bottom-right-radius: 0px;
border-right-width: 0px;
`}
&:active {
border-color: ${({ hasError }) =>
hasError ? IntentColors.danger : Colors.HIT_GRAY};
@ -138,7 +146,6 @@ class InputComponent extends React.Component<
disabled={this.props.disabled}
intent={this.props.intent}
className={this.props.isLoading ? "bp3-skeleton" : Classes.FILL}
defaultValue={this.props.defaultValue}
onValueChange={this.onNumberChange}
leftIcon={
this.props.inputType === "PHONE_NUMBER" ? "phone" : this.props.leftIcon
@ -150,7 +157,7 @@ class InputComponent extends React.Component<
onBlur={() => this.setFocusState(false)}
/>
);
private textAreaInputComponent = (
private textAreaInputComponent = () => (
<TextArea
value={this.props.value}
placeholder={this.props.placeholder}
@ -158,7 +165,6 @@ class InputComponent extends React.Component<
maxLength={this.props.maxChars}
intent={this.props.intent}
onChange={this.onTextChange}
defaultValue={this.props.defaultValue}
className={this.props.isLoading ? "bp3-skeleton" : ""}
growVertically={false}
onFocus={() => this.setFocusState(true)}
@ -168,7 +174,7 @@ class InputComponent extends React.Component<
private textInputComponent = (isTextArea: boolean) =>
isTextArea ? (
this.textAreaInputComponent
this.textAreaInputComponent()
) : (
<InputGroup
value={this.props.value}
@ -177,7 +183,6 @@ class InputComponent extends React.Component<
maxLength={this.props.maxChars}
intent={this.props.intent}
onChange={this.onTextChange}
defaultValue={this.props.defaultValue}
className={this.props.isLoading ? "bp3-skeleton" : ""}
rightElement={
this.props.inputType === "PASSWORD" ? (
@ -207,6 +212,7 @@ class InputComponent extends React.Component<
<InputComponentWrapper
fill
multiline={this.props.multiline.toString()}
numeric={this.isNumberInputType(this.props.inputType)}
hasError={this.props.isInvalid}
>
{this.props.label && (

View File

@ -17,15 +17,19 @@ const StyledControlGroup = styled(ControlGroup)`
& > label {
${labelStyle}
flex: 0 1 30%;
align-self: flex-start;
margin: 7px ${WIDGET_PADDING * 2}px 0 0;
text-align: right;
margin: 0 ${WIDGET_PADDING * 2}px 0 0;
align-self: flex-start;
max-width: calc(30% - ${WIDGET_PADDING}px);
}
}
`;
const StyledRadioGroup = styled(RadioGroup)`
${BlueprintControlTransform};
label {
margin: 7px ${WIDGET_PADDING * 2}px 0 0;
}
`;
class RadioGroupComponent extends React.Component<RadioGroupComponentProps> {
@ -44,11 +48,7 @@ class RadioGroupComponent extends React.Component<RadioGroupComponentProps> {
</Label>
)}
<StyledRadioGroup
selectedValue={
this.props.selectedOptionValue === undefined
? this.props.defaultOptionValue
: this.props.selectedOptionValue
}
selectedValue={this.props.selectedOptionValue}
onChange={this.onRadioSelectionChange}
>
{this.props.options.map(option => {
@ -77,7 +77,6 @@ export interface RadioGroupComponentProps extends ComponentProps {
onRadioSelectionChange: (updatedOptionValue: string) => void;
selectedOptionValue: string;
isLoading: boolean;
defaultOptionValue: string;
}
export default RadioGroupComponent;

View File

@ -318,7 +318,7 @@ class DynamicActionCreator extends React.Component<Props & ReduxStateProps> {
) => void,
) => {
return (
<div style={{ paddingLeft: 5 }}>
<div>
{selectedOption.arguments.map(arg => {
switch (arg.field) {
case "ACTION_SELECTOR_FIELD":
@ -328,6 +328,7 @@ class DynamicActionCreator extends React.Component<Props & ReduxStateProps> {
<StyledDropdown
options={allOptions}
selectedValue={arg.getSelectedValue(value, false)}
defaultText={"Select Action"}
onSelect={value =>
handleUpdate(value, arg.valueChangeHandler)
}
@ -347,6 +348,7 @@ class DynamicActionCreator extends React.Component<Props & ReduxStateProps> {
<StyledDropdown
options={this.props.pageNameDropdown}
selectedValue={arg.getSelectedValue(value, false)}
defaultText={"Select Page"}
onSelect={value =>
handleUpdate(value, arg.valueChangeHandler)
}
@ -355,14 +357,15 @@ class DynamicActionCreator extends React.Component<Props & ReduxStateProps> {
);
case "TEXT_FIELD":
return (
<React.Fragment key={arg.label}>
<ControlWrapper key={arg.label}>
<label>{arg.label}</label>
<InputText
label={arg.label}
value={arg.getSelectedValue(value, false)}
onChange={e => handleUpdate(e, arg.valueChangeHandler)}
isValid={true}
/>
</React.Fragment>
</ControlWrapper>
);
case "ALERT_TYPE_SELECTOR_FIELD":
return (
@ -370,6 +373,7 @@ class DynamicActionCreator extends React.Component<Props & ReduxStateProps> {
<label>{arg.label}</label>
<StyledDropdown
options={ALERT_STYLE_OPTIONS}
defaultText={"Select type"}
selectedValue={arg.getSelectedValue(value, false)}
onSelect={value =>
handleUpdate(value, arg.valueChangeHandler)
@ -421,6 +425,7 @@ class DynamicActionCreator extends React.Component<Props & ReduxStateProps> {
<StyledDropdown
options={actionOptions}
selectedValue={topLevelFuncValue}
defaultText={"Select"}
onSelect={value =>
this.handleValueUpdate(value, handleTopLevelFuncUpdate)
}

View File

@ -1,6 +1,27 @@
import React from "react";
import { Popover } from "@blueprintjs/core";
import styled from "styled-components";
import styled, { createGlobalStyle } from "styled-components";
import { Colors } from "constants/Colors";
const TooltipStyles = createGlobalStyle`
.error-tooltip{
.bp3-popover {
.bp3-popover-arrow {
display: block;
}
.bp3-popover-content {
padding: 8px;
color: ${Colors.RED};
text-align: center;
border-radius: 4px;
text-transform: initial;
font-weight: 500;
font-size: 12px;
line-height: 16px;
}
}
}
`;
const Wrapper = styled.div`
position: relative;
@ -10,21 +31,6 @@ const Wrapper = styled.div`
width: 100%;
height: 100%;
}
.bp3-popover {
.bp3-popover-arrow {
display: block;
}
.bp3-popover-content {
padding: 8px;
color: ${props => props.theme.colors.error};
text-align: center;
border-radius: 4px;
text-transform: initial;
font-weight: 500;
font-size: 12px;
line-height: 16px;
}
}
`;
interface Props {
@ -36,16 +42,15 @@ interface Props {
const ErrorTooltip = (props: Props) => {
return (
<Wrapper>
<TooltipStyles />
<Popover
autoFocus={true}
canEscapeKeyClose={true}
content={props.message}
position="bottom"
isOpen={props.isOpen && !!props.message}
usePortal={false}
modifiers={{
offset: { offset: "0,0,-11px" },
}}
usePortal
portalClassName="error-tooltip"
>
{props.children}
</Popover>

View File

@ -17,6 +17,7 @@ import { IconNames } from "@blueprintjs/icons";
type ActionTypeDropdownProps = {
options: DropdownOption[];
selectedValue: string;
defaultText: string;
onSelect: (value: string) => void;
};
@ -55,7 +56,9 @@ class StyledDropdown extends React.Component<ActionTypeDropdownProps> {
render() {
const { selectedValue } = this.props;
let selectedOption = this.props.options[0];
let selectedOption = {
label: this.props.defaultText,
};
this.props.options.forEach(o => {
if (o.value === selectedValue) {
selectedOption = o;

View File

@ -30,6 +30,7 @@ export interface ControlData {
id: string;
label: string;
propertyName: string;
isJSConvertible?: boolean;
controlType: ControlType;
propertyValue?: any;
isValid: boolean;

View File

@ -11,8 +11,9 @@ export function InputText(props: {
onChange: (event: React.ChangeEvent<HTMLTextAreaElement> | string) => void;
isValid: boolean;
validationMessage?: string;
placeholder?: string;
}) {
const { validationMessage, value, isValid, onChange } = props;
const { validationMessage, value, isValid, onChange, placeholder } = props;
return (
<StyledDynamicInput>
<DynamicAutocompleteInput
@ -26,6 +27,7 @@ export function InputText(props: {
}}
theme={"DARK"}
singleLine={false}
placeholder={placeholder}
/>
</StyledDynamicInput>
);
@ -33,7 +35,13 @@ export function InputText(props: {
class InputTextControl extends BaseControl<InputControlProps> {
render() {
const { validationMessage, propertyValue, isValid, label } = this.props;
const {
validationMessage,
propertyValue,
isValid,
label,
placeholderText,
} = this.props;
return (
<InputText
label={label}
@ -41,6 +49,7 @@ class InputTextControl extends BaseControl<InputControlProps> {
onChange={this.onTextChange}
isValid={isValid}
validationMessage={validationMessage}
placeholder={placeholderText}
/>
);
}

View File

@ -30,7 +30,10 @@ const StyledOptionControlWrapper = styled(ControlWrapper)`
class OptionControl extends BaseControl<ControlProps> {
render() {
const options: DropdownOption[] = this.props.propertyValue || [{}];
const { propertyValue } = this.props;
const options: DropdownOption[] = Array.isArray(propertyValue)
? propertyValue
: [{}];
return (
<React.Fragment>
{options.map((option, index) => {
@ -77,13 +80,19 @@ class OptionControl extends BaseControl<ControlProps> {
}
deleteOption = (index: number) => {
const options: DropdownOption[] = this.props.propertyValue.slice();
options.splice(index, 1);
this.updateProperty("options", options);
const { propertyValue } = this.props;
const options: DropdownOption[] = Array.isArray(propertyValue)
? propertyValue
: [{}];
const newOptions = options.filter((o, i) => i !== index);
this.updateProperty("options", newOptions);
};
updateOptionLabel = (index: number, updatedLabel: string) => {
const options: DropdownOption[] = this.props.propertyValue;
const { propertyValue } = this.props;
const options: DropdownOption[] = Array.isArray(propertyValue)
? propertyValue
: [{}];
this.updateProperty(
"options",
options.map((option, optionIndex) => {
@ -99,7 +108,10 @@ class OptionControl extends BaseControl<ControlProps> {
};
updateOptionValue = (index: number, updatedValue: string) => {
const options: DropdownOption[] = this.props.propertyValue;
const { propertyValue } = this.props;
const options: DropdownOption[] = Array.isArray(propertyValue)
? propertyValue
: [{}];
this.updateProperty(
"options",
options.map((option, optionIndex) => {
@ -115,9 +127,10 @@ class OptionControl extends BaseControl<ControlProps> {
};
addOption = () => {
const options: DropdownOption[] = this.props.propertyValue
? this.props.propertyValue.slice()
: [];
const { propertyValue } = this.props;
const options: DropdownOption[] = Array.isArray(propertyValue)
? propertyValue
: [{}];
options.push({ label: "", value: "" });
this.updateProperty("options", options);
};

View File

@ -19,10 +19,3 @@ export type ControlType =
| "TIME_ZONE"
| "CODE_EDITOR"
| "COLUMN_ACTION_SELECTOR";
export const CONVERTIBLE_CONTROLS = [
"SWITCH",
"OPTION_INPUT",
"ACTION_SELECTOR",
"DATE_PICKER",
];

View File

@ -11,6 +11,7 @@ export type WidgetType =
| "RADIO_GROUP_WIDGET"
| "FILE_PICKER_WIDGET"
| "INPUT_WIDGET"
| "CHART_WIDGET"
| "SWITCH_WIDGET"
| "FORM_WIDGET"
| "FORM_BUTTON_WIDGET";
@ -28,6 +29,7 @@ export const WidgetTypes: { [id: string]: WidgetType } = {
DROP_DOWN_WIDGET: "DROP_DOWN_WIDGET",
CHECKBOX_WIDGET: "CHECKBOX_WIDGET",
RADIO_GROUP_WIDGET: "RADIO_GROUP_WIDGET",
CHART_WIDGET: "CHART_WIDGET",
FORM_WIDGET: "FORM_WIDGET",
FORM_BUTTON_WIDGET: "FORM_BUTTON_WIDGET",
};

View File

@ -8,6 +8,7 @@ export const VALIDATION_TYPES = {
TABLE_DATA: "TABLE_DATA",
OPTIONS_DATA: "OPTIONS_DATA",
DATE: "DATE",
CHART_DATA: "CHART_DATA",
};
export type ValidationResponse = {

View File

@ -14,6 +14,7 @@ import { ReactComponent as SwitchIcon } from "assets/icons/widget/switch.svg";
import { ReactComponent as TextIcon } from "assets/icons/widget/text.svg";
import { ReactComponent as ImageIcon } from "assets/icons/widget/image.svg";
import { ReactComponent as FilePickerIcon } from "assets/icons/widget/filepicker.svg";
import { ReactComponent as ChartIcon } from "assets/icons/widget/chart.svg";
import { ReactComponent as FormIcon } from "assets/icons/widget/form.svg";
/* eslint-disable react/display-name */
@ -91,6 +92,11 @@ export const WidgetIcons: {
<FilePickerIcon />
</IconWrapper>
),
CHART_WIDGET: (props: IconProps) => (
<IconWrapper {...props}>
<ChartIcon />
</IconWrapper>
),
FORM_WIDGET: (props: IconProps) => (
<IconWrapper {...props}>
<FormIcon />

View File

@ -102,7 +102,7 @@ export default class RealmExecutor implements JSExecutor {
triggers,
};
} catch (e) {
console.error(`Error: "${e.message}" when evaluating {{${sourceText}}}`);
// console.error(`Error: "${e.message}" when evaluating {{${sourceText}}}`);
return { result: undefined, triggers: [] };
}
}

View File

@ -885,6 +885,67 @@ const PropertyPaneConfigResponse = {
],
},
],
CHART_WIDGET: [
{
sectionName: "General",
id: "21",
children: [
{
id: "21.1",
propertyName: "chartType",
label: "Chart Type",
controlType: "DROP_DOWN",
options: [
{
label: "Line Chart",
value: "LINE_CHART",
},
{
label: "Bar Chart",
value: "BAR_CHART",
},
{
label: "Pie Chart",
value: "PIE_CHART",
},
{
label: "Column Chart",
value: "COLUMN_CHART",
},
{
label: "Area Chart",
value: "AREA_CHART",
},
],
},
{
id: "21.2",
propertyName: "chartName",
label: "Chart Name",
controlType: "INPUT_TEXT",
},
{
id: "21.3",
propertyName: "xAxisName",
label: "X-axis Label",
controlType: "INPUT_TEXT",
},
{
id: "21.4",
propertyName: "yAxisName",
label: "Y-axis Label",
controlType: "INPUT_TEXT",
},
{
id: "21.5",
propertyName: "chartData",
label: "Chart Data",
controlType: "INPUT_TEXT",
inputType: "ARRAY",
},
],
},
],
},
name: "propertyPane",
};

View File

@ -34,7 +34,6 @@ const WidgetConfigResponse: WidgetConfigReducerState = {
rows: 1,
columns: 8,
widgetName: "Input",
text: "",
},
SWITCH_WIDGET: {
isOn: false,
@ -119,7 +118,6 @@ const WidgetConfigResponse: WidgetConfigReducerState = {
{ label: "Option 5", value: "5" },
],
widgetName: "Dropdown",
selectedIndex: 0,
},
CHECKBOX_WIDGET: {
rows: 1,
@ -156,6 +154,45 @@ const WidgetConfigResponse: WidgetConfigReducerState = {
widgetName: "FilePicker",
isDefaultClickDisabled: true,
},
CHART_WIDGET: {
rows: 8,
columns: 6,
widgetName: "Chart",
chartType: "LINE_CHART",
chartName: "Sales on working days",
chartData: [
{
x: "Mon",
y: 10000,
},
{
x: "Tue",
y: 12000,
},
{
x: "Wed",
y: 32000,
},
{
x: "Thu",
y: 28000,
},
{
x: "Fri",
y: 14000,
},
{
x: "Sat",
y: 19000,
},
{
x: "Sun",
y: 36000,
},
],
xAxisName: "Last Week",
yAxisName: "Total Order Revenue $",
},
FORM_BUTTON_WIDGET: {
rows: 1,
columns: 3,

View File

@ -96,6 +96,11 @@ const WidgetSidebarResponse: {
widgetCardName: "Table",
key: generateReactKey(),
},
{
type: "CHART_WIDGET",
widgetCardName: "Chart",
key: generateReactKey(),
},
],
["Layout widgets"]: [
{

View File

@ -35,7 +35,7 @@ interface ReduxStateProps {
interface ReduxActionProps {
submitForm: (name: string) => void;
createAction: (values: RestAction) => void;
runAction: (id: string, paginationField: PaginationField) => void;
runAction: (id: string, paginationField?: PaginationField) => void;
deleteAction: (id: string, name: string) => void;
updateAction: (data: RestAction) => void;
}
@ -158,7 +158,7 @@ const mapStateToProps = (state: AppState): ReduxStateProps => ({
const mapDispatchToProps = (dispatch: any): ReduxActionProps => ({
submitForm: (name: string) => dispatch(submit(name)),
createAction: (action: RestAction) => dispatch(createActionRequest(action)),
runAction: (id: string, paginationField: PaginationField) =>
runAction: (id: string, paginationField?: PaginationField) =>
dispatch(runApiAction(id, paginationField)),
deleteAction: (id: string, name: string) =>
dispatch(deleteAction({ id, name })),

View File

@ -1,6 +1,5 @@
import React from "react";
import _ from "lodash";
import { CONVERTIBLE_CONTROLS } from "constants/PropertyControlConstants";
import {
ControlPropertyLabelContainer,
ControlWrapper,
@ -54,7 +53,7 @@ const PropertyControl = (props: Props) => {
["dynamicProperties", propertyName],
false,
);
const isConvertible = CONVERTIBLE_CONTROLS.indexOf(config.controlType) > -1;
const isConvertible = !!propertyConfig.isJSConvertible;
const className = propertyConfig.label
.split(" ")
.join("")

View File

@ -9,13 +9,14 @@ import { ImageWidgetProps } from "widgets/ImageWidget";
import { InputWidgetProps } from "widgets/InputWidget";
import { SwitchWidgetProps } from "widgets/SwitchWidget";
import { SpinnerWidgetProps } from "widgets/SpinnerWidget";
import { DatePickerWidgetProps } from "widgets/DatePickerWidget";
import { TableWidgetProps } from "widgets/TableWidget";
import { DropdownWidgetProps } from "widgets/DropdownWidget";
import { CheckboxWidgetProps } from "widgets/CheckboxWidget";
import { RadioGroupWidgetProps } from "widgets/RadioGroupWidget";
import { AlertWidgetProps } from "widgets/AlertWidget";
import { FilePickerWidgetProps } from "widgets/FilepickerWidget";
import { DatePickerWidgetProps } from "../../widgets/DatePickerWidget";
import { TableWidgetProps } from "../../widgets/TableWidget";
import { DropdownWidgetProps } from "../../widgets/DropdownWidget";
import { CheckboxWidgetProps } from "../../widgets/CheckboxWidget";
import { RadioGroupWidgetProps } from "../../widgets/RadioGroupWidget";
import { AlertWidgetProps } from "../../widgets/AlertWidget";
import { FilePickerWidgetProps } from "../../widgets/FilepickerWidget";
import { ChartWidgetProps } from "../../widgets/ChartWidget";
import { FormWidgetProps } from "widgets/FormWidget";
import { FormButtonWidgetProps } from "widgets/FormButtonWidget";
@ -53,6 +54,7 @@ export interface WidgetConfigReducerState {
RADIO_GROUP_WIDGET: Partial<RadioGroupWidgetProps> & WidgetConfigProps;
ALERT_WIDGET: Partial<AlertWidgetProps> & WidgetConfigProps;
FILE_PICKER_WIDGET: Partial<FilePickerWidgetProps> & WidgetConfigProps;
CHART_WIDGET: Partial<ChartWidgetProps> & WidgetConfigProps;
FORM_WIDGET: Partial<FormWidgetProps> & WidgetConfigProps;
FORM_BUTTON_WIDGET: Partial<FormButtonWidgetProps> & WidgetConfigProps;
};

View File

@ -38,12 +38,12 @@ import {
deleteActionSuccess,
executeApiActionRequest,
executeApiActionSuccess,
fetchActionsForPageSuccess,
FetchActionsPayload,
moveActionError,
moveActionSuccess,
runApiAction,
updateActionSuccess,
fetchActionsForPageSuccess,
} from "actions/actionActions";
import {
getDynamicBindings,
@ -82,11 +82,20 @@ export const getAction = (
return action ? action.config : undefined;
};
const createActionResponse = (response: ActionApiResponse): ActionResponse => ({
const createActionSuccessResponse = (
response: ActionApiResponse,
): ActionResponse => ({
...response.data,
...response.clientMeta,
});
const isErrorResponse = (response: ActionApiResponse) => {
return (
(response.responseMeta && response.responseMeta.error) ||
!/2\d\d/.test(response.data.statusCode)
);
};
function getCurrentPageNameByActionId(
state: AppState,
actionId: string,
@ -102,8 +111,7 @@ function getPageNameByPageId(state: AppState, pageId: string): string {
const page = state.entities.pageList.pages.find(
page => page.pageId === pageId,
);
const pageName = page ? page.pageName : "";
return pageName;
return page ? page.pageName : "";
}
const createActionErrorResponse = (
@ -111,7 +119,7 @@ const createActionErrorResponse = (
): ActionResponse => ({
body: response.responseMeta.error || { error: "Error" },
statusCode: response.responseMeta.error
? response.responseMeta.error.code
? response.responseMeta.error.code.toString()
: "Error",
headers: {},
duration: "0",
@ -157,7 +165,7 @@ export function* getActionParams(jsonPathKeys: string[] | undefined) {
// });
// }
export function* executeAPIQueryActionSaga(
export function* executeActionSaga(
apiAction: RunActionPayload,
event: ExecuteActionPayloadEvent,
) {
@ -180,9 +188,8 @@ export function* executeAPIQueryActionSaga(
const response: ActionApiResponse = yield ActionAPI.executeAction(
executeActionRequest,
);
let payload = createActionResponse(response);
if (response.responseMeta && response.responseMeta.error) {
payload = createActionErrorResponse(response);
if (isErrorResponse(response)) {
const payload = createActionErrorResponse(response);
if (onError) {
yield put(
executeAction({
@ -206,6 +213,7 @@ export function* executeAPIQueryActionSaga(
}),
);
} else {
const payload = createActionSuccessResponse(response);
yield put(
executeApiActionSuccess({
id: apiAction.actionId,
@ -281,7 +289,7 @@ export function* executeActionTriggers(
) {
switch (trigger.type) {
case "RUN_ACTION":
yield call(executeAPIQueryActionSaga, trigger.payload, event);
yield call(executeActionSaga, trigger.payload, event);
break;
case "NAVIGATE_TO":
AnalyticsUtil.logEvent("NAVIGATE", {
@ -513,7 +521,7 @@ export function* runApiActionSaga(
params,
paginationField,
});
let payload = createActionResponse(response);
let payload = createActionSuccessResponse(response);
if (response.responseMeta && response.responseMeta.error) {
payload = createActionErrorResponse(response);
}
@ -541,27 +549,44 @@ export function* runApiActionSaga(
}
}
function* executePageLoadAction(pageAction: PageAction) {
yield put(executeApiActionRequest({ id: pageAction.id }));
const params: Property[] = yield call(
getActionParams,
pageAction.jsonPathKeys,
);
const executeActionRequest: ExecuteActionRequest = {
action: { id: pageAction.id },
params,
};
const response: ActionApiResponse = yield ActionAPI.executeAction(
executeActionRequest,
);
if (isErrorResponse(response)) {
yield put(
executeActionError({
actionId: pageAction.id,
error: response.responseMeta.error,
}),
);
} else {
const payload = createActionSuccessResponse(response);
yield put(
executeApiActionSuccess({
id: pageAction.id,
response: payload,
}),
);
}
}
function* executePageLoadActionsSaga(action: ReduxAction<PageAction[][]>) {
const pageActions = action.payload;
const actionPayloads: RunActionPayload[][] = pageActions.map(actionSet =>
actionSet.map(action => ({
actionId: action.id,
onSuccess: "",
onError: "",
})),
);
for (const actionSet of actionPayloads) {
for (const actionSet of pageActions) {
const apiResponses = yield select(getActionResponses);
const filteredSet = actionSet.filter(
action => !apiResponses[action.actionId],
);
yield* yield all(
filteredSet.map(a =>
call(executeAPIQueryActionSaga, a, {
type: EventType.ON_PAGE_LOAD,
}),
),
);
const filteredSet = actionSet.filter(action => !apiResponses[action.id]);
yield* yield all(filteredSet.map(a => call(executePageLoadAction, a)));
}
}

View File

@ -24,7 +24,7 @@ import {
takeLatest,
all,
} from "redux-saga/effects";
import { getNextEntityName } from "utils/AppsmithUtils";
import { convertToString, getNextEntityName } from "utils/AppsmithUtils";
import {
SetWidgetDynamicPropertyPayload,
updateWidgetProperty,
@ -262,7 +262,8 @@ function* setWidgetDynamicPropertySaga(
};
if (isDynamic) {
dynamicProperties[propertyName] = true;
yield put(updateWidgetProperty(widgetId, propertyName, "{{}}"));
const value = convertToString(widget[propertyName]);
yield put(updateWidgetProperty(widgetId, propertyName, value));
} else {
delete dynamicProperties[propertyName];
yield put(updateWidgetProperty(widgetId, propertyName, undefined));

View File

@ -1,4 +1,4 @@
import { ReduxAction } from "../constants/ReduxActionConstants";
import { ReduxAction } from "constants/ReduxActionConstants";
import { getAppsmithConfigs } from "configs";
import * as Sentry from "@sentry/browser";
import AnalyticsUtil from "./AnalyticsUtil";
@ -77,3 +77,14 @@ export const getNextEntityName = (prefix: string, existingNames: string[]) => {
export const noop = () => {
console.log("noop");
};
export const convertToString = (value: any): string => {
if (_.isUndefined(value)) {
return "";
}
if (_.isObject(value)) {
return JSON.stringify(value, null, 2);
}
if (_.isString(value)) return value;
return value.toString();
};

View File

@ -1,12 +1,18 @@
import { WidgetType } from "constants/WidgetConstants";
import WidgetFactory from "./WidgetFactory";
import {
VALIDATION_TYPES,
ValidationResponse,
ValidationType,
Validator,
} from "constants/WidgetValidation";
// TODO: need to be strict about what the key can be
export const BASE_WIDGET_VALIDATION = {
isLoading: VALIDATION_TYPES.BOOLEAN,
isVisible: VALIDATION_TYPES.BOOLEAN,
isDisabled: VALIDATION_TYPES.BOOLEAN,
};
export type WidgetPropertyValidationType = Record<string, ValidationType>;
class ValidationFactory {

View File

@ -11,11 +11,11 @@ import { WIDGET_TYPE_VALIDATION_ERROR } from "constants/messages";
export const VALIDATORS: Record<ValidationType, Validator> = {
[VALIDATION_TYPES.TEXT]: (value: any): ValidationResponse => {
let parsed = value;
if (_.isUndefined(value)) {
if (_.isUndefined(value) || value === null) {
return {
isValid: false,
parsed: "",
message: `${WIDGET_TYPE_VALIDATION_ERROR}: text`,
isValid: true,
parsed: value,
message: "",
};
}
if (_.isObject(value)) {
@ -84,20 +84,16 @@ export const VALIDATORS: Record<ValidationType, Validator> = {
message: `${WIDGET_TYPE_VALIDATION_ERROR}: boolean`,
};
}
let isValid = _.isBoolean(value);
const isBoolean = _.isBoolean(value);
const isStringTrueFalse = value === "true" || value === "false";
const isValid = isBoolean || isStringTrueFalse;
if (isStringTrueFalse) parsed = value !== "false";
if (!isValid) {
try {
parsed = !!value;
isValid = true;
} catch (e) {
console.error(`Error when parsing ${value} to boolean`);
console.error(e);
return {
isValid: false,
parsed: false,
message: `${WIDGET_TYPE_VALIDATION_ERROR}: boolean`,
};
}
return {
isValid: isValid,
parsed: parsed,
message: `${WIDGET_TYPE_VALIDATION_ERROR}: boolean`,
};
}
return { isValid, parsed };
},
@ -174,6 +170,23 @@ export const VALIDATORS: Record<ValidationType, Validator> = {
}
return { isValid, parsed };
},
[VALIDATION_TYPES.CHART_DATA]: (value: any): ValidationResponse => {
const { isValid, parsed } = VALIDATORS[VALIDATION_TYPES.ARRAY](value);
if (!isValid) {
return {
isValid,
parsed,
message: `${WIDGET_TYPE_VALIDATION_ERROR}: Chart Data`,
};
} else if (!_.every(parsed, datum => _.isObject(datum))) {
return {
isValid: false,
parsed: [],
message: `${WIDGET_TYPE_VALIDATION_ERROR}: Chart Data`,
};
}
return { isValid, parsed };
},
[VALIDATION_TYPES.OPTIONS_DATA]: (value: any): ValidationResponse => {
const { isValid, parsed } = VALIDATORS[VALIDATION_TYPES.ARRAY](value);
if (!isValid) {
@ -200,6 +213,15 @@ export const VALIDATORS: Record<ValidationType, Validator> = {
return { isValid, parsed };
},
[VALIDATION_TYPES.DATE]: (value: any): ValidationResponse => {
if (value === undefined) {
const today = new Date();
today.setHours(0, 0, 0);
return {
isValid: false,
parsed: today,
message: `${WIDGET_TYPE_VALIDATION_ERROR}: Date`,
};
}
const isValid = moment(value).isValid();
const parsed = isValid ? moment(value).toDate() : new Date();
return {

View File

@ -4,7 +4,10 @@ import {
WidgetProps,
WidgetDataProps,
} from "widgets/BaseWidget";
import { WidgetPropertyValidationType } from "./ValidationFactory";
import {
WidgetPropertyValidationType,
BASE_WIDGET_VALIDATION,
} from "./ValidationFactory";
import React from "react";
type WidgetDerivedPropertyType = any;
@ -78,7 +81,7 @@ class WidgetFactory {
const map = this.widgetPropValidationMap.get(widgetType);
if (!map) {
console.error("Widget type validation is not defined");
return {};
return BASE_WIDGET_VALIDATION;
}
return map;
}

View File

@ -13,6 +13,7 @@ import ButtonWidget, { ButtonWidgetProps } from "widgets/ButtonWidget";
import DropdownWidget, { DropdownWidgetProps } from "widgets/DropdownWidget";
import ImageWidget, { ImageWidgetProps } from "widgets/ImageWidget";
import TableWidget, { TableWidgetProps } from "widgets/TableWidget";
import ChartWidget, { ChartWidgetProps } from "widgets/ChartWidget";
import FilePickerWidget, {
FilePickerWidgetProps,
@ -169,7 +170,17 @@ class WidgetBuilderRegistry {
DatePickerWidget.getDerivedPropertiesMap(),
DatePickerWidget.getTriggerPropertyMap(),
);
WidgetFactory.registerWidgetBuilder(
"CHART_WIDGET",
{
buildWidget(widgetData: ChartWidgetProps): JSX.Element {
return <ChartWidget {...widgetData} />;
},
},
ChartWidget.getPropertyValidationMap(),
ChartWidget.getDerivedPropertiesMap(),
ChartWidget.getTriggerPropertyMap(),
);
WidgetFactory.registerWidgetBuilder(
"FORM_WIDGET",
{

View File

@ -27,7 +27,10 @@ import { EditorContext } from "components/editorComponents/EditorContextProvider
import { PositionTypes } from "constants/WidgetConstants";
import ErrorBoundary from "components/editorComponents/ErrorBoundry";
import { WidgetPropertyValidationType } from "utils/ValidationFactory";
import {
BASE_WIDGET_VALIDATION,
WidgetPropertyValidationType,
} from "utils/ValidationFactory";
import {
DerivedPropertiesMap,
TriggerPropertiesMap,
@ -66,7 +69,7 @@ abstract class BaseWidget<
// Needed to send a default no validation option. In case a widget needs
// validation implement this in the widget class again
static getPropertyValidationMap(): WidgetPropertyValidationType {
return {};
return BASE_WIDGET_VALIDATION;
}
static getDerivedPropertiesMap(): DerivedPropertiesMap {
@ -166,7 +169,14 @@ abstract class BaseWidget<
}
render() {
return this.getWidgetView();
let isValid = true;
if (this.props.invalidProps) {
isValid = _.keys(this.props.invalidProps).length === 0;
}
if (this.props.isLoading) isValid = true;
return (
<ErrorBoundary isValid={isValid}>{this.getWidgetView()}</ErrorBoundary>
);
}
private getWidgetView(): JSX.Element {
@ -202,7 +212,7 @@ abstract class BaseWidget<
this.props.widgetId === "0"
}
>
<ErrorBoundary isValid>{this.getPageView()}</ErrorBoundary>
{this.getPageView()}
</PositionedContainer>
);
}
@ -215,14 +225,7 @@ abstract class BaseWidget<
abstract getPageView(): JSX.Element;
getCanvasView(): JSX.Element {
let isValid = true;
if (this.props.invalidProps) {
isValid = _.keys(this.props.invalidProps).length === 0;
}
if (this.props.isLoading) isValid = true;
return (
<ErrorBoundary isValid={isValid}>{this.getPageView()}</ErrorBoundary>
);
return this.getPageView();
}
// TODO(Nikhil): Revisit the inclusion of another library for shallowEqual.

View File

@ -5,7 +5,10 @@ import ButtonComponent, {
ButtonType,
} from "components/designSystems/blueprint/ButtonComponent";
import { EventType } from "constants/ActionConstants";
import { WidgetPropertyValidationType } from "utils/ValidationFactory";
import {
WidgetPropertyValidationType,
BASE_WIDGET_VALIDATION,
} from "utils/ValidationFactory";
import { VALIDATION_TYPES } from "constants/WidgetValidation";
import { TriggerPropertiesMap } from "utils/WidgetFactory";
@ -30,9 +33,8 @@ class ButtonWidget extends BaseWidget<
static getPropertyValidationMap(): WidgetPropertyValidationType {
return {
...BASE_WIDGET_VALIDATION,
text: VALIDATION_TYPES.TEXT,
isDisabled: VALIDATION_TYPES.BOOLEAN,
isVisible: VALIDATION_TYPES.BOOLEAN,
buttonStyle: VALIDATION_TYPES.TEXT,
};
}

View File

@ -0,0 +1,63 @@
import React from "react";
import BaseWidget, { WidgetProps, WidgetState } from "./BaseWidget";
import { WidgetType } from "constants/WidgetConstants";
import ChartComponent from "components/designSystems/appsmith/ChartComponent";
import { WidgetPropertyValidationType } from "utils/ValidationFactory";
import { VALIDATION_TYPES } from "constants/WidgetValidation";
class ChartWidget extends BaseWidget<ChartWidgetProps, WidgetState> {
static getPropertyValidationMap(): WidgetPropertyValidationType {
return {
chartData: VALIDATION_TYPES.CHART_DATA,
xAxisName: VALIDATION_TYPES.TEXT,
yAxisName: VALIDATION_TYPES.TEXT,
chartName: VALIDATION_TYPES.TEXT,
};
}
getPageView() {
return (
<ChartComponent
key={this.props.widgetId}
isVisible={this.props.isVisible}
chartType={this.props.chartType}
xAxisName={this.props.xAxisName}
yAxisName={this.props.yAxisName}
chartName={this.props.chartName}
componentWidth={this.state.componentWidth - 10}
componentHeight={this.state.componentHeight - 10}
chartData={this.props.chartData}
/>
);
}
getWidgetType(): WidgetType {
return "CHART_WIDGET";
}
}
export type ChartType =
| "LINE_CHART"
| "BAR_CHART"
| "PIE_CHART"
| "COLUMN_CHART"
| "AREA_CHART"
| "SCATTER_CHART";
export interface ChartData {
x: any;
y: any;
}
export interface ChartWidgetProps extends WidgetProps {
chartType: ChartType;
chartData: ChartData[];
xAxisName: string;
yAxisName: string;
chartName: string;
componentWidth: number;
componentHeight: number;
isVisible?: boolean;
}
export default ChartWidget;

View File

@ -4,16 +4,18 @@ import { WidgetType } from "constants/WidgetConstants";
import CheckboxComponent from "components/designSystems/blueprint/CheckboxComponent";
import { EventType } from "constants/ActionConstants";
import { VALIDATION_TYPES } from "constants/WidgetValidation";
import { WidgetPropertyValidationType } from "utils/ValidationFactory";
import {
WidgetPropertyValidationType,
BASE_WIDGET_VALIDATION,
} from "utils/ValidationFactory";
import { TriggerPropertiesMap } from "utils/WidgetFactory";
class CheckboxWidget extends BaseWidget<CheckboxWidgetProps, WidgetState> {
static getPropertyValidationMap(): WidgetPropertyValidationType {
return {
isDisabled: VALIDATION_TYPES.BOOLEAN,
...BASE_WIDGET_VALIDATION,
label: VALIDATION_TYPES.TEXT,
defaultCheckedState: VALIDATION_TYPES.BOOLEAN,
isChecked: VALIDATION_TYPES.BOOLEAN,
};
}
@ -23,10 +25,35 @@ class CheckboxWidget extends BaseWidget<CheckboxWidgetProps, WidgetState> {
};
}
componentDidMount() {
super.componentDidMount();
if (this.props.defaultCheckedState) {
this.updateWidgetMetaProperty(
"isChecked",
this.props.defaultCheckedState,
);
}
}
componentDidUpdate(prevProps: CheckboxWidgetProps) {
super.componentDidUpdate(prevProps);
if (
(this.props.isChecked !== prevProps.isChecked &&
this.props.isChecked === undefined) ||
this.props.defaultCheckedState.toString() !==
prevProps.defaultCheckedState.toString()
) {
this.updateWidgetMetaProperty(
"isChecked",
this.props.defaultCheckedState,
);
}
}
getPageView() {
return (
<CheckboxComponent
defaultCheckedState={this.props.defaultCheckedState}
isChecked={!!this.props.isChecked}
label={this.props.label}
widgetId={this.props.widgetId}
key={this.props.widgetId}

View File

@ -14,7 +14,6 @@ import {
CONTAINER_GRID_PADDING,
WIDGET_PADDING,
MAIN_CONTAINER_WIDGET_ID,
RenderModes,
} from "constants/WidgetConstants";
import ResizeBoundsContainerComponent from "components/editorComponents/ResizeBoundsContainerComponent";
@ -64,12 +63,8 @@ class ContainerWidget extends BaseWidget<
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 consitent.
_.sortBy(this.props.children, child => {
return this.props.renderMode === RenderModes.CANVAS
? child.topRow
: -child.topRow;
}),
// Figure out a way in which the stacking context is consistent.
_.sortBy(this.props.children, child => child.topRow),
this.renderChildWidget,
);
};

View File

@ -3,7 +3,10 @@ import BaseWidget, { WidgetProps, WidgetState } from "./BaseWidget";
import { WidgetType } from "constants/WidgetConstants";
import { EventType } from "constants/ActionConstants";
import DatePickerComponent from "components/designSystems/blueprint/DatePickerComponent";
import { WidgetPropertyValidationType } from "utils/ValidationFactory";
import {
WidgetPropertyValidationType,
BASE_WIDGET_VALIDATION,
} from "utils/ValidationFactory";
import { VALIDATION_TYPES } from "constants/WidgetValidation";
import {
DerivedPropertiesMap,
@ -13,8 +16,8 @@ import {
class DatePickerWidget extends BaseWidget<DatePickerWidgetProps, WidgetState> {
static getPropertyValidationMap(): WidgetPropertyValidationType {
return {
...BASE_WIDGET_VALIDATION,
defaultDate: VALIDATION_TYPES.DATE,
selectedDate: VALIDATION_TYPES.DATE,
timezone: VALIDATION_TYPES.TEXT,
enableTimePicker: VALIDATION_TYPES.BOOLEAN,
dateFormat: VALIDATION_TYPES.TEXT,
@ -37,6 +40,21 @@ class DatePickerWidget extends BaseWidget<DatePickerWidgetProps, WidgetState> {
onDateSelected: true,
};
}
componentDidUpdate(prevProps: DatePickerWidgetProps) {
super.componentDidUpdate(prevProps);
if (this.props.defaultDate) {
if (
(this.props.selectedDate !== prevProps.selectedDate &&
this.props.selectedDate === undefined) ||
this.props.defaultDate.toDateString() !==
prevProps.defaultDate.toDateString()
) {
this.updateWidgetMetaProperty("selectedDate", this.props.defaultDate);
}
}
}
getPageView() {
return (
<DatePickerComponent
@ -45,7 +63,6 @@ class DatePickerWidget extends BaseWidget<DatePickerWidgetProps, WidgetState> {
widgetId={this.props.widgetId}
timezone={this.props.timezone}
enableTimePicker={this.props.enableTimePicker}
defaultDate={this.props.defaultDate}
datePickerType={"DATE_PICKER"}
onDateSelected={this.onDateSelected}
selectedDate={this.props.selectedDate}
@ -74,7 +91,7 @@ class DatePickerWidget extends BaseWidget<DatePickerWidgetProps, WidgetState> {
export type DatePickerType = "DATE_PICKER" | "DATE_RANGE_PICKER";
export interface DatePickerWidgetProps extends WidgetProps {
defaultDate?: Date;
defaultDate: Date;
selectedDate: Date;
timezone?: string;
enableTimePicker: boolean;

View File

@ -4,18 +4,21 @@ import { WidgetType } from "constants/WidgetConstants";
import { EventType } from "constants/ActionConstants";
import DropDownComponent from "components/designSystems/blueprint/DropdownComponent";
import _ from "lodash";
import { WidgetPropertyValidationType } from "utils/ValidationFactory";
import {
WidgetPropertyValidationType,
BASE_WIDGET_VALIDATION,
} from "utils/ValidationFactory";
import { VALIDATION_TYPES } from "constants/WidgetValidation";
import { TriggerPropertiesMap } from "utils/WidgetFactory";
class DropdownWidget extends BaseWidget<DropdownWidgetProps, WidgetState> {
static getPropertyValidationMap(): WidgetPropertyValidationType {
return {
...BASE_WIDGET_VALIDATION,
placeholderText: VALIDATION_TYPES.TEXT,
label: VALIDATION_TYPES.TEXT,
options: VALIDATION_TYPES.OPTIONS_DATA,
selectionType: VALIDATION_TYPES.TEXT,
selectedIndex: VALIDATION_TYPES.NUMBER,
selectedIndexArr: VALIDATION_TYPES.ARRAY,
isRequired: VALIDATION_TYPES.BOOLEAN,
};
@ -44,6 +47,16 @@ class DropdownWidget extends BaseWidget<DropdownWidgetProps, WidgetState> {
};
}
componentDidMount() {
super.componentDidMount();
if (this.props.defaultOptionValue) {
const selectedIndex = _.findIndex(this.props.options, option => {
return option.value === this.props.defaultOptionValue;
});
this.updateWidgetMetaProperty("selectedIndex", selectedIndex);
}
}
componentDidUpdate(prevProps: DropdownWidgetProps) {
super.componentDidUpdate(prevProps);
if (
@ -51,22 +64,26 @@ class DropdownWidget extends BaseWidget<DropdownWidgetProps, WidgetState> {
) {
this.updateWidgetMetaProperty("selectedIndex", undefined);
this.updateWidgetMetaProperty("selectedIndexArr", []);
} else if (
(this.props.selectedIndex !== prevProps.selectedIndex &&
this.props.selectedIndex === undefined) ||
this.props.defaultOptionValue !== prevProps.defaultOptionValue
) {
const selectedIndex = _.findIndex(this.props.options, option => {
return option.value === this.props.defaultOptionValue;
});
if (selectedIndex > -1) {
this.updateWidgetMetaProperty("selectedIndex", selectedIndex);
} else {
this.updateWidgetMetaProperty("selectedIndex", undefined);
}
}
}
getPageView() {
const options = this.props.options || [];
let selectedIndex: number | undefined = undefined;
if (
this.props.selectedIndex !== undefined &&
this.props.selectedIndex < options.length &&
this.props.selectedIndex >= 0
) {
selectedIndex = this.props.selectedIndex;
}
const selectedIndexArr = this.props.selectedIndexArr || [];
let computedSelectedIndexArr = selectedIndexArr.slice();
selectedIndexArr.forEach((selectedIndex, index) => {
selectedIndexArr.forEach(selectedIndex => {
if (options[selectedIndex] === undefined) {
computedSelectedIndexArr = [];
}
@ -80,7 +97,7 @@ class DropdownWidget extends BaseWidget<DropdownWidgetProps, WidgetState> {
placeholder={this.props.placeholderText}
options={options}
selectionType={this.props.selectionType}
selectedIndex={selectedIndex}
selectedIndex={this.props.selectedIndex}
selectedIndexArr={computedSelectedIndexArr}
label={`${this.props.label}${this.props.isRequired ? " *" : ""}`}
isLoading={this.props.isLoading}
@ -156,6 +173,7 @@ export interface DropdownWidgetProps extends WidgetProps {
selectionType: SelectionType;
options?: DropdownOption[];
onOptionChange?: string;
defaultOptionValue?: string;
isRequired: boolean;
}

View File

@ -7,9 +7,12 @@ import Webcam from "@uppy/webcam";
import Url from "@uppy/url";
import OneDrive from "@uppy/onedrive";
import FilePickerComponent from "components/designSystems/appsmith/FilePickerComponent";
import { WidgetPropertyValidationType } from "utils/ValidationFactory";
import {
WidgetPropertyValidationType,
BASE_WIDGET_VALIDATION,
} from "utils/ValidationFactory";
import { VALIDATION_TYPES } from "constants/WidgetValidation";
import { EventType } from "constants/ActionConstants";
import { EventType, ExecutionResult } from "constants/ActionConstants";
import {
DerivedPropertiesMap,
TriggerPropertiesMap,
@ -28,9 +31,11 @@ class FilePickerWidget extends BaseWidget<FilePickerWidgetProps, WidgetState> {
static getPropertyValidationMap(): WidgetPropertyValidationType {
return {
...BASE_WIDGET_VALIDATION,
label: VALIDATION_TYPES.TEXT,
maxNumFiles: VALIDATION_TYPES.NUMBER,
allowedFileTypes: VALIDATION_TYPES.ARRAY,
files: VALIDATION_TYPES.ARRAY,
isRequired: VALIDATION_TYPES.BOOLEAN,
};
}
@ -96,13 +101,15 @@ class FilePickerWidget extends BaseWidget<FilePickerWidgetProps, WidgetState> {
locale: {},
});
this.uppy.on("file-removed", (file: any) => {
const updatedFiles = this.props.files.filter(dslFile => {
return file.id !== dslFile.id;
});
const updatedFiles = this.props.files
? this.props.files.filter(dslFile => {
return file.id !== dslFile.id;
})
: [];
this.updateWidgetMetaProperty("files", updatedFiles);
});
this.uppy.on("file-added", (file: any) => {
const dslFiles = this.props.files;
const dslFiles = this.props.files || [];
const reader = new FileReader();
reader.readAsDataURL(file.data);
reader.onloadend = () => {
@ -133,14 +140,30 @@ class FilePickerWidget extends BaseWidget<FilePickerWidgetProps, WidgetState> {
dynamicString: this.props.onFilesSelected,
event: {
type: EventType.ON_FILES_SELECTED,
callback: this.handleFileUploaded,
},
});
}
}
handleFileUploaded = (result: ExecutionResult) => {
if (result.success) {
this.updateWidgetMetaProperty(
"uploadedFileData",
this.props.uploadedFileUrls,
);
}
};
componentDidUpdate(prevProps: FilePickerWidgetProps) {
super.componentDidUpdate(prevProps);
if (
prevProps.files &&
prevProps.files.length > 0 &&
this.props.files === undefined
) {
this.uppy.reset();
} else if (
!shallowequal(prevProps.allowedFileTypes, this.props.allowedFileTypes) ||
prevProps.maxNumFiles !== this.props.maxNumFiles ||
prevProps.maxFileSize !== this.props.maxFileSize
@ -165,7 +188,7 @@ class FilePickerWidget extends BaseWidget<FilePickerWidgetProps, WidgetState> {
widgetId={this.props.widgetId}
key={this.props.widgetId}
label={this.props.label}
files={this.props.files}
files={this.props.files || []}
isLoading={this.props.isLoading}
/>
);
@ -180,10 +203,11 @@ export interface FilePickerWidgetProps extends WidgetProps {
label: string;
maxNumFiles?: number;
maxFileSize?: number;
files: any[];
files?: any[];
allowedFileTypes: string[];
onFilesSelected?: string;
isRequired?: boolean;
uploadedFileUrls?: string;
}
export default FilePickerWidget;

View File

@ -5,7 +5,10 @@ import ButtonComponent, {
ButtonType,
} from "components/designSystems/blueprint/ButtonComponent";
import { EventType, ExecutionResult } from "constants/ActionConstants";
import { WidgetPropertyValidationType } from "utils/ValidationFactory";
import {
BASE_WIDGET_VALIDATION,
WidgetPropertyValidationType,
} from "utils/ValidationFactory";
import { VALIDATION_TYPES } from "constants/WidgetValidation";
import { TriggerPropertiesMap } from "utils/WidgetFactory";
@ -30,10 +33,11 @@ class FormButtonWidget extends BaseWidget<
static getPropertyValidationMap(): WidgetPropertyValidationType {
return {
...BASE_WIDGET_VALIDATION,
text: VALIDATION_TYPES.TEXT,
disabledWhenInvalid: VALIDATION_TYPES.BOOLEAN,
isVisible: VALIDATION_TYPES.BOOLEAN,
buttonStyle: VALIDATION_TYPES.TEXT,
buttonType: VALIDATION_TYPES.TEXT,
};
}
@ -55,6 +59,8 @@ class FormButtonWidget extends BaseWidget<
callback: this.handleActionResult,
},
});
} else if (this.props.resetFormOnClick && this.props.onReset) {
this.props.onReset();
}
}
@ -84,7 +90,7 @@ class FormButtonWidget extends BaseWidget<
disabled={disabled}
onClick={this.onButtonClickBound}
isLoading={this.props.isLoading || this.state.isLoading}
type={ButtonType.SUBMIT}
type={this.props.buttonType || ButtonType.BUTTON}
/>
);
}
@ -105,6 +111,7 @@ export interface FormButtonWidgetProps extends WidgetProps {
buttonStyle?: ButtonStyle;
onClick?: string;
isVisible?: boolean;
buttonType: ButtonType;
isFormValid?: boolean;
resetFormOnClick?: boolean;
onReset?: () => void;

View File

@ -2,12 +2,16 @@ import * as React from "react";
import BaseWidget, { WidgetProps, WidgetState } from "./BaseWidget";
import { WidgetType } from "constants/WidgetConstants";
import ImageComponent from "components/designSystems/appsmith/ImageComponent";
import { WidgetPropertyValidationType } from "utils/ValidationFactory";
import {
WidgetPropertyValidationType,
BASE_WIDGET_VALIDATION,
} from "utils/ValidationFactory";
import { VALIDATION_TYPES } from "constants/WidgetValidation";
class ImageWidget extends BaseWidget<ImageWidgetProps, WidgetState> {
static getPropertyValidationMap(): WidgetPropertyValidationType {
return {
...BASE_WIDGET_VALIDATION,
image: VALIDATION_TYPES.TEXT,
imageShape: VALIDATION_TYPES.TEXT,
defaultImage: VALIDATION_TYPES.TEXT,

View File

@ -5,7 +5,10 @@ import InputComponent, {
InputComponentProps,
} from "components/designSystems/blueprint/InputComponent";
import { EventType } from "constants/ActionConstants";
import { WidgetPropertyValidationType } from "utils/ValidationFactory";
import {
WidgetPropertyValidationType,
BASE_WIDGET_VALIDATION,
} from "utils/ValidationFactory";
import { VALIDATION_TYPES } from "constants/WidgetValidation";
import { FIELD_REQUIRED_ERROR } from "constants/messages";
import {
@ -16,6 +19,7 @@ import {
class InputWidget extends BaseWidget<InputWidgetProps, WidgetState> {
static getPropertyValidationMap(): WidgetPropertyValidationType {
return {
...BASE_WIDGET_VALIDATION,
inputType: VALIDATION_TYPES.TEXT,
defaultText: VALIDATION_TYPES.TEXT,
isDisabled: VALIDATION_TYPES.BOOLEAN,
@ -30,7 +34,9 @@ class InputWidget extends BaseWidget<InputWidgetProps, WidgetState> {
inputValidators: VALIDATION_TYPES.ARRAY,
focusIndex: VALIDATION_TYPES.NUMBER,
isAutoFocusEnabled: VALIDATION_TYPES.BOOLEAN,
onTextChanged: VALIDATION_TYPES.TEXT,
isRequired: VALIDATION_TYPES.BOOLEAN,
isValid: VALIDATION_TYPES.BOOLEAN,
};
}
static getTriggerPropertyMap(): TriggerPropertiesMap {
@ -64,6 +70,16 @@ class InputWidget extends BaseWidget<InputWidgetProps, WidgetState> {
}
}
componentDidUpdate(prevProps: InputWidgetProps) {
super.componentDidUpdate(prevProps);
if (
(this.props.text !== prevProps.text && this.props.text === undefined) ||
this.props.defaultText !== prevProps.defaultText
) {
this.updateWidgetMetaProperty("text", this.props.defaultText);
}
}
onValueChange = (value: string) => {
this.updateWidgetMetaProperty("text", value);
if (!this.props.isDirty) {
@ -96,7 +112,6 @@ class InputWidget extends BaseWidget<InputWidgetProps, WidgetState> {
if (this.props.maxNum) conditionalProps.maxNum = this.props.maxNum;
if (this.props.minNum) conditionalProps.minNum = this.props.minNum;
if (this.props.isRequired) conditionalProps.label = `${this.props.label} *`;
return (
<InputComponent
value={value}

View File

@ -3,16 +3,22 @@ import BaseWidget, { WidgetProps, WidgetState } from "./BaseWidget";
import { WidgetType } from "constants/WidgetConstants";
import RadioGroupComponent from "components/designSystems/blueprint/RadioGroupComponent";
import { EventType } from "constants/ActionConstants";
import { WidgetPropertyValidationType } from "utils/ValidationFactory";
import {
WidgetPropertyValidationType,
BASE_WIDGET_VALIDATION,
} from "utils/ValidationFactory";
import { VALIDATION_TYPES } from "constants/WidgetValidation";
import { TriggerPropertiesMap } from "utils/WidgetFactory";
class RadioGroupWidget extends BaseWidget<RadioGroupWidgetProps, WidgetState> {
static getPropertyValidationMap(): WidgetPropertyValidationType {
return {
...BASE_WIDGET_VALIDATION,
label: VALIDATION_TYPES.TEXT,
options: VALIDATION_TYPES.OPTIONS_DATA,
selectedOptionValue: VALIDATION_TYPES.TEXT,
onSelectionChange: VALIDATION_TYPES.TEXT,
defaultOptionValue: VALIDATION_TYPES.TEXT,
isRequired: VALIDATION_TYPES.BOOLEAN,
};
}
@ -28,6 +34,30 @@ class RadioGroupWidget extends BaseWidget<RadioGroupWidgetProps, WidgetState> {
onSelectionChange: true,
};
}
componentDidMount() {
super.componentDidMount();
if (this.props.defaultOptionValue) {
this.updateWidgetMetaProperty(
"selectedOptionValue",
this.props.defaultOptionValue,
);
}
}
componentDidUpdate(prevProps: RadioGroupWidgetProps) {
super.componentDidUpdate(prevProps);
if (
(this.props.selectedOptionValue !== prevProps.selectedOptionValue &&
this.props.selectedOptionValue === undefined) ||
this.props.defaultOptionValue !== prevProps.defaultOptionValue
) {
this.updateWidgetMetaProperty(
"selectedOptionValue",
this.props.defaultOptionValue,
);
}
}
getPageView() {
return (
<RadioGroupComponent
@ -35,7 +65,6 @@ class RadioGroupWidget extends BaseWidget<RadioGroupWidgetProps, WidgetState> {
onRadioSelectionChange={this.onRadioSelectionChange}
key={this.props.widgetId}
label={`${this.props.label}${this.props.isRequired ? " *" : ""}`}
defaultOptionValue={this.props.defaultOptionValue}
selectedOptionValue={this.props.selectedOptionValue}
options={this.props.options}
isLoading={this.props.isLoading}

View File

@ -3,12 +3,16 @@ import BaseWidget, { WidgetProps, WidgetState } from "./BaseWidget";
import { WidgetType } from "constants/WidgetConstants";
import { Intent } from "@blueprintjs/core";
import SpinnerComponent from "components/designSystems/blueprint/SpinnerComponent";
import { WidgetPropertyValidationType } from "utils/ValidationFactory";
import {
WidgetPropertyValidationType,
BASE_WIDGET_VALIDATION,
} from "utils/ValidationFactory";
import { VALIDATION_TYPES } from "constants/WidgetValidation";
class SpinnerWidget extends BaseWidget<SpinnerWidgetProps, WidgetState> {
static getPropertyValidationMap(): WidgetPropertyValidationType {
return {
...BASE_WIDGET_VALIDATION,
size: VALIDATION_TYPES.NUMBER,
value: VALIDATION_TYPES.NUMBER,
ellipsize: VALIDATION_TYPES.BOOLEAN,

View File

@ -6,7 +6,10 @@ import { forIn } from "lodash";
import TableComponent from "components/designSystems/syncfusion/TableComponent";
import { VALIDATION_TYPES } from "constants/WidgetValidation";
import { WidgetPropertyValidationType } from "utils/ValidationFactory";
import {
WidgetPropertyValidationType,
BASE_WIDGET_VALIDATION,
} from "utils/ValidationFactory";
import { ColumnModel } from "@syncfusion/ej2-grids";
import { ColumnDirTypecast } from "@syncfusion/ej2-react-grids";
import { ColumnAction } from "components/propertyControls/ColumnActionSelectorControl";
@ -30,6 +33,7 @@ function constructColumns(data: object[]): ColumnModel[] | ColumnDirTypecast[] {
class TableWidget extends BaseWidget<TableWidgetProps, WidgetState> {
static getPropertyValidationMap(): WidgetPropertyValidationType {
return {
...BASE_WIDGET_VALIDATION,
tableData: VALIDATION_TYPES.TABLE_DATA,
nextPageKey: VALIDATION_TYPES.TEXT,
prevPageKey: VALIDATION_TYPES.TEXT,
@ -156,11 +160,6 @@ class TableWidget extends BaseWidget<TableWidgetProps, WidgetState> {
}
}
type RowData = {
rowIndex: number;
};
type SelectedRow = object & RowData;
export interface TableWidgetProps extends WidgetProps {
nextPageKey?: string;
prevPageKey?: string;

View File

@ -3,7 +3,10 @@ import BaseWidget, { WidgetProps, WidgetState } from "./BaseWidget";
import { WidgetType } from "constants/WidgetConstants";
import TextComponent from "components/designSystems/blueprint/TextComponent";
import { VALIDATION_TYPES } from "constants/WidgetValidation";
import { WidgetPropertyValidationType } from "utils/ValidationFactory";
import {
WidgetPropertyValidationType,
BASE_WIDGET_VALIDATION,
} from "utils/ValidationFactory";
const LINE_HEIGHTS: { [key in TextStyle]: number } = {
// The following values are arrived at by multiplying line-height with font-size
@ -16,9 +19,10 @@ const LINE_HEIGHTS: { [key in TextStyle]: number } = {
class TextWidget extends BaseWidget<TextWidgetProps, WidgetState> {
static getPropertyValidationMap(): WidgetPropertyValidationType {
return {
...BASE_WIDGET_VALIDATION,
text: VALIDATION_TYPES.TEXT,
textStyle: VALIDATION_TYPES.TEXT,
isVisible: VALIDATION_TYPES.BOOLEAN,
shouldScroll: VALIDATION_TYPES.BOOLEAN,
};
}

File diff suppressed because it is too large Load Diff