README
GitHub license">
Mateus
Antd components as a form fields + redux-saga flow for easier handle of redux-form form's submit.
Name of package Mateus refers to name of the apostole Matthew which is a patron of all office workers. Mateus is a Portuguese equivalent for Matthew.
Table of contents
Installation
Using npm:
npm i -S @ackee/mateus
API
Action creators
submitForm(formId: string, submitActionCreator: () => object): function
formId
Form unique identificatorsubmitActionCreator
Action creator for form specific submit actionReturns Form submit handler that is used for
handleSubmit
prop ofredux-form
Form
.Example - use
submitForm
inreact-redux
containerimport { connect } from 'react-redux'; import { bindActionCreators, compose } from 'redux'; import UserForm from './components/UserForm'; const createUserAction = () => ({ type: 'CREATE_USER' }) const formId = 'createUserForm'; export default compose( connect( (state) => ({ initialValues: { }, }), dispatch => ({ onSubmit: bindActionCreators( formActions.submitForm(formId, createUserAction), dispatch, ), }), ), reduxForm({ form: formId, }), )(UserForm);
Action types
FORM_SUBMIT
import { actionTypes } from '@ackee/mateus';
import { takeEvery } from 'redux-saga/effects';
takeEvery(actionTypes.FORM_SUBMIT, action => {
const { data, form } = action;
console.log('Submitted form', form);
});
Sagas
submitFormSaga(): void
Form saga ensures you receive all neccessary data and methods (eg. for handling form flow) in form submit handler. Just use it as your only saga, or in list of sagas.
In parallel with form submit saga, you will likely want to use submitForm
action creator.
Examples - Usage
import { submitFormSaga as formSaga } from 'ackee-frontend-toolkit/sagas';
// main saga
export default function* () {
yield all([
// ... other sagas
formSaga(),
]);
}
Complete example of using form saga flow
Handling form submit using our submitForm saga in redux-form
is easy and enable separation of concerns.
It's all divided into few simple steps
- Plug
submitFormSaga
into main saga - Use
submitForm
action as a handler of form submit - Catch action provided as a second parameter to
submitForm
and hadle it by custom saga. - Manage whole
redux-form
submit process, including startSubmit, stopSubmit and reset, in custom saga.
Example is a bit shortened and simplified:
// config/index.js
export default {
// ...
forms: {
addUser: 'addUserForm',
}
api: {
user: '/api/v1/user',
}
}
// actions/user.js
export const addUserAction = () => ({
type: 'ADD_USER'
})
// components/UserForm.js
const UserForm = ({ handleSubmit, submitting }) => (
<Form onSubmit={handleSubmit}>
<Field
disabled={submitting}
id="firstname"
name="firstname"
component={Input}
type="text"
/>
<Field
disabled={submitting}
id="lastname"
name="lastname"
component={Input}
type="text"
/>
<button type="submit">Add user</button>
</Form>
);
UserForm.propTypes = {
// these props are automatically supplied by reduxForm()
handleSubmit: PropTypes.func.isRequired,
submitting: PropTypes.bool.isRequired,
invalid: PropTypes.bool.isRequired,
};
export default UserForm;
// containers/UserForm.js
import { submitForm } from '@ackee/mateus';
import { addUserAction } from '../actions/user';
import config from '../config';
export default compose(
connect(
(state) => ({
initialValues: {},
}),
dispatch => ({
onSubmit: bindActionCreators(
submitForm(config.forms.addUser, addUserAction),
dispatch,
),
}),
),
reduxForm({
form: config.forms.addUser,
}),
)(UserForm);
// sagas/index.js
import { submitFormSaga } from '@ackee/mateus';
import userSaga from '../sagas/userSaga';
export default function* () {
yield all([
submitFormSaga(),
userSaga(),
]);
}
// sagas/userSaga.js
import config from '../config';
function* handleAddUserForm(action) {
const { data, startSubmit, stopSubmit, reset } = action;
yield startSubmit();
try {
yield api.post(
config.api.user,
{
firstname: data.firstname,
lastname: data.lastname,
},
);
yield stopSubmit();
yield reset();
} catch (e) {
const errors = { 'user add error': e };
yield stopSubmit(errors);
}
}
export default function* () {
yield all([
takeEvery('ADD_USER', handleAddUserForm),
]);
}
Form fields
All form fields are available either as an Antd component (eg. TextInput) wrapped with FormItem or the same, but enclosed into redux-form Field.
List of fields:
Text
TextInput
accept AntdInput
props.TextField
accept same props as Input plus all the props you can pass to redux-form Field.import { TextInput, TextField } from '@ackee/mateus';
TextArea
TextAreaInput
accept AntdTextArea
props.TextAreaField
accept same props as Input plus all the props you can pass to redux-form Field.import { TextAreaInput, TextAreaField } from '@ackee/mateus';
Select
SelectInput
accept AntdSelect
props, but instead of passing options as children components, they'are passed as an array in props.Default name for that prop is
options
and it's shape is{ label: ReactNode, value: string|number }
. The names can be changed by specifyingoptionsKey
,labelKey
orvalueKey
prop (look at example below).SelectField
accept same props as Input plus all the props you can pass to redux-form Field.import { SelectInput, SelectField } from '@ackee/mateus'; const select = ( <SelectInput options={[ { label: 'Option1': value: 1 }, { label: <span>Option2</span>: value: 2 }, ]} /> ); const selectWithCustomNames = ( <SelectInput optionsKey="users" labelKey="name" valueKey="id" users={[ { name: 'Anakin', id: 'siths1' }, { name: 'Luke', id: 'jedis1' }, ]} /> );
NumberInput
NumberInput
accept AntdInputNumber
props.NumberField
accept same props as Input plus all the props you can pass to redux-form Field.import { NumberInput, NumberField } from '@ackee/mateus';
Switch
SwitchInput
accept AntdSwitch
props.SwitchField
accept same props as Input plus all the props you can pass to redux-form Field.import { SwitchInput, SwitchField } from '@ackee/mateus';
Slider
SliderInput
accept AntdSlider
props.SliderField
accept same props as Input plus all the props you can pass to redux-form Field.import { SliderInput, SliderField } from '@ackee/mateus';
Radio
This is a bit confusing, because RadioInput
is actually RadioGroup
- in most cases we want to render group of radios to let user select one option so we don't need an individual Radio
. If from any reason you realy need only one Radio
without RadioGroup
just feel free to use it directly from antd
.
RadioInput
accept AntdRadioGroup
props, but with several differencies- the definition
options
prop is a bit changed. It lacksdisabled
field so the shape is just{ label: ReactNode, value: string|number }
. - on the other hand, as same as for
Select
you can specify custom names for props and the shape keys by specifyingoptionsKey
,labelKey
orvalueKey
prop (look at example below). - you can pass
button
prop of type boolean that force use of radio buttons instead of plain radios.
- the definition
RadioField
accept same props as Input plus all the props you can pass to redux-form Field.import { RadioInput, RadioField } from '@ackee/mateus'; const selectYourFavourite = ( <RadioInput optionsKey="southPark" labelKey="name" valueKey="color" southPark={[ { name: 'Kenny', color: 'orange' }, { name: 'Cartman', color: 'fat' }, { name: 'Stan', color: 'blue' }, { name: 'Kyle', color: 'green' }, ]} button /> );
CheckboxGroup
CheckboxGroupInput
accept AntdCheckboxGroup
props.CheckboxGroupField
accept same props as Input plus all the props you can pass to redux-form Field.import { CheckboxGroupInput, CheckboxGroupField } from '@ackee/mateus';
DatePicker & TimePicker
Both pickers accept props from shared API plus:
DatePickerInput
accept AntdDatePicker
props.TimePickerInput
accept AntdTimePicker
props.- both accept
displayFormat
prop which is the same asformat
prop defined in picker's API. But since redux-formField
has alsoformat
prop and so Input's one would be overriden when usingDatePickerField
orTimePickerField
, wee need to provide an alternative.
DatePickerField
/TimePickerField
accept same props asDatePickerInput
/TimePickerInput
plus all the props you can pass to redux-form Field.import { DatePickerInput, DatePickerField, TimePickerInput, TimePickerField } from '@ackee/mateus';
pickerEnhancer
TBD
import { pickerEnhancer } from '@ackee/mateus';
HOC
wrapWithField(component: React.Component, injectProps?: InjectPropsType): React.Component
Hight order component that wrap supplied component with redux-form's Field
and inject extra properties into it (see InjectPropsType
). This is useful for form fields that are not part of @ackee/mateus
.
InjectPropsType
is either an propsobject
or function(props: object): object
that receives props passed to component and returns extra props that will be also passed to componentExample - use it fo Antd
Rate
componentconst Rate = wrapWithField( AntdRate, { character: <Icon type="heart" /> } ); const RateForm = ({ handleSubmit, invalid }) => ( <Form onSubmit={handleSubmit}> <Rate /> </Form> );