Add new form components
This commit is contained in:
parent
e4161531aa
commit
62f6228f81
8 changed files with 227 additions and 35 deletions
|
@ -30,7 +30,7 @@ export default class StandardFieldComponent extends React.Component<StandardFiel
|
|||
let stickyErrorDiv = this.props.inStickyError ? <div className="help-block" role="alert">{stickyErrorMessage}</div> : ""
|
||||
|
||||
return <div>
|
||||
{this.renderChildren()}
|
||||
{this.props.children}
|
||||
{errorDiv}
|
||||
{stickyErrorDiv }
|
||||
</div>
|
||||
|
|
|
@ -6,6 +6,8 @@ import LabeledFieldComponent from "./LabeledFieldComponent";
|
|||
import {injectIntl, InjectedIntl} from 'react-intl';
|
||||
import {HoudiniField} from "../../lib/houdini_form";
|
||||
import ReactInput from "./form/ReactInput";
|
||||
import ReactSelect from './form/ReactSelect';
|
||||
import ReactTextarea from "./form/ReactTextarea";
|
||||
|
||||
|
||||
export const BasicField = observer((props:{field:Field, placeholder?:string, label?:string, wrapperClassName?:string, inputClassNames?:string}) =>{
|
||||
|
@ -14,7 +16,46 @@ export const BasicField = observer((props:{field:Field, placeholder?:string, lab
|
|||
inputId={props.field.id} labelText={field.label} inError={field.hasError} error={field.error}
|
||||
inStickyError={field.hasServerError} stickyError={field.serverError}
|
||||
className={props.wrapperClassName} >
|
||||
|
||||
<ReactInput field={field} label={props.label} placeholder={props.placeholder} className={`form-control ${props.inputClassNames || ''}`}/>
|
||||
</LabeledFieldComponent>
|
||||
})
|
||||
})
|
||||
|
||||
export const SelectField = observer((props:{field:Field, placeholder?:string, label?:string, wrapperClassName?:string, inputClassNames?:string, options?:Array<{id:any, name:string}>}) =>{
|
||||
let field = props.field as HoudiniField
|
||||
return <LabeledFieldComponent
|
||||
inputId={props.field.id} labelText={field.label} inError={field.hasError} error={field.error}
|
||||
inStickyError={field.hasServerError} stickyError={field.serverError}
|
||||
className={props.wrapperClassName} >
|
||||
|
||||
<ReactSelect field={field} label={props.label} placeholder={props.placeholder} className={`form-control ${props.inputClassNames}`} options={props.options}/>
|
||||
|
||||
</LabeledFieldComponent>
|
||||
})
|
||||
|
||||
export const TextareaField = observer((props:{field:Field, placeholder?:string, label?:string, wrapperClassName?:string, inputClassNames?:string, rows?:number}) =>{
|
||||
let field = props.field as HoudiniField
|
||||
return <LabeledFieldComponent
|
||||
inputId={props.field.id} labelText={field.label} inError={field.hasError} error={field.error}
|
||||
inStickyError={field.hasServerError} stickyError={field.serverError}
|
||||
className={props.wrapperClassName} >
|
||||
|
||||
<ReactTextarea field={field} label={props.label} placeholder={props.placeholder} className={`form-control ${props.inputClassNames}`} rows={props.rows}/>
|
||||
|
||||
</LabeledFieldComponent>
|
||||
})
|
||||
|
||||
export const CurrencyField = observer((props:{field:Field,placeholder?:string, label?:string, currencySymbol?:string, wrapperClassName?:string, inputClassNames?:string}) => {
|
||||
let field = props.field as HoudiniField
|
||||
let currencySymbolId = props.field.id + "_____currency_symbol"
|
||||
return <LabeledFieldComponent
|
||||
inputId={props.field.id} labelText={field.label} inError={field.hasError} error={field.error}
|
||||
inStickyError={field.hasServerError} stickyError={field.serverError}
|
||||
className={props.wrapperClassName} >
|
||||
<div className="input-group">
|
||||
<span className="input-group-addon" id={currencySymbolId}>{props.currencySymbol}</span>
|
||||
<ReactInput field={field} label={props.label} placeholder={props.placeholder} className={`form-control ${props.inputClassNames}`} aria-describedby={currencySymbolId}/>
|
||||
</div>
|
||||
</LabeledFieldComponent>
|
||||
|
||||
|
||||
});
|
|
@ -5,24 +5,18 @@ import {InjectedIntlProps, injectIntl} from 'react-intl';
|
|||
import {Field} from "mobx-react-form";
|
||||
import {observable, action, toJS, runInAction} from 'mobx';
|
||||
import {InputHTMLAttributes} from 'react';
|
||||
import {ReactInputProps} from "./react_input_props";
|
||||
import {SelectHTMLAttributes} from "react";
|
||||
import {ReactSelectProps} from "./ReactSelect";
|
||||
import {castToNullIfUndef} from "../../../lib/utils";
|
||||
|
||||
|
||||
type InputTypes = ReactInputProps &
|
||||
InputHTMLAttributes<HTMLInputElement>
|
||||
|
||||
export interface ReactInputProps
|
||||
{
|
||||
field:Field
|
||||
label?:string
|
||||
placeholder?:string
|
||||
}
|
||||
class ReactInput extends React.Component<InputTypes, {}> {
|
||||
|
||||
function castToNullIfUndef(i:any){
|
||||
return i === undefined ? null : i
|
||||
}
|
||||
|
||||
|
||||
class ReactInput extends React.Component<ReactInputProps & InputHTMLAttributes<HTMLInputElement>, {}> {
|
||||
|
||||
constructor(props:ReactInputProps){
|
||||
constructor(props:InputTypes){
|
||||
super(props)
|
||||
}
|
||||
|
||||
|
@ -43,7 +37,7 @@ class ReactInput extends React.Component<ReactInputProps & InputHTMLAttributes<H
|
|||
}
|
||||
|
||||
|
||||
componentDidUpdate(prevProps: Readonly<ReactInputProps>, prevState: Readonly<{}>): void {
|
||||
componentDidUpdate(prevProps: Readonly<InputTypes>, prevState: Readonly<{}>): void {
|
||||
this.updateProps()
|
||||
}
|
||||
|
||||
|
@ -53,18 +47,9 @@ class ReactInput extends React.Component<ReactInputProps & InputHTMLAttributes<H
|
|||
this.field.set('placeholder', castToNullIfUndef(this.props.placeholder))
|
||||
}
|
||||
|
||||
@action.bound
|
||||
renderChildren(){
|
||||
let ourProps = this.winnowProps()
|
||||
let elem = React.cloneElement(this.props.children as React.ReactElement<any>,
|
||||
{...ourProps, ...this.field.bind() })
|
||||
return elem
|
||||
|
||||
}
|
||||
|
||||
///Removes the properties we don't want to put into the input element
|
||||
@action.bound
|
||||
winnowProps(): ReactInputProps & InputHTMLAttributes<HTMLInputElement> {
|
||||
winnowProps(): InputTypes {
|
||||
let ourProps = {...this.props}
|
||||
delete ourProps.field
|
||||
delete ourProps.value
|
||||
|
@ -73,14 +58,7 @@ class ReactInput extends React.Component<ReactInputProps & InputHTMLAttributes<H
|
|||
}
|
||||
|
||||
render() {
|
||||
|
||||
if (this.props.children)
|
||||
{
|
||||
return this.renderChildren()
|
||||
}
|
||||
else {
|
||||
return <input {...this.winnowProps()} {...this.field.bind()}/>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
10
javascripts/src/components/common/form/ReactSelect.spec.tsx
Normal file
10
javascripts/src/components/common/form/ReactSelect.spec.tsx
Normal file
|
@ -0,0 +1,10 @@
|
|||
// License: LGPL-3.0-or-later
|
||||
import * as React from 'react';
|
||||
import 'jest';
|
||||
import ReactSelect from './ReactSelect'
|
||||
|
||||
describe('ReactSelect', () => {
|
||||
test('your test here', () => {
|
||||
expect(false).toBe(true)
|
||||
})
|
||||
})
|
80
javascripts/src/components/common/form/ReactSelect.tsx
Normal file
80
javascripts/src/components/common/form/ReactSelect.tsx
Normal file
|
@ -0,0 +1,80 @@
|
|||
// License: LGPL-3.0-or-later
|
||||
import * as React from 'react';
|
||||
import { observer } from 'mobx-react';
|
||||
import {InjectedIntlProps, injectIntl} from 'react-intl';
|
||||
import {Field} from "../../../../../types/mobx-react-form";
|
||||
import {InputHTMLAttributes} from "react";
|
||||
import {action, observable} from "mobx";
|
||||
import {SelectHTMLAttributes} from "react";
|
||||
import {ReactInputProps} from "./react_input_props";
|
||||
import {castToNullIfUndef} from "../../../lib/utils";
|
||||
|
||||
|
||||
export interface ReactSelectProps extends ReactInputProps
|
||||
{
|
||||
options?:Array<{id:any, name:string}>
|
||||
}
|
||||
|
||||
type InputTypes = ReactSelectProps & SelectHTMLAttributes<HTMLSelectElement>
|
||||
|
||||
class ReactSelect extends React.Component<InputTypes, {}> {
|
||||
|
||||
constructor(props:InputTypes){
|
||||
super(props)
|
||||
}
|
||||
|
||||
@observable
|
||||
field:Field
|
||||
|
||||
|
||||
@action.bound
|
||||
componentWillMount(){
|
||||
|
||||
this.field = this.props.field
|
||||
|
||||
|
||||
this.updateProps()
|
||||
}
|
||||
|
||||
componentWillUnmount(){
|
||||
}
|
||||
|
||||
|
||||
componentDidUpdate(prevProps: Readonly<InputTypes >, prevState: Readonly<{}>): void {
|
||||
this.updateProps()
|
||||
}
|
||||
|
||||
@action.bound
|
||||
updateProps() {
|
||||
this.field.set('label', castToNullIfUndef(this.props.label))
|
||||
this.field.set('placeholder', castToNullIfUndef(this.props.placeholder))
|
||||
}
|
||||
|
||||
|
||||
///Removes the properties we don't want to put into the input element
|
||||
@action.bound
|
||||
winnowProps(): InputTypes {
|
||||
let ourProps = {...this.props}
|
||||
delete ourProps.field
|
||||
delete ourProps.value
|
||||
delete ourProps.options
|
||||
return ourProps
|
||||
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
return <select {...this.winnowProps()} {...this.field.bind()}>
|
||||
{ this.props.options ? this.props.options.map(option =>
|
||||
<option key={option.id} value={option.id}>{option.name}</option>
|
||||
) : this.props.children
|
||||
}
|
||||
</select>
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
export default observer(ReactSelect)
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
// License: LGPL-3.0-or-later
|
||||
import * as React from 'react';
|
||||
import 'jest';
|
||||
import ReactSelect from './ReactSelect'
|
||||
|
||||
describe('ReactSelect', () => {
|
||||
test('your test here', () => {
|
||||
expect(false).toBe(true)
|
||||
})
|
||||
})
|
65
javascripts/src/components/common/form/ReactTextarea.tsx
Normal file
65
javascripts/src/components/common/form/ReactTextarea.tsx
Normal file
|
@ -0,0 +1,65 @@
|
|||
// License: LGPL-3.0-or-later
|
||||
import * as React from 'react';
|
||||
import { observer } from 'mobx-react';
|
||||
import {InjectedIntlProps, injectIntl} from 'react-intl';
|
||||
import {Field} from "../../../../../types/mobx-react-form";
|
||||
import {InputHTMLAttributes, ReactText, TextareaHTMLAttributes} from "react";
|
||||
import {action, observable} from "mobx";
|
||||
import {ReactInputProps} from "./react_input_props";
|
||||
import {castToNullIfUndef} from "../../../lib/utils";
|
||||
|
||||
type InputTypes = ReactInputProps & TextareaHTMLAttributes<HTMLTextAreaElement>
|
||||
|
||||
|
||||
class ReactTextarea extends React.Component<InputTypes, {}> {
|
||||
|
||||
constructor(props:InputTypes){
|
||||
super(props)
|
||||
}
|
||||
|
||||
@observable
|
||||
field:Field
|
||||
|
||||
|
||||
@action.bound
|
||||
componentWillMount(){
|
||||
|
||||
this.field = this.props.field
|
||||
|
||||
|
||||
this.updateProps()
|
||||
}
|
||||
|
||||
componentWillUnmount(){
|
||||
}
|
||||
|
||||
|
||||
componentDidUpdate(prevProps: Readonly<InputTypes>, prevState: Readonly<{}>): void {
|
||||
this.updateProps()
|
||||
}
|
||||
|
||||
@action.bound
|
||||
updateProps() {
|
||||
this.field.set('label', castToNullIfUndef(this.props.label))
|
||||
this.field.set('placeholder', castToNullIfUndef(this.props.placeholder))
|
||||
}
|
||||
|
||||
///Removes the properties we don't want to put into the input element
|
||||
@action.bound
|
||||
winnowProps(): InputTypes {
|
||||
let ourProps = {...this.props}
|
||||
delete ourProps.field
|
||||
delete ourProps.value
|
||||
return ourProps
|
||||
|
||||
}
|
||||
|
||||
render() {
|
||||
return <textarea {...this.winnowProps()} {...this.field.bind()}/>
|
||||
}
|
||||
}
|
||||
|
||||
export default observer(ReactTextarea)
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
import {Field} from "mobx-react-form";
|
||||
|
||||
export interface ReactInputProps
|
||||
{
|
||||
field:Field
|
||||
label?:string
|
||||
placeholder?:string
|
||||
}
|
Loading…
Reference in a new issue