Skip to content

Implement USER_SELECT, AUTH_FLOW_SELECT and USER_TYPE_SELECT inputs in the JavaScript core and React SDK - #106

Merged
brionmario merged 1 commit into
thunder-id:mainfrom
Dilusha-Madushan:feature/user-select-input
Oct 16, 2026
Merged

brionmario merged 1 commit into
thunder-id:mainfrom
Dilusha-Madushan:feature/user-select-input

Conversation

@Dilusha-Madushan

@Dilusha-Madushan Dilusha-Madushan commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Purpose

This PR adds three flow inputs for selecting users, authentication flows, and user types. Each picker loads its first page of options when it renders and lists a Load more option for the rest. It also lets a flow mark a copyable value as masked, so a value such as a client secret stays hidden until the user chooses to reveal it.

Input What the user selects Submitted value
USER_SELECT A user User ID by default
AUTH_FLOW_SELECT An authentication flow, shown by name Flow ID
USER_TYPE_SELECT A user type, shown by handle User type handle

ThunderID's agent onboarding flow uses USER_SELECT to choose an owner and masked COPYABLE_TEXT to display the generated client secret. Copying a masked value still copies the original value.

The built-in flow integration supports these pickers in InviteUser, where a signed-in user's access token is available for the management API calls. Sign-in, sign-up, recovery, and accept-invite skip these inputs with the same warning the SDK logs for OU_SELECT. They do not replace them with free-text fields.

Approach

The three pickers share the same building blocks. Select displays the options, the React useInfiniteOptions hook and Vue composable manage pagination, and each picker connects them to its management API. Shared mapping, pagination, retry, and validation logic lives in core. The clipboard helper lives in browser.

  • Keep the native select. Select still uses the browser's <select>. It accepts optional loading props and a Load more option at the end of the list. Choosing Load more requests another page without changing or submitting the selected value. Reopen the dropdown to see the new options. The control shows loading feedback and removes Load more when there are no pages left.
  • Load pages consistently. Resource pickers load their first page when rendered, request 30 items by default, and cap the configurable page size at 100. The shared hook appends pages, removes duplicate values, and can continue through a limited number of empty or duplicate-only pages. selectedOption preserves the label of a selected value that is not in the loaded pages.
  • Keep failed loads recoverable. A failed request leaves the existing options and page position intact. Load more can retry that page. Server errors and failures without a response are retried once; client errors are not. Final failures are logged without the upstream response text and passed to onError. The picker does not display an inline error. Requests also support cancellation through AbortSignal.
  • Support browser and server integrations. Core adds getFlows and getUserTypes alongside the existing user API. Node and Next.js expose all three management calls using the session's access token. Express inherits the Node methods, while React Router and TanStack Router inherit the React components.
  • Keep session tokens on the server. Next.js pickers use server actions. Nuxt uses /api/auth/users, /api/auth/auth-flows, and /api/auth/user-types, enabled individually through the new resourcePickers option. These paths validate paging inputs, limit user filters to 512 characters, and return normalized options. Errors sent to the browser retain their status but use a controlled message instead of the upstream response text.
  • Connect the pickers to flows. React and Vue InviteUser supply loaders, validate required selections, and forward load errors to the existing onError. They also pass flow metadata and translations so templated headings resolve. Each picker has a Base variant, and the fetch hooks and composables are exported for custom integrations.
  • Reuse the password field for masking. CopyableText accepts masked and displays the value in a read-only PasswordField with Show/Hide and Copy controls. Copy always copies the original value, even while hidden. Masking uses the browser's password display, which reveals the value's length; it does not remove the value from the input.
  • Improve the existing controls. Copy is now an icon button inside the value box, with an accessible label and copied feedback. Password and text-field icon controls gain accessible labels and keyboard activation. Optional adornment slots support the copy button, and the new picker and masking messages are included in all nine locales.

The PR also fixes required SELECT validation in React and Vue accept-invite flows, passes additionalData to Vue's invite and accept-invite factories for copyable values, and adds the missing React key to elements returned by custom flow renderers.

Test coverage and validation

