diff --git a/awx/ui_next/src/components/LaunchPrompt/steps/SurveyStep.jsx b/awx/ui_next/src/components/LaunchPrompt/steps/SurveyStep.jsx index b5efa0c477..941f14b4ed 100644 --- a/awx/ui_next/src/components/LaunchPrompt/steps/SurveyStep.jsx +++ b/awx/ui_next/src/components/LaunchPrompt/steps/SurveyStep.jsx @@ -123,7 +123,7 @@ function MultiSelectField({ question, i18n }) { validate: question.isrequired ? required(null, i18n) : null, }); const id = `survey-question-${question.variable}`; - const hasActualValue = !question.required || meta.value.length > 0; + const hasActualValue = !question.required || meta.value?.length > 0; const isValid = !meta.touched || (!meta.error && hasActualValue); return ( diff --git a/awx/ui_next/src/screens/Template/Survey/SurveyQuestionForm.jsx b/awx/ui_next/src/screens/Template/Survey/SurveyQuestionForm.jsx index 4d960f0964..793b928216 100644 --- a/awx/ui_next/src/screens/Template/Survey/SurveyQuestionForm.jsx +++ b/awx/ui_next/src/screens/Template/Survey/SurveyQuestionForm.jsx @@ -18,6 +18,8 @@ import { noWhiteSpace, combine, maxLength, + integer, + number as numberValidator, } from '../../../util/validators'; function AnswerTypeField({ i18n }) { @@ -177,7 +179,15 @@ function SurveyQuestionForm({ diff --git a/awx/ui_next/src/util/validators.jsx b/awx/ui_next/src/util/validators.jsx index 111736c3ed..499957f6e5 100644 --- a/awx/ui_next/src/util/validators.jsx +++ b/awx/ui_next/src/util/validators.jsx @@ -76,6 +76,20 @@ export function integer(i18n) { }; } +export function number(i18n) { + return value => { + const str = String(value); + if (/^-?[0-9]*(\.[0-9]*)?$/.test(str)) { + return undefined; + } + // large number scientific notation (e.g. '1e+21') + if (/^-?[0-9]*e[+-][0-9]*$/.test(str)) { + return undefined; + } + return i18n._(t`This field must be a number`); + }; +} + export function url(i18n) { return value => { if (!value) { diff --git a/awx/ui_next/src/util/validators.test.js b/awx/ui_next/src/util/validators.test.js index fcde053dbb..a660c8ea5c 100644 --- a/awx/ui_next/src/util/validators.test.js +++ b/awx/ui_next/src/util/validators.test.js @@ -4,6 +4,7 @@ import { maxLength, noWhiteSpace, integer, + number, url, combine, regExp, @@ -112,6 +113,33 @@ describe('validators', () => { }); }); + test('number should accept number (number)', () => { + expect(number(i18n)(13)).toBeUndefined(); + }); + + test('number should accept number (string)', () => { + expect(number(i18n)('13')).toBeUndefined(); + }); + + test('number should accept negative number', () => { + expect(number(i18n)(-14)).toBeUndefined(); + }); + + test('number should accept decimal/float', () => { + expect(number(i18n)(13.1)).toBeUndefined(); + }); + + test('number should accept large number', () => { + expect(number(i18n)(999999999999999999999.9)).toBeUndefined(); + expect(number(i18n)(-999999999999999999999.9)).toBeUndefined(); + }); + + test('number should reject string containing alphanum', () => { + expect(number(i18n)('15a')).toEqual({ + id: 'This field must be a number', + }); + }); + test('url should reject incomplete url', () => { expect(url(i18n)('abcd')).toEqual({ id: 'Please enter a valid URL',