Enhance OTP field functionality with length and numeric-only options - #66
Conversation
📝 WalkthroughWalkthroughReact and Vue OTP fields now accept configurable lengths and numeric-only settings. React authentication metadata supplies validated OTP options. React supports uppercase input and filtered paste handling. Vue supports numeric and uppercase alphanumeric sanitization. ChangesOTP configuration
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant AuthOptionFactory
participant createField
participant OtpField
AuthOptionFactory->>createField: validated OTP metadata
createField->>OtpField: length and numericOnly options
OtpField-->>createField: OTP value changes
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
packages/react/src/components/factories/FieldFactory.tsxESLint skipped: missing config or dependency (missing-dependency). The ESLint configuration references a package that is not available in the sandbox. packages/react/src/components/presentation/auth/AuthOptionFactory.tsxESLint skipped: the ESLint configuration for this file references a package that is not available in the sandbox. packages/react/src/components/primitives/OtpField/OtpField.tsxESLint skipped: the ESLint configuration for this file references a package that is not available in the sandbox.
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/react/src/components/primitives/OtpField/OtpField.tsx (1)
137-143: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winReject non-alphanumeric characters in text OTP mode.
Line 137 only rejects invalid characters when
type === 'number'. Lines 201-205 have the same gap for pasted input.FieldFactoryusestype="text"whennumericOnlyis false. Inputs such as!are then emitted as OTP characters.Add an explicit alphanumeric mode. Validate typed and pasted characters against
^[A-Z0-9]$. Add regression tests for both paths.Also applies to: 194-205
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/react/src/components/primitives/OtpField/OtpField.tsx` around lines 137 - 143, Update the typed-input validation in the OtpField handler and the corresponding pasted-input validation to support explicit alphanumeric mode: when the field is text and not numeric-only, accept only characters matching ^[A-Z0-9]$ after uppercase normalization. Preserve numeric and pattern validation behavior, and add regression tests covering invalid typed and pasted characters.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/react/src/components/presentation/auth/AuthOptionFactory.tsx`:
- Around line 347-350: Update the otpLength validation in AuthOptionFactory so
positive integers above the backend protocol maximum are rejected before
createField is called. Preserve valid lengths and the existing undefined
fallback for invalid values, using the established protocol maximum constant if
one exists.
In
`@packages/react/src/components/primitives/OtpField/__tests__/OtpField.test.tsx`:
- Around line 104-111: Add a call-count assertion to the `calls onComplete once
every box is filled` test, verifying `onComplete` is invoked exactly once after
pasting the complete OTP while preserving the existing argument assertion.
In `@packages/vue/src/components/factories/FieldFactory.ts`:
- Around line 21-23: Update the FieldFactory component props and its createField
invocation to declare and forward both length and numericOnly from component
configuration. Preserve these values when creating OTP fields so configured
lengths and alphanumeric behavior reach the existing FieldConfig/createField
implementation instead of falling back to defaults.
---
Outside diff comments:
In `@packages/react/src/components/primitives/OtpField/OtpField.tsx`:
- Around line 137-143: Update the typed-input validation in the OtpField handler
and the corresponding pasted-input validation to support explicit alphanumeric
mode: when the field is text and not numeric-only, accept only characters
matching ^[A-Z0-9]$ after uppercase normalization. Preserve numeric and pattern
validation behavior, and add regression tests covering invalid typed and pasted
characters.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 59f13f01-13fd-49db-87e7-ef4e33bb9fb5
📒 Files selected for processing (6)
packages/react/src/components/factories/FieldFactory.tsxpackages/react/src/components/presentation/auth/AuthOptionFactory.tsxpackages/react/src/components/primitives/OtpField/OtpField.tsxpackages/react/src/components/primitives/OtpField/__tests__/OtpField.test.tsxpackages/vue/src/components/factories/FieldFactory.tspackages/vue/src/components/primitives/OtpField/OtpField.ts
| const reportedLength: number = Number(options.additionalData?.['otpLength']); | ||
| const otpLength: number | undefined = | ||
| Number.isInteger(reportedLength) && reportedLength > 0 ? reportedLength : undefined; | ||
| const numericOnly: boolean = options.additionalData?.['otpNumericOnly'] !== 'false'; |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Set a maximum OTP length.
Line 349 accepts every positive integer. An otpLength of 1000000 causes OtpField to allocate state and render one million inputs. This can lock the authentication UI.
Reject values above the backend protocol maximum before calling createField.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/react/src/components/presentation/auth/AuthOptionFactory.tsx` around
lines 347 - 350, Update the otpLength validation in AuthOptionFactory so
positive integers above the backend protocol maximum are rejected before
createField is called. Preserve valid lengths and the existing undefined
fallback for invalid values, using the established protocol maximum constant if
one exists.
| it('calls onComplete once every box is filled', () => { | ||
| const onComplete = vi.fn(); | ||
| render(withTheme(<OtpField type="number" onComplete={onComplete} />)); | ||
|
|
||
| paste(boxes()[0], '123456'); | ||
|
|
||
| expect(onComplete).toHaveBeenCalledWith('123456'); | ||
| }); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -eu
file=$(fd -i -t f 'OtpField.test.tsx' . | head -n 1)
printf '%s\n' "TEST_FILE=$file"
wc -l "$file"
sed -n '1,180p' "$file"
printf '%s\n' '--- OtpField candidates ---'
fd -i -t f 'OtpField' packages/react packages 2>/dev/null | head -n 30
printf '%s\n' '--- completion references ---'
rg -n -C 4 'onComplete|complete|paste' packages/react/src/components/primitives/OtpField packages/react/src 2>/dev/null | head -n 240Repository: thunder-id/javascript-sdks
Length of output: 25262
🏁 Script executed:
#!/bin/bash
set -eu
file=packages/react/src/components/primitives/OtpField/OtpField.tsx
wc -l "$file"
sed -n '120,230p' "$file"
printf '%s\n' '--- test and implementation event-handler declarations ---'
rg -n 'const handle(Change|KeyDown|Paste)|onPaste|onChange=|onKeyDown=|onComplete' \
packages/react/src/components/primitives/OtpField/OtpField.tsx \
packages/react/src/components/primitives/OtpField/__tests__/OtpField.test.tsxRepository: thunder-id/javascript-sdks
Length of output: 6990
🏁 Script executed:
#!/bin/bash
set -eu
file=packages/react/src/components/primitives/OtpField/OtpField.tsx
python3 - <<'PY'
from pathlib import Path
import re
path = Path("packages/react/src/components/primitives/OtpField/OtpField.tsx")
source = path.read_text()
paste = re.search(
r"const handlePaste.*?(?=\n\s*return \()",
source,
re.S,
)
if not paste:
raise SystemExit("handlePaste implementation not found")
body = paste.group(0)
calls = re.findall(r"\bonComplete\s*\(", body)
print(f"handlePaste onComplete call sites: {len(calls)}")
print("single paste path invokes onComplete at most once:", len(calls) == 1)
test = Path(
"packages/react/src/components/primitives/OtpField/__tests__/OtpField.test.tsx"
).read_text()
section = re.search(
r"it\('calls onComplete once every box is filled'.*?\n\s*\}\);",
test,
re.S,
)
if not section:
raise SystemExit("target test not found")
assertion = section.group(0)
print("exact call-count assertion present:",
bool(re.search(r"toHaveBeenCalledTimes\s*\(\s*1\s*\)", assertion)))
PYRepository: thunder-id/javascript-sdks
Length of output: 299
Assert the single-completion contract.
Add expect(onComplete).toHaveBeenCalledTimes(1) so the test detects duplicate callbacks.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@packages/react/src/components/primitives/OtpField/__tests__/OtpField.test.tsx`
around lines 104 - 111, Add a call-count assertion to the `calls onComplete once
every box is filled` test, verifying `onComplete` is invoked exactly once after
pasting the complete OTP while preserving the existing argument assertion.
| length?: number; | ||
| name: string; | ||
| numericOnly?: boolean; |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Expose and forward the OTP options from FieldFactory.
FieldConfig and createField support length and numericOnly, but the FieldFactory component does not declare either prop or pass either value to createField at Lines 224-238. Component callers therefore receive the default length of 6 and numeric-only mode. This breaks configured alphanumeric and non-six-digit OTP flows.
Forward the props through the component wrapper
interface FieldFactorySetupProps {
className?: string;
disabled: boolean;
error?: string;
label: string;
+ length?: number;
name: string;
+ numericOnly: boolean;
options: SelectOption[];
placeholder?: string;
required: boolean;
touched: boolean;
type: FieldType;
value: string;
}
props: {
className: {default: undefined, type: String},
disabled: {default: false, type: Boolean},
error: {default: undefined, type: String},
label: {required: true, type: String},
+ length: {default: undefined, type: Number},
name: {required: true, type: String},
+ numericOnly: {default: true, type: Boolean},
options: {default: () => [], type: Array as PropType<SelectOption[]>},
placeholder: {default: undefined, type: String},
required: {default: false, type: Boolean},
touched: {default: false, type: Boolean},
type: {required: true, type: String as PropType<FieldType>},
value: {default: '', type: String},
},
createField({
className: props.className,
disabled: props.disabled,
error: props.error,
label: props.label,
+ length: props.length,
name: props.name,
+ numericOnly: props.numericOnly,
onBlur: () => emit('blur'),This follows the PR objective that Vue field configuration passes OTP length and numeric-only settings into the OTP field.
Also applies to: 98-99, 159-160
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/vue/src/components/factories/FieldFactory.ts` around lines 21 - 23,
Update the FieldFactory component props and its createField invocation to
declare and forward both length and numericOnly from component configuration.
Preserve these values when creating OTP fields so configured lengths and
alphanumeric behavior reach the existing FieldConfig/createField implementation
instead of falling back to defaults.
Purpose
This pull request enhances the OTP (One-Time Password) field components in both the React and Vue packages to support configurable OTP length, character set (numeric or alphanumeric), and automatic uppercasing of alphanumeric codes. It also adds comprehensive tests for the React
OtpFieldcomponent to ensure correct behavior.Approach
OTP Field Enhancements:
OtpFieldcomponent in both React and Vue now supports a configurablelength(number of characters) and anumericOnlyflag to allow either only digits or uppercase alphanumeric codes. [1] [2] [3] [4]OtpFieldalso supports anuppercaseprop, which automatically uppercases user input and pasted codes for alphanumeric OTPs. [1] [2] [3]OtpFieldautomatically uppercases and filters input for alphanumeric OTPs, ensuring only valid characters are accepted.Dynamic OTP Configuration from Server:
Component Factory Updates:
FieldFactory.tsx/FieldFactory.ts) are updated to pass the newlengthandnumericOnlyprops to the OTP field, and to defaultnumericOnlytotruefor backward compatibility. [1] [2] [3] [4]Testing Improvements:
OtpFieldcomponent, covering rendering, input validation, uppercasing, pasting, and completion behavior.These changes make the OTP input fields more flexible, robust, and compatible with a variety of authentication scenarios.
Related Issues
Related PRs
Checklist
breaking changelabel added.Security checks
Summary by CodeRabbit
New Features
Bug Fixes