The tests cover the shared logic, framework components, and server integrations:

  • Core and browser: option mapping, duplicate handling, page boundaries, page-size limits, empty batches, retries, sanitized failure logs, query validation, cancellation, clipboard fallback, and fallback messages.
  • Selects and loading: native select behavior, Load more, clearing optional selections, loading and empty states, selected labels, blur events, required fields, and flow rendering. Hook tests cover concurrent requests, failed pages, resets, and initial loading under React StrictMode.
  • Picker data sources: user, flow, and user-type loaders, including page-size caps, abort signals, endpoint overrides, and the Vue provider instance.
  • Copying and masking: copying hidden values, Show/Hide, accessible labels, and keyboard interaction in React and Vue. Browser-based component tests run in Chromium.
  • Server integrations: session-backed Node calls, Next.js actions, Nuxt routes, input validation, retries, safe error messages, status propagation, and Nuxt's picker opt-in settings.

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.
  • Cross-SDK parity. Exactly one of parity/prs-raised or parity/prs-not-needed added.
    • If parity/prs-raised, the port links are posted as a reply on the parity check's comment.

Security checks

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

@coderabbitai

coderabbitai Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration
  • Configuration used: Repository: thunder-id/javascript-sdks/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 519f2f56-0676-48ee-ac63-26ac588fb506
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

@github-actions

github-actions Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

🔀 Cross-SDK feature parity, ports raised

This pull request is labelled parity/prs-raised, so the capability it introduces is expected in the other ThunderID SDKs as well.

Ports (DO NOT EDIT)

  • thunder-id/ios-sdks, iOS => #30, #123
  • thunder-id/android-sdks, Android => #36
  • thunder-id/flutter-sdks, Flutter => #47

3 of 3 sibling SDKs accounted for.

Every sibling SDK is accounted for.

The contract is in the SDK development specification.

@Dilusha-Madushan

Copy link
Copy Markdown
Contributor Author

🔀 Cross-SDK feature parity

Does this change need to ship in the other ThunderID SDKs too? This check stays red until one of these labels answers that:

Label What it asserts
parity/prs-raised The ports exist in every other SDK this change reaches, or are recorded as not applying there.
parity/prs-not-needed Nothing to port: a fix, refactor, docs, CI, dependency bump, release, or a change specific to this platform.

Ports (DO NOT EDIT)

  • thunder-id/ios-sdks, iOS
  • thunder-id/android-sdks, Android
  • thunder-id/flutter-sdks, Flutter

No sibling SDKs accounted for on this thread yet.

Important

Reply to this comment with this block, filled in. The boxes above tick themselves when you post it.

- thunder-id/ios-sdks: <paste the port link here>
- thunder-id/android-sdks: <paste the port link here>
- thunder-id/flutter-sdks: <paste the port link here>

A link can be a pull request or a tracked issue, written in full or as thunder-id/ios-sdks#123. Where the change does not reach an SDK, replace the placeholder with N/A and a short reason, as in - thunder-id/ios-sdks: N/A, the capability has no equivalent there.

The contract is in the SDK development specification.

@Dilusha-Madushan
Dilusha-Madushan force-pushed the feature/user-select-input branch 5 times, most recently from ce68fb3 to d1fb5c2 Compare October 7, 2026 09:26
@Dilusha-Madushan
Dilusha-Madushan force-pushed the feature/user-select-input branch from d1fb5c2 to 313e70e Compare October 9, 2026 04:53
@DonOmalVindula DonOmalVindula added the parity/prs-raised Ports exist in the other SDKs, or are recorded as not applying there label Oct 12, 2026
@Dilusha-Madushan
Dilusha-Madushan force-pushed the feature/user-select-input branch from 313e70e to 65b9c06 Compare October 13, 2026 03:10
@Dilusha-Madushan Dilusha-Madushan changed the title Implement USER_SELECT flow input in the JavaScript core and React SDK Implement USER_SELECT, AUTH_FLOW_SELECT and USER_TYPE_SELECT inputs in the JavaScript core and React SDK Oct 13, 2026
@Dilusha-Madushan
Dilusha-Madushan force-pushed the feature/user-select-input branch from 65b9c06 to 30d6ab0 Compare October 14, 2026 14:04
Signed-off-by: Dilusha-Madushan <[email protected]>

# Conflicts:
#	packages/javascript/src/index.ts
#	packages/javascript/src/utils/resolveResourceEndpoint.ts

# Conflicts:
#	packages/node/src/ThunderIDNodeClient.ts

# Conflicts:
#	packages/react/src/components/presentation/auth/AuthOptionFactory.tsx
#	packages/react/src/components/presentation/auth/__tests__/AuthOptionFactory.test.tsx
#	packages/vue/src/components/auth/sign-in/AuthOptionFactoryCore.ts
@Dilusha-Madushan
Dilusha-Madushan force-pushed the feature/user-select-input branch from 30d6ab0 to fea0abb Compare October 15, 2026 20:12
@brionmario
brionmario merged commit 7570ead into thunder-id:main Oct 16, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

parity/prs-raised Ports exist in the other SDKs, or are recorded as not applying there

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants