UNPKG

@razorpay/blade-mcp

Version:

Model Context Protocol server for Blade

381 lines (330 loc) 11.1 kB
## Component Name OTPInput ## Description OTPInput is a specialized input component for collecting one-time passwords or verification codes. It presents a sequence of individual input fields (typically 4 or 6) where each field accepts a single character. The component supports both controlled and uncontrolled modes, auto-focusing between fields, masking for secure inputs, and validation states to provide feedback during verification processes. ## TypeScript Types The following types define the props that the OTPInput component accepts. These types are essential for proper usage of the component in TypeScript projects. ```typescript type FormInputOnEventWithIndex = ({ name, value, inputIndex, }: { name?: string; value?: string; inputIndex: number; }) => void; type OTPInputCommonProps = { /** * Determines the number of input fields to show for the OTP * @default 6 */ otpLength?: 4 | 6; /** * The callback function to be invoked when all the values of the OTPInput are filled */ onOTPFilled?: ({ name, value }: { name?: string; value?: string }) => void; /** * Masks input characters in all the fields */ isMasked?: boolean; /** * Determines what autoComplete suggestion type to show. Defaults to `oneTimeCode`. * It's not recommended to turn this off in favor of otp input practices. */ autoCompleteSuggestionType?: 'none' | 'oneTimeCode'; /** * The callback function to be invoked when one of the input fields gets focus */ onFocus?: FormInputOnEventWithIndex; /** * The callback function to be invoked when one of the input fields is blurred */ onBlur?: FormInputOnEventWithIndex; /* Common input props */ label?: string; accessibilityLabel?: string; labelPosition?: 'top' | 'left'; validationState?: 'none' | 'error' | 'success'; helpText?: string; errorText?: string; successText?: string; name?: string; onChange?: ({ name, value }: { name?: string; value?: string }) => void; value?: string; isDisabled?: boolean; autoFocus?: boolean; keyboardReturnKeyType?: 'default' | 'go' | 'done' | 'next' | 'search' | 'send'; keyboardType?: 'text' | 'search' | 'telephone' | 'email' | 'url' | 'decimal'; placeholder?: string; testID?: string; size?: 'medium' | 'large'; } & DataAnalyticsAttribute & StyledPropsBlade; type OTPInputPropsWithA11yLabel = { /** * Label to be shown for the input field */ label?: undefined; /** * Accessibility label for the input */ accessibilityLabel: string; } & OTPInputCommonProps; type OTPInputPropsWithLabel = { /** * Label to be shown for the input field */ label: string; /** * Accessibility label for the input */ accessibilityLabel?: string; } & OTPInputCommonProps; type OTPInputProps = OTPInputPropsWithA11yLabel | OTPInputPropsWithLabel; ``` ## Example ### Basic OTP Input with Validation This example shows a basic OTP verification flow with different states based on the input validation. ```tsx import { useState, useEffect } from 'react'; import { OTPInput, Box, Button, Text } from '@razorpay/blade/components'; function OTPVerificationExample() { const [otp, setOtp] = useState(''); const [validationState, setValidationState] = useState<'none' | 'error' | 'success'>('none'); const [isVerifying, setIsVerifying] = useState(false); const [attempts, setAttempts] = useState(0); // Mock verification - in real apps, this would call your API const verifyOTP = () => { setIsVerifying(true); // Simulate API call delay setTimeout(() => { if (otp === '123456') { setValidationState('success'); } else { setValidationState('error'); setAttempts((prev) => prev + 1); } setIsVerifying(false); }, 1000); }; // Reset validation state when OTP changes useEffect(() => { if (otp && validationState !== 'none') { setValidationState('none'); } }, [otp]); return ( <Box width="100%" maxWidth="568px"> <OTPInput label="Verification Code" name="verificationCode" otpLength={6} value={otp} onChange={({ value }) => setOtp(value || '')} onOTPFilled={({ value }) => { console.log('OTP filled:', value); setOtp(value || ''); }} validationState={validationState} errorText={ attempts >= 3 ? 'Too many incorrect attempts. Please request a new code.' : 'Incorrect verification code. Please try again.' } successText="Verification successful!" helpText="Enter the 6-digit code sent to your phone" autoFocus={true} size="medium" keyboardType="decimal" autoCompleteSuggestionType="oneTimeCode" data-analytics-section="verification" data-analytics-field="otp-input" /> <Button onClick={verifyOTP} isLoading={isVerifying} isDisabled={otp.length !== 6 || isVerifying || attempts >= 3} marginBottom="spacing.3" > Verify </Button> {attempts > 0 && ( <Text size="small" color="feedback.text.negative.intense"> Attempts: {attempts}/3 </Text> )} </Box> ); } ``` ### Secure PIN Input with Masking This example demonstrates using OTPInput for PIN entry with masked input for enhanced security. ```jsx import { useState, useRef } from 'react'; import { OTPInput, Box, Button, Text } from '@razorpay/blade/components'; function SecurePINEntryExample() { const [pin, setPin] = useState(''); const [confirmPin, setConfirmPin] = useState(''); const [step, setStep] = useState('create'); const confirmInputRef = useRef(null); const handlePinChange = ({ value }) => { setPin(value || ''); }; const handleConfirmPinChange = ({ value }) => { setConfirmPin(value || ''); }; const handleContinue = () => { if (step === 'create') { setStep('confirm'); // Focus on first input of confirm PIN field setTimeout(() => { confirmInputRef.current?.[0]?.focus(); }, 100); } else if (pin === confirmPin) { setStep('success'); } else { setStep('mismatch'); } }; const resetForm = () => { setPin(''); setConfirmPin(''); setStep('create'); }; return ( <Box width="100%" maxWidth="450px" padding="spacing.5" backgroundColor="surface.background.gray.subtle" borderRadius="medium" > <Text size="large" weight="semibold"> {step === 'success' ? 'PIN Created Successfully' : 'Create Transaction PIN'} </Text> {step !== 'success' && ( <> <Box marginBottom="spacing.5"> <OTPInput label="Enter PIN" name="pin" otpLength={4} value={pin} onChange={handlePinChange} onOTPFilled={handlePinChange} isMasked={true} validationState={step === 'mismatch' ? 'error' : 'none'} helpText="Create a 4-digit PIN for secure transactions" autoFocus={step === 'create'} size="large" labelPosition="top" autoCompleteSuggestionType="none" keyboardType="decimal" data-analytics-field="create-pin" /> </Box> {(step === 'confirm' || step === 'mismatch') && ( <Box marginBottom="spacing.5"> <OTPInput ref={confirmInputRef} label="Confirm PIN" name="confirmPin" otpLength={4} value={confirmPin} onChange={handleConfirmPinChange} onOTPFilled={handleConfirmPinChange} isMasked={true} validationState={step === 'mismatch' ? 'error' : 'none'} errorText={step === 'mismatch' ? "PINs don't match" : undefined} helpText="Re-enter the same PIN to confirm" size="large" labelPosition="top" autoCompleteSuggestionType="none" keyboardType="decimal" data-analytics-field="confirm-pin" /> </Box> )} <Button onClick={handleContinue} isDisabled={ (step === 'create' && pin.length !== 4) || (step === 'confirm' && confirmPin.length !== 4) } isFullWidth > {step === 'confirm' || step === 'mismatch' ? 'Confirm PIN' : 'Continue'} </Button> </> )} {step === 'success' && ( <Box> <Text>Your PIN has been created. You can use it for future transactions.</Text> <Button onClick={resetForm} variant="secondary"> Create New PIN </Button> </Box> )} </Box> ); } ``` ### OTP Input with Ref and Programmatic Focus This example shows how to use refs with OTPInput to programmatically control focus, useful for complex forms or when specific behaviors are needed. ```jsx import { useState, useRef } from 'react'; import { OTPInput, Box, Button, Text } from '@razorpay/blade/components'; function ProgrammaticOTPExample() { const [otp, setOtp] = useState(''); const [focusIndex, setFocusIndex] = useState(0); const otpInputRef = useRef(null); const handleFocusInput = (index) => { if (otpInputRef.current && otpInputRef.current[index]) { otpInputRef.current[index].focus(); setFocusIndex(index); } }; const handleClear = () => { setOtp(''); // Focus on first input after clearing handleFocusInput(0); }; return ( <Box width="100%" maxWidth="568px"> <Text>Current focus position: {focusIndex + 1} of 6</Text> <OTPInput ref={otpInputRef} label="One-time Password" accessibilityLabel="Enter your one-time password" name="otpCode" value={otp} onChange={({ value }) => setOtp(value || '')} onFocus={({ inputIndex }) => setFocusIndex(inputIndex)} helpText="Use the buttons below to manipulate input focus" size="medium" otpLength={6} data-analytics-field="otp-with-ref" /> <Box display="flex" gap="spacing.3" marginBottom="spacing.4"> <Button onClick={() => handleFocusInput(0)} variant="secondary" size="small"> Focus 1st </Button> <Button onClick={() => handleFocusInput(2)} variant="secondary" size="small"> Focus 3rd </Button> <Button onClick={() => handleFocusInput(5)} variant="secondary" size="small"> Focus Last </Button> <Button onClick={handleClear} variant="tertiary" size="small"> Clear </Button> </Box> <Text size="small"> Note: The OTPInput ref exposes an array of input references that can be used to programmatically focus specific inputs. </Text> </Box> ); } ```