Skip to content

Commit

Permalink
feat(core/presentation): Migrate form validation API to class-based API
Browse files Browse the repository at this point in the history
This is an iteration on the form validation API.  The primary changes are:
- Migrate from `buildValidators(values)` to `new FormValidator(values)`
- Make fields optional by default
- Migrate from `.required([validator1, validator2], 'field is required')`
  to `.required('field is required').withValidators(validator1, validator2)`
- Add `spelAware()` to ValidatableField which allows validation to pass
  when the value contains a SpEL expression, even if other validators
  would fail the validation.
- Add a magic string that can be used to short circuit validation of a field
  - Used to implement optional() and spelAware()
  • Loading branch information
christopherthielen committed Sep 26, 2019
1 parent 447d76f commit 45f3c24
Show file tree
Hide file tree
Showing 7 changed files with 492 additions and 153 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as React from 'react';

import { Formik, Form } from 'formik';
import { Modal } from 'react-bootstrap';
import { buildValidators, IModalComponentProps, ReactModal, SpinFormik } from 'core/presentation';
import { FormValidator, IModalComponentProps, ReactModal, SpinFormik } from 'core/presentation';
import { INotification } from 'core/domain';
import { SubmitButton, ModalClose } from 'core/modal';

Expand All @@ -27,11 +27,12 @@ export class EditNotificationModal extends React.Component<IEditNotificationModa
}

private validate = (values: INotification): any => {
const validation = buildValidators(values);
validation
const formValidator = new FormValidator(values);
formValidator
.field('when', 'Notify when')
.required([(value: any[]) => !value.length && 'Please select when the notification should execute']);
return validation.result();
.required()
.withValidators((value: any[]) => !value.length && 'Please select when the notification should execute');
return formValidator.validateForm();
};

public render(): React.ReactElement<EditNotificationModal> {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { buildValidators, IContextualValidator, IStage } from '@spinnaker/core';
import { FormValidator, IContextualValidator, IStage } from '@spinnaker/core';

export const validate: IContextualValidator = (stage: IStage) => {
const validation = buildValidators(stage);
validation.field('account', 'Account').required();
return validation.result();
const formValidator = new FormValidator(stage);
formValidator.field('account', 'Account').required();
return formValidator.validateForm();
};
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as React from 'react';

import { IStageConfigProps, FormikStageConfig, IContextualValidator } from 'core/pipeline';
import { FormikFormField, TextInput, TextAreaInput, CheckboxInput, buildValidators } from 'core/presentation';
import { FormikFormField, TextInput, TextAreaInput, CheckboxInput, FormValidator } from 'core/presentation';
import { HelpField } from 'core/help';

export const ScriptStageConfig: React.SFC<IStageConfigProps> = stageConfigProps => (
Expand Down Expand Up @@ -82,8 +82,8 @@ export const ScriptStageConfig: React.SFC<IStageConfigProps> = stageConfigProps
);

export const validate: IContextualValidator = stage => {
const validation = buildValidators(stage);
validation.field('command', 'Command').required();
validation.field('scriptPath', 'Script Path').required();
return validation.result();
const formValidator = new FormValidator(stage);
formValidator.field('command', 'Command').required();
formValidator.field('scriptPath', 'Script Path').required();
return formValidator.validateForm();
};
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { SubmitButton, ModalClose } from 'core/modal';
import { Application } from 'core/application';
import { AuthenticationService } from 'core/authentication';
import {
buildValidators,
FormValidator,
IModalComponentProps,
ReactModal,
SpinFormik,
Expand Down Expand Up @@ -339,8 +339,8 @@ export class ManualExecutionModal extends React.Component<IManualExecutionModalP
};

private validate = (values: IPipelineCommand): any => {
const validation = buildValidators(values);
return validation.result();
const formValidator = new FormValidator(values);
return formValidator.validateForm();
};

public render(): React.ReactElement<ManualExecutionModal> {
Expand Down
11 changes: 7 additions & 4 deletions app/scripts/modules/core/src/presentation/forms/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,15 +61,18 @@ In addition to the `validate` prop, the field can also be validated at the form
This example shows validation of a formik field using the `Formik` component's `validate` prop.

```js
import { buildValidators } from 'core/presentation';
import { FormValidator } from 'core/presentation';

<Formik
initialValues={{ email: '' }}
validate={values => {
const emailRegexp = /[^@]+@[^@]+/;
const validator = buildValidators(values);
validator.field('email').required([value => (emailRegexp.exec(value) ? null : 'Please enter a valid email')]);
return validator.result();
const formValidator = new FormValidator(values);
formValidator
.field('email')
.required()
.withValidators(value => (emailRegexp.exec(value) ? null : 'Please enter a valid email'));
return formValidator.result();
}}
render={formik => {
return (
Expand Down
Loading

0 comments on commit 45f3c24

Please sign in to comment.