Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ Validation error messages #2150

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion client/public/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -545,7 +545,21 @@
"minCount_plural": "At least {{count}} {{types}} must be selected.",
"minLength": "This field must contain at least {{length}} characters.",
"minOneStakeholderOrGroupRequired": "At least one stakeholder or stakeholder groups is required.",
"required": "This field is required."
"required": "This field is required.",
"unsupportedMode": "Selected mode not supported for selected applications",
"requiredFile": "A file is required",
"minOneRequired": "At least one {{type}} is required",
"requiredRepositoryURL": "Repository URL is required",
"duplicateName": "{type} with this name already exists. Use a different name.",
"enterRepositoryUrl": "Enter repository url.",
"duplicateEmail": "A stakeholder with this email address already exists. Use a different email address.",
"invalidDateFormat": "Date must be formatted as MM/DD/YYYY",
"nameInvalid": "Name is invalid. The name must be between 3 and 120 characters.",
"startDateAfterToday": "Start date can be no sooner than today.",
"endDateAfterStartDate": "End date must be after start date",
"validEmail": "Username must be a valid email",
"requiredField": "{{field}} is a required field",
"minOneRequiredRuleFile": "At least 1 valid custom rule file must be uploaded."
},
"wizard": {
"alert": {
Expand Down
49 changes: 30 additions & 19 deletions client/src/app/pages/applications/analysis-wizard/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,16 @@ const useModeStepSchema = ({
mode: yup
.mixed<AnalysisMode>()
.required(t("validation.required"))
.test(
"isModeCompatible",
"Selected mode not supported for selected applications", // Message not exposed to the user
(mode) => {
const analyzableApplications = mode ? analyzableAppsByMode[mode] : [];
if (mode === "binary-upload") {
return analyzableApplications.length === 1;
}
return analyzableApplications.length > 0;
.test("isModeCompatible", t("validation.unsupportedMode"), (mode) => {
const analyzableApplications = mode ? analyzableAppsByMode[mode] : [];
if (mode === "binary-upload") {
return analyzableApplications.length === 1;
}
),
return analyzableApplications.length > 0;
}),
artifact: yup.mixed<File>().when("mode", {
is: "binary-upload",
then: (schema) => schema.required(),
then: (schema) => schema.required(t("validation.requiredFile")),
}),
});
};
Expand Down Expand Up @@ -84,16 +80,27 @@ const useScopeStepSchema = (): yup.SchemaOf<ScopeStepValues> => {
includedPackages: yup
.array()
.of(yup.string().defined())
.when("withKnownLibs", (withKnownLibs, schema) =>
withKnownLibs.includes("select") ? schema.min(1) : schema
),
.when("withKnownLibs", {
is: (withKnownLibs: AnalysisScope) => withKnownLibs.includes("select"),
then: (schema) =>
schema.min(1, t("validation.minOneRequired", { type: "package" })),
otherwise: (schema) => schema,
}),

hasExcludedPackages: yup.bool().defined(),

excludedPackages: yup
.array()
.of(yup.string().defined())
.when("hasExcludedPackages", (hasExcludedPackages, schema) =>
hasExcludedPackages ? schema.min(1) : schema
),
.when("hasExcludedPackages", {
is: true,
then: (schema) =>
schema.min(
1,
t("validation.minOneRequired", { type: "excluded package" })
),
otherwise: (schema) => schema,
}),
});
};

Expand All @@ -117,6 +124,7 @@ export const customRulesFilesSchema: yup.SchemaOf<IReadFile> = yup.object({
});

