I'm adding custom fields to the Sign Up form in Supertokens. I see there is a builtin validate
function and you can basically have a true or false return on some conditional. I was wondering if there was a way to apply a "mask" to a phone number field which forces a format ie: (123)456-7890
something like an "onBlur()" or "onKeydown()" type of event. Here's my frontendConfig() code. (Snipped for brevity):
function inputMask(value) {
console.log("value: ", value);
const _num = value.replace(/\D/g, "").match(/(\d{3})(\d{3})(\d{4})/);
const phoneNumber = `(${_num[1]})${_num[2]}-${_num[3]}`;
return phoneNumber;
}
EmailPasswordReact.init({
signInAndUpFeature: {
signUpForm: {
formFields: [
{
id: "firstname",
label: "First Name",
placeholder: "Jane",
},
{
id: "lastname",
label: "Last Name",
placeholder: "Doe",
},
{
id: "phone",
label: "Phone",
placeholder: "(123)456-7890",
// as an example
validate: (value) => {
return inputMask(value);
},
},
],
},
},```
CodePudding user response:
The validate
function is only meant for checking if the input is correct or not. It's not meant for normalisation.
The SuperTokens react library render react components for the login / sign up UI, so you can use plain JS to change the form behaviour:
- You want to add an event listener on the custom input field which fires on the
blur
event. In order to add this, you need to find that input field using JS and calladdEventListener
on it - this should be done in theuseEffect
block of the page that shows the sign up form. - When the event is fired, you want to call the
inputMask
function and change the value of the form field to be equal to that function's output. - You should also implement logic in the
validate
function to check that the user's input (normalised or not) is correct. - Finally, in case the input field blur event doesn't happen, then you want to normalise the user's input when the sign up button is clicked as well. This can be achieved via the override feature.
First, in order to add the event listener, you want override the react component in the sign up form and use a useEffect
hook in your custom component. The component to override is called EmailPasswordSignUpForm_Override
(supertokens-auth-react SDK version >= 0.20.0)
EmailPasswordReact.init({
override: {
components: {
EmailPasswordSignUpForm_Override: ({ DefaultComponent, ...props }) => {
React.useEffect(() => {
// This will get fired each time the sign up form is shown to the user
// TODO: adding the event listener logic come in here...
}, [])
return <DefaultComponent {...props} />;
}
}
},
signInAndUpFeature: {...}
})
Then you want to find the input field representing the input for the phone number. You can find it like:
React.useEffect(() => {
// Note that the phone number input is the 5th element in the sign up form. That's why we do nth-child(5)
let input = document.querySelector("#supertokens-root").shadowRoot.querySelector("form > div:nth-child(5) > div:nth-child(2) > div > input");
function onBlur() {
let phoneNumber = input.value;
let normalisedPhoneNumber = inputMask(phoneNumber);
input.value = normalisedPhoneNumber;
}
input.addEventListener('blur', onBlur);
return () => {
input.removeEventListener('blur', onBlur);
}
}, [])
The above will noramlise the phone number when the phone number input is blurred. Whilst the above works from a UI point of view, when the sign up button is clicked, or when the validate
function is called, the user's input may still be unnormalised (cause the react state in the library has not been updated). So you should also call the inputMask
function during the validate
function call and when signUp
is clicked:
Normalise when signUp
is clicked
EmailPasswordReact.init({
override: {
functions: (oI) => {
return {
...oI,
signUp: async function (input) {
// we normalise the phone input
input.formFields = input.formFields.map(f => {
if (f.id === "phone") {
return {
...f,
value: inputMask(f.value)
}
}
return f;
})
// we call the original implementation
return oI.signUp(input);
}
}
}
}
})
Normalise when validate
function is called:
validate: (value) => {
value = inputMask(value);
// TODO: logic for checking input syntax. If there is an error, return a string else undefined
}
So the overall code looks like:
import React from 'react'
import EmailPasswordReact from 'supertokens-auth-react/recipe/emailpassword'
import SessionReact from 'supertokens-auth-react/recipe/session'
import { appInfo } from './appInfo'
export let frontendConfig = () => {
return {
appInfo,
recipeList: [
EmailPasswordReact.init({
override: {
functions: (oI) => {
return {
...oI,
signUp: async function (input) {
// we normalise the phone input
input.formFields = input.formFields.map(f => {
if (f.id === "phone") {
return {
...f,
value: inputMask(f.value)
}
}
return f;
})
// we call the original implementation
return oI.signUp(input);
}
}
},
components: {
EmailPasswordSignUpForm_Override: ({ DefaultComponent, ...props }) => {
React.useEffect(() => {
let input = document.querySelector("#supertokens-root").shadowRoot.querySelector("form > div:nth-child(5) > div:nth-child(2) > div > input");
function onBlur() {
let phoneNumber = input.value;
let normalisedPhoneNumber = inputMask(phoneNumber);
input.value = normalisedPhoneNumber;
}
input.addEventListener('blur', onBlur);
return () => {
input.removeEventListener('blur', onBlur);
}
}, [])
return <DefaultComponent {...props} />;
}
}
},
signInAndUpFeature: {
signUpForm: {
formFields: [
{
id: "firstname",
label: "First Name",
placeholder: "Jane",
},
{
id: "lastname",
label: "Last Name",
placeholder: "Doe",
},
{
id: "phone",
label: "Phone",
placeholder: "(123)456-7890",
validate: (value) => {
value = inputMask(value);
// TODO: logic for checking input syntax
return undefined;
}
},
],
},
}
}),
SessionReact.init(),
],
}
}
function inputMask(value) {
const _num = value.replace(/\D/g, "").match(/(\d{3})(\d{3})(\d{4})/);
const phoneNumber = `(${_num[1]})${_num[2]}-${_num[3]}`;
return phoneNumber;
}
Hope this helps!