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> : ""
|
let stickyErrorDiv = this.props.inStickyError ? <div className="help-block" role="alert">{stickyErrorMessage}</div> : ""
|
||||||
|
|
||||||
return <div>
|
return <div>
|
||||||
{this.renderChildren()}
|
{this.props.children}
|
||||||
{errorDiv}
|
{errorDiv}
|
||||||
{stickyErrorDiv }
|
{stickyErrorDiv }
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -6,6 +6,8 @@ import LabeledFieldComponent from "./LabeledFieldComponent";
|
||||||
import {injectIntl, InjectedIntl} from 'react-intl';
|
import {injectIntl, InjectedIntl} from 'react-intl';
|
||||||
import {HoudiniField} from "../../lib/houdini_form";
|
import {HoudiniField} from "../../lib/houdini_form";
|
||||||
import ReactInput from "./form/ReactInput";
|
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}) =>{
|
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}
|
inputId={props.field.id} labelText={field.label} inError={field.hasError} error={field.error}
|
||||||
inStickyError={field.hasServerError} stickyError={field.serverError}
|
inStickyError={field.hasServerError} stickyError={field.serverError}
|
||||||
className={props.wrapperClassName} >
|
className={props.wrapperClassName} >
|
||||||
|
|
||||||
<ReactInput field={field} label={props.label} placeholder={props.placeholder} className={`form-control ${props.inputClassNames || ''}`}/>
|
<ReactInput field={field} label={props.label} placeholder={props.placeholder} className={`form-control ${props.inputClassNames || ''}`}/>
|
||||||
</LabeledFieldComponent>
|
</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 {Field} from "mobx-react-form";
|
||||||
import {observable, action, toJS, runInAction} from 'mobx';
|
import {observable, action, toJS, runInAction} from 'mobx';
|
||||||
import {InputHTMLAttributes} from 'react';
|
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
|
class ReactInput extends React.Component<InputTypes, {}> {
|
||||||
{
|
|
||||||
field:Field
|
|
||||||
label?:string
|
|
||||||
placeholder?:string
|
|
||||||
}
|
|
||||||
|
|
||||||
function castToNullIfUndef(i:any){
|
constructor(props:InputTypes){
|
||||||
return i === undefined ? null : i
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class ReactInput extends React.Component<ReactInputProps & InputHTMLAttributes<HTMLInputElement>, {}> {
|
|
||||||
|
|
||||||
constructor(props:ReactInputProps){
|
|
||||||
super(props)
|
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()
|
this.updateProps()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,18 +47,9 @@ class ReactInput extends React.Component<ReactInputProps & InputHTMLAttributes<H
|
||||||
this.field.set('placeholder', castToNullIfUndef(this.props.placeholder))
|
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
|
///Removes the properties we don't want to put into the input element
|
||||||
@action.bound
|
@action.bound
|
||||||
winnowProps(): ReactInputProps & InputHTMLAttributes<HTMLInputElement> {
|
winnowProps(): InputTypes {
|
||||||
let ourProps = {...this.props}
|
let ourProps = {...this.props}
|
||||||
delete ourProps.field
|
delete ourProps.field
|
||||||
delete ourProps.value
|
delete ourProps.value
|
||||||
|
@ -73,15 +58,8 @@ class ReactInput extends React.Component<ReactInputProps & InputHTMLAttributes<H
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
|
||||||
if (this.props.children)
|
|
||||||
{
|
|
||||||
return this.renderChildren()
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return <input {...this.winnowProps()} {...this.field.bind()}/>
|
return <input {...this.winnowProps()} {...this.field.bind()}/>
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default observer(ReactInput)
|
export default observer(ReactInput)
|
||||||
|
|
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