Skip to content

Enhance OTP field functionality with length and numeric-only options - #66

Merged
brionmario merged 1 commit into
thunder-id:mainfrom
NipuniBhagya:otp-field-fixes
Aug 11, 2026
Merged

Enhance OTP field functionality with length and numeric-only options#66
brionmario merged 1 commit into
thunder-id:mainfrom
NipuniBhagya:otp-field-fixes

Conversation

@NipuniBhagya

@NipuniBhagya NipuniBhagya commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

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 OtpField component to ensure correct behavior.


Approach

OTP Field Enhancements:

  • The OtpField component in both React and Vue now supports a configurable length (number of characters) and a numericOnly flag to allow either only digits or uppercase alphanumeric codes. [1] [2] [3] [4]
  • The React OtpField also supports an uppercase prop, which automatically uppercases user input and pasted codes for alphanumeric OTPs. [1] [2] [3]
  • The Vue OtpField automatically uppercases and filters input for alphanumeric OTPs, ensuring only valid characters are accepted.

Dynamic OTP Configuration from Server:

  • The OTP field in the authentication flow is now dynamically configured based on server-provided options for code length and character set, improving compatibility with different server implementations.

Component Factory Updates:

  • The field factories in both React and Vue (FieldFactory.tsx/FieldFactory.ts) are updated to pass the new length and numericOnly props to the OTP field, and to default numericOnly to true for backward compatibility. [1] [2] [3] [4]

Testing Improvements:

  • A comprehensive test suite is added for the React OtpField component, 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

  • Followed the contribution guidelines.
  • Manual test round performed and verified.
  • Documentation provided. (Add links if there are any)
  • Tests provided. (Add links if there are any)
    • Unit Tests
    • Integration Tests
  • Breaking changes. (Fill if applicable)
    • Breaking changes section filled.
    • breaking change label added.

Security checks

  • Followed secure coding standards.
  • Confirmed that this PR doesn't commit any keys, passwords, tokens, usernames, or other secrets.

Summary by CodeRabbit

  • New Features

    • OTP fields now support configurable lengths.
    • Added numeric-only or alphanumeric OTP input modes across React and Vue.
    • Alphanumeric entries are automatically converted to uppercase.
    • Paste handling filters invalid characters while respecting the configured OTP length.
    • OTP settings can be configured through authentication options.
  • Bug Fixes

    • Improved OTP input validation and completion behavior for customized formats.

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

React 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.

Changes

OTP configuration

Layer / File(s) Summary
React OTP behavior
packages/react/src/components/primitives/OtpField/OtpField.tsx, packages/react/src/components/primitives/OtpField/__tests__/OtpField.test.tsx
React OTP input supports configurable lengths, numeric validation, uppercase conversion, filtered paste handling, and completion tests.
React OTP configuration wiring
packages/react/src/components/factories/FieldFactory.tsx, packages/react/src/components/presentation/auth/AuthOptionFactory.tsx
React factories pass OTP length and numeric-only settings. Authentication metadata supplies validated values.
Vue OTP behavior
packages/vue/src/components/primitives/OtpField/OtpField.ts
Vue OTP input supports numeric-only or uppercase alphanumeric sanitization and selects the matching input mode.
Vue OTP configuration wiring
packages/vue/src/components/factories/FieldFactory.ts
Vue field configuration passes optional OTP length and numeric-only settings to the OTP component.

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
Loading

Suggested reviewers: brionmario, senthalan

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main OTP enhancements: configurable length and numeric-only options.
Description check ✅ Passed The description covers the purpose, approach, related work, implementation details, and tests, although checklist items remain unchecked.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

packages/react/src/components/factories/FieldFactory.tsx

ESLint 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.tsx

ESLint skipped: the ESLint configuration for this file references a package that is not available in the sandbox.

packages/react/src/components/primitives/OtpField/OtpField.tsx

ESLint skipped: the ESLint configuration for this file references a package that is not available in the sandbox.

  • 3 others

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 win

Reject 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. FieldFactory uses type="text" when numericOnly is 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

📥 Commits

Reviewing files that changed from the base of the PR and between 5a7cc88 and caaabf1.

📒 Files selected for processing (6)
  • packages/react/src/components/factories/FieldFactory.tsx
  • packages/react/src/components/presentation/auth/AuthOptionFactory.tsx
  • packages/react/src/components/primitives/OtpField/OtpField.tsx
  • packages/react/src/components/primitives/OtpField/__tests__/OtpField.test.tsx
  • packages/vue/src/components/factories/FieldFactory.ts
  • packages/vue/src/components/primitives/OtpField/OtpField.ts

Comment on lines +347 to +350
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';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 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.

Comment on lines +104 to +111
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');
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 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 240

Repository: 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.tsx

Repository: 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)))
PY

Repository: 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.

Comment on lines +21 to +23
length?: number;
name: string;
numericOnly?: boolean;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ 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.

@brionmario
brionmario merged commit d417925 into thunder-id:main Aug 11, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants