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 .idea
.storybook-out/ .storybook-out/
cypress/videos cypress/videos
results/ cypress/screenshots
results/

View File

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

View File

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

View File

@ -62,12 +62,12 @@ export interface RestAction {
cacheResponse?: string; cacheResponse?: string;
} }
export type PaginationField = "PREV" | "NEXT" | undefined; export type PaginationField = "PREV" | "NEXT";
export interface ExecuteActionRequest extends APIRequest { export interface ExecuteActionRequest extends APIRequest {
action: Pick<RestAction, "id"> | Omit<RestAction, "id">; action: Pick<RestAction, "id"> | Omit<RestAction, "id">;
params?: Property[]; params?: Property[];
paginationField: PaginationField; paginationField?: PaginationField;
} }
export interface ExecuteActionResponse extends ApiResponse { export interface ExecuteActionResponse extends ApiResponse {
@ -80,7 +80,7 @@ export interface ActionApiResponse {
data: { data: {
body: object; body: object;
headers: Record<string, string[]>; headers: Record<string, string[]>;
statusCode: string | number; statusCode: string;
}; };
clientMeta: { clientMeta: {
duration: string; duration: string;
@ -91,7 +91,7 @@ export interface ActionApiResponse {
export interface ActionResponse { export interface ActionResponse {
body: object; body: object;
headers: Record<string, string[]>; headers: Record<string, string[]>;
statusCode: string | number; statusCode: string;
duration: string; duration: string;
size: 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 <BaseButton
accent="primary" accent="primary"
filled filled
className={this.props.isLoading ? "bp3-skeleton" : ""} loading={this.props.isLoading}
text={label} text={label}
onClick={this.openModal} onClick={this.openModal}
/> />

View File

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

View File

@ -8,15 +8,39 @@ import moment from "moment-timezone";
import "../../../../node_modules/@blueprintjs/datetime/lib/css/blueprint-datetime.css"; import "../../../../node_modules/@blueprintjs/datetime/lib/css/blueprint-datetime.css";
import { DatePickerType } from "widgets/DatePickerWidget"; import { DatePickerType } from "widgets/DatePickerWidget";
import { WIDGET_PADDING } from "constants/WidgetConstants"; import { WIDGET_PADDING } from "constants/WidgetConstants";
import { Colors } from "constants/Colors";
const StyledControlGroup = styled(ControlGroup)` 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 { label {
${labelStyle} ${labelStyle}
flex: 0 1 30%; flex: 0 1 30%;
margin: 7px ${WIDGET_PADDING * 2}px 0 0;
text-align: right; text-align: right;
margin: 0 ${WIDGET_PADDING * 2}px 0 0; align-self: flex-start;
align-self: center; max-width: calc(30% - ${WIDGET_PADDING}px);
} }
} }
`; `;
@ -47,14 +71,14 @@ class DatePickerComponent extends React.Component<DatePickerComponentProps> {
this.props.enableTimePicker this.props.enableTimePicker
? { ? {
useAmPm: true, useAmPm: true,
value: this.props.selectedDate || this.props.defaultDate, value: this.props.selectedDate,
showArrowButtons: true, showArrowButtons: true,
} }
: undefined : undefined
} }
closeOnSelection={true} closeOnSelection={true}
onChange={this.onDateSelected} onChange={this.onDateSelected}
value={this.props.selectedDate || this.props.defaultDate} value={this.props.selectedDate}
/> />
) : ( ) : (
<DateRangeInput <DateRangeInput
@ -97,7 +121,6 @@ class DatePickerComponent extends React.Component<DatePickerComponentProps> {
export interface DatePickerComponentProps extends ComponentProps { export interface DatePickerComponentProps extends ComponentProps {
label: string; label: string;
defaultDate?: Date;
dateFormat: string; dateFormat: string;
enableTimePicker?: boolean; enableTimePicker?: boolean;
selectedDate?: Date; selectedDate?: Date;

View File

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

View File

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

View File

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

View File

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

View File

@ -1,6 +1,27 @@
import React from "react"; import React from "react";
import { Popover } from "@blueprintjs/core"; 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` const Wrapper = styled.div`
position: relative; position: relative;
@ -10,21 +31,6 @@ const Wrapper = styled.div`
width: 100%; width: 100%;
height: 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 { interface Props {
@ -36,16 +42,15 @@ interface Props {
const ErrorTooltip = (props: Props) => { const ErrorTooltip = (props: Props) => {
return ( return (
<Wrapper> <Wrapper>
<TooltipStyles />
<Popover <Popover
autoFocus={true} autoFocus={true}
canEscapeKeyClose={true} canEscapeKeyClose={true}
content={props.message} content={props.message}
position="bottom" position="bottom"
isOpen={props.isOpen && !!props.message} isOpen={props.isOpen && !!props.message}
usePortal={false} usePortal
modifiers={{ portalClassName="error-tooltip"
offset: { offset: "0,0,-11px" },
}}
> >
{props.children} {props.children}
</Popover> </Popover>

View File

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

View File

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

View File

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

View File

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

View File

@ -19,10 +19,3 @@ export type ControlType =
| "TIME_ZONE" | "TIME_ZONE"
| "CODE_EDITOR" | "CODE_EDITOR"
| "COLUMN_ACTION_SELECTOR"; | "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" | "RADIO_GROUP_WIDGET"
| "FILE_PICKER_WIDGET" | "FILE_PICKER_WIDGET"
| "INPUT_WIDGET" | "INPUT_WIDGET"
| "CHART_WIDGET"
| "SWITCH_WIDGET" | "SWITCH_WIDGET"
| "FORM_WIDGET" | "FORM_WIDGET"
| "FORM_BUTTON_WIDGET"; | "FORM_BUTTON_WIDGET";
@ -28,6 +29,7 @@ export const WidgetTypes: { [id: string]: WidgetType } = {
DROP_DOWN_WIDGET: "DROP_DOWN_WIDGET", DROP_DOWN_WIDGET: "DROP_DOWN_WIDGET",
CHECKBOX_WIDGET: "CHECKBOX_WIDGET", CHECKBOX_WIDGET: "CHECKBOX_WIDGET",
RADIO_GROUP_WIDGET: "RADIO_GROUP_WIDGET", RADIO_GROUP_WIDGET: "RADIO_GROUP_WIDGET",
CHART_WIDGET: "CHART_WIDGET",
FORM_WIDGET: "FORM_WIDGET", FORM_WIDGET: "FORM_WIDGET",
FORM_BUTTON_WIDGET: "FORM_BUTTON_WIDGET", FORM_BUTTON_WIDGET: "FORM_BUTTON_WIDGET",
}; };

View File

@ -8,6 +8,7 @@ export const VALIDATION_TYPES = {
TABLE_DATA: "TABLE_DATA", TABLE_DATA: "TABLE_DATA",
OPTIONS_DATA: "OPTIONS_DATA", OPTIONS_DATA: "OPTIONS_DATA",
DATE: "DATE", DATE: "DATE",
CHART_DATA: "CHART_DATA",
}; };
export type ValidationResponse = { 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 TextIcon } from "assets/icons/widget/text.svg";
import { ReactComponent as ImageIcon } from "assets/icons/widget/image.svg"; import { ReactComponent as ImageIcon } from "assets/icons/widget/image.svg";
import { ReactComponent as FilePickerIcon } from "assets/icons/widget/filepicker.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"; import { ReactComponent as FormIcon } from "assets/icons/widget/form.svg";
/* eslint-disable react/display-name */ /* eslint-disable react/display-name */
@ -91,6 +92,11 @@ export const WidgetIcons: {
<FilePickerIcon /> <FilePickerIcon />
</IconWrapper> </IconWrapper>
), ),
CHART_WIDGET: (props: IconProps) => (
<IconWrapper {...props}>
<ChartIcon />
</IconWrapper>
),
FORM_WIDGET: (props: IconProps) => ( FORM_WIDGET: (props: IconProps) => (
<IconWrapper {...props}> <IconWrapper {...props}>
<FormIcon /> <FormIcon />

View File

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

View File

@ -34,7 +34,6 @@ const WidgetConfigResponse: WidgetConfigReducerState = {
rows: 1, rows: 1,
columns: 8, columns: 8,
widgetName: "Input", widgetName: "Input",
text: "",
}, },
SWITCH_WIDGET: { SWITCH_WIDGET: {
isOn: false, isOn: false,
@ -119,7 +118,6 @@ const WidgetConfigResponse: WidgetConfigReducerState = {
{ label: "Option 5", value: "5" }, { label: "Option 5", value: "5" },
], ],
widgetName: "Dropdown", widgetName: "Dropdown",
selectedIndex: 0,
}, },
CHECKBOX_WIDGET: { CHECKBOX_WIDGET: {
rows: 1, rows: 1,
@ -156,6 +154,45 @@ const WidgetConfigResponse: WidgetConfigReducerState = {
widgetName: "FilePicker", widgetName: "FilePicker",
isDefaultClickDisabled: true, 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: { FORM_BUTTON_WIDGET: {
rows: 1, rows: 1,
columns: 3, columns: 3,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -24,7 +24,7 @@ import {
takeLatest, takeLatest,
all, all,
} from "redux-saga/effects"; } from "redux-saga/effects";
import { getNextEntityName } from "utils/AppsmithUtils"; import { convertToString, getNextEntityName } from "utils/AppsmithUtils";
import { import {
SetWidgetDynamicPropertyPayload, SetWidgetDynamicPropertyPayload,
updateWidgetProperty, updateWidgetProperty,
@ -262,7 +262,8 @@ function* setWidgetDynamicPropertySaga(
}; };
if (isDynamic) { if (isDynamic) {
dynamicProperties[propertyName] = true; dynamicProperties[propertyName] = true;
yield put(updateWidgetProperty(widgetId, propertyName, "{{}}")); const value = convertToString(widget[propertyName]);
yield put(updateWidgetProperty(widgetId, propertyName, value));
} else { } else {
delete dynamicProperties[propertyName]; delete dynamicProperties[propertyName];
yield put(updateWidgetProperty(widgetId, propertyName, undefined)); 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 { getAppsmithConfigs } from "configs";
import * as Sentry from "@sentry/browser"; import * as Sentry from "@sentry/browser";
import AnalyticsUtil from "./AnalyticsUtil"; import AnalyticsUtil from "./AnalyticsUtil";
@ -77,3 +77,14 @@ export const getNextEntityName = (prefix: string, existingNames: string[]) => {
export const noop = () => { export const noop = () => {
console.log("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 { WidgetType } from "constants/WidgetConstants";
import WidgetFactory from "./WidgetFactory"; import WidgetFactory from "./WidgetFactory";
import { import {
VALIDATION_TYPES,
ValidationResponse, ValidationResponse,
ValidationType, ValidationType,
Validator, Validator,
} from "constants/WidgetValidation"; } 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>; export type WidgetPropertyValidationType = Record<string, ValidationType>;
class ValidationFactory { class ValidationFactory {

View File

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

View File

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

View File

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

View File

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

View File

@ -5,7 +5,10 @@ import ButtonComponent, {
ButtonType, ButtonType,
} from "components/designSystems/blueprint/ButtonComponent"; } from "components/designSystems/blueprint/ButtonComponent";
import { EventType } from "constants/ActionConstants"; 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 { VALIDATION_TYPES } from "constants/WidgetValidation";
import { TriggerPropertiesMap } from "utils/WidgetFactory"; import { TriggerPropertiesMap } from "utils/WidgetFactory";
@ -30,9 +33,8 @@ class ButtonWidget extends BaseWidget<
static getPropertyValidationMap(): WidgetPropertyValidationType { static getPropertyValidationMap(): WidgetPropertyValidationType {
return { return {
...BASE_WIDGET_VALIDATION,
text: VALIDATION_TYPES.TEXT, text: VALIDATION_TYPES.TEXT,
isDisabled: VALIDATION_TYPES.BOOLEAN,
isVisible: VALIDATION_TYPES.BOOLEAN,
buttonStyle: VALIDATION_TYPES.TEXT, 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 CheckboxComponent from "components/designSystems/blueprint/CheckboxComponent";
import { EventType } from "constants/ActionConstants"; import { EventType } from "constants/ActionConstants";
import { VALIDATION_TYPES } from "constants/WidgetValidation"; import { VALIDATION_TYPES } from "constants/WidgetValidation";
import { WidgetPropertyValidationType } from "utils/ValidationFactory"; import {
WidgetPropertyValidationType,
BASE_WIDGET_VALIDATION,
} from "utils/ValidationFactory";
import { TriggerPropertiesMap } from "utils/WidgetFactory"; import { TriggerPropertiesMap } from "utils/WidgetFactory";
class CheckboxWidget extends BaseWidget<CheckboxWidgetProps, WidgetState> { class CheckboxWidget extends BaseWidget<CheckboxWidgetProps, WidgetState> {
static getPropertyValidationMap(): WidgetPropertyValidationType { static getPropertyValidationMap(): WidgetPropertyValidationType {
return { return {
isDisabled: VALIDATION_TYPES.BOOLEAN, ...BASE_WIDGET_VALIDATION,
label: VALIDATION_TYPES.TEXT, label: VALIDATION_TYPES.TEXT,
defaultCheckedState: VALIDATION_TYPES.BOOLEAN, 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() { getPageView() {
return ( return (
<CheckboxComponent <CheckboxComponent
defaultCheckedState={this.props.defaultCheckedState} isChecked={!!this.props.isChecked}
label={this.props.label} label={this.props.label}
widgetId={this.props.widgetId} widgetId={this.props.widgetId}
key={this.props.widgetId} key={this.props.widgetId}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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