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

Custom form fields #760

Merged
merged 10 commits into from
Nov 23, 2023
75 changes: 75 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,81 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html)

## [0.35.7] - 2023-11-16

### Added

- EmailPassword and ThirdPartyEmailPassword recipe enhancements:
- Introduced the capability to utilize custom components by exposing `inputComponent` types.
- Allow setting default values in signup/signin form fields.
- Made the `onChange` prop in `inputComponent` simpler by removing the need for an `id` attribute.
- Added a feature to customize the "Field is not optional" error message for each form field with the `nonOptionalErrorMsg` prop.

Following is an example of how to use above features.

```tsx
EmailPassword.init({
amitbadala marked this conversation as resolved.
Show resolved Hide resolved
signInAndUpFeature: {
signUpForm: {
formFields: [
{
id: "select-dropdown",
label: "Select Option",
getDefaultValue: () => "option 2",
nonOptionalErrorMsg: "Select dropdown is required",
inputComponent: ({ value, name, onChange }) => (
<select
value={value}
name={name}
onChange={(e) => onChange(e.target.value)}
placeholder="Select Option">
<option value="" disabled hidden>
Select an option
</option>
<option value="option 1">Option 1</option>
<option value="option 2">Option 2</option>
<option value="option 3">Option 3</option>
</select>
),
},
],
},
},
});

ThirdPartyEmailPassword.init({
signInAndUpFeature: {
signUpForm: {
formFields: [
{
id: "terms",
label: "",
optional: false,
getDefaultValue: () => "true",
nonOptionalErrorMsg: "You must accept the terms and conditions",
inputComponent: ({ name, onChange, value }) => (
<div
style={{
display: "flex",
alignItems: "center",
justifyContent: "left",
}}>
<input
value={value}
checked={value === "true"}
name={name}
type="checkbox"
onChange={(e) => onChange(e.target.checked.toString())}></input>
<span style={{ marginLeft: 5 }}>I agree to the terms and conditions</span>
</div>
),
},
],
},
},
});
```

## [0.35.6] - 2023-10-16

### Test changes
Expand Down
223 changes: 218 additions & 5 deletions examples/for-tests/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,167 @@ const formFields = [
},
];

const formFieldsWithDefault = [
{
id: "country",
label: "Your Country",
placeholder: "Where do you live?",
optional: true,
getDefaultValue: () => "India",
},
{
id: "select-dropdown",
label: "Select Option",
getDefaultValue: () => "option 2",
inputComponent: ({ value, name, onChange }) => (
<select value={value} name={name} onChange={(e) => onChange(e.target.value)}>
<option value="" disabled hidden>
Select an option
</option>
<option value="option 1">Option 1</option>
<option value="option 2">Option 2</option>
<option value="option 3">Option 3</option>
</select>
),
optional: true,
},
{
id: "terms",
label: "",
optional: false,
getDefaultValue: () => "true",
inputComponent: ({ name, onChange, value }) => (
<div
style={{
display: "flex",
alignItems: "center",
justifyContent: "left",
}}>
<input
value={value}
checked={value === "true"}
name={name}
type="checkbox"
onChange={(e) => onChange(e.target.checked.toString())}></input>
<span style={{ marginLeft: 5 }}>I agree to the terms and conditions</span>
</div>
),
validate: async (value) => {
if (value === "true") {
return undefined;
}
return "Please check Terms and conditions";
},
},
{
id: "email",
label: "Email",
getDefaultValue: () => "[email protected]",
},
{
id: "password",
label: "Password",
getDefaultValue: () => "fakepassword123",
},
];

const incorrectFormFields = [
{
id: "country",
label: "Your Country",
placeholder: "Where do you live?",
optional: true,
getDefaultValue: () => 23, // return should be a string
},
{
id: "select-dropdown",
label: "Select Dropdown",
getDefaultValue: "option 2", // should be function
inputComponent: ({ value, name, onChange }) => (
<select value={value} name={name} onChange={(e) => onChange(e.target.value)}>
<option value="" disabled hidden>
Select an option
</option>
<option value="option 1">Option 1</option>
<option value="option 2">Option 2</option>
<option value="option 3">Option 3</option>
</select>
),
optional: true,
},
{
// onChange accepts only string value, here we pass boolean
id: "terms",
label: "",
optional: false,
inputComponent: ({ name, onChange }) => (
<div
style={{
display: "flex",
alignItems: "center",
justifyContent: "left",
}}>
<input name={name} type="checkbox" onChange={(e) => onChange(e.target.checked)}></input>
<span style={{ marginLeft: 5 }}>I agree to the terms and conditions</span>
</div>
),
validate: async (value) => {
if (value === "true") {
return undefined;
}
return "Please check Terms and conditions";
},
},
{
id: "city",
label: "Your city",
optional: false,
nonOptionalErrorMsg: "", // empty string should throw error
},
];

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>
Select an option
</option>
<option value="option 1">Option 1</option>
<option value="option 2">Option 2</option>
<option value="option 3">Option 3</option>
</select>
),
optional: true,
},
{
id: "terms",
label: " ",
optional: false,
nonOptionalErrorMsg: "You must accept the terms and conditions",
inputComponent: ({ name, onChange }) => (
<div
style={{
display: "flex",
alignItems: "center",
justifyContent: "left",
}}>
<input name={name} type="checkbox" onChange={(e) => onChange(e.target.checked.toString())}></input>
<span style={{ marginLeft: 5 }}>I agree to the terms and conditions</span>
</div>
),
validate: async (value) => {
if (value === "true") {
return undefined;
}
return "Please check Terms and conditions";
},
},
];

