mirror of
https://github.com/ansible/awx.git
synced 2024-10-30 13:55:31 +03:00
add survey form
This commit is contained in:
parent
e92acce4eb
commit
1412bf6232
41
awx/ui_next/package-lock.json
generated
41
awx/ui_next/package-lock.json
generated
@ -10625,7 +10625,8 @@
|
|||||||
"ansi-regex": {
|
"ansi-regex": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"aproba": {
|
"aproba": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
@ -10646,12 +10647,14 @@
|
|||||||
"balanced-match": {
|
"balanced-match": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"brace-expansion": {
|
"brace-expansion": {
|
||||||
"version": "1.1.11",
|
"version": "1.1.11",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"balanced-match": "^1.0.0",
|
"balanced-match": "^1.0.0",
|
||||||
"concat-map": "0.0.1"
|
"concat-map": "0.0.1"
|
||||||
@ -10666,17 +10669,20 @@
|
|||||||
"code-point-at": {
|
"code-point-at": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"concat-map": {
|
"concat-map": {
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"console-control-strings": {
|
"console-control-strings": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"core-util-is": {
|
"core-util-is": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
@ -10793,7 +10799,8 @@
|
|||||||
"inherits": {
|
"inherits": {
|
||||||
"version": "2.0.3",
|
"version": "2.0.3",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"ini": {
|
"ini": {
|
||||||
"version": "1.3.5",
|
"version": "1.3.5",
|
||||||
@ -10805,6 +10812,7 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"number-is-nan": "^1.0.0"
|
"number-is-nan": "^1.0.0"
|
||||||
}
|
}
|
||||||
@ -10819,6 +10827,7 @@
|
|||||||
"version": "3.0.4",
|
"version": "3.0.4",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"brace-expansion": "^1.1.7"
|
"brace-expansion": "^1.1.7"
|
||||||
}
|
}
|
||||||
@ -10826,12 +10835,14 @@
|
|||||||
"minimist": {
|
"minimist": {
|
||||||
"version": "0.0.8",
|
"version": "0.0.8",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"minipass": {
|
"minipass": {
|
||||||
"version": "2.3.5",
|
"version": "2.3.5",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"safe-buffer": "^5.1.2",
|
"safe-buffer": "^5.1.2",
|
||||||
"yallist": "^3.0.0"
|
"yallist": "^3.0.0"
|
||||||
@ -10850,6 +10861,7 @@
|
|||||||
"version": "0.5.1",
|
"version": "0.5.1",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"minimist": "0.0.8"
|
"minimist": "0.0.8"
|
||||||
}
|
}
|
||||||
@ -10930,7 +10942,8 @@
|
|||||||
"number-is-nan": {
|
"number-is-nan": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"object-assign": {
|
"object-assign": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
@ -10942,6 +10955,7 @@
|
|||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"wrappy": "1"
|
"wrappy": "1"
|
||||||
}
|
}
|
||||||
@ -11027,7 +11041,8 @@
|
|||||||
"safe-buffer": {
|
"safe-buffer": {
|
||||||
"version": "5.1.2",
|
"version": "5.1.2",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"safer-buffer": {
|
"safer-buffer": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
@ -11063,6 +11078,7 @@
|
|||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"code-point-at": "^1.0.0",
|
"code-point-at": "^1.0.0",
|
||||||
"is-fullwidth-code-point": "^1.0.0",
|
"is-fullwidth-code-point": "^1.0.0",
|
||||||
@ -11082,6 +11098,7 @@
|
|||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"ansi-regex": "^2.0.0"
|
"ansi-regex": "^2.0.0"
|
||||||
}
|
}
|
||||||
@ -11125,12 +11142,14 @@
|
|||||||
"wrappy": {
|
"wrappy": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"yallist": {
|
"yallist": {
|
||||||
"version": "3.0.3",
|
"version": "3.0.3",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -8,6 +8,7 @@ import AlertModal from '@components/AlertModal';
|
|||||||
import ErrorDetail from '@components/ErrorDetail';
|
import ErrorDetail from '@components/ErrorDetail';
|
||||||
import useRequest, { useDismissableError } from '@util/useRequest';
|
import useRequest, { useDismissableError } from '@util/useRequest';
|
||||||
import SurveyList from './shared/SurveyList';
|
import SurveyList from './shared/SurveyList';
|
||||||
|
import SurveyQuestionAdd from './shared/SurveyQuestionAdd';
|
||||||
|
|
||||||
function TemplateSurvey({ template, i18n }) {
|
function TemplateSurvey({ template, i18n }) {
|
||||||
const [surveyEnabled, setSurveyEnabled] = useState(template.survey_enabled);
|
const [surveyEnabled, setSurveyEnabled] = useState(template.survey_enabled);
|
||||||
@ -37,6 +38,12 @@ function TemplateSurvey({ template, i18n }) {
|
|||||||
[template.id, setSurvey]
|
[template.id, setSurvey]
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
const updateSurveySpec = spec => {
|
||||||
|
updateSurvey({
|
||||||
|
...survey,
|
||||||
|
spec,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const { request: deleteSurvey, error: deleteError } = useRequest(
|
const { request: deleteSurvey, error: deleteError } = useRequest(
|
||||||
useCallback(async () => {
|
useCallback(async () => {
|
||||||
@ -64,13 +71,16 @@ function TemplateSurvey({ template, i18n }) {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Switch>
|
<Switch>
|
||||||
<Route path="/templates/:templateType/:id/survey">
|
<Route path="/templates/:templateType/:id/survey/add">
|
||||||
|
<SurveyQuestionAdd survey={survey} updateSurvey={updateSurveySpec} />
|
||||||
|
</Route>
|
||||||
|
<Route path="/templates/:templateType/:id/survey" exact>
|
||||||
<SurveyList
|
<SurveyList
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
survey={survey}
|
survey={survey}
|
||||||
surveyEnabled={surveyEnabled}
|
surveyEnabled={surveyEnabled}
|
||||||
toggleSurvey={toggleSurvey}
|
toggleSurvey={toggleSurvey}
|
||||||
updateSurvey={spec => updateSurvey({ ...survey, spec })}
|
updateSurvey={updateSurveySpec}
|
||||||
deleteSurvey={deleteSurvey}
|
deleteSurvey={deleteSurvey}
|
||||||
/>
|
/>
|
||||||
</Route>
|
</Route>
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import { useHistory, useRouteMatch } from 'react-router-dom';
|
||||||
|
import { CardBody } from '@components/Card';
|
||||||
|
import SurveyQuestionForm from './SurveyQuestionForm';
|
||||||
|
|
||||||
|
export default function SurveyQuestionAdd({ template }) {
|
||||||
|
const [formError, setFormError] = useState(null);
|
||||||
|
const history = useHistory();
|
||||||
|
const match = useRouteMatch();
|
||||||
|
|
||||||
|
const handleSubmit = async formData => {
|
||||||
|
//
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCancel = () => {
|
||||||
|
history.push(match.url.replace('/add', ''));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<CardBody>
|
||||||
|
<SurveyQuestionForm
|
||||||
|
handleSubmit={handleSubmit}
|
||||||
|
handleCancel={handleCancel}
|
||||||
|
/>
|
||||||
|
</CardBody>
|
||||||
|
);
|
||||||
|
}
|
231
awx/ui_next/src/screens/Template/shared/SurveyQuestionForm.jsx
Normal file
231
awx/ui_next/src/screens/Template/shared/SurveyQuestionForm.jsx
Normal file
@ -0,0 +1,231 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { func, string, bool, number, shape } from 'prop-types';
|
||||||
|
import { Formik, useField } from 'formik';
|
||||||
|
import { withI18n } from '@lingui/react';
|
||||||
|
import { t } from '@lingui/macro';
|
||||||
|
import { Form, FormGroup } from '@patternfly/react-core';
|
||||||
|
import { FormColumnLayout } from '@components/FormLayout';
|
||||||
|
import FormActionGroup from '@components/FormActionGroup/FormActionGroup';
|
||||||
|
import FormField, {
|
||||||
|
CheckboxField,
|
||||||
|
PasswordField,
|
||||||
|
FormSubmitError,
|
||||||
|
FieldTooltip,
|
||||||
|
} from '@components/FormField';
|
||||||
|
import AnsibleSelect from '@components/AnsibleSelect';
|
||||||
|
import { required, noWhiteSpace, combine } from '@util/validators';
|
||||||
|
|
||||||
|
function AnswerType({ i18n }) {
|
||||||
|
const [field] = useField({
|
||||||
|
name: 'type',
|
||||||
|
validate: required(i18n._(t`Select a value for this field`), i18n),
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormGroup
|
||||||
|
label={i18n._(t`Answer Type`)}
|
||||||
|
isRequired
|
||||||
|
fieldId="question-answer-type"
|
||||||
|
>
|
||||||
|
<FieldTooltip
|
||||||
|
content={i18n._(
|
||||||
|
t`Choose an answer type or format you want as the prompt for the user.
|
||||||
|
Refer to the Ansible Tower Documentation for more additional
|
||||||
|
information about each option.`
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<AnsibleSelect
|
||||||
|
id="question-type"
|
||||||
|
{...field}
|
||||||
|
data={[
|
||||||
|
{ key: 'text', value: 'text', label: i18n._(t`Text`) },
|
||||||
|
{ key: 'textarea', value: 'textarea', label: i18n._(t`Textarea`) },
|
||||||
|
{ key: 'password', value: 'password', label: i18n._(t`Password`) },
|
||||||
|
{
|
||||||
|
key: 'multiplechoice',
|
||||||
|
value: 'multiplechoice',
|
||||||
|
label: i18n._(t`Multiple Choice (single select)`),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'multiselect',
|
||||||
|
value: 'multiselect',
|
||||||
|
label: i18n._(t`Multiple Choice (multiple select)`),
|
||||||
|
},
|
||||||
|
{ key: 'integer', value: 'integer', label: i18n._(t`Integer`) },
|
||||||
|
{ key: 'float', value: 'float', label: i18n._(t`Float`) },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SurveyQuestionForm({
|
||||||
|
question,
|
||||||
|
handleSubmit,
|
||||||
|
handleCancel,
|
||||||
|
submitError,
|
||||||
|
i18n,
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<Formik
|
||||||
|
initialValues={{
|
||||||
|
question_name: question?.question_name || '',
|
||||||
|
question_description: question?.question_description || '',
|
||||||
|
required: question ? question?.required : true,
|
||||||
|
type: question?.type || 'text',
|
||||||
|
variable: question?.variable || '',
|
||||||
|
min: question?.min || 0,
|
||||||
|
max: question?.max || 1024,
|
||||||
|
default: question?.default,
|
||||||
|
choices: question?.choices,
|
||||||
|
new_question: !question,
|
||||||
|
}}
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
>
|
||||||
|
{formik => (
|
||||||
|
<Form autoComplete="off" onSubmit={formik.handleSubmit}>
|
||||||
|
<FormColumnLayout>
|
||||||
|
<FormField
|
||||||
|
id="question-name"
|
||||||
|
name="question_name"
|
||||||
|
type="text"
|
||||||
|
label={i18n._(t`Question`)}
|
||||||
|
validate={required(null, i18n)}
|
||||||
|
isRequired
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
id="question-description"
|
||||||
|
name="question_description"
|
||||||
|
type="text"
|
||||||
|
label={i18n._(t`Description`)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
id="question-variable"
|
||||||
|
name="variable"
|
||||||
|
type="text"
|
||||||
|
label={i18n._(t`Answer Variable Name`)}
|
||||||
|
validate={combine([noWhiteSpace(i18n), required(null, i18n)])}
|
||||||
|
isRequired
|
||||||
|
tooltip={i18n._(
|
||||||
|
t`The suggested format for variable names is lowercase and
|
||||||
|
underscore-separated (for example, foo_bar, user_id, host_name,
|
||||||
|
etc.). Variable names with spaces are not allowed.`
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<AnswerType i18n={i18n} />
|
||||||
|
<CheckboxField
|
||||||
|
id="question-required"
|
||||||
|
name="required"
|
||||||
|
label="Required"
|
||||||
|
/>
|
||||||
|
</FormColumnLayout>
|
||||||
|
<FormColumnLayout>
|
||||||
|
{['text', 'textarea', 'password'].includes(formik.values.type) && (
|
||||||
|
<>
|
||||||
|
<FormField
|
||||||
|
id="question-min"
|
||||||
|
name="min"
|
||||||
|
type="number"
|
||||||
|
label={i18n._(t`Minimum length`)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
id="question-max"
|
||||||
|
name="max"
|
||||||
|
type="number"
|
||||||
|
label={i18n._(t`Maximum length`)}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{['integer', 'float'].includes(formik.values.type) && (
|
||||||
|
<>
|
||||||
|
<FormField
|
||||||
|
id="question-min"
|
||||||
|
name="min"
|
||||||
|
type="number"
|
||||||
|
label={i18n._(t`Minimum`)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
id="question-max"
|
||||||
|
name="max"
|
||||||
|
type="number"
|
||||||
|
label={i18n._(t`Maximum`)}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{['text', 'integer', 'float'].includes(formik.values.type) && (
|
||||||
|
<FormField
|
||||||
|
id="question-default"
|
||||||
|
name="default"
|
||||||
|
type="text"
|
||||||
|
label={i18n._(t`Default answer`)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{formik.values.type === 'textarea' && (
|
||||||
|
<FormField
|
||||||
|
id="question-default"
|
||||||
|
name="default"
|
||||||
|
type="textarea"
|
||||||
|
label={i18n._(t`Default answer`)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{formik.values.type === 'password' && (
|
||||||
|
<PasswordField
|
||||||
|
id="question-default"
|
||||||
|
name="default"
|
||||||
|
label={i18n._(t`Default answer`)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{['multiplechoice', 'multiselect'].includes(formik.values.type) && (
|
||||||
|
<>
|
||||||
|
<FormField
|
||||||
|
id="question-options"
|
||||||
|
name="choices"
|
||||||
|
type="textarea"
|
||||||
|
label={i18n._(t`Multiple Choice Options`)}
|
||||||
|
validate={required(null, i18n)}
|
||||||
|
isRequired
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
id="question-default"
|
||||||
|
name="default"
|
||||||
|
type={
|
||||||
|
formik.values.type === 'multiplechoice'
|
||||||
|
? 'text'
|
||||||
|
: 'textarea'
|
||||||
|
}
|
||||||
|
label={i18n._(t`Default answer`)}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</FormColumnLayout>
|
||||||
|
<FormSubmitError error={submitError} />
|
||||||
|
<FormActionGroup
|
||||||
|
onCancel={handleCancel}
|
||||||
|
onSubmit={formik.handleSubmit}
|
||||||
|
/>
|
||||||
|
</Form>
|
||||||
|
)}
|
||||||
|
</Formik>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
SurveyQuestionForm.propTypes = {
|
||||||
|
question: shape({
|
||||||
|
question_name: string.isRequired,
|
||||||
|
question_description: string.isRequired,
|
||||||
|
required: bool,
|
||||||
|
type: string.isRequired,
|
||||||
|
min: number,
|
||||||
|
max: number,
|
||||||
|
}),
|
||||||
|
handleSubmit: func.isRequired,
|
||||||
|
handleCancel: func.isRequired,
|
||||||
|
submitError: shape({}),
|
||||||
|
};
|
||||||
|
|
||||||
|
SurveyQuestionForm.defaultProps = {
|
||||||
|
question: null,
|
||||||
|
submitError: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default withI18n()(SurveyQuestionForm);
|
@ -1,4 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { useRouteMatch } from 'react-router-dom';
|
||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { withI18n } from '@lingui/react';
|
import { withI18n } from '@lingui/react';
|
||||||
|
|
||||||
@ -20,6 +21,7 @@ function SurveyToolbar({
|
|||||||
isDeleteDisabled,
|
isDeleteDisabled,
|
||||||
onToggleDeleteModal,
|
onToggleDeleteModal,
|
||||||
}) {
|
}) {
|
||||||
|
const match = useRouteMatch();
|
||||||
return (
|
return (
|
||||||
<DataToolbar id="survey-toolbar">
|
<DataToolbar id="survey-toolbar">
|
||||||
<DataToolbarContent>
|
<DataToolbarContent>
|
||||||
@ -45,7 +47,7 @@ function SurveyToolbar({
|
|||||||
</DataToolbarItem>
|
</DataToolbarItem>
|
||||||
<DataToolbarGroup>
|
<DataToolbarGroup>
|
||||||
<DataToolbarItem>
|
<DataToolbarItem>
|
||||||
<ToolbarAddButton linkTo="/" />
|
<ToolbarAddButton linkTo={`${match.url}/add`} />
|
||||||
</DataToolbarItem>
|
</DataToolbarItem>
|
||||||
<DataToolbarItem>
|
<DataToolbarItem>
|
||||||
<Button
|
<Button
|
||||||
|
@ -47,3 +47,24 @@ export function requiredEmail(i18n) {
|
|||||||
return undefined;
|
return undefined;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function noWhiteSpace(i18n) {
|
||||||
|
return value => {
|
||||||
|
if (/\s/.test(value)) {
|
||||||
|
return i18n._(t`This field must not contain spaces`);
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function combine(validators) {
|
||||||
|
return value => {
|
||||||
|
for (let i = 0; i < validators.length; i++) {
|
||||||
|
const error = validators[i](value);
|
||||||
|
if (error) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { required, maxLength } from './validators';
|
import { required, maxLength, noWhiteSpace, combine } from './validators';
|
||||||
|
|
||||||
const i18n = { _: val => val };
|
const i18n = { _: val => val };
|
||||||
|
|
||||||
@ -51,4 +51,31 @@ describe('validators', () => {
|
|||||||
values: { max: 8 },
|
values: { max: 8 },
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('noWhiteSpace returns error', () => {
|
||||||
|
expect(noWhiteSpace(i18n)('this has spaces')).toEqual({
|
||||||
|
id: 'This field must not contain spaces',
|
||||||
|
});
|
||||||
|
expect(noWhiteSpace(i18n)('this has\twhitespace')).toEqual({
|
||||||
|
id: 'This field must not contain spaces',
|
||||||
|
});
|
||||||
|
expect(noWhiteSpace(i18n)('this\nhas\nnewlines')).toEqual({
|
||||||
|
id: 'This field must not contain spaces',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('noWhiteSpace should accept valid string', () => {
|
||||||
|
expect(noWhiteSpace(i18n)('this_has_no_whitespace')).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('combine should run all validators', () => {
|
||||||
|
const validators = [required(null, i18n), noWhiteSpace(i18n)];
|
||||||
|
expect(combine(validators)('')).toEqual({
|
||||||
|
id: 'This field must not be blank',
|
||||||
|
});
|
||||||
|
expect(combine(validators)('one two')).toEqual({
|
||||||
|
id: 'This field must not contain spaces',
|
||||||
|
});
|
||||||
|
expect(combine(validators)('ok')).toBeUndefined();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user