Merge pull request #80 from houdiniproject/reactify_inputs
Making inputs more natural in React
This commit is contained in:
commit
1a779f48d4
12 changed files with 384 additions and 87 deletions
|
@ -5,15 +5,16 @@ import {Field} from "../../../../types/mobx-react-form";
|
||||||
import LabeledFieldComponent from "./LabeledFieldComponent";
|
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";
|
||||||
|
|
||||||
|
|
||||||
export const BasicField = injectIntl(observer((props:{field:Field, intl?:InjectedIntl, wrapperClassName?:string}) =>{
|
export const BasicField = observer((props:{field:Field, placeholder?:string, label?:string, wrapperClassName?:string}) =>{
|
||||||
let field = props.field as HoudiniField
|
let field = props.field as HoudiniField
|
||||||
return <LabeledFieldComponent
|
return <LabeledFieldComponent
|
||||||
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} >
|
||||||
|
|
||||||
<input {...props.field.bind()} placeholder={field.placeholder ? props.intl.formatMessage({id:field.placeholder}) : undefined} className="form-control"/>
|
<ReactInput field={field} label={props.label} placeholder={props.placeholder} className="form-control"/>
|
||||||
</LabeledFieldComponent>
|
</LabeledFieldComponent>
|
||||||
}))
|
})
|
49
javascripts/src/components/common/form/ReactForm.tsx
Normal file
49
javascripts/src/components/common/form/ReactForm.tsx
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
// License: LGPL-3.0-or-later
|
||||||
|
import * as React from 'react';
|
||||||
|
import { observer, Provider } from 'mobx-react';
|
||||||
|
import {Field, Form} from "mobx-react-form";
|
||||||
|
import {observable, action, toJS} from 'mobx';
|
||||||
|
|
||||||
|
export interface ReactFormProps
|
||||||
|
{
|
||||||
|
form:Form
|
||||||
|
}
|
||||||
|
|
||||||
|
///Mostly useless class but, at some point, will replace all our form elements
|
||||||
|
@observer
|
||||||
|
export class ReactForm extends React.Component<ReactFormProps, {}> {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@observable
|
||||||
|
form:Form
|
||||||
|
|
||||||
|
@action.bound
|
||||||
|
componentWillMount()
|
||||||
|
{
|
||||||
|
this.form = this.props.form
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
componentDidUpdate(){
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount(){
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
render() {
|
||||||
|
|
||||||
|
return <form >
|
||||||
|
{this.props.children}
|
||||||
|
</form>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
164
javascripts/src/components/common/form/ReactInput.spec.tsx
Normal file
164
javascripts/src/components/common/form/ReactInput.spec.tsx
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
// License: LGPL-3.0-or-later
|
||||||
|
import * as React from 'react';
|
||||||
|
import 'jest';
|
||||||
|
import ReactInput from './ReactInput'
|
||||||
|
import {Form} from "mobx-react-form";
|
||||||
|
import {mount} from 'enzyme';
|
||||||
|
import {toJS, observable, action, runInAction} from 'mobx';
|
||||||
|
import {observer} from 'mobx-react';
|
||||||
|
import {InputHTMLAttributes} from 'react';
|
||||||
|
import {ReactForm} from "./ReactForm";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@observer
|
||||||
|
class TestChange extends React.Component{
|
||||||
|
@observable
|
||||||
|
remove:boolean
|
||||||
|
@observable
|
||||||
|
form: Form
|
||||||
|
|
||||||
|
@action.bound
|
||||||
|
componentWillMount(){
|
||||||
|
this.form = new Form({fields:[{
|
||||||
|
name: 'name',
|
||||||
|
extra: null}
|
||||||
|
]})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@action.bound
|
||||||
|
onClick(){
|
||||||
|
this.remove = true
|
||||||
|
}
|
||||||
|
render() {
|
||||||
|
let reactInput = !this.remove ? <ReactInput field={this.form.$('name')} label={'label1'} placeholder={"holder"}>
|
||||||
|
{this.props.children}
|
||||||
|
</ReactInput> : undefined
|
||||||
|
|
||||||
|
return <ReactForm form={this.form}>
|
||||||
|
|
||||||
|
{reactInput}
|
||||||
|
<button onClick={() => this.onClick()}/>
|
||||||
|
</ReactForm>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class WrappedInput extends React.Component<InputHTMLAttributes<HTMLInputElement>>{
|
||||||
|
|
||||||
|
render(){
|
||||||
|
let notChildren = {...this.props}
|
||||||
|
delete notChildren.children
|
||||||
|
return <div>
|
||||||
|
<input {...notChildren} />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('ReactInput', () => {
|
||||||
|
|
||||||
|
let form: Form
|
||||||
|
beforeEach(() => {
|
||||||
|
form = new Form({
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'name',
|
||||||
|
extra: null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('no children passed in', () => {
|
||||||
|
test('gets added properly', () => {
|
||||||
|
let res = mount(<ReactForm form={form}>
|
||||||
|
<ReactInput field={form.$('name')} label={"label"}
|
||||||
|
placeholder={"holder"} value={'snapshot'} aria-required={true}/>
|
||||||
|
|
||||||
|
</ReactForm>)
|
||||||
|
|
||||||
|
|
||||||
|
//Did the attributes settings work as expected back to the objects
|
||||||
|
expect(form.$('name').label).toEqual('label')
|
||||||
|
expect(form.$('name').placeholder).toEqual('holder')
|
||||||
|
expect(form.$('name').value).toEqual('')
|
||||||
|
|
||||||
|
//is the aria attribute passted through to the input
|
||||||
|
let input = res.find('input')
|
||||||
|
expect(input.prop('aria-required')).toEqual(true)
|
||||||
|
|
||||||
|
|
||||||
|
// is the input properly bound?
|
||||||
|
input.simulate('change', {target: { value: 'something' } })
|
||||||
|
expect(form.$('name').value).toEqual('something')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('gets removed properly', () => {
|
||||||
|
|
||||||
|
let res = mount(<TestChange/>)
|
||||||
|
|
||||||
|
// The two casts are needed because Typescript was going blowing up without the 'any' first.
|
||||||
|
// Why was it? *shrugs*
|
||||||
|
let f = res.find('ReactForm').instance() as any as ReactForm
|
||||||
|
expect(f.form.size).toEqual(1)
|
||||||
|
|
||||||
|
res.find('input').simulate('change', {target: { value: 'something' } })
|
||||||
|
|
||||||
|
expect(f.form.$('name').value).toEqual('something')
|
||||||
|
|
||||||
|
res.find('button').simulate('click')
|
||||||
|
expect(f.form.size).toEqual(1)
|
||||||
|
|
||||||
|
expect(toJS(res.find('form'))).toMatchSnapshot()
|
||||||
|
|
||||||
|
expect(f.form.$('name').label).toEqual('label1')
|
||||||
|
expect(f.form.$('name').placeholder).toEqual('holder')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('children passed in', () => {
|
||||||
|
test('gets added properly', () => {
|
||||||
|
let res = mount(<ReactForm form={form}>
|
||||||
|
<ReactInput field={form.$('name')} label={"label"}
|
||||||
|
placeholder={"holder"} value={'snapshot'} aria-required={true}>
|
||||||
|
<WrappedInput/>
|
||||||
|
</ReactInput>
|
||||||
|
|
||||||
|
</ReactForm>)
|
||||||
|
|
||||||
|
//Did the attributes settings work as expected back to the objects
|
||||||
|
expect(form.$('name').label).toEqual('label')
|
||||||
|
expect(form.$('name').placeholder).toEqual('holder')
|
||||||
|
expect(form.$('name').value).toEqual('')
|
||||||
|
|
||||||
|
//is the aria attribute passted through to the input
|
||||||
|
let input = res.find('input')
|
||||||
|
expect(input.prop('aria-required')).toEqual(true)
|
||||||
|
|
||||||
|
|
||||||
|
// is the input properly bound?
|
||||||
|
input.simulate('change', {target: { value: 'something' } })
|
||||||
|
expect(form.$('name').value).toEqual('something')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('gets removed properly', () => {
|
||||||
|
|
||||||
|
let res = mount(<TestChange>
|
||||||
|
<WrappedInput/>
|
||||||
|
</TestChange>)
|
||||||
|
let f = res.find('ReactForm').instance() as any as ReactForm
|
||||||
|
res.find('input').simulate('change', {target: { value: 'something' } })
|
||||||
|
|
||||||
|
expect(f.form.$('name').value).toEqual('something')
|
||||||
|
expect(f.form.size).toEqual(1)
|
||||||
|
res.find('button').simulate('click')
|
||||||
|
expect(f.form.size).toEqual(1)
|
||||||
|
|
||||||
|
expect(f.form.$('name').label).toEqual('label1')
|
||||||
|
expect(f.form.$('name').placeholder).toEqual('holder')
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
})
|
90
javascripts/src/components/common/form/ReactInput.tsx
Normal file
90
javascripts/src/components/common/form/ReactInput.tsx
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
// License: LGPL-3.0-or-later
|
||||||
|
import * as React from 'react';
|
||||||
|
import { observer, inject, Provider } from 'mobx-react';
|
||||||
|
import {InjectedIntlProps, injectIntl} from 'react-intl';
|
||||||
|
import {Field} from "mobx-react-form";
|
||||||
|
import {observable, action, toJS, runInAction} from 'mobx';
|
||||||
|
import {InputHTMLAttributes} from 'react';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export interface ReactInputProps
|
||||||
|
{
|
||||||
|
field:Field
|
||||||
|
label?:string
|
||||||
|
placeholder?:string
|
||||||
|
}
|
||||||
|
|
||||||
|
function castToNullIfUndef(i:any){
|
||||||
|
return i === undefined ? null : i
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class ReactInput extends React.Component<ReactInputProps & InputHTMLAttributes<HTMLInputElement>, {}> {
|
||||||
|
|
||||||
|
constructor(props:ReactInputProps){
|
||||||
|
super(props)
|
||||||
|
}
|
||||||
|
|
||||||
|
@observable
|
||||||
|
field:Field
|
||||||
|
|
||||||
|
|
||||||
|
@action.bound
|
||||||
|
componentWillMount(){
|
||||||
|
|
||||||
|
this.field = this.props.field
|
||||||
|
|
||||||
|
|
||||||
|
this.updateProps()
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount(){
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps: Readonly<ReactInputProps>, prevState: Readonly<{}>): void {
|
||||||
|
this.updateProps()
|
||||||
|
}
|
||||||
|
|
||||||
|
@action.bound
|
||||||
|
updateProps() {
|
||||||
|
this.field.set('label', castToNullIfUndef(this.props.label))
|
||||||
|
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> {
|
||||||
|
let ourProps = {...this.props}
|
||||||
|
delete ourProps.field
|
||||||
|
delete ourProps.value
|
||||||
|
return ourProps
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
|
||||||
|
if (this.props.children)
|
||||||
|
{
|
||||||
|
return this.renderChildren()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return <input {...this.winnowProps()} {...this.field.bind()}/>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default observer(ReactInput)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`ReactInput no children passed in gets removed properly 1`] = `
|
||||||
|
<form>
|
||||||
|
<button
|
||||||
|
onClick={[Function]}
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
`;
|
|
@ -17,47 +17,31 @@ export interface NonprofitInfoFormProps
|
||||||
export const FieldDefinitions : Array<FieldDefinition> = [
|
export const FieldDefinitions : Array<FieldDefinition> = [
|
||||||
{
|
{
|
||||||
name: 'organization_name',
|
name: 'organization_name',
|
||||||
label: 'registration.wizard.nonprofit.name.label',
|
|
||||||
placeholder: 'registration.wizard.nonprofit.name.placeholder',
|
|
||||||
type: 'text',
|
|
||||||
validators: [Validations.isFilled]
|
validators: [Validations.isFilled]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'website',
|
name: 'website',
|
||||||
label: 'registration.wizard.nonprofit.website.label',
|
|
||||||
placeholder: 'registration.wizard.nonprofit.website.placeholder',
|
|
||||||
validators: [Validations.optional(Validations.isUrl)]
|
validators: [Validations.optional(Validations.isUrl)]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'org_email',
|
name: 'org_email',
|
||||||
label: 'registration.wizard.nonprofit.email.label',
|
|
||||||
placeholder: 'registration.wizard.nonprofit.email.placeholder',
|
|
||||||
validators: [Validations.optional(Validations.isEmail)]
|
validators: [Validations.optional(Validations.isEmail)]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'org_phone',
|
name: 'org_phone',
|
||||||
label: 'registration.wizard.nonprofit.phone.label',
|
|
||||||
placeholder: 'registration.wizard.nonprofit.phone.placeholder',
|
|
||||||
type: "tel"
|
type: "tel"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'city',
|
name: 'city',
|
||||||
label: 'registration.wizard.nonprofit.city.label',
|
|
||||||
placeholder: 'registration.wizard.nonprofit.city.placeholder',
|
|
||||||
validators: [Validations.isFilled]
|
validators: [Validations.isFilled]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'state',
|
name: 'state',
|
||||||
label: 'registration.wizard.nonprofit.state.label',
|
|
||||||
placeholder: 'registration.wizard.nonprofit.state.placeholder',
|
|
||||||
type: 'text',
|
|
||||||
validators: [Validations.isFilled]
|
validators: [Validations.isFilled]
|
||||||
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'zip',
|
name: 'zip',
|
||||||
label: 'registration.wizard.nonprofit.zip.label',
|
|
||||||
placeholder: 'registration.wizard.nonprofit.zip.placeholder',
|
|
||||||
validators: [Validations.isFilled]
|
validators: [Validations.isFilled]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -67,17 +51,33 @@ class NonprofitInfoForm extends React.Component<NonprofitInfoFormProps & Injecte
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return <fieldset >
|
return <fieldset >
|
||||||
<BasicField field={this.props.form.$("organization_name")}/>
|
<BasicField field={this.props.form.$("organization_name")}
|
||||||
<BasicField field={this.props.form.$('website')}/>
|
label={this.props.intl.formatMessage({id:'registration.wizard.nonprofit.name.label' })}
|
||||||
|
placeholder={this.props.intl.formatMessage({id: 'registration.wizard.nonprofit.name.placeholder'})}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<BasicField field={this.props.form.$('website')}
|
||||||
|
label={this.props.intl.formatMessage({id: 'registration.wizard.nonprofit.website.label'})}
|
||||||
|
placeholder={this.props.intl.formatMessage({id: 'registration.wizard.nonprofit.website.placeholder'})}/>
|
||||||
<TwoColumnFields>
|
<TwoColumnFields>
|
||||||
<BasicField field={this.props.form.$('org_email')}/>
|
<BasicField field={this.props.form.$('org_email')}
|
||||||
<BasicField field={this.props.form.$('org_phone')}/>
|
label={this.props.intl.formatMessage({id: 'registration.wizard.nonprofit.email.label'})}
|
||||||
|
placeholder={this.props.intl.formatMessage({id: 'registration.wizard.nonprofit.email.placeholder'})}/>
|
||||||
|
<BasicField field={this.props.form.$('org_phone')}
|
||||||
|
label={this.props.intl.formatMessage({id: 'registration.wizard.nonprofit.phone.label'})}
|
||||||
|
placeholder={this.props.intl.formatMessage({id: 'registration.wizard.nonprofit.phone.placeholder'})}/>
|
||||||
</TwoColumnFields>
|
</TwoColumnFields>
|
||||||
|
|
||||||
<ThreeColumnFields>
|
<ThreeColumnFields>
|
||||||
<BasicField field={this.props.form.$('city')}/>
|
<BasicField field={this.props.form.$('city')}
|
||||||
<BasicField field={this.props.form.$('state')}/>
|
label={this.props.intl.formatMessage({id: 'registration.wizard.nonprofit.city.label'})}
|
||||||
<BasicField field={this.props.form.$('zip')}/>
|
placeholder={this.props.intl.formatMessage({id: 'registration.wizard.nonprofit.city.placeholder'})}/>
|
||||||
|
<BasicField field={this.props.form.$('state')}
|
||||||
|
label={this.props.intl.formatMessage({id: 'registration.wizard.nonprofit.state.label'})}
|
||||||
|
placeholder={this.props.intl.formatMessage({id: 'registration.wizard.nonprofit.state.placeholder'})}/>
|
||||||
|
<BasicField field={this.props.form.$('zip')}
|
||||||
|
label={this.props.intl.formatMessage({id: 'registration.wizard.nonprofit.zip.label' })}
|
||||||
|
placeholder={this.props.intl.formatMessage({id: 'registration.wizard.nonprofit.zip.placeholder'})}/>
|
||||||
</ThreeColumnFields>
|
</ThreeColumnFields>
|
||||||
<ProgressableButton onClick={this.props.form.onSubmit} className="button" disabled={!this.props.form.isValid} buttonText={this.props.intl.formatMessage({id: this.props.buttonText})}
|
<ProgressableButton onClick={this.props.form.onSubmit} className="button" disabled={!this.props.form.isValid} buttonText={this.props.intl.formatMessage({id: this.props.buttonText})}
|
||||||
inProgress={this.props.form.submitting || this.props.form.container().submitting} disableOnProgress={true}/>
|
inProgress={this.props.form.submitting || this.props.form.container().submitting} disableOnProgress={true}/>
|
||||||
|
|
|
@ -175,24 +175,6 @@ export class InnerRegistrationWizard extends React.Component<RegistrationWizardP
|
||||||
this.form.signinApi = this.props.ApiManager.get(WebUserSignInOut)
|
this.form.signinApi = this.props.ApiManager.get(WebUserSignInOut)
|
||||||
}
|
}
|
||||||
|
|
||||||
//set up labels
|
|
||||||
let label: {[props:string]: string} = {
|
|
||||||
'nonprofitTab[organization_name]': "registration.wizard.nonprofit.name.label",
|
|
||||||
"nonprofitTab[website]": 'registration.wizard.nonprofit.website.label',
|
|
||||||
"nonprofitTab[org_email]": 'registration.wizard.nonprofit.email.label',
|
|
||||||
'nonprofitTab[org_phone]': 'registration.wizard.nonprofit.phone.label',
|
|
||||||
'nonprofitTab[city]': 'registration.wizard.nonprofit.city.label',
|
|
||||||
'nonprofitTab[state]': 'registration.wizard.nonprofit.state.label',
|
|
||||||
'nonprofitTab[zip]': 'registration.wizard.nonprofit.zip.label',
|
|
||||||
'userTab[name]': 'registration.wizard.contact.name.label',
|
|
||||||
'userTab[email]': 'registration.wizard.contact.email.label',
|
|
||||||
'userTab[password]': 'registration.wizard.contact.password.label',
|
|
||||||
'userTab[password_confirmation]': 'registration.wizard.contact.password_confirmation.label'
|
|
||||||
}
|
|
||||||
for (let key in label){
|
|
||||||
this.form.$(key).set('label', this.props.intl.formatMessage({id: label[key]}))
|
|
||||||
}
|
|
||||||
|
|
||||||
return <Wizard wizardState={this.registrationWizardState} disableTabs={this.form.submitting}>
|
return <Wizard wizardState={this.registrationWizardState} disableTabs={this.form.submitting}>
|
||||||
<NonprofitInfoPanel tab={this.registrationWizardState.tabsByName['nonprofitTab']}
|
<NonprofitInfoPanel tab={this.registrationWizardState.tabsByName['nonprofitTab']}
|
||||||
buttonText="registration.wizard.next"/>
|
buttonText="registration.wizard.next"/>
|
||||||
|
|
|
@ -12,8 +12,6 @@ import {areWeOrAnyParentSubmitting} from "../../lib/houdini_form";
|
||||||
export const FieldDefinitions : Array<FieldDefinition> = [
|
export const FieldDefinitions : Array<FieldDefinition> = [
|
||||||
{
|
{
|
||||||
name: 'name',
|
name: 'name',
|
||||||
label: 'registration.wizard.contact.name.label',
|
|
||||||
placeholder: 'registration.wizard.contact.name.placeholder',
|
|
||||||
validators: [Validations.isFilled]
|
validators: [Validations.isFilled]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -24,14 +22,12 @@ export const FieldDefinitions : Array<FieldDefinition> = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'password',
|
name: 'password',
|
||||||
label: 'registration.wizard.contact.password.label',
|
|
||||||
type: 'password',
|
type: 'password',
|
||||||
validators: [Validations.isFilled],
|
validators: [Validations.isFilled],
|
||||||
related: ['userTab.password_confirmation']
|
related: ['userTab.password_confirmation']
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'password_confirmation',
|
name: 'password_confirmation',
|
||||||
label: 'registration.wizard.contact.password_confirmation.label',
|
|
||||||
type: 'password',
|
type: 'password',
|
||||||
validators: [Validations.shouldBeEqualTo("userTab.password")]
|
validators: [Validations.shouldBeEqualTo("userTab.password")]
|
||||||
}
|
}
|
||||||
|
@ -50,12 +46,22 @@ class UserInfoForm extends React.Component<UserInfoFormProps & InjectedIntlProps
|
||||||
render() {
|
render() {
|
||||||
return <fieldset>
|
return <fieldset>
|
||||||
<TwoColumnFields>
|
<TwoColumnFields>
|
||||||
<BasicField field={this.props.form.$("name")}/>
|
<BasicField field={this.props.form.$("name")}
|
||||||
<BasicField field={this.props.form.$('email')}/>
|
label={
|
||||||
|
this.props.intl.formatMessage({id: "registration.wizard.contact.name.label"})}
|
||||||
|
placeholder={this.props.intl.formatMessage({id: "registration.wizard.contact.name.placeholder"})}/>
|
||||||
|
<BasicField field={this.props.form.$('email')}
|
||||||
|
label={this.props.intl.formatMessage({id: "registration.wizard.contact.email.label"})}
|
||||||
|
placeholder={this.props.intl.formatMessage({id: "registration.wizard.contact.email.placeholder"})}
|
||||||
|
/>
|
||||||
</TwoColumnFields>
|
</TwoColumnFields>
|
||||||
|
|
||||||
<BasicField field={this.props.form.$('password')}/>
|
<BasicField field={this.props.form.$('password')}
|
||||||
<BasicField field={this.props.form.$('password_confirmation')}/>
|
label={this.props.intl.formatMessage({id:'registration.wizard.contact.password.label'})}
|
||||||
|
/>
|
||||||
|
<BasicField field={this.props.form.$('password_confirmation')}
|
||||||
|
label={this.props.intl.formatMessage({id:'registration.wizard.contact.password_confirmation.label'})}
|
||||||
|
/>
|
||||||
|
|
||||||
|
|
||||||
<ProgressableButton onClick={this.props.form.onSubmit}
|
<ProgressableButton onClick={this.props.form.onSubmit}
|
||||||
|
|
|
@ -23,13 +23,11 @@ export interface SessionLoginFormProps
|
||||||
export const FieldDefinitions : Array<FieldDefinition> = [
|
export const FieldDefinitions : Array<FieldDefinition> = [
|
||||||
{
|
{
|
||||||
name: 'email',
|
name: 'email',
|
||||||
label: 'email',
|
|
||||||
type: 'text',
|
type: 'text',
|
||||||
validators: [Validations.isFilled]
|
validators: [Validations.isFilled]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'password',
|
name: 'password',
|
||||||
label: 'password',
|
|
||||||
type: 'password',
|
type: 'password',
|
||||||
validators: [Validations.isFilled]
|
validators: [Validations.isFilled]
|
||||||
}
|
}
|
||||||
|
@ -100,20 +98,14 @@ class InnerSessionLoginForm extends React.Component<SessionLoginFormProps & Inje
|
||||||
if(!this.form.signinApi){
|
if(!this.form.signinApi){
|
||||||
this.form.signinApi = this.props.ApiManager.get(WebUserSignInOut)
|
this.form.signinApi = this.props.ApiManager.get(WebUserSignInOut)
|
||||||
}
|
}
|
||||||
let label: {[props:string]: string} = {
|
|
||||||
'email': "login.email",
|
|
||||||
"password": 'login.password',
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let key in label){
|
|
||||||
this.form.$(key).set('label', this.props.intl.formatMessage({id: label[key]}))
|
|
||||||
}
|
|
||||||
|
|
||||||
let errorDiv = !this.form.isValid ? <div className="form-group has-error"><div className="help-block" role="alert">{(this.form as any).error}</div></div> : ''
|
let errorDiv = !this.form.isValid ? <div className="form-group has-error"><div className="help-block" role="alert">{(this.form as any).error}</div></div> : ''
|
||||||
|
|
||||||
return <form onSubmit={this.form.onSubmit}>
|
return <form onSubmit={this.form.onSubmit}>
|
||||||
<BasicField field={this.form.$('email')}/>
|
<BasicField field={this.form.$('email')}
|
||||||
<BasicField field={this.form.$('password')}/>
|
label={this.props.intl.formatMessage({id: 'login.email'})}/>
|
||||||
|
<BasicField field={this.form.$('password')}
|
||||||
|
label={this.props.intl.formatMessage({id: 'login.password'})}/>
|
||||||
{errorDiv}
|
{errorDiv}
|
||||||
<div className={'form-group'}>
|
<div className={'form-group'}>
|
||||||
<ProgressableButton onClick={this.form.onSubmit} className="button" disabled={!this.form.isValid || this.form.submitting} inProgress={this.form.submitting}
|
<ProgressableButton onClick={this.form.onSubmit} className="button" disabled={!this.form.isValid || this.form.submitting} inProgress={this.form.submitting}
|
||||||
|
|
30
package-lock.json
generated
30
package-lock.json
generated
|
@ -5697,9 +5697,9 @@
|
||||||
"integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0="
|
"integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0="
|
||||||
},
|
},
|
||||||
"hoist-non-react-statics": {
|
"hoist-non-react-statics": {
|
||||||
"version": "2.5.0",
|
"version": "2.5.5",
|
||||||
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz",
|
||||||
"integrity": "sha512-6Bl6XsDT1ntE0lHbIhr4Kp2PGcleGZ66qu5Jqk8lc0Xc/IeG6gVLmwUGs/K0Us+L8VWoKgj0uWdPMataOsm31w==",
|
"integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"home-or-tmp": {
|
"home-or-tmp": {
|
||||||
|
@ -8900,19 +8900,19 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mobx": {
|
"mobx": {
|
||||||
"version": "4.2.1",
|
"version": "4.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/mobx/-/mobx-4.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/mobx/-/mobx-4.3.1.tgz",
|
||||||
"integrity": "sha1-3UGQ2vG0PUGjoihYUlP5lwsKJ90=",
|
"integrity": "sha1-M05aq0kWsdQ/D682BaZLG0s8y40=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"mobx-react": {
|
"mobx-react": {
|
||||||
"version": "5.1.2",
|
"version": "5.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/mobx-react/-/mobx-react-5.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/mobx-react/-/mobx-react-5.2.3.tgz",
|
||||||
"integrity": "sha1-7FwtKaHfgj29GzfiFPo2oJBwVOI=",
|
"integrity": "sha512-OuSlF2nJEa1PGookZcZnINbvEK4iWNNYiqUh6aebk2AkWxj3sG8OafDOQMcMYApQALTHRsrBIjOx/K8TFxcz7w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"hoist-non-react-statics": "2.5.0",
|
"hoist-non-react-statics": "2.5.5",
|
||||||
"react-lifecycles-compat": "3.0.3"
|
"react-lifecycles-compat": "3.0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mobx-react-devtools": {
|
"mobx-react-devtools": {
|
||||||
|
@ -8922,7 +8922,7 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"mobx-react-form": {
|
"mobx-react-form": {
|
||||||
"version": "github:houdiniproject/mobx-react-form#63a163397102d86745b7ac381d42b794ec172231",
|
"version": "github:houdiniproject/mobx-react-form#8421c456a88cd3d7f887168a8228bb1a04911e7d",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"lodash": "4.17.10"
|
"lodash": "4.17.10"
|
||||||
|
@ -11584,9 +11584,9 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"react-lifecycles-compat": {
|
"react-lifecycles-compat": {
|
||||||
"version": "3.0.3",
|
"version": "3.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
|
||||||
"integrity": "sha512-bOr65SSYgxDgDNqLnDqt+gropXGPNB1Wbyys4tOYiNuP/qYWC4qFM9XH1ruzq+tT6EjE29pJsCr19rclKtpUEg==",
|
"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"react-reconciler": {
|
"react-reconciler": {
|
||||||
|
|
|
@ -54,8 +54,8 @@
|
||||||
"less": "^3.0.4",
|
"less": "^3.0.4",
|
||||||
"less-loader": "^4.1.0",
|
"less-loader": "^4.1.0",
|
||||||
"lodash": "^4.17.5",
|
"lodash": "^4.17.5",
|
||||||
"mobx": "^4.2.0",
|
"mobx": "^4.3.1",
|
||||||
"mobx-react": "^5.0.0",
|
"mobx-react": "^5.2.3",
|
||||||
"mobx-react-devtools": "^5.0.1",
|
"mobx-react-devtools": "^5.0.1",
|
||||||
"mobx-react-form": "github:houdiniproject/mobx-react-form#our_fix",
|
"mobx-react-form": "github:houdiniproject/mobx-react-form#our_fix",
|
||||||
"no-scroll": "^2.1.0",
|
"no-scroll": "^2.1.0",
|
||||||
|
|
16
types/mobx-react-form/index.d.ts
vendored
16
types/mobx-react-form/index.d.ts
vendored
|
@ -72,7 +72,7 @@ export declare class Field implements Base, FieldProperties, FieldMethods, Field
|
||||||
|
|
||||||
$(fieldName: string): Field;
|
$(fieldName: string): Field;
|
||||||
|
|
||||||
add(obj: any): any;
|
add(obj:{FieldDefinition}): any;
|
||||||
|
|
||||||
bind(): object;
|
bind(): object;
|
||||||
|
|
||||||
|
@ -82,7 +82,7 @@ export declare class Field implements Base, FieldProperties, FieldMethods, Field
|
||||||
|
|
||||||
container(): Form |Field
|
container(): Form |Field
|
||||||
|
|
||||||
del(key: any);
|
del(path?: string);
|
||||||
|
|
||||||
each(callback: (i: Field) => void);
|
each(callback: (i: Field) => void);
|
||||||
|
|
||||||
|
@ -173,6 +173,7 @@ interface FieldHandlers {
|
||||||
|
|
||||||
interface FieldDefinition {
|
interface FieldDefinition {
|
||||||
name: string
|
name: string
|
||||||
|
key?: string
|
||||||
label?: string
|
label?: string
|
||||||
value?: any
|
value?: any
|
||||||
default?: any
|
default?: any
|
||||||
|
@ -253,12 +254,13 @@ interface FormInitializer{
|
||||||
|
|
||||||
interface initializationDefinition {
|
interface initializationDefinition {
|
||||||
fields?:FieldDefinitions[]
|
fields?:FieldDefinitions[]
|
||||||
|
hooks?: FormHooks
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Form implements Base {
|
export class Form implements Base {
|
||||||
|
|
||||||
|
|
||||||
constructor(definition:initializationDefinition, options?:any)
|
constructor(definition?:initializationDefinition, options?:any)
|
||||||
plugins(): void
|
plugins(): void
|
||||||
setup(): any
|
setup(): any
|
||||||
onInit(): void
|
onInit(): void
|
||||||
|
@ -269,7 +271,7 @@ export class Form implements Base {
|
||||||
|
|
||||||
$(fieldName: string): Field;
|
$(fieldName: string): Field;
|
||||||
|
|
||||||
add(obj: any): any;
|
add(obj:FieldDefinition): any;
|
||||||
|
|
||||||
check(computed: string, deep?: boolean): boolean;
|
check(computed: string, deep?: boolean): boolean;
|
||||||
|
|
||||||
|
@ -292,7 +294,7 @@ export class Form implements Base {
|
||||||
|
|
||||||
map(callback: (i: Field) => void);
|
map(callback: (i: Field) => void);
|
||||||
|
|
||||||
observe(obj: any);
|
observe(...obj: any)
|
||||||
|
|
||||||
select(path: string): Field;
|
select(path: string): Field;
|
||||||
|
|
||||||
|
@ -304,10 +306,12 @@ export class Form implements Base {
|
||||||
update(obj: any): void;
|
update(obj: any): void;
|
||||||
|
|
||||||
readonly submitting: boolean;
|
readonly submitting: boolean;
|
||||||
|
readonly isValid:boolean;
|
||||||
|
|
||||||
protected validator :any
|
protected validator :any
|
||||||
|
|
||||||
readonly isValid :boolean;
|
readonly isValid :boolean;
|
||||||
|
readonly size:number
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -327,7 +331,7 @@ interface SharedFieldFormMethods {
|
||||||
has(key:string):boolean
|
has(key:string):boolean
|
||||||
map(callback:(i:Field) => void)
|
map(callback:(i:Field) => void)
|
||||||
each(callback:(i:Field) => void)
|
each(callback:(i:Field) => void)
|
||||||
add(obj:any):any
|
add(obj:FieldDefinition): any;
|
||||||
del(key:any)
|
del(key:any)
|
||||||
observe(obj:any)
|
observe(obj:any)
|
||||||
intercept(obj:any)
|
intercept(obj:any)
|
||||||
|
|
Loading…
Reference in a new issue