const testContext = getTestContext();

let recipeList = [
Expand Down Expand Up @@ -552,7 +713,51 @@ function getEmailVerificationConfigs({ disableDefaultUI }) {
});
}

function getEmailPasswordConfigs({ disableDefaultUI }) {
function getSignUpFormFields(formType) {
switch (formType) {
case "INCORRECT_FIELDS":
return incorrectFormFields;
case "INCORRECT_ONCHANGE":
return incorrectFormFields.filter(({ id }) => id === "terms");
case "INCORRECT_NON_OPTIONAL_ERROR_MSG":
return incorrectFormFields.filter(({ id }) => id === "city");
case "INCORRECT_GETDEFAULT":
return incorrectFormFields.filter(({ id }) => id === "country");
case "CUSTOM_FIELDS_WITH_DEFAULT_VALUES":
return formFieldsWithDefault;
case "CUSTOM_FIELDS":
return customFields;
default:
return formFields;
}
}

function getSignInFormFields(formType) {
switch (formType) {
case "DEFAULT_FIELDS":
return [
{
id: "email",
getDefaultValue: () => "[email protected]",
},
{
id: "password",
getDefaultValue: () => "fakepassword123",
},
];
case "FIELDS_WITH_NON_OPTIONAL_ERROR_MESSAGE":
return [
{
id: "email",
nonOptionalErrorMsg: "Please add email",
},
];
default:
return;
}
}

function getEmailPasswordConfigs({ disableDefaultUI, formFieldType }) {
return EmailPassword.init({
style: `
[data-supertokens~=container] {
Expand Down Expand Up @@ -632,12 +837,13 @@ function getEmailPasswordConfigs({ disableDefaultUI }) {
defaultToSignUp,
signInForm: {
style: theme,
formFields: getSignInFormFields(formFieldType.signIn),
},
signUpForm: {
style: theme,
privacyPolicyLink: "https://supertokens.com/legal/privacy-policy",
termsOfServiceLink: "https://supertokens.com/legal/terms-and-conditions",
formFields,
formFields: getSignUpFormFields(formFieldType.signUp),
},
},
});
Expand Down Expand Up @@ -981,7 +1187,12 @@ function getThirdPartyConfigs({ staticProviderList, disableDefaultUI, thirdParty
});
}

function getThirdPartyEmailPasswordConfigs({ staticProviderList, disableDefaultUI, thirdPartyRedirectURL }) {
function getThirdPartyEmailPasswordConfigs({
staticProviderList,
disableDefaultUI,
thirdPartyRedirectURL,
formFieldType,
}) {
let providers = [
ThirdParty.Github.init(),
ThirdParty.Google.init(),
Expand Down Expand Up @@ -1160,9 +1371,11 @@ function getThirdPartyEmailPasswordConfigs({ staticProviderList, disableDefaultU
},
signInAndUpFeature: {
disableDefaultUI,
signInForm: {},
signInForm: {
formFields: getSignInFormFields(formFieldType.signIn),
},
signUpForm: {
formFields,
formFields: getSignUpFormFields(formFieldType.signUp),
privacyPolicyLink: "https://supertokens.com/legal/privacy-policy",
termsOfServiceLink: "https://supertokens.com/legal/terms-and-conditions",
},
Expand Down
4 changes: 4 additions & 0 deletions examples/for-tests/src/testContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ export function getTestContext() {
staticProviderList: localStorage.getItem("staticProviderList"),
mockTenantId: localStorage.getItem("mockTenantId"),
clientType: localStorage.getItem("clientType") || undefined,
formFieldType: {
signIn: localStorage.getItem("SIGNIN_SETTING_TYPE"),
signUp: localStorage.getItem("SIGNUP_SETTING_TYPE"),
},
};
return ret;
}
Expand Down
7 changes: 7 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.

Loading
Loading