Skip to content

Added prefix and postfix support in react sdk.#27

Merged
brionmario merged 1 commit into
thunder-id:mainfrom
Infosys:feat/prefix-postfix-support
Jul 13, 2026
Merged

Added prefix and postfix support in react sdk.#27
brionmario merged 1 commit into
thunder-id:mainfrom
Infosys:feat/prefix-postfix-support

Conversation

@SajidMannikeri17

@SajidMannikeri17 SajidMannikeri17 commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Purpose

Adds prefix and postfix support to text-family login-id inputs (TEXT_INPUT, EMAIL_INPUT, PASSWORD_INPUT, PHONE_INPUT) rendered by the SDK from an embedded flow response. Prefixes render as either a fixed leading span (single option) or a dropdown (multiple options); postfix is silently appended to the submitted value and never rendered.

Example flow component now supported end-to-end:

- type: PHONE_INPUT
  ref: mobileId
  label: Mobile Number
  prefixes:
    - {label: "+91",  value: "+91"}
    - {label: "+855", value: "+855"}
  postfix: "@phone"

Typed 9876598765 with +855 selected is submitted as +8559876598765@phone.

Approach

1. Contract additions (@thunderid/javascript)

  • New exported type PrefixOption { label, value, maxLength?, regex? }.
  • Two optional fields on EmbeddedFlowComponent: postfix?: string and prefixes?: string | PrefixOption[]. The polymorphic prefixes shape mirrors the develop-branch schema (single static string OR selectable list). All additions are additive and optional — existing flows are unaffected.

2. New primitive AffixedField (@thunderid/react)

  • Sibling to TextField / PasswordField. Accepts type prop, so the same primitive backs text/email/password/tel inputs.
  • Normalizes prefixes (string → single entry) so render logic works from a uniform PrefixOption[]. Renders a fixed span for 0–1 entries and a <select> for 2+. Postfix is intentionally not rendered.
  • Kept as a separate primitive (rather than extending TextField) to avoid bloating a widely-used base component with a feature only some flows need.

3. Routing lifted in AuthOptionFactory

  • The four text-family cases (TEXT_INPUT, EMAIL_INPUT, PASSWORD_INPUT, PHONE_INPUT) are merged into one block.
  • A guarded branch inside that block routes to AffixedField when prefixes or postfix is present; otherwise the existing createField path runs unchanged. Flows that don't opt in walk the identical pre-change code path.

4. Submit-time composition (composeAffixedInputs)

  • Prefix and postfix state are carried through useForm's flat value map via namespaced sentinel keys (__thunderid_prefix__<id>, __thunderid_postfix__<id>). These are seeded lazily on onFocus to avoid render-time side effects.
  • composeAffixedInputs(formValues) runs inside BaseSignIn / BaseSignUp / BaseRecovery just before the parent onSubmit, rewriting each affixed identifier to ${prefix}${typed}${postfix} and stripping sentinel keys. useForm is not modified — composition is a transform on the outbound payload only.

5. Backward compatibility

  • No changes to public exports beyond the additive PrefixOption type. Internal renames (PhoneFieldAffixedField, composePhoneInputscomposeAffixedInputs) are invisible to SDK consumers — neither symbol was ever exported.
  • Runtime behavior for flows without prefixes / postfix is byte-identical to prior behavior.

Deferred (follow-up): per-prefix maxLength / regex are carried in PrefixOption but not yet wired into the form validator, which would require making the field validator reactive to the currently selected prefix. Tracked as a separate change.

Related Issues

  • N/A

Related PRs

  • N/A

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 in WSO2 Secure Coding Guidelines
  • Confirmed that this PR doesn't commit any keys, passwords, tokens, usernames, or other secrets.

Summary by CodeRabbit

Summary

  • New Features
    • Added configurable input prefixes and postfixes for embedded auth fields.
    • Support for single fixed prefix or a dropdown when multiple prefix options are available.
    • Introduced the AffixedField primitive for rendering affixed inputs with consistent styling and accessibility.
    • Updated sign-in, sign-up, and recovery submissions to automatically combine selected affixes with typed values.
    • Exported the PrefixOption type to the public TypeScript API.

@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

Affixed input support

