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

feat: add nonOptionalErrorMsg property #754

Closed
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
12 changes: 12 additions & 0 deletions examples/for-tests/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,7 @@ const customFields = [
{
id: "select-dropdown",
label: "Select Dropdown",
nonOptionalErrorMsg: "Select dropdown is not an optional",
inputComponent: ({ value, name, onChange }) => (
<select value={value} name={name} onChange={(e) => onChange(e.target.value)}>
<option value="" disabled hidden>
Expand All @@ -301,6 +302,7 @@ const customFields = [
id: "terms",
label: "",
optional: false,
nonOptionalErrorMsg: "You must accept the terms and conditions",
inputComponent: ({ name, onChange }) => (
<div
style={{
Expand Down Expand Up @@ -723,6 +725,7 @@ function getFormFields() {

function getSignInFormFields() {
let showDefaultFields = localStorage.getItem("SHOW_SIGNIN_DEFAULT_FIELDS");
let showFieldsWithNonOptionalErrMsg = localStorage.getItem("SHOW_SIGNIN_WITH_NON_OPTIONAL_ERROR_MESSAGE");
if (showDefaultFields === "YES") {
return {
formFields: [
Expand All @@ -736,6 +739,15 @@ function getSignInFormFields() {
},
],
};
} else if (showFieldsWithNonOptionalErrMsg === "YES") {
return {
formFields: [
{
id: "email",
nonOptionalErrorMsg: "Please add email",
},
],
};
}
return {};
}
Expand Down
3 changes: 3 additions & 0 deletions lib/build/emailpassword-shared4.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

50 changes: 40 additions & 10 deletions lib/build/emailpassword-shared7.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions lib/build/types.d.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 15 additions & 4 deletions lib/ts/recipe/emailpassword/components/library/formBase.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -251,10 +251,21 @@ export const FormBase: React.FC<FormBaseProps<any>> = (props) => {
// If field error.
if (result.status === "FIELD_ERROR") {
const errorFields = result.formFields;

setFieldStates((os) =>
os.map((fs) => ({ ...fs, error: errorFields.find((ef: any) => ef.id === fs.id)?.error }))
);
const getErrorMessage = (fs: FieldState) => {
const errorMessage = errorFields.find((ef: any) => ef.id === fs.id)?.error;
if (errorMessage === "Field is not optional") {
const fieldConfigData = props.formFields.find((f) => f.id === fs.id);
// replace non-optional server error message from nonOptionalErrorMsg
if (
fieldConfigData?.nonOptionalErrorMsg !== undefined &&
fieldConfigData?.nonOptionalErrorMsg !== ""
) {
return fieldConfigData?.nonOptionalErrorMsg;
}
}
return errorMessage;
};
setFieldStates((os) => os.map((fs) => ({ ...fs, error: getErrorMessage(fs) })));
}
}
} catch (e) {
Expand Down
3 changes: 3 additions & 0 deletions lib/ts/recipe/emailpassword/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,9 @@ export function getFormattedFormField(field: NormalisedFormField): NormalisedFor
validate: async (value: any): Promise<string | undefined> => {
// Absent or not optional empty field
if (value === "" && field.optional === false) {
if (field.nonOptionalErrorMsg !== undefined && field.nonOptionalErrorMsg !== "") {
return field.nonOptionalErrorMsg;
}
return "ERROR_NON_OPTIONAL";
}

Expand Down
10 changes: 10 additions & 0 deletions lib/ts/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,11 @@ export type FormField = FormFieldBaseConfig & {
* Whether the field is optional or not.
*/
optional?: boolean;

/*
* Error message for non optional field.
*/
nonOptionalErrorMsg?: string;
};

export type APIFormField = {
Expand Down Expand Up @@ -279,6 +284,11 @@ export type NormalisedFormField = {
*/
optional: boolean;

/*
* Error message for non optional field.
*/
nonOptionalErrorMsg?: string;

/*
* Autocomplete input.
*/
Expand Down
18 changes: 18 additions & 0 deletions test/end-to-end/signin.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -757,6 +757,24 @@ describe("SuperTokens SignIn", function () {
}
});
});

describe("Check if nonOptionalErrorMsg works as expected", function () {
it("Check on blank form submit nonOptionalErrorMsg gets displayed as expected", async function () {
// set cookie and reload which loads the form with custom field
await page.evaluate(() =>
window.localStorage.setItem("SHOW_SIGNIN_WITH_NON_OPTIONAL_ERROR_MESSAGE", "YES")
);
await page.reload({
waitUntil: "domcontentloaded",
});

await submitForm(page);
let formFieldErrors = await getFieldErrors(page);

// Also standard non-optional-error is displayed if nonOptionalErrorMsg is not provided
assert.deepStrictEqual(formFieldErrors, ["Please add email", "Field is not optional"]);
});
});
});

describe("SuperTokens SignIn => Server Error", function () {
Expand Down
75 changes: 72 additions & 3 deletions test/end-to-end/signup.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -374,11 +374,11 @@ describe("SuperTokens SignUp", function () {
let formFieldErrors = await getFieldErrors(page);

// 2 regular form field errors +
// 1 required custom field => terms checkbox
// 1 required custom field => terms checkbox with nonOptionalErrorMsg
assert.deepStrictEqual(formFieldErrors, [
"Field is not optional",
"Field is not optional",
"Field is not optional",
"You must accept the terms and conditions",
]);

// supply values for regular-required fields only
Expand All @@ -389,7 +389,7 @@ describe("SuperTokens SignUp", function () {

await submitForm(page);
formFieldErrors = await getFieldErrors(page);
assert.deepStrictEqual(formFieldErrors, ["Field is not optional"]);
assert.deepStrictEqual(formFieldErrors, ["You must accept the terms and conditions"]);

// check terms and condition checkbox
let termsCheckbox = await waitForSTElement(page, '[name="terms"]');
Expand Down Expand Up @@ -476,6 +476,75 @@ describe("SuperTokens SignUp", function () {
throw new Error("test failed");
}
});

it("Check on blank form submit nonOptionalErrorMsg gets displayed as expected", async function () {
await submitForm(page);
let formFieldErrors = await getFieldErrors(page);
// Also standard non-optional-error is displayed if nonOptionalErrorMsg is not provided
assert.deepStrictEqual(formFieldErrors, [
"Field is not optional",
"Field is not optional",
"You must accept the terms and conditions",
]);
});

it("Check if nonOptionalErrorMsg overwrites server error message for non-optional fields", async function () {
const requestHandler = (request) => {
if (request.method() === "POST" && request.url() === SIGN_UP_API) {
request.respond({
status: 200,
contentType: "application/json",
headers: {
"access-control-allow-origin": TEST_CLIENT_BASE_URL,
"access-control-allow-credentials": "true",
},
body: JSON.stringify({
status: "FIELD_ERROR",
formFields: [
{
id: "select-dropdown",
error: "Field is not optional",
},
{
id: "email",
error: "Field is not optional",
},
],
}),
});
} else {
request.continue();
}
};

try {
await page.setRequestInterception(true);
page.on("request", requestHandler);

// Fill and submit the form with custom fields
await setInputValues(page, [
{ name: "email", value: "[email protected]" },
{ name: "password", value: "Str0ngP@assw0rd" },
]);
// Check terms and condition checkbox
let termsCheckbox = await waitForSTElement(page, '[name="terms"]');
await page.evaluate((e) => e.click(), termsCheckbox);

// Perform the button click and wait for all network activity to finish
await Promise.all([page.waitForNetworkIdle(), submitForm(page)]);

await waitForSTElement(page, "[data-supertokens~='inputErrorMessage']");
// should also show the server error message if nonOptionalErrorMsg is not provided
let formFieldsErrors = await getFieldErrors(page);
assert.deepStrictEqual(formFieldsErrors, [
"Field is not optional",
"Select dropdown is not an optional",
]);
} finally {
page.off("request", requestHandler);
await page.setRequestInterception(false);
}
});
});

// Default values test
Expand Down
1 change: 1 addition & 0 deletions test/with-typescript/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,7 @@ function getEmailPasswordConfigs() {
id: "terms",
label: "",
optional: false,
nonOptionalErrorMsg: "Please check Terms and conditions",
getDefaultValue: () => "true",
inputComponent: (inputProps) => (
<div
Expand Down
Loading