From 9d659ddff0ed615bafbb0ef33a54f080b806b7b6 Mon Sep 17 00:00:00 2001 From: Nikhil Nandagopal Date: Thu, 31 Oct 2019 05:28:11 +0000 Subject: [PATCH] Feature/dropdown --- .../src/components/appsmith/BaseComponent.tsx | 2 + .../components/appsmith/TextViewComponent.tsx | 33 ---- .../blueprint/BreadcrumbsComponent.tsx | 29 ---- .../components/blueprint/ButtonComponent.tsx | 20 ++- .../blueprint/DropdownComponent.tsx | 125 ++++++++++++++ .../components/blueprint/InputComponent.tsx | 161 +++++++++++------- .../blueprint/TagInputComponent.tsx | 35 ---- .../components/blueprint/TextComponent.tsx | 48 ++++++ .../src/components/editor/ApiResponseView.tsx | 2 +- .../components/editor/DraggableComponent.tsx | 2 +- .../PropertyPaneConfigResponse.tsx | 58 +++---- .../mockResponses/WidgetConfigResponse.tsx | 23 ++- app/client/src/pages/Editor/WidgetsEditor.tsx | 12 ++ .../src/propertyControls/DropDownControl.tsx | 13 +- .../src/propertyControls/InputTextControl.tsx | 14 ++ .../src/propertyControls/StyledControls.tsx | 1 + .../propertyPaneConfigReducer.tsx | 4 + app/client/src/utils/WidgetRegistry.tsx | 7 + app/client/src/widgets/BaseWidget.tsx | 5 + app/client/src/widgets/ButtonWidget.tsx | 10 +- app/client/src/widgets/DropdownWidget.tsx | 67 +++++++- app/client/src/widgets/InputWidget.tsx | 45 +++++ app/client/src/widgets/TextWidget.tsx | 7 +- 23 files changed, 496 insertions(+), 227 deletions(-) delete mode 100644 app/client/src/components/appsmith/TextViewComponent.tsx delete mode 100644 app/client/src/components/blueprint/BreadcrumbsComponent.tsx create mode 100644 app/client/src/components/blueprint/DropdownComponent.tsx delete mode 100644 app/client/src/components/blueprint/TagInputComponent.tsx create mode 100644 app/client/src/components/blueprint/TextComponent.tsx diff --git a/app/client/src/components/appsmith/BaseComponent.tsx b/app/client/src/components/appsmith/BaseComponent.tsx index d882828947..b122fd544d 100644 --- a/app/client/src/components/appsmith/BaseComponent.tsx +++ b/app/client/src/components/appsmith/BaseComponent.tsx @@ -25,6 +25,8 @@ export interface ComponentProps { widgetId: string; widgetName?: string; style: BaseStyle; + isDisabled?: boolean; + isVisibile?: boolean; } export default BaseComponent; diff --git a/app/client/src/components/appsmith/TextViewComponent.tsx b/app/client/src/components/appsmith/TextViewComponent.tsx deleted file mode 100644 index 6246a6ec0a..0000000000 --- a/app/client/src/components/appsmith/TextViewComponent.tsx +++ /dev/null @@ -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)` - 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 { - render() { - return ( - - - {this.props.text} - - - ); - } -} - -export default TextComponent; diff --git a/app/client/src/components/blueprint/BreadcrumbsComponent.tsx b/app/client/src/components/blueprint/BreadcrumbsComponent.tsx deleted file mode 100644 index 5ad06dd735..0000000000 --- a/app/client/src/components/blueprint/BreadcrumbsComponent.tsx +++ /dev/null @@ -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 { - render() { - return ( - - - - ); - } -} - -export interface BreadcrumbsComponentProps extends ComponentProps { - width?: number; - collapseFrom?: Boundary; - className?: string; - minVisibleItems?: number; - items?: IBreadcrumbProps[]; -} - -export default BreadcrumbsComponent; diff --git a/app/client/src/components/blueprint/ButtonComponent.tsx b/app/client/src/components/blueprint/ButtonComponent.tsx index a7c93d6033..151e3feae5 100644 --- a/app/client/src/components/blueprint/ButtonComponent.tsx +++ b/app/client/src/components/blueprint/ButtonComponent.tsx @@ -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` color: ${props => { @@ -87,8 +88,22 @@ interface ButtonContainerProps extends TextComponentProps { icon?: MaybeElement; onClick?: (event: React.MouseEvent) => 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) => { diff --git a/app/client/src/components/blueprint/DropdownComponent.tsx b/app/client/src/components/blueprint/DropdownComponent.tsx new file mode 100644 index 0000000000..1d3f7c83ae --- /dev/null +++ b/app/client/src/components/blueprint/DropdownComponent.tsx @@ -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(); +const MultiDropDown = MultiSelect.ofType(); + +class DropDownComponent extends React.Component { + constructor(props: DropDownComponentProps) { + super(props); + } + + render() { + const selectedItems = this.props.selectedIndexArr + ? _.map(this.props.selectedIndexArr, index => { + return this.props.options[index]; + }) + : []; + return ( +
+ {this.props.selectionType === "SINGLE_SELECT" ? ( + + +
+ ); + } + + onItemSelect = ( + item: DropdownOption, + event?: React.SyntheticEvent, + ): 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 ( + + ); + }; +} + +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; diff --git a/app/client/src/components/blueprint/InputComponent.tsx b/app/client/src/components/blueprint/InputComponent.tsx index cd07cdcfaf..0a6bc2aadc 100644 --- a/app/client/src/components/blueprint/InputComponent.tsx +++ b/app/client/src/components/blueprint/InputComponent.tsx @@ -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) => { + 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 ( - {this.props.inputType === "INTEGER" || - this.props.inputType === "PHONE_NUMBER" || - this.props.inputType === "NUMBER" || - this.props.inputType === "CURRENCY" ? ( - - ) : this.props.inputType === "TEXT" || - this.props.inputType === "EMAIL" || - this.props.inputType === "PASSWORD" || - this.props.inputType === "SEARCH" ? ( - { - 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 - )} + + {this.props.errorMessage} ); } @@ -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; } diff --git a/app/client/src/components/blueprint/TagInputComponent.tsx b/app/client/src/components/blueprint/TagInputComponent.tsx deleted file mode 100644 index f97e235eb5..0000000000 --- a/app/client/src/components/blueprint/TagInputComponent.tsx +++ /dev/null @@ -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 { - render() { - return ( - - - - ); - } -} - -export interface TagInputComponentProps extends ComponentProps { - addOnPaste?: boolean; - className?: string; - disabled?: boolean; - fill?: boolean; - inputProps?: HTMLInputProps; - inputValue?: string; //Controlled value of the element. - intent?: Intent; - large?: boolean; //Whether the tag input should use a large size - onInputChange?: React.FormEventHandler; - placeholder?: string; - rightElement?: JSX.Element; - separator?: string | RegExp | false; - tagProps?: ITagProps; - values?: string[]; //Required field -} - -export default TagInputComponent; diff --git a/app/client/src/components/blueprint/TextComponent.tsx b/app/client/src/components/blueprint/TextComponent.tsx new file mode 100644 index 0000000000..fdf9fb43f9 --- /dev/null +++ b/app/client/src/components/blueprint/TextComponent.tsx @@ -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)``; + +export interface TextComponentProps extends ComponentProps { + text?: string; + ellipsize?: boolean; + textStyle?: TextStyle; +} + +class TextComponent extends React.Component { + 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 ( + + + {this.props.text} + + + ); + } +} + +export default TextComponent; diff --git a/app/client/src/components/editor/ApiResponseView.tsx b/app/client/src/components/editor/ApiResponseView.tsx index c7a349e34e..16dab691d8 100644 --- a/app/client/src/components/editor/ApiResponseView.tsx +++ b/app/client/src/components/editor/ApiResponseView.tsx @@ -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"; diff --git a/app/client/src/components/editor/DraggableComponent.tsx b/app/client/src/components/editor/DraggableComponent.tsx index 373c0ab2f6..ecb905342e 100644 --- a/app/client/src/components/editor/DraggableComponent.tsx +++ b/app/client/src/components/editor/DraggableComponent.tsx @@ -187,7 +187,7 @@ const DraggableComponent = (props: DraggableComponentProps) => { - + {editControlIcon} diff --git a/app/client/src/mockResponses/PropertyPaneConfigResponse.tsx b/app/client/src/mockResponses/PropertyPaneConfigResponse.tsx index 4fb051a469..95447deb46 100644 --- a/app/client/src/mockResponses/PropertyPaneConfigResponse.tsx +++ b/app/client/src/mockResponses/PropertyPaneConfigResponse.tsx @@ -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", diff --git a/app/client/src/mockResponses/WidgetConfigResponse.tsx b/app/client/src/mockResponses/WidgetConfigResponse.tsx index c7d3539d0d..79dfbac398 100644 --- a/app/client/src/mockResponses/WidgetConfigResponse.tsx +++ b/app/client/src/mockResponses/WidgetConfigResponse.tsx @@ -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" }, diff --git a/app/client/src/pages/Editor/WidgetsEditor.tsx b/app/client/src/pages/Editor/WidgetsEditor.tsx index c2220fd754..8a0150cd82 100644 --- a/app/client/src/pages/Editor/WidgetsEditor.tsx +++ b/app/client/src/pages/Editor/WidgetsEditor.tsx @@ -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 { value={{ executeAction: this.props.executeAction, updateWidget: this.props.updateWidget, + updateWidgetProperty: this.props.updateWidgetProperty, }} > @@ -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) => diff --git a/app/client/src/propertyControls/DropDownControl.tsx b/app/client/src/propertyControls/DropDownControl.tsx index 891f035e44..532d27fa00 100644 --- a/app/client/src/propertyControls/DropDownControl.tsx +++ b/app/client/src/propertyControls/DropDownControl.tsx @@ -8,7 +8,6 @@ import { ControlWrapper, StyledDropDown } from "./StyledControls"; class DropDownControl extends BaseControl { constructor(props: DropDownControlProps) { super(props); - this.onItemSelect = this.onItemSelect.bind(this); } render() { @@ -21,6 +20,7 @@ class DropDownControl extends BaseControl { } @@ -34,14 +34,14 @@ class DropDownControl extends BaseControl { ); } - onItemSelect( + onItemSelect = ( item: DropdownOption, event?: SyntheticEvent, - ): 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 { ); - } + }; filterOption(query: string, option: DropdownOption): boolean { return ( diff --git a/app/client/src/propertyControls/InputTextControl.tsx b/app/client/src/propertyControls/InputTextControl.tsx index 15d68b2dd1..08fed0b892 100644 --- a/app/client/src/propertyControls/InputTextControl.tsx +++ b/app/client/src/propertyControls/InputTextControl.tsx @@ -10,13 +10,27 @@ class InputTextControl extends BaseControl { ); } + isNumberType(inputType: InputType): boolean { + switch (inputType) { + case "CURRENCY": + case "INTEGER": + case "NUMBER": + case "PHONE_NUMBER": + return true; + default: + return false; + } + } + onTextChange = (event: React.ChangeEvent) => { this.updateProperty(this.props.propertyName, event.target.value); }; diff --git a/app/client/src/propertyControls/StyledControls.tsx b/app/client/src/propertyControls/StyledControls.tsx index 448ebe0636..28296ccb5a 100644 --- a/app/client/src/propertyControls/StyledControls.tsx +++ b/app/client/src/propertyControls/StyledControls.tsx @@ -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}; } diff --git a/app/client/src/reducers/entityReducers/propertyPaneConfigReducer.tsx b/app/client/src/reducers/entityReducers/propertyPaneConfigReducer.tsx index b712ea249f..30de4c72fc 100644 --- a/app/client/src/reducers/entityReducers/propertyPaneConfigReducer.tsx +++ b/app/client/src/reducers/entityReducers/propertyPaneConfigReducer.tsx @@ -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, ) => { + return { ...PropertyPaneConfigResponse }; return { ...action.payload }; }, }); diff --git a/app/client/src/utils/WidgetRegistry.tsx b/app/client/src/utils/WidgetRegistry.tsx index 509270775d..31e075b859 100644 --- a/app/client/src/utils/WidgetRegistry.tsx +++ b/app/client/src/utils/WidgetRegistry.tsx @@ -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 ; + }, + }); + WidgetFactory.registerWidgetBuilder("RADIO_GROUP_WIDGET", { buildWidget(widgetData: RadioGroupWidgetProps): JSX.Element { return ; diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index ce2b26a545..57cf2a9063 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -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 { diff --git a/app/client/src/widgets/ButtonWidget.tsx b/app/client/src/widgets/ButtonWidget.tsx index 4de83d7583..8ea024aa16 100644 --- a/app/client/src/widgets/ButtonWidget.tsx +++ b/app/client/src/widgets/ButtonWidget.tsx @@ -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 { @@ -19,14 +17,10 @@ class ButtonWidget extends BaseWidget { } 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 ( { - getPageView() { - return
; + constructor(props: DropdownWidgetProps) { + super(props); } + getPageView() { + return ( + + ); + } + + 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[]; diff --git a/app/client/src/widgets/InputWidget.tsx b/app/client/src/widgets/InputWidget.tsx index 891f0dc538..b99485ace2 100644 --- a/app/client/src/widgets/InputWidget.tsx +++ b/app/client/src/widgets/InputWidget.tsx @@ -4,13 +4,55 @@ import { WidgetType } from "../constants/WidgetConstants"; import InputComponent from "../components/blueprint/InputComponent"; class InputWidget extends BaseWidget { + 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 ( { getPageView() { return ( - ); @@ -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;