@razorpay/blade-mcp
Version:
Model Context Protocol server for Blade
518 lines (463 loc) • 15.2 kB
Markdown
## 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>
);
}
```