const useCustomRulesStepSchema = (): yup.SchemaOf<CustomRulesStepValues> => {
const { t } = useTranslation();
return yup.object({
rulesKind: yup.string().defined(),
customRulesFiles: yup
Expand All @@ -134,7 +142,8 @@ const useCustomRulesStepSchema = (): yup.SchemaOf<CustomRulesStepValues> => {
selectedTargets: number
) =>
labels.length === 0 && rulesKind === "manual" && selectedTargets <= 0,
then: (schema) => schema.min(1, "At least 1 Rule File is required"),
then: (schema) =>
schema.min(1, t("validation.minOneRequired", { type: "rule file" })),
}),
repositoryType: yup.mixed<string>().when("rulesKind", {
is: "repository",
Expand All @@ -143,7 +152,9 @@ const useCustomRulesStepSchema = (): yup.SchemaOf<CustomRulesStepValues> => {
sourceRepository: yup.string().when("rulesKind", {
is: "repository",
then: (schema) =>
customURLValidation(schema).required("Enter repository url."),
customURLValidation(schema).required(
t("validation.requiredRepositoryURL")
),
}),
branch: yup.mixed<string>().when("rulesKind", {
is: "repository",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ export const useApplicationFormHook = ({
.max(120, t("validation.maxLength", { length: 120 }))
.test(
"Duplicate name",
"An application with this name already exists. Use a different name.",
t("duplicateName", { type: "An application" }),
(value) =>
duplicateNameCheck(
existingApplications,
Expand Down Expand Up @@ -176,46 +176,50 @@ export const useApplicationFormHook = ({
.when("branch", {
is: (branch: string) => branch?.length > 0,
then: (schema) =>
customURLValidation(schema).required("Enter repository url."),
customURLValidation(schema).required(
t("validation.requiredRepositoryURL")
),
otherwise: (schema) => customURLValidation(schema),
})
.when("rootPath", {
is: (rootPath: string) => rootPath?.length > 0,
then: (schema) =>
customURLValidation(schema).required("Enter repository url."),
customURLValidation(schema).required(
t("validation.requiredRepositoryURL")
),
otherwise: (schema) => customURLValidation(schema),
}),
group: string()
.when("artifact", {
is: (artifact: string) => artifact?.length > 0,
then: (schema) => schema.required("This field is required."),
then: (schema) => schema.required(t("validation.required")),
otherwise: (schema) => schema.trim(),
})
.when("version", {
is: (version: string) => version?.length > 0,
then: (schema) => schema.required("This field is required."),
then: (schema) => schema.required(t("validation.required")),
otherwise: (schema) => schema.trim(),
}),
artifact: string()
.when("group", {
is: (group: string) => group?.length > 0,
then: (schema) => schema.required("This field is required."),
then: (schema) => schema.required(t("validation.required")),
otherwise: (schema) => schema.trim(),
})
.when("version", {
is: (version: string) => version?.length > 0,
then: (schema) => schema.required("This field is required."),
then: (schema) => schema.required(t("validation.required")),
otherwise: (schema) => schema.trim(),
}),
version: string()
.when("group", {
is: (group: string) => group?.length > 0,
then: (schema) => schema.required("This field is required."),
then: (schema) => schema.required(t("validation.required")),
otherwise: (schema) => schema.trim(),
})
.when("artifact", {
is: (artifact: string) => artifact?.length > 0,
then: (schema) => schema.required("This field is required."),
then: (schema) => schema.required(t("validation.required")),
otherwise: (schema) => schema.trim(),
}),
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ const ArchetypeForm: React.FC<ArchetypeFormProps> = ({
.max(120, t("validation.maxLength", { length: 120 }))
.test(
"Duplicate name",
"An archetype with this name already exists. Use a different name.",
t("duplicateName", { type: "An archetype" }),
(value) =>
duplicateNameCheck(
existingArchetypes,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export const BusinessServiceForm: React.FC<BusinessServiceFormProps> = ({
.max(120, t("validation.maxLength", { length: 120 }))
.test(
"Duplicate name",
"A business service with this name already exists. Use a different name.",
t("duplicateName", { type: "A business service" }),
(value) => {
return duplicateNameCheck(
businessServices || [],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export const JobFunctionForm: React.FC<JobFunctionFormProps> = ({
.max(120, t("validation.maxLength", { length: 120 }))
.test(
"Duplicate name",
"A job function with this name already exists. Use a different name.",
t("duplicateName", { type: "A job function " }),
(value) => {
return duplicateNameCheck(
jobFunctions || [],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ export const StakeholderGroupForm: React.FC<StakeholderGroupFormProps> = ({
.max(120, t("validation.maxLength", { length: 120 }))
.test(
"Duplicate name",
"An stakeholder group with this name already exists. Use a different name.",
t("duplicateName", { type: "An stakeholder group" }),
(value) => {
return duplicateNameCheck(
stakeholderGroups || [],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,16 +72,13 @@ export const StakeholderForm: React.FC<StakeholderFormProps> = ({
.min(3, t("validation.minLength", { length: 3 }))
.max(120, t("validation.maxLength", { length: 120 }))
.email(t("validation.email"))
.test(
"Duplicate email",
"A stakeholder with this email address already exists. Use a different email address.",
(value) =>
duplicateFieldCheck(
"email",
stakeholders,
stakeholder || null,
value || ""
)
.test("Duplicate email", t("duplicateEmail"), (value) =>
duplicateFieldCheck(
"email",
stakeholders,
stakeholder || null,
value || ""
)
),
name: string()
.trim()
Expand All @@ -90,7 +87,7 @@ export const StakeholderForm: React.FC<StakeholderFormProps> = ({
.max(120, t("validation.maxLength", { length: 120 }))
.test(
"Duplicate name",
"A stakeholder with this name already exists. Use a different name.",
t("duplicateName", { type: "An stakeholder group" }),
(value) =>
duplicateNameCheck(stakeholders, stakeholder || null, value || "")
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export const TagCategoryForm: React.FC<TagCategoryFormProps> = ({
.max(40, t("validation.maxLength", { length: 40 }))
.test(
"Duplicate name",
"A tag type with this name already exists. Use a different name.",
t("duplicateName", { type: "A tag type" }),
(value) => {
return duplicateNameCheck(
tagCategories || [],
Expand Down
2 changes: 1 addition & 1 deletion client/src/app/pages/external/jira/tracker-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ export const TrackerForm: React.FC<TrackerFormProps> = ({
.max(120, t("validation.maxLength", { length: 120 }))
.test(
"Duplicate name",
"An identity with this name already exists. Use a different name.",
t("duplicateName", { type: "An identity" }),
(value) => duplicateNameCheck(trackers, tracker || null, value || "")
)
.required(t("validation.required")),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ export const IdentityForm: React.FC<IdentityFormProps> = ({
.max(120, t("validation.maxLength", { length: 120 }))
.test(
"Duplicate name",
"An identity with this name already exists. Use a different name.",
t("duplicateName", { type: "An identity" }),
(value) =>
duplicateNameCheck(identities, identity || null, value || "")
),
Expand Down Expand Up @@ -288,7 +288,7 @@ export const IdentityForm: React.FC<IdentityFormProps> = ({
is: (value: string) => value === "proxy" || value === "jira",
then: yup
.string()
.required("This value is required")
.required(t("validation.required"))
.min(3, t("validation.minLength", { length: 3 }))
.max(120, t("validation.maxLength", { length: 120 })),
otherwise: (schema) => schema.trim(),
Expand All @@ -297,7 +297,7 @@ export const IdentityForm: React.FC<IdentityFormProps> = ({
is: (value: string) => value === "proxy",
then: yup
.string()
.required("This value is required")
.required(t("validation.required"))
.min(3, t("validation.minLength", { length: 3 }))
.max(120, t("validation.maxLength", { length: 120 })),
otherwise: (schema) => schema.trim(),
Expand All @@ -306,18 +306,18 @@ export const IdentityForm: React.FC<IdentityFormProps> = ({
is: (value: string) => value === "jira",
then: yup
.string()
.email("Username must be a valid email")
.email(t("validation.validEmail"))
.min(3, t("validation.minLength", { length: 3 }))
.max(120, t("validation.maxLength", { length: 120 }))
.required("This value is required"),
.required(t("validation.required")),
otherwise: (schema) => schema.trim(),
})
.when(["kind", "userCredentials"], {
is: (kind: string, userCredentials: string) =>
kind === "source" && userCredentials === "userpass",
then: (schema) =>
schema
.required("This field is required.")
.required(t("validation.required"))
.min(3, t("validation.minLength", { length: 3 }))
.max(120, t("validation.maxLength", { length: 120 })),
}),
Expand All @@ -328,7 +328,7 @@ export const IdentityForm: React.FC<IdentityFormProps> = ({
is: (value: string) => value === "proxy",
then: yup
.string()
.required("This value is required")
.required(t("validation.required"))
.min(3, t("validation.minLength", { length: 3 }))
.max(220, t("validation.maxLength", { length: 220 })),
otherwise: (schema) => schema.trim(),
Expand All @@ -337,7 +337,7 @@ export const IdentityForm: React.FC<IdentityFormProps> = ({
is: (value: string) => value === "basic-auth",
then: yup
.string()
.required("This value is required")
.required(t("validation.required"))
.min(3, t("validation.minLength", { length: 3 }))
.max(281, t("validation.maxLength", { length: 281 })),
otherwise: (schema) => schema.trim(),
Expand All @@ -347,7 +347,7 @@ export const IdentityForm: React.FC<IdentityFormProps> = ({
kind === "source" && userCredentials === "userpass",
then: (schema) =>
schema
.required("This field is required.")
.required(t("validation.required"))
.min(3, t("validation.minLength", { length: 3 }))
.max(120, t("validation.maxLength", { length: 120 })),
}),
Expand All @@ -357,12 +357,12 @@ export const IdentityForm: React.FC<IdentityFormProps> = ({
.when(["kind", "userCredentials"], {
is: (kind: string, userCredentials: string) =>
kind === "source" && userCredentials === "source",
then: (schema) => schema.required("This field is required."),
then: (schema) => schema.required(t("validation.required")),
otherwise: (schema) => schema.trim(),
})
.when("kind", {
is: (kind: string) => kind === "bearer",
then: (schema) => schema.required("This field is required."),
then: (schema) => schema.required(t("validation.required")),
otherwise: (schema) => schema.trim(),
}),
keyFilename: yup.string().defined(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ export const CustomTargetForm: React.FC<CustomTargetFormProps> = ({
.max(120, t("validation.maxLength", { length: 120 }))
.test(
"Duplicate name",
"A custom target with this name already exists. Use a different name.",
t("duplicateName", { type: "A custom target" }),
(value) => duplicateNameCheck(targets, target || null, value || "")
),
description: yup.string(),
Expand All @@ -136,7 +136,7 @@ export const CustomTargetForm: React.FC<CustomTargetFormProps> = ({
then: yup
.array()
.of(customRulesFilesSchema)
.min(1, "At least 1 valid custom rule file must be uploaded."),
.min(1, t("validation.minOneRequiredRuleFile")),
otherwise: (schema) => schema,
}),
repositoryType: yup.mixed<string>().when("rulesKind", {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,15 @@ export const ExportForm: React.FC<ExportFormProps> = ({
issueManager: yup
.mixed<IssueManagerKind>()
.required("Issue Manager is a required field"),
tracker: yup.string().required("Instance is a required field"),
project: yup.string().required("Project is a required field"),
kind: yup.string().required("Issue type is a required field"),
tracker: yup
.string()
.required(t("validation.requiredField", { field: "Instance" })),
project: yup
.string()
.required(t("validation.requiredField", { field: "Project" })),
kind: yup
.string()
.required(t("validation.requiredField", { field: "Issue type" })),
});

const {
Expand Down
Loading
Loading