Skip to content

Commit

Permalink
feat(contribution-form): find global error manually depending on each…
Browse files Browse the repository at this point in the history
… fields state
  • Loading branch information
lutangar committed Sep 12, 2019
1 parent cd127cb commit ed2de7b
Show file tree
Hide file tree
Showing 13 changed files with 115 additions and 31 deletions.
21 changes: 21 additions & 0 deletions src/app/content/App/Contribute/ContributeScreen/FormErrors.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from 'react';
import { Error } from 'components/atoms/Forms';

export interface FormErrorsProps {
errors: string[];
globalError: string;
}

export const FormErrors = ({ errors, globalError }: FormErrorsProps) => {
if (errors.length > 0) {
if (errors.length === 1) {
return <Error>{errors[0]}</Error>;
}

return <Error>{globalError}</Error>;
}

return null;
};

export default FormErrors;
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@ import SubmitContribution from './SubmitContributionForm';

storiesOf('forms/SubmitContribution', module)
.addDecorator(getStory => <Router>{getStory()}</Router>)
.add('normal', () => <SubmitContribution onSubmit={action('onSubmit')} />);
.add('normal', () => (
<SubmitContribution onSubmit={action('onSubmit')} errors={[]} />
));
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import React, { Component } from 'react';
import { Field, InjectedFormProps } from 'redux-form';
import { Form, Error } from 'components/atoms/Forms';
import { Form } from 'components/atoms/Forms';
import { CenterContainer, BorderButton } from 'components/atoms';
import {
InputField,
IntentionsSelectorField,
TextareaField
} from 'components/organisms/Fields';
import FormErrors from '../FormErrors';
import { Contribution } from 'app/lmem/notice';
import withReduxForm from './withReduxForm';

export interface SubmitContributionFormOwnProps {
onSubmit: (...args: any[]) => void;
errors: string[];
}

export type SubmitContributionFormProps = InjectedFormProps<
Expand All @@ -26,7 +28,7 @@ class SubmitContributionForm extends Component<SubmitContributionFormProps> {
}

