Feature/dropdown

This commit is contained in:
Nikhil Nandagopal 2019-10-31 05:28:11 +00:00
parent 3b393a8089
commit 9d659ddff0
23 changed files with 496 additions and 227 deletions

View File

@ -25,6 +25,8 @@ export interface ComponentProps {
widgetId: string;
widgetName?: string;
style: BaseStyle;
isDisabled?: boolean;
isVisibile?: boolean;
}
export default BaseComponent;

View File

@ -1,33 +0,0 @@
import * as React from "react";
import { Text } from "@blueprintjs/core";
import styled from "styled-components";
import { ComponentProps } from "./BaseComponent";
import { Container } from "./ContainerComponent";
type TextStyleProps = {
styleName: "primary" | "secondary" | "error";
};
export const BaseText = styled(Text)<TextStyleProps>`
color: ${props => props.theme.colors[props.styleName]};
`;
export interface TextComponentProps extends ComponentProps {
text?: string;
ellipsize?: boolean;
tagName?: keyof JSX.IntrinsicElements;
}
class TextComponent extends React.Component<TextComponentProps> {
render() {
return (
<Container {...this.props}>
<Text ellipsize={this.props.ellipsize} tagName={this.props.tagName}>
{this.props.text}
</Text>
</Container>
);
}
}
export default TextComponent;

View File

@ -1,29 +0,0 @@
import * as React from "react";
import { ComponentProps } from "../appsmith/BaseComponent";
import { Boundary, Breadcrumbs, IBreadcrumbProps } from "@blueprintjs/core";
import { Container } from "../appsmith/ContainerComponent";
class BreadcrumbsComponent extends React.Component<BreadcrumbsComponentProps> {
render() {
return (
<Container {...this.props}>
<Breadcrumbs
collapseFrom={this.props.collapseFrom}
items={this.props.items}
minVisibleItems={this.props.minVisibleItems}
className={this.props.className}
/>
</Container>
);
}
}
export interface BreadcrumbsComponentProps extends ComponentProps {
width?: number;
collapseFrom?: Boundary;
className?: string;
minVisibleItems?: number;
items?: IBreadcrumbProps[];
}
export default BreadcrumbsComponent;

View File

@ -2,7 +2,8 @@ import React from "react";
import { AnchorButton, IButtonProps, MaybeElement } from "@blueprintjs/core";
import styled, { css } from "styled-components";
import { Container } from "../appsmith/ContainerComponent";
import { TextComponentProps } from "../appsmith/TextViewComponent";
import { TextComponentProps } from "./TextComponent";
import { ButtonStyle } from "../../widgets/ButtonWidget";
const ButtonColorStyles = css<ButtonStyleProps>`
color: ${props => {
@ -87,8 +88,22 @@ interface ButtonContainerProps extends TextComponentProps {
icon?: MaybeElement;
onClick?: (event: React.MouseEvent<HTMLElement>) => void;
disabled?: boolean;
buttonStyle?: ButtonStyle;
}
const mapButtonStyleToStyleName = (buttonStyle?: ButtonStyle) => {
switch (buttonStyle) {
case "PRIMARY_BUTTON":
return "primary";
case "SECONDARY_BUTTON":
return "secondary";
case "DANGER_BUTTON":
return "error";
default:
return undefined;
}
};
// To be used with the canvas
const ButtonContainer = (props: ButtonContainerProps & ButtonStyleProps) => {
return (
@ -96,7 +111,8 @@ const ButtonContainer = (props: ButtonContainerProps & ButtonStyleProps) => {
<BaseButton
icon={props.icon}
text={props.text}
styleName={props.styleName}
filled={props.buttonStyle === "PRIMARY_BUTTON"}
styleName={mapButtonStyleToStyleName(props.buttonStyle)}
onClick={props.onClick}
disabled={props.disabled}
/>

View File

@ -0,0 +1,125 @@
import * as React from "react";
import { ComponentProps } from "../appsmith/BaseComponent";
import { MenuItem, Button } from "@blueprintjs/core";
import { Container } from "../appsmith/ContainerComponent";
import { SelectionType, DropdownOption } from "../../widgets/DropdownWidget";
import {
Select,
MultiSelect,
IItemRendererProps,
ItemRenderer,
} from "@blueprintjs/select";
import _ from "lodash";
const SingleDropDown = Select.ofType<DropdownOption>();
const MultiDropDown = MultiSelect.ofType<DropdownOption>();
class DropDownComponent extends React.Component<DropDownComponentProps> {
constructor(props: DropDownComponentProps) {
super(props);
}
render() {
const selectedItems = this.props.selectedIndexArr
? _.map(this.props.selectedIndexArr, index => {
return this.props.options[index];
})
: [];
return (
<div style={{ textAlign: "center" }}>
{this.props.selectionType === "SINGLE_SELECT" ? (
<SingleDropDown
items={this.props.options}
filterable={false}
itemRenderer={this.renderItem}
onItemSelect={this.onItemSelect}
>
<Container {...this.props}>
<Button
intent={"primary"}
text={
!_.isEmpty(this.props.options)
? this.props.options[this.props.selectedIndex].label
: "Add options"
}
/>
</Container>
</SingleDropDown>
) : (
<Container {...this.props}>
<MultiDropDown
items={this.props.options}
placeholder={this.props.placeholder}
tagRenderer={this.renderTag}
itemRenderer={this.renderItem}
selectedItems={selectedItems}
tagInputProps={{ onRemove: this.onItemRemoved }}
onItemSelect={this.onItemSelect}
></MultiDropDown>
</Container>
)}
</div>
);
}
onItemSelect = (
item: DropdownOption,
event?: React.SyntheticEvent<HTMLElement>,
): void => {
this.props.onOptionSelected(item);
};
onItemRemoved = (_tag: string, index: number) => {
this.props.onOptionRemoved(index);
};
renderTag = (option: DropdownOption) => {
return option.label;
};
isOptionSelected = (selectedOption: DropdownOption) => {
const optionIndex = _.findIndex(this.props.options, option => {
return option.value === selectedOption.value;
});
if (this.props.selectionType === "SINGLE_SELECT") {
return optionIndex === this.props.selectedIndex;
} else {
return (
_.findIndex(this.props.selectedIndexArr, index => {
return index === optionIndex;
}) !== -1
);
}
};
renderItem = (option: DropdownOption, itemProps: IItemRendererProps) => {
if (!itemProps.modifiers.matchesPredicate) {
return null;
}
const isSelected: boolean = this.isOptionSelected(option);
console.log("is selected " + isSelected);
return (
<MenuItem
icon={isSelected ? "tick" : "blank"}
active={itemProps.modifiers.active}
key={option.value}
onClick={itemProps.handleClick}
text={option.label}
/>
);
};
}
export interface DropDownComponentProps extends ComponentProps {
selectionType: SelectionType;
disabled?: boolean;
onOptionSelected: (optionSelected: DropdownOption) => void;
onOptionRemoved: (removedIndex: number) => void;
placeholder?: string;
label?: string;
selectedIndex: number;
selectedIndexArr: number[];
options: DropdownOption[];
}
export default DropDownComponent;

View File

@ -6,6 +6,9 @@ import {
IconName,
InputGroup,
Button,
Label,
Classes,
Text,
} from "@blueprintjs/core";
import { Container } from "../appsmith/ContainerComponent";
import { InputType } from "../../widgets/InputWidget";
@ -25,70 +28,99 @@ class InputComponent extends React.Component<
this.state = { showPassword: false };
}
onTextChange = (event: React.ChangeEvent<HTMLInputElement>) => {
this.props.onValueChange(event.target.value);
};
onNumberChange = (valueAsNum: number, valueAsString: string) => {
this.props.onValueChange(valueAsString);
};
isNumberInputType(inputType: InputType) {
return (
this.props.inputType === "INTEGER" ||
this.props.inputType === "PHONE_NUMBER" ||
this.props.inputType === "NUMBER" ||
this.props.inputType === "CURRENCY"
);
}
getIcon(inputType: InputType) {
switch (inputType) {
case "PHONE_NUMBER":
return "phone";
case "SEARCH":
return "search";
case "EMAIL":
return "envelope";
default:
return undefined;
}
}
getType(inputType: InputType) {
switch (inputType) {
case "PASSWORD":
return this.state.showPassword ? "password" : "text";
case "EMAIL":
return "email";
case "SEARCH":
return "search";
default:
return "text";
}
}
render() {
return (
<Container {...this.props}>
{this.props.inputType === "INTEGER" ||
this.props.inputType === "PHONE_NUMBER" ||
this.props.inputType === "NUMBER" ||
this.props.inputType === "CURRENCY" ? (
<NumericInput
placeholder={this.props.placeholder}
min={this.props.minNum}
max={this.props.maxNum}
disabled={this.props.disabled}
intent={this.props.intent}
defaultValue={this.props.defaultValue}
leftIcon={
this.props.inputType === "PHONE_NUMBER"
? "phone"
: this.props.leftIcon
}
type={this.props.inputType === "PHONE_NUMBER" ? "tel" : undefined}
allowNumericCharactersOnly={true}
stepSize={this.props.stepSize}
/>
) : this.props.inputType === "TEXT" ||
this.props.inputType === "EMAIL" ||
this.props.inputType === "PASSWORD" ||
this.props.inputType === "SEARCH" ? (
<InputGroup
placeholder={this.props.placeholder}
disabled={this.props.disabled}
intent={this.props.intent}
defaultValue={this.props.defaultValue}
rightElement={
this.props.inputType === "PASSWORD" ? (
<Button
icon={"lock"}
onClick={() => {
this.setState({ showPassword: !this.state.showPassword });
}}
/>
) : (
undefined
)
}
type={
this.props.inputType === "PASSWORD" && !this.state.showPassword
? "password"
: this.props.inputType === "EMAIL"
? "email"
: this.props.inputType === "SEARCH"
? "search"
: "text"
}
leftIcon={
this.props.inputType === "SEARCH"
? "search"
: this.props.inputType === "EMAIL"
? "envelope"
: this.props.leftIcon
}
/>
) : (
undefined
)}
<Label>
{this.props.label}
{this.isNumberInputType(this.props.inputType) ? (
<NumericInput
placeholder={this.props.placeholder}
min={this.props.minNum}
max={this.props.maxNum}
maxLength={this.props.maxChars}
disabled={this.props.disabled}
intent={this.props.intent}
defaultValue={this.props.defaultValue}
onValueChange={this.onNumberChange}
leftIcon={
this.props.inputType === "PHONE_NUMBER"
? "phone"
: this.props.leftIcon
}
type={this.props.inputType === "PHONE_NUMBER" ? "tel" : undefined}
allowNumericCharactersOnly={true}
stepSize={this.props.stepSize}
/>
) : (
<InputGroup
placeholder={this.props.placeholder}
disabled={this.props.disabled}
maxLength={this.props.maxChars}
intent={this.props.intent}
onChange={this.onTextChange}
defaultValue={this.props.defaultValue}
rightElement={
this.props.inputType === "PASSWORD" ? (
<Button
icon={"lock"}
onClick={() => {
this.setState({ showPassword: !this.state.showPassword });
}}
/>
) : (
undefined
)
}
type={this.getType(this.props.inputType)}
leftIcon={this.getIcon(this.props.inputType)}
/>
)}
</Label>
<Text>{this.props.errorMessage}</Text>
</Container>
);
}
@ -99,16 +131,19 @@ export interface InputComponentState {
}
export interface InputComponentProps extends ComponentProps {
inputType?: InputType;
inputType: InputType;
disabled?: boolean;
intent?: Intent;
defaultValue?: string;
label: string;
leftIcon?: IconName;
allowNumericCharactersOnly?: boolean;
fill?: boolean;
errorMessage?: string;
maxChars?: number;
maxNum?: number;
minNum?: number;
onValueChange?: (valueAsNumber: number, valueAsString: string) => void;
onValueChange: (valueAsString: string) => void;
stepSize?: number;
placeholder?: string;
}

View File

@ -1,35 +0,0 @@
import * as React from "react";
import { ComponentProps } from "../appsmith/BaseComponent";
import { Intent, ITagProps, TagInput, HTMLInputProps } from "@blueprintjs/core";
import { Container } from "../appsmith/ContainerComponent";
class TagInputComponent extends React.Component<TagInputComponentProps> {
render() {
return (
<Container {...this.props}>
<TagInput
placeholder={this.props.placeholder}
values={this.props.values}
/>
</Container>
);
}
}
export interface TagInputComponentProps extends ComponentProps {
addOnPaste?: boolean;
className?: string;
disabled?: boolean;
fill?: boolean;
inputProps?: HTMLInputProps;
inputValue?: string; //Controlled value of the <input> element.
intent?: Intent;
large?: boolean; //Whether the tag input should use a large size
onInputChange?: React.FormEventHandler<HTMLInputElement>;
placeholder?: string;
rightElement?: JSX.Element;
separator?: string | RegExp | false;
tagProps?: ITagProps;
values?: string[]; //Required field
}
export default TagInputComponent;

View File

@ -0,0 +1,48 @@
import * as React from "react";
import { Text, Classes } from "@blueprintjs/core";
import styled from "styled-components";
import { ComponentProps } from "../appsmith/BaseComponent";
import { Container } from "../appsmith/ContainerComponent";
import { TextStyle } from "../../widgets/TextWidget";
type TextStyleProps = {
styleName: "primary" | "secondary" | "error";
};
export const BaseText = styled(Text)<TextStyleProps>``;
export interface TextComponentProps extends ComponentProps {
text?: string;
ellipsize?: boolean;
textStyle?: TextStyle;
}
class TextComponent extends React.Component<TextComponentProps> {
getTextClass(textStyle?: TextStyle) {
switch (textStyle) {
case "HEADING":
return Classes.TEXT_LARGE;
case "LABEL":
return undefined;
case "BODY":
return Classes.TEXT_SMALL;
default:
return undefined;
}
}
render() {
return (
<Container {...this.props}>
<Text
className={this.getTextClass(this.props.textStyle)}
ellipsize={this.props.ellipsize}
>
{this.props.text}
</Text>
</Container>
);
}
}
export default TextComponent;

View File

@ -2,7 +2,7 @@ import React from "react";
import { connect } from "react-redux";
import { withRouter, RouteComponentProps } from "react-router";
import FormRow from "./FormRow";
import { BaseText } from "../appsmith/TextViewComponent";
import { BaseText } from "../blueprint/TextComponent";
import { BaseTabbedView } from "../appsmith/TabbedView";
import styled from "styled-components";
import { AppState } from "../../reducers";

View File

@ -187,7 +187,7 @@ const DraggableComponent = (props: DraggableComponentProps) => {
</Tooltip>
</DeleteControl>
<EditControl className="control" onClick={togglePropertyEditor}>
<Tooltip content="Toggle properties pane" hoverOpenDelay={500}>
<Tooltip content="Toggle props" hoverOpenDelay={500}>
{editControlIcon}
</Tooltip>
</EditControl>

View File

@ -12,7 +12,7 @@ const PropertyPaneConfigResponse: PropertyPaneConfigState = {
propertyName: "text",
label: "Button Text",
controlType: "INPUT_TEXT",
placeholderText: "Enter button text here",
placeholderText: "Enter button text",
},
{
id: "1.2",
@ -22,6 +22,7 @@ const PropertyPaneConfigResponse: PropertyPaneConfigState = {
options: [
{ label: "Primary Button", value: "PRIMARY_BUTTON" },
{ label: "Secondary Button", value: "SECONDARY_BUTTON" },
{ label: "Danger Button", value: "DANGER_BUTTON" },
],
},
{
@ -61,7 +62,7 @@ const PropertyPaneConfigResponse: PropertyPaneConfigState = {
propertyName: "text",
label: "Text",
controlType: "INPUT_TEXT",
placeholderText: "Enter your text here",
placeholderText: "Enter your text",
},
{
id: "3.2",
@ -72,7 +73,6 @@ const PropertyPaneConfigResponse: PropertyPaneConfigState = {
{ label: "Heading", value: "HEADING" },
{ label: "Label", value: "LABEL" },
{ label: "Body", value: "BODY" },
{ label: "Sub text", value: "SUB_TEXT" },
],
},
{
@ -140,7 +140,6 @@ const PropertyPaneConfigResponse: PropertyPaneConfigState = {
{ label: "Password", value: "PASSWORD" },
{ label: "Phone Number", value: "PHONE_NUMBER" },
{ label: "Email", value: "EMAIL" },
{ label: "Search", value: "SEARCH" },
],
},
{
@ -148,7 +147,7 @@ const PropertyPaneConfigResponse: PropertyPaneConfigState = {
propertyName: "placeholderText",
label: "Placeholder",
controlType: "INPUT_TEXT",
placeholderText: "Enter your text here",
placeholderText: "Enter your text",
},
{
id: "5.4",
@ -156,41 +155,32 @@ const PropertyPaneConfigResponse: PropertyPaneConfigState = {
label: "Max Chars",
controlType: "INPUT_TEXT",
inputType: "INTEGER",
placeholderText: "Maximum character length",
placeholderText: "Enter the max length",
},
{
id: "5.5",
propertyName: "validators",
label: "Validators",
controlType: "VALIDATION_INPUT",
propertyName: "regex",
label: "Regex",
controlType: "INPUT_TEXT",
inputType: "TEXT",
placeholderText: "Enter the regex",
},
{
id: "5.6",
children: [
{
id: "5.6.1",
propertyName: "focusIndexx",
label: "Focus Index",
controlType: "INPUT_TEXT",
inputType: "INTEGER",
placeholderText: "Enter the order of tab focus",
},
{
id: "5.6.2",
propertyName: "autoFocus",
label: "Auto Focus",
controlType: "SWITCH",
},
],
propertyName: "errorMessage",
label: "Error Message",
controlType: "INPUT_TEXT",
inputType: "TEXT",
placeholderText: "Enter the message",
},
{
id: "5.7",
id: "5.8",
propertyName: "isVisible",
label: "Visibile",
controlType: "SWITCH",
},
{
id: "5.8",
id: "5.9",
propertyName: "isDisabled",
label: "Disabled",
controlType: "SWITCH",
@ -407,7 +397,7 @@ const PropertyPaneConfigResponse: PropertyPaneConfigState = {
children: [
{
id: "13.1",
propertyName: "type",
propertyName: "selectionType",
label: "Selection Type",
controlType: "DROP_DOWN",
options: [
@ -415,6 +405,12 @@ const PropertyPaneConfigResponse: PropertyPaneConfigState = {
{ label: "Multi Select", value: "MULTI_SELECT" },
],
},
{
id: "13.4",
propertyName: "options",
label: "Options",
controlType: "INPUT_TEXT",
},
{
id: "13.2",
propertyName: "label",
@ -429,12 +425,6 @@ const PropertyPaneConfigResponse: PropertyPaneConfigState = {
controlType: "INPUT_TEXT",
placeholderText: "Enter the placeholder",
},
{
id: "13.4",
propertyName: "options",
label: "Options",
controlType: "OPTION_INPUT",
},
{
id: "13.5",
propertyName: "isVisible",

View File

@ -12,10 +12,10 @@ const WidgetConfigResponse: WidgetConfigReducerState = {
isVisible: true,
},
TEXT_WIDGET: {
text: "Not all labels are bad!",
text: "Label me",
textStyle: "LABEL",
rows: 1,
columns: 3,
columns: 4,
widgetName: "Text",
},
IMAGE_WIDGET: {
@ -30,20 +30,20 @@ const WidgetConfigResponse: WidgetConfigReducerState = {
inputType: "TEXT",
label: "Label me",
rows: 1,
columns: 3,
columns: 5,
widgetName: "Input",
},
SWITCH_WIDGET: {
isOn: false,
label: "Turn me on",
label: "Switch",
rows: 1,
columns: 4,
widgetName: "Switch",
},
CONTAINER_WIDGET: {
backgroundColor: "#FFFFFF",
rows: 1,
columns: 4,
rows: 8,
columns: 8,
widgetName: "Container",
},
SPINNER_WIDGET: {
@ -62,14 +62,19 @@ const WidgetConfigResponse: WidgetConfigReducerState = {
TABLE_WIDGET: {
rows: 5,
columns: 7,
label: "Don't table me!",
label: "Data",
widgetName: "Table",
},
DROP_DOWN_WIDGET: {
rows: 1,
columns: 3,
selectionType: "SINGLE_SELECT",
label: "Pick me!",
label: "Select",
options: [
{ label: "Option 1", value: "1" },
{ label: "Option 2", value: "2" },
{ label: "Option 3", value: "3" },
],
widgetName: "Dropdown",
},
CHECKBOX_WIDGET: {
@ -82,7 +87,7 @@ const WidgetConfigResponse: WidgetConfigReducerState = {
RADIO_GROUP_WIDGET: {
rows: 3,
columns: 3,
label: "Alpha - come in!",
label: "Labels",
options: [
{ label: "Alpha", value: "1" },
{ label: "Bravo", value: "2" },

View File

@ -27,6 +27,7 @@ import {
fetchEditorConfigs,
} from "../../actions/configsActions";
import { ReduxActionTypes } from "../../constants/ReduxActionConstants";
import { updateWidgetProperty } from "../../actions/controlActions";
const EditorWrapper = styled.div`
display: flex;
@ -59,6 +60,11 @@ type EditorProps = {
fetchCanvasWidgets: Function;
executeAction: (actionPayloads?: ActionPayload[]) => void;
updateWidget: Function;
updateWidgetProperty: (
widgetId: string,
propertyName: string,
propertyValue: any,
) => void;
savePageLayout: Function;
currentPageName: string;
currentPageId: string;
@ -92,6 +98,7 @@ class WidgetsEditor extends React.Component<EditorProps> {
value={{
executeAction: this.props.executeAction,
updateWidget: this.props.updateWidget,
updateWidgetProperty: this.props.updateWidgetProperty,
}}
>
<EditorWrapper>
@ -123,6 +130,11 @@ const mapStateToProps = (state: AppState) => {
const mapDispatchToProps = (dispatch: any) => {
return {
updateWidgetProperty: (
widgetId: string,
propertyName: string,
propertyValue: any,
) => dispatch(updateWidgetProperty(widgetId, propertyName, propertyValue)),
executeAction: (actionPayloads?: ActionPayload[]) =>
dispatch(executeAction(actionPayloads)),
fetchCanvasWidgets: (pageId: string) =>

View File

@ -8,7 +8,6 @@ import { ControlWrapper, StyledDropDown } from "./StyledControls";
class DropDownControl extends BaseControl<DropDownControlProps> {
constructor(props: DropDownControlProps) {
super(props);
this.onItemSelect = this.onItemSelect.bind(this);
}
render() {
@ -21,6 +20,7 @@ class DropDownControl extends BaseControl<DropDownControlProps> {
<StyledDropDown
items={this.props.options}
itemPredicate={this.filterOption}
filterable={false}
itemRenderer={this.renderItem}
onItemSelect={this.onItemSelect}
noResults={<MenuItem disabled={true} text="No results." />}
@ -34,14 +34,14 @@ class DropDownControl extends BaseControl<DropDownControlProps> {
);
}
onItemSelect(
onItemSelect = (
item: DropdownOption,
event?: SyntheticEvent<HTMLElement>,
): void {
): void => {
this.updateProperty(this.props.propertyName, item.value);
}
};
renderItem(option: DropdownOption, itemProps: IItemRendererProps) {
renderItem = (option: DropdownOption, itemProps: IItemRendererProps) => {
if (!itemProps.modifiers.matchesPredicate) {
return null;
}
@ -49,12 +49,11 @@ class DropDownControl extends BaseControl<DropDownControlProps> {
<MenuItem
active={itemProps.modifiers.active}
key={option.value}
label={option.label}
onClick={itemProps.handleClick}
text={option.label}
/>
);
}
};
filterOption(query: string, option: DropdownOption): boolean {
return (

View File

@ -10,13 +10,27 @@ class InputTextControl extends BaseControl<InputControlProps> {
<ControlWrapper>
<label>{this.props.label}</label>
<StyledInputGroup
type={this.isNumberType(this.props.inputType) ? "number" : "text"}
onChange={this.onTextChange}
placeholder={this.props.placeholderText}
defaultValue={this.props.propertyValue}
/>
</ControlWrapper>
);
}
isNumberType(inputType: InputType): boolean {
switch (inputType) {
case "CURRENCY":
case "INTEGER":
case "NUMBER":
case "PHONE_NUMBER":
return true;
default:
return false;
}
}
onTextChange = (event: React.ChangeEvent<HTMLInputElement>) => {
this.updateProperty(this.props.propertyName, event.target.value);
};

View File

@ -34,6 +34,7 @@ export const StyledSwitch = styled(Switch)`
export const StyledInputGroup = styled(InputGroup)`
& > input {
placeholderText: ${props => props.placeholder}
color: ${props => props.theme.colors.textOnDarkBG};
background: ${props => props.theme.colors.paneInputBG};
}

View File

@ -43,12 +43,16 @@ export interface PropertyPaneConfigState {
config: PropertyConfig;
configVersion: number;
}
/**
* Todo: Remove hardcoding of config response
*/
const propertyPaneConfigReducer = createReducer(initialState, {
[ReduxActionTypes.FETCH_PROPERTY_PANE_CONFIGS_SUCCESS]: (
state: PropertyPaneConfigState,
action: ReduxAction<PropertyPaneConfigState>,
) => {
return { ...PropertyPaneConfigResponse };
return { ...action.payload };
},
});

View File

@ -12,6 +12,7 @@ import RadioGroupWidget, {
import WidgetFactory from "./WidgetFactory";
import React from "react";
import ButtonWidget, { ButtonWidgetProps } from "../widgets/ButtonWidget";
import DropdownWidget, { DropdownWidgetProps } from "../widgets/DropdownWidget";
class WidgetBuilderRegistry {
static registerWidgetBuilders() {
@ -51,6 +52,12 @@ class WidgetBuilderRegistry {
},
});
WidgetFactory.registerWidgetBuilder("DROP_DOWN_WIDGET", {
buildWidget(widgetData: DropdownWidgetProps): JSX.Element {
return <DropdownWidget {...widgetData} />;
},
});
WidgetFactory.registerWidgetBuilder("RADIO_GROUP_WIDGET", {
buildWidget(widgetData: RadioGroupWidgetProps): JSX.Element {
return <RadioGroupWidget {...widgetData} />;

View File

@ -173,6 +173,11 @@ export interface WidgetDataProps {
export interface WidgetFunctions {
executeAction?: (actionPayloads?: ActionPayload[]) => void;
updateWidget?: Function;
updateWidgetProperty?: (
widgetId: string,
propertyName: string,
propertyValue: any,
) => void;
}
export interface WidgetCardProps {

View File

@ -1,9 +1,7 @@
import React from "react";
import BaseWidget, { WidgetProps, WidgetState } from "./BaseWidget";
import { WidgetType } from "../constants/WidgetConstants";
import ButtonComponent, {
ButtonStyleName,
} from "../components/blueprint/ButtonComponent";
import ButtonComponent from "../components/blueprint/ButtonComponent";
import { ActionPayload } from "../constants/ActionConstants";
class ButtonWidget extends BaseWidget<ButtonWidgetProps, WidgetState> {
@ -19,14 +17,10 @@ class ButtonWidget extends BaseWidget<ButtonWidgetProps, WidgetState> {
}
getPageView() {
// TODO(abhinav): This is a hack. Need to standardize the style names
const translatedButtonStyleName: ButtonStyleName | undefined =
this.props.buttonStyle &&
(this.props.buttonStyle.split("_")[0].toLowerCase() as ButtonStyleName);
return (
<ButtonComponent
style={this.getPositionStyle()}
styleName={translatedButtonStyleName}
buttonStyle={this.props.buttonStyle}
widgetId={this.props.widgetId}
widgetName={this.props.widgetName}
key={this.props.widgetId}

View File

@ -2,12 +2,73 @@ import React from "react";
import BaseWidget, { WidgetProps, WidgetState } from "./BaseWidget";
import { WidgetType } from "../constants/WidgetConstants";
import { ActionPayload } from "../constants/ActionConstants";
import DropDownComponent from "../components/blueprint/DropdownComponent";
import _ from "lodash";
class DropdownWidget extends BaseWidget<DropdownWidgetProps, WidgetState> {
getPageView() {
return <div />;
constructor(props: DropdownWidgetProps) {
super(props);
}
getPageView() {
return (
<DropDownComponent
onOptionSelected={this.onOptionSelected}
onOptionRemoved={this.onOptionRemoved}
widgetId={this.props.widgetId}
style={this.getPositionStyle()}
placeholder={this.props.placeholderText}
options={this.props.options || []}
selectionType={this.props.selectionType}
selectedIndex={this.props.selectedIndex || 0}
selectedIndexArr={this.props.selectedIndexArr || []}
label={this.props.label}
/>
);
}
onOptionSelected = (selectedOption: DropdownOption) => {
const selectedIndex = _.findIndex(this.props.options, option => {
return option.value === selectedOption.value;
});
if (this.props.selectionType === "SINGLE_SELECT") {
this.context.updateWidgetProperty(
this.props.widgetId,
"selectedIndex",
selectedIndex,
);
} else if (this.props.selectionType === "MULTI_SELECT") {
const selectedIndexArr = this.props.selectedIndexArr || [];
const isAlreadySelected =
_.find(selectedIndexArr, index => {
return index === selectedIndex;
}) !== undefined;
if (isAlreadySelected) {
this.onOptionRemoved(selectedIndex);
} else {
selectedIndexArr.push(selectedIndex);
this.context.updateWidgetProperty(
this.props.widgetId,
"selectedIndexArr",
selectedIndexArr,
);
}
}
};
onOptionRemoved = (removedIndex: number) => {
const updateIndexArr = this.props.selectedIndexArr
? this.props.selectedIndexArr.filter(index => {
return removedIndex !== index;
})
: [];
this.context.updateWidgetProperty(
this.props.widgetId,
"selectedIndexArr",
updateIndexArr,
);
};
getWidgetType(): WidgetType {
return "DROP_DOWN_WIDGET";
}
@ -22,6 +83,8 @@ export interface DropdownOption {
export interface DropdownWidgetProps extends WidgetProps {
placeholderText?: string;
label?: string;
selectedIndex?: number;
selectedIndexArr?: number[];
selectionType: SelectionType;
options?: DropdownOption[];
onOptionSelected?: ActionPayload[];

View File

@ -4,13 +4,55 @@ import { WidgetType } from "../constants/WidgetConstants";
import InputComponent from "../components/blueprint/InputComponent";
class InputWidget extends BaseWidget<InputWidgetProps, WidgetState> {
regex = new RegExp("");
constructor(props: InputWidgetProps) {
super(props);
}
componentDidMount() {
super.componentDidMount();
if (this.props.regex) {
try {
this.regex = new RegExp(this.props.regex);
} catch (e) {
console.log("invalid regex");
}
}
}
componentDidUpdate(prevProps: InputWidgetProps) {
super.componentDidUpdate(prevProps);
if (this.props.regex !== prevProps.regex && this.props.regex) {
try {
this.regex = new RegExp(this.props.regex);
} catch (e) {
console.log("invalid regex");
}
}
}
onValueChange = (value: string) => {
this.context.updateWidgetProperty(this.props.widgetId, "text", value);
};
getPageView() {
return (
<InputComponent
onValueChange={this.onValueChange}
style={this.getPositionStyle()}
widgetId={this.props.widgetId}
errorMessage={
this.props.regex &&
this.props.text &&
!this.regex.test(this.props.text)
? this.props.errorMessage
: undefined
}
inputType={this.props.inputType}
disabled={this.props.isDisabled}
maxChars={this.props.maxChars}
label={this.props.label}
defaultValue={this.props.defaultText}
maxNum={this.props.maxNum}
minNum={this.props.minNum}
@ -43,6 +85,9 @@ export interface InputWidgetProps extends WidgetProps {
inputType: InputType;
defaultText?: string;
isDisabled?: boolean;
text?: string;
regex?: string;
errorMessage?: string;
placeholderText?: string;
maxChars?: number;
minNum?: number;

View File

@ -1,15 +1,16 @@
import React from "react";
import BaseWidget, { WidgetProps, WidgetState } from "./BaseWidget";
import { WidgetType } from "../constants/WidgetConstants";
import TextViewComponent from "../components/appsmith/TextViewComponent";
import TextComponent from "../components/blueprint/TextComponent";
class TextWidget extends BaseWidget<TextWidgetProps, WidgetState> {
getPageView() {
return (
<TextViewComponent
<TextComponent
style={this.getPositionStyle()}
widgetId={this.props.widgetId}
key={this.props.widgetId}
textStyle={this.props.textStyle}
text={this.props.text}
/>
);
@ -24,7 +25,7 @@ export type TextStyle = "BODY" | "HEADING" | "LABEL" | "SUB_TEXT";
export interface TextWidgetProps extends WidgetProps {
text?: string;
textStyle?: TextStyle;
textStyle: TextStyle;
}
export default TextWidget;