Layer / File(s) Summary
Affix configuration contracts
packages/javascript/src/index.ts, packages/javascript/src/models/embedded-flow.ts
Embedded flow models add optional prefix and postfix configuration, with PrefixOption exposed publicly.
Affixed field component
packages/react/src/components/primitives/AffixedField/*
Adds an affixed input component supporting fixed prefixes, selectable prefix options, labels, errors, and themed styling.
Affix value composition
packages/react/src/utils/composeAffixedInputs.ts
Adds vendor-aware sentinel key helpers and composes prefix, typed, and postfix values into immutable form data.
Authentication flow integration
packages/react/src/components/presentation/auth/AuthOptionFactory.tsx, packages/react/src/components/presentation/auth/{Recovery,SignIn,SignUp}/*
Text-family inputs render affixed fields, while recovery, sign-in, and sign-up submissions compose affixed values before payload filtering.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant EmbeddedFlow
  participant AuthOptionFactory
  participant AffixedField
  participant AuthSubmit
  participant composeAffixedInputs

  EmbeddedFlow->>AuthOptionFactory: provide prefixes and postfix
  AuthOptionFactory->>AffixedField: render affixed input
  AffixedField->>AuthOptionFactory: update typed and affix state
  AuthSubmit->>composeAffixedInputs: compose form data
  composeAffixedInputs-->>AuthSubmit: return combined inputs
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding prefix and postfix support in the React SDK.
Description check ✅ Passed The description follows the template well and includes Purpose, Approach, related sections, checklist, and security notes.
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

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

🤖 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/javascript/src/models/embedded-flow.ts`:
- Around line 515-525: Correct the JSDoc on the public PrefixOption interface:
mark label as required, replace the misleading icon-URL comment with
documentation describing the optional maxLength number, and add a comment
explaining the optional regex string. Keep the interface fields and types
unchanged.

In `@packages/react/src/components/presentation/auth/AuthOptionFactory.tsx`:
- Around line 271-304: The postfix-only path in the AuthOptionFactory
AffixedField rendering does not display the configured postfix even though
submission uses it. Extend AffixedField to accept and render a postfix value,
then pass component.postfix from this branch while preserving the existing
prefix behavior.
- Around line 282-292: Update the AffixedField invocation in AuthOptionFactory
to pass through component.id and component.classes, matching the corresponding
regular field configuration. Preserve all existing affixed-field props while
ensuring configured styling and field identification are retained.

In
`@packages/react/src/components/primitives/AffixedField/AffixedField.styles.ts`:
- Line 37: Update the borderRadius assignment in AffixedField styles to use
nullish coalescing instead of logical OR, preserving an explicit 0 value while
still falling back for null or undefined.
- Around line 66-69: Update the focus styles in AffixedField.styles.ts to avoid
concatenating `20` onto the CSS-variable-based `focusColor`; use the existing
alpha-safe color helper for the focus ring. Apply the same change to the prefix
select focus state so both focus selectors produce valid box-shadow colors.

In `@packages/react/src/components/primitives/AffixedField/index.ts`:
- Around line 1-2: Add the standard WSO2 Apache 2.0 copyright header at the top
of the AffixedField module, before its export statements, matching the header
format used by other new files in the repository.

In `@packages/react/src/utils/composeAffixedInputs.ts`:
- Around line 24-42: Update AFFIX_PREFIX_SENTINEL, AFFIX_POSTFIX_SENTINEL,
affixPrefixKey, and affixPostfixKey to derive the runtime namespace through
getVendorPrefix(vendor) instead of hardcoded __thunderid_*__ values. Add the
vendor configuration parameter to both key helpers and ensure all callers pass
and use the configured vendor when reading or generating affix keys.
🪄 Autofix (Beta)

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: c1d6867a-b92e-4a41-a767-8ef06c7fa8d2

📥 Commits

Reviewing files that changed from the base of the PR and between 3e258a6 and 63bea21.

📒 Files selected for processing (10)
  • packages/javascript/src/index.ts
  • packages/javascript/src/models/embedded-flow.ts
  • packages/react/src/components/presentation/auth/AuthOptionFactory.tsx
  • packages/react/src/components/presentation/auth/Recovery/BaseRecovery.tsx
  • packages/react/src/components/presentation/auth/SignIn/BaseSignIn.tsx
  • packages/react/src/components/presentation/auth/SignUp/BaseSignUp.tsx
  • packages/react/src/components/primitives/AffixedField/AffixedField.styles.ts
  • packages/react/src/components/primitives/AffixedField/AffixedField.tsx
  • packages/react/src/components/primitives/AffixedField/index.ts
  • packages/react/src/utils/composeAffixedInputs.ts

Comment thread packages/javascript/src/models/embedded-flow.ts
Comment thread packages/react/src/components/primitives/AffixedField/AffixedField.styles.ts Outdated
Comment thread packages/react/src/components/primitives/AffixedField/index.ts Outdated
Comment thread packages/react/src/utils/composeAffixedInputs.ts Outdated
@SajidMannikeri17 SajidMannikeri17 force-pushed the feat/prefix-postfix-support branch from 63bea21 to c3b2074 Compare July 13, 2026 08:42

@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: 2

🧹 Nitpick comments (1)
packages/react/src/components/presentation/auth/SignIn/BaseSignIn.tsx (1)

448-455: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider using Object.entries for consistency with BaseRecovery.

BaseRecovery filters composed inputs via Object.entries(composedData).forEach(([key, value]) => ...), while this file uses Object.keys(composedData).forEach((key) => ... composedData[key] ...). Both are functionally equivalent, but aligning on one approach reduces cognitive overhead when maintaining both auth flows side by side.

♻️ Optional refactor for consistency
       const filteredInputs: Record<string, any> = {};
       if (composedData) {
-        Object.keys(composedData).forEach((key: any) => {
-          if (composedData[key] !== undefined && composedData[key] !== null && composedData[key] !== '') {
-            filteredInputs[key] = composedData[key];
+        Object.entries(composedData).forEach(([key, value]: [string, any]) => {
+          if (value !== null && value !== undefined && value !== '') {
+            filteredInputs[key] = value;
           }
         });
       }
🤖 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/SignIn/BaseSignIn.tsx` around
lines 448 - 455, Update the composed-input filtering in BaseSignIn to iterate
with Object.entries(composedData), destructuring each key and value like
BaseRecovery. Preserve the existing exclusion of undefined, null, and
empty-string values and the resulting filteredInputs contents.
🤖 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 265-311: Update the AffixedField onChange handler to initialize
missing prefixStateKey and postfixStateKey entries before or alongside updating
the field value, using defaultPrefixValue and component.postfix when configured.
Keep the existing onFocus initialization and ensure autofilled changes create
the sentinel keys so composeAffixedInputs submits the affixed value.

In `@packages/react/src/components/primitives/AffixedField/AffixedField.tsx`:
- Around line 96-108: Update renderPrefix and the associated input accessibility
props so the fixed prefix remains announced to screen readers while the visual
prefix span stays aria-hidden. Add a visually hidden announcement or
aria-describedby relationship containing the prefix value (such as +1), using
the component’s existing styling/utilities where available, and ensure it
applies to the submitted input context.

---

Nitpick comments:
In `@packages/react/src/components/presentation/auth/SignIn/BaseSignIn.tsx`:
- Around line 448-455: Update the composed-input filtering in BaseSignIn to
iterate with Object.entries(composedData), destructuring each key and value like
BaseRecovery. Preserve the existing exclusion of undefined, null, and
empty-string values and the resulting filteredInputs contents.
🪄 Autofix (Beta)

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: ff347b51-ac46-4e48-b263-330e032e940d

📥 Commits

Reviewing files that changed from the base of the PR and between 63bea21 and c3b2074.

📒 Files selected for processing (10)
  • packages/javascript/src/index.ts
  • packages/javascript/src/models/embedded-flow.ts
  • packages/react/src/components/presentation/auth/AuthOptionFactory.tsx
  • packages/react/src/components/presentation/auth/Recovery/BaseRecovery.tsx
  • packages/react/src/components/presentation/auth/SignIn/BaseSignIn.tsx
  • packages/react/src/components/presentation/auth/SignUp/BaseSignUp.tsx
  • packages/react/src/components/primitives/AffixedField/AffixedField.styles.ts
  • packages/react/src/components/primitives/AffixedField/AffixedField.tsx
  • packages/react/src/components/primitives/AffixedField/index.ts
  • packages/react/src/utils/composeAffixedInputs.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • packages/javascript/src/index.ts
  • packages/react/src/components/primitives/AffixedField/index.ts
  • packages/react/src/components/primitives/AffixedField/AffixedField.styles.ts
  • packages/javascript/src/models/embedded-flow.ts

@SajidMannikeri17 SajidMannikeri17 force-pushed the feat/prefix-postfix-support branch from c3b2074 to ffb423b Compare July 13, 2026 08:53
Signed-off-by: sajid.mannikeri <sajid.mannikeri@infosys.com>

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

🧹 Nitpick comments (1)
packages/react/src/components/presentation/auth/SignIn/BaseSignIn.tsx (1)

443-452: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Same compose-then-filter logic duplicated in three submit handlers.

BaseSignIn.tsx, BaseSignUp.tsx, and BaseRecovery.tsx each independently call composeAffixedInputs(data, vendor) and then loop to drop null/undefined/'' values into a filteredInputs object. Extracting this into a single exported helper (e.g. composeAndFilterAffixedInputs(data, vendor) in composeAffixedInputs.ts) would remove the triplication and give one place to fix/extend this behavior later.

  • packages/react/src/components/presentation/auth/SignIn/BaseSignIn.tsx#L443-L452: replace the inline compose+filter block with a call to the new shared helper.
  • packages/react/src/components/presentation/auth/SignUp/BaseSignUp.tsx#L800-L807: replace the inline compose+filter block with a call to the new shared helper.
  • packages/react/src/components/presentation/auth/Recovery/BaseRecovery.tsx#L314-L320: replace the inline compose+filter block with a call to the new shared helper.
🤖 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/SignIn/BaseSignIn.tsx` around
lines 443 - 452, Extract the shared compose-and-filter behavior into an exported
helper such as composeAndFilterAffixedInputs in
packages/react/src/components/presentation/auth/composeAffixedInputs.ts,
preserving removal of undefined, null, and empty-string values. Replace the
duplicated inline logic in
packages/react/src/components/presentation/auth/SignIn/BaseSignIn.tsx:443-452,
packages/react/src/components/presentation/auth/SignUp/BaseSignUp.tsx:800-807,
and
packages/react/src/components/presentation/auth/Recovery/BaseRecovery.tsx:314-320
with calls to the helper and update imports accordingly.
🤖 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.

Nitpick comments:
In `@packages/react/src/components/presentation/auth/SignIn/BaseSignIn.tsx`:
- Around line 443-452: Extract the shared compose-and-filter behavior into an
exported helper such as composeAndFilterAffixedInputs in
packages/react/src/components/presentation/auth/composeAffixedInputs.ts,
preserving removal of undefined, null, and empty-string values. Replace the
duplicated inline logic in
packages/react/src/components/presentation/auth/SignIn/BaseSignIn.tsx:443-452,
packages/react/src/components/presentation/auth/SignUp/BaseSignUp.tsx:800-807,
and
packages/react/src/components/presentation/auth/Recovery/BaseRecovery.tsx:314-320
with calls to the helper and update imports accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: a1cf51f5-b88e-4f10-8176-e02dd7d0b63c

📥 Commits

Reviewing files that changed from the base of the PR and between c3b2074 and 7062068.

📒 Files selected for processing (10)
  • packages/javascript/src/index.ts
  • packages/javascript/src/models/embedded-flow.ts
  • packages/react/src/components/presentation/auth/AuthOptionFactory.tsx
  • packages/react/src/components/presentation/auth/Recovery/BaseRecovery.tsx
  • packages/react/src/components/presentation/auth/SignIn/BaseSignIn.tsx
  • packages/react/src/components/presentation/auth/SignUp/BaseSignUp.tsx
  • packages/react/src/components/primitives/AffixedField/AffixedField.styles.ts
  • packages/react/src/components/primitives/AffixedField/AffixedField.tsx
  • packages/react/src/components/primitives/AffixedField/index.ts
  • packages/react/src/utils/composeAffixedInputs.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • packages/javascript/src/index.ts
  • packages/react/src/components/primitives/AffixedField/index.ts
  • packages/react/src/components/primitives/AffixedField/AffixedField.styles.ts
  • packages/javascript/src/models/embedded-flow.ts
  • packages/react/src/utils/composeAffixedInputs.ts

@brionmario brionmario merged commit 786f7bd into thunder-id:main Jul 13, 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