render() {
const { handleSubmit, submitting, error, anyTouched } = this.props;
const { handleSubmit, submitting, error, errors } = this.props;
return (
<Form onSubmit={handleSubmit}>
<Field name="url" type="hidden" component={InputField} />
Expand Down Expand Up @@ -58,8 +60,7 @@ class SubmitContributionForm extends Component<SubmitContributionFormProps> {
prévisualiser et publier
</BorderButton>
</CenterContainer>

{anyTouched && error && <Error>{error}</Error>}
<FormErrors errors={errors} globalError={error} />
</Form>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@ import { connect } from 'react-redux';
import { push } from 'connected-react-router';
import { Intention } from 'app/lmem/intention';
import { State } from 'app/content/store';
import { getURL } from 'app/content/selectors';
import { getFlatFormErrors, getURL } from 'app/content/selectors';
import { form } from './withReduxForm';

export default connect(
(state: State) => ({
initialValues: {
intention: 'approval' as Intention,
url: getURL(state),
created: new Date()
}
},
errors: getFlatFormErrors(form)(state)
}),
{
onSubmit: () => push('/contribute/preview')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ storiesOf('screens/Contribute/Preview', module)
intention: 'approval',
created: new Date()
}}
errors={[]}
modify={action('modify')}
publish={action('publish')}
/>
Expand Down
11 changes: 6 additions & 5 deletions src/app/content/App/Contribute/PreviewScreen/PreviewScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import styled from 'styled-components';
import { Contribution } from 'app/lmem/notice';
import NoticePreview from 'components/organisms/NoticePreview';
import { BorderButton, Button } from 'components/atoms';
import { Error, Form } from 'components/atoms/Forms';
import handleFormSubmit from 'app/utils/form/handleFormSubmit';
import { Form } from 'components/atoms/Forms';
import FormErrors from '../ContributeScreen/FormErrors';
import { handleFormSubmit } from 'app/utils/form';
import withReduxForm from './withReduxForm';

const PreviewForm = styled(Form)`
Expand All @@ -22,6 +23,7 @@ export interface PreviewScreenOwnProps {
contribution: Contribution;
modify: () => void;
publish: (contribution: Contribution, meta: any) => void;
errors: string[];
}

export type PreviewScreenProps = InjectedFormProps<
Expand All @@ -45,7 +47,7 @@ class PreviewScreen extends Component<PreviewScreenProps> {
modify,
publish,
error,
dirty
errors
} = this.props;

return (
Expand All @@ -61,8 +63,7 @@ class PreviewScreen extends Component<PreviewScreenProps> {
>
Publier
</BorderButton>

{dirty && error && <Error>{error}</Error>}
<FormErrors errors={errors} globalError={error} />
</PreviewForm>
</NoticePreview>
);
Expand Down
6 changes: 4 additions & 2 deletions src/app/content/App/Contribute/PreviewScreen/withConnect.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { connect } from 'react-redux';
import { goBack } from 'connected-react-router';
import { State } from 'app/content/store';
import { getContribution } from 'app/content/selectors';
import { getContribution, getFlatFormErrors } from 'app/content/selectors';
import { close } from 'app/actions/ui';
import { submitContribution } from 'app/actions/contribution';
import { form } from '../ContributeScreen/SubmitContributionForm/withReduxForm';

const mapDispatchToProps = {
close,
Expand All @@ -13,7 +14,8 @@ const mapDispatchToProps = {

export default connect(
(state: State) => ({
contribution: getContribution(state)
contribution: getContribution(state),
errors: getFlatFormErrors(form)(state)
}),
mapDispatchToProps
);
40 changes: 38 additions & 2 deletions src/app/content/selectors/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import { createSelector } from 'reselect';
import { RouteComponentProps } from 'react-router';
import { getLocation } from 'connected-react-router';
import { getFormValues } from 'redux-form';

import {
getFormMeta,
getFormSyncErrors,
getFormValues,
RegisteredFieldState
} from 'redux-form';
import * as R from 'ramda';
import {
getNotice,
isMarkedUnread,
Expand All @@ -12,6 +17,7 @@ import {
import { InstallationDetails } from 'app/lmem/installation';
import { OpenState, MountedState, TitleState, UIState } from '../reducers/ui';
import { ContentState } from '../store';
import { getRegisteredFieldsPaths } from '../../utils/form';

export const getNotices = (state: ContentState) => state.notices;

Expand Down Expand Up @@ -73,3 +79,33 @@ export const isNoticeContext = (state: ContentState) => {
export const getContribution = (state: State): Contribution =>
// @ts-ignore
getFormValues('contribution')(state);

export const getFormState = (formName: string) => (state: State) =>
state.form[formName];

export const getFormRegisteredFields = (formName: string) => (
state: State
): RegisteredFieldState[] => {
const form = getFormState(formName)(state);
return R.path(['registeredFields'], form) || [];
};

export const getFlatFormErrors = (formName: string) => (
state: State
): string[] => {
const fieldsPaths = getRegisteredFieldsPaths(
getFormRegisteredFields(formName)(state)
);
const metas = getFormMeta(formName)(state);
const syncErrors = getFormSyncErrors(formName)(state);

return fieldsPaths
.map(fieldPath => {
const errorMessage = R.path(fieldPath, syncErrors);
const touched = R.path(fieldPath.concat('touched'), metas);

// @ts-ignore
return touched && errorMessage ? errorMessage.toString() : undefined;
}, {})
.filter(errorMessage => errorMessage);
};
2 changes: 2 additions & 0 deletions src/app/content/store.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { applyMiddleware, createStore } from 'redux';
import { routerMiddleware, RouterState } from 'connected-react-router';
import { createMemoryHistory } from 'history';
import { FormStateMap } from 'redux-form';
import rootReducer from './reducers';
import rootSaga from './sagas';
import { InstallationDetailsState } from './reducers/installationDetails';
Expand All @@ -17,6 +18,7 @@ export interface ContentState {
notices: NoticesState;
tab: TabState;
router: RouterState;
form: FormStateMap;
}

const sagaMiddleware = createSagaMiddleware();
Expand Down
31 changes: 16 additions & 15 deletions src/app/lmem/contribution/validate.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,31 @@
import * as R from 'ramda';
import { Contribution } from 'app/lmem/notice';
import isEmail from '../../utils/isEmail';
import Errors from '../Errors';

export default (contribution: Contribution): Errors => {
const errors: Errors = {};
const requiredFields: string[] = ['intention', 'message'];
let errors: Errors = {
contributor: {}
};
const requiredPaths: any[] = [
['intention'],
['message'],
['contributor', 'name'],
['contributor', 'email']
];
const requiredFieldMessage = 'Ce champs est obligatoire.';

requiredFields.forEach(requiredField => {
if (!contribution[requiredField]) {
errors[requiredField] = requiredFieldMessage;
requiredPaths.forEach(requiredPath => {
if (!R.path(requiredPath, contribution)) {
// @ts-ignore
errors = R.assocPath(requiredPath, requiredFieldMessage, errors);
console.log('=>', errors);
}
});

const { contributor } = contribution;

errors.contributor = {};
if (!contributor || !contributor.name) {
errors.contributor.name = requiredFieldMessage;
}

if (!contributor || !contributor.email) {
errors.contributor.email = requiredFieldMessage;
}

if (contributor && contributor.email && !isEmail(contributor.email)) {
// @ts-ignore
errors.contributor.email = "L'email n'est pas valide";
}

Expand Down
5 changes: 5 additions & 0 deletions src/app/utils/form/FormMeta.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { FieldState } from 'redux-form';

export default interface FormMeta {
[name: string]: FieldState & { [name: string]: FieldState };
}
6 changes: 6 additions & 0 deletions src/app/utils/form/getRegisteredFieldsPaths.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { RegisteredFieldState } from 'redux-form';

export default (registeredFields: RegisteredFieldState[]) =>
Object.keys(registeredFields).map(registeredField =>
registeredField.split('.')
);
4 changes: 4 additions & 0 deletions src/app/utils/form/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
export { default as createSubmissionError } from './createSubmissionError';
export { default as FormMeta } from './FormMeta';
export {
default as getRegisteredFieldsPaths
} from './getRegisteredFieldsPaths';
export { default as handleFormSubmit } from './handleFormSubmit';

0 comments on commit ed2de7b

Please sign in to comment.