UNPKG

@razorpay/blade-mcp

Version:

Model Context Protocol server for Blade

518 lines (463 loc) 15.2 kB
## Component Name TextInput ## Description TextInput is a component for collecting user input in a text field. It supports various input types (text, email, number, url, etc.), validation states, and visual elements like icons, prefix/suffix text, and character counters. It can be used for names, email addresses, search queries, and other text-based inputs with optional validation and visual feedback. ## TypeScript Types The following types define the props that the TextInput component accepts. These types are essential for proper usage of the component in TypeScript projects. ```typescript type TextInputSizes = 'medium' | 'large'; type Type = 'text' | 'telephone' | 'email' | 'url' | 'number' | 'search'; type TextInputCommonProps = { label?: string; accessibilityLabel?: string; labelPosition?: 'top' | 'left'; necessityIndicator?: 'optional' | 'required'; validationState?: 'none' | 'error' | 'success'; helpText?: string; errorText?: string; successText?: string; placeholder?: string; defaultValue?: string; name?: string; onChange?: ({ name, value, rawValue, }: { name?: string; value?: string; rawValue?: string; }) => void; onFocus?: ({ name, value }: { name?: string; value?: string }) => void; onBlur?: ({ name, value }: { name?: string; value?: string }) => void; value?: string; isDisabled?: boolean; isRequired?: boolean; prefix?: string; suffix?: string; maxCharacters?: number; autoFocus?: boolean; keyboardReturnKeyType?: 'default' | 'go' | 'done' | 'next' | 'search' | 'send'; autoCompleteSuggestionType?: | 'none' | 'name' | 'email' | 'username' | 'password' | 'newPassword' | 'oneTimeCode' | 'telephone' | 'postalCode' | 'countryName' | 'creditCardNumber' | 'creditCardCSC' | 'creditCardExpiry' | 'creditCardExpiryMonth' | 'creditCardExpiryYear' | 'on'; onSubmit?: ({ name, value }: { name?: string; value?: string }) => void; onClick?: ({ name, value }: { name?: string; value?: string }) => void; size?: TextInputSizes; leadingIcon?: React.ComponentType<any>; /** * Leading React component (e.g., Icon, Badge, Dropdown, or custom component) * When provided, component will be shown at the beginning of the input * Commonly used for currency symbols, country codes, or dropdown selectors * * @example <BankIcon /> for financial inputs * @example <Badge>+91</Badge> for country codes * @example <Dropdown>...</Dropdown> for prefix selectors */ leading?: React.ReactElement | React.ComponentType<any>; trailingButton?: React.ReactElement; trailingIcon?: React.ComponentType<any>; /** * Trailing React component (e.g., Icon, Badge, Dropdown, or custom component) * When provided, component will be shown at the end of the input * Commonly used for payment card icons, status indicators, or dropdown selectors * * @example <VisaIcon /> for payment card detection * @example <CheckIcon /> for validation status * @example <Dropdown>...</Dropdown> for suffix selectors */ trailing?: React.ReactElement | React.ComponentType<any>; textAlign?: 'left' | 'center' | 'right'; autoCapitalize?: 'none' | 'sentences' | 'words' | 'characters'; showClearButton?: boolean; onClearButtonClick?: () => void; isLoading?: boolean; icon?: React.ComponentType<any>; type?: Type; isTaggedInput?: boolean; tags?: string[]; onTagChange?: ({ tags }: { tags: string[] }) => void; testID?: string; /** * Format pattern where # represents input characters and other symbols act as delimiters * When provided, input will be automatically formatted and onChange will include rawValue * * **Note:** * 1. Format pattern should only contain # symbols and special characters as delimiters. * Alphanumeric characters (letters and numbers) are not allowed in the format pattern. * 2. When format is provided, user input is restricted to alphanumeric characters only. * Special characters and symbols will be filtered out automatically from user input. * * @example "#### #### #### ####" for card numbers * @example "##/##" for expiry dates * @example "(###) ###-####" for phone numbers */ format?: | '#### #### #### ####' | '##/##' | '##/##/####' | '(###) ###-####' | '###-##-####' | '##:##' | '##:##:##' | '#### #### ####' | '###.###.###.###' | '## ## ####' | '##-###-##' | (string & {}); } & DataAnalyticsAttribute & StyledPropsBlade; type TextInputPropsWithA11yLabel = { label?: undefined; accessibilityLabel: string; } & TextInputCommonProps; type TextInputPropsWithLabel = { label: string; accessibilityLabel?: string; } & TextInputCommonProps; type TextInputProps = TextInputPropsWithA11yLabel | TextInputPropsWithLabel; ``` ## Example ### Basic Usage with Validation States This example demonstrates basic TextInput usage with different validation states. ```jsx import { TextInput, Box } from '@razorpay/blade/components'; function TextInputExample() { const handleChange = ({ name, value }) => { console.log(`Input ${name} changed to ${value}`); }; return ( <Box display="flex" flexDirection="column" gap="spacing.4"> <TextInput label="Full Name" placeholder="Enter your full name" name="fullName" onChange={handleChange} necessityIndicator="required" data-analytics-field="full-name" marginBottom="spacing.2" /> <TextInput label="Email Address" placeholder="Enter your email" name="email" type="email" validationState="error" errorText="Please enter a valid email address" data-analytics-field="email" marginBottom="spacing.2" /> <TextInput label="Username" placeholder="Choose a username" name="username" validationState="success" successText="Username is available" data-analytics-field="username" marginBottom="spacing.2" /> <TextInput label="Phone Number" placeholder="Enter your phone number" name="phone" type="telephone" isDisabled={true} defaultValue="+919876543210" data-analytics-field="phone" /> </Box> ); } ``` ### Feature-Rich TextInput Examples This example showcases TextInput with combined visual and functional features. ```jsx import { TextInput, Box, Link, SearchIcon, CreditCardIcon, InfoIcon, } from '@razorpay/blade/components'; function FeatureRichTextInputExample() { return ( <Box display="flex" flexDirection="column" gap="spacing.5"> <TextInput label="Search Products" placeholder="Search the catalog" name="search" type="search" leadingIcon={SearchIcon} showClearButton={true} maxCharacters={50} helpText="Search by product name or SKU" size="medium" data-analytics-action="search" marginBottom="spacing.3" /> <TextInput label="Transaction Amount" placeholder="0.00" name="amount" type="number" prefix="₹" suffix=".00" textAlign="right" labelPosition="left" leadingIcon={CreditCardIcon} necessityIndicator="required" data-analytics-field="transaction-amount" marginBottom="spacing.3" /> <TextInput label="Discount Code" placeholder="Enter code" name="discountCode" trailingButton={<Link>Apply</Link>} necessityIndicator="optional" isLoading={false} maxCharacters={10} data-analytics-field="discount-code" position="relative" /> </Box> ); } ``` ### TextInput Without Visible Labels This example demonstrates how to create TextInput components without visible labels but with proper accessibility support using the accessibilityLabel prop, useful for compact UI designs. ```jsx import { TextInput, Box, SearchIcon, UserIcon } from '@razorpay/blade/components'; function AccessibleTextInputExample() { return ( <Box display="flex" gap="spacing.4"> <TextInput accessibilityLabel="Search" placeholder="Search" name="search" leadingIcon={SearchIcon} showClearButton={true} type="search" data-analytics-action="search" marginRight="spacing.2" /> <TextInput accessibilityLabel="First Name" placeholder="First Name" name="firstName" leadingIcon={UserIcon} data-analytics-field="first-name" /> </Box> ); } ``` ### Tagged Input for Multiple Values This example demonstrates using TextInput to collect multiple values as tags. ```jsx import { useState } from 'react'; import { TextInput, Box, MailIcon } from '@razorpay/blade/components'; function TaggedTextInputExample() { const [emails, setEmails] = useState(['user@example.com']); const handleTagChange = ({ tags }) => { setEmails(tags); }; const isValidEmail = (email) => { const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; return regex.test(email); }; return ( <Box width="100%"> <TextInput label="Recipients" placeholder="Type email and press Enter" name="recipients" isTaggedInput={true} tags={emails} onTagChange={handleTagChange} helpText="Add multiple email addresses by pressing Enter after each one" validationState={emails.every(isValidEmail) ? 'success' : 'error'} errorText="Please enter valid email addresses" autoCompleteSuggestionType="email" leadingIcon={MailIcon} showClearButton={true} data-analytics-section="email-recipients" data-analytics-action="add-email" position="relative" zIndex={1} /> </Box> ); } ``` ### TextInput with Leading/Trailing Elements and Formatting This example demonstrates TextInput with leading/trailing elements, formatting patterns, and dynamic trailing icons. ```jsx import { useState } from 'react'; import { TextInput, Box, Text, Badge, CreditCardIcon, CheckIcon, ClockIcon, BankIcon, Dropdown, DropdownOverlay, InputDropdownButton, ActionList, ActionListItem, } from '@razorpay/blade/components'; function AdvancedTextInputExample() { const [cardNumber, setCardNumber] = useState(''); const [rawCardNumber, setRawCardNumber] = useState(''); const [cardIcon, setCardIcon] = useState(null); const [phoneNumber, setPhoneNumber] = useState(''); const [rawPhoneNumber, setRawPhoneNumber] = useState(''); const [currencyAmount, setCurrencyAmount] = useState(''); const [upiId, setUpiId] = useState(''); const detectCardType = (number) => { const patterns = { visa: /^4/, mastercard: /^5[1-5]/, amex: /^3[47]/, }; for (const [type, pattern] of Object.entries(patterns)) { if (pattern.test(number)) return type; } return 'unknown'; }; const getCardIcon = (cardType) => { const icons = { visa: 'https://cdn.razorpay.com/card-networks/visa.svg', mastercard: 'https://cdn.razorpay.com/card-networks/mastercard.svg', amex: 'https://cdn.razorpay.com/card-networks/amex.svg', unknown: CreditCardIcon, }; return icons[cardType] || CreditCardIcon; }; const handleCardNumberChange = ({ value, rawValue }) => { if (rawValue && rawValue.length >= 1) { const cardType = detectCardType(rawValue); setCardIcon(getCardIcon(cardType)); } else { setCardIcon(null); } setCardNumber(value || ''); setRawCardNumber(rawValue || ''); }; const handlePhoneChange = ({ value, rawValue }) => { setPhoneNumber(value || ''); setRawPhoneNumber(rawValue || ''); }; return ( <Box display="flex" flexDirection="column" gap="spacing.5"> {/* Card Number with Dynamic Trailing Icon */} <Box> <TextInput label="Card Number" placeholder="Enter card number" name="cardNumber" value={cardNumber} format="#### #### #### ####" trailing={cardIcon} onChange={handleCardNumberChange} helpText="Try: 4111111111111111 (Visa), 5555555555554444 (Mastercard)" type="number" autoCompleteSuggestionType="creditCardNumber" data-analytics-field="card-number" /> <Box backgroundColor="surface.background.gray.moderate" padding="spacing.3" borderRadius="medium" marginTop="spacing.2" > <Text size="small" color="surface.text.gray.muted"> Formatted: {cardNumber} | Raw: {rawCardNumber} </Text> </Box> </Box> {/* Phone Number with Leading Badge and Formatting */} <TextInput label="Phone Number" placeholder="Enter phone number" name="phoneNumber" value={phoneNumber} leading={<Badge>+91</Badge>} format="(###) ###-####" trailing={rawPhoneNumber.length === 10 ? CheckIcon : undefined} onChange={handlePhoneChange} validationState={rawPhoneNumber.length === 10 ? 'success' : 'none'} successText={rawPhoneNumber.length === 10 ? 'Valid phone number' : undefined} helpText="Enter 10-digit phone number" type="telephone" autoCompleteSuggestionType="telephone" data-analytics-field="phone-number" /> {/* Currency Input with Leading Dropdown */} <TextInput label="Amount" placeholder="Enter amount" name="currencyAmount" value={currencyAmount} onChange={({ value }) => setCurrencyAmount(value || '')} leading={ <Dropdown> <InputDropdownButton defaultValue="inr" icon={BankIcon} /> <DropdownOverlay> <ActionList> <ActionListItem title="INR" value="inr" /> <ActionListItem title="USD" value="usd" /> <ActionListItem title="EUR" value="eur" /> </ActionList> </DropdownOverlay> </Dropdown> } type="number" helpText="Select currency and enter amount" data-analytics-field="currency-amount" /> {/* UPI ID with Trailing Badge */} <TextInput label="UPI ID" placeholder="Enter UPI handle" name="upiHandle" value={upiId} onChange={({ value }) => setUpiId(value || '')} trailing={<Badge>@oksbi</Badge>} helpText="Enter your UPI handle" data-analytics-field="upi-handle" /> {/* Expiry Date with Static Trailing Icon */} <TextInput label="Expiry Date" placeholder="MM/YY" name="expiryDate" format="##/##" trailing={ClockIcon} helpText="Enter expiry date in MM/YY format" type="number" autoCompleteSuggestionType="creditCardExpiry" data-analytics-field="expiry-date" /> </Box> ); } ```