Skip to content

Implement USER_SELECT flow input in the JavaScript core and React SDK - #106

Open
Dilusha-Madushan wants to merge 1 commit into
thunder-id:mainfrom
Dilusha-Madushan:feature/user-select-input
Open

Dilusha-Madushan wants to merge 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

Purpose

Adds USER_SELECT, a flow input that lets a step ask the user to pick one user from the directory and submits the chosen user's ID. Until now the SDK had no member for it in EmbeddedFlowComponentType, so a step that declared it could not be rendered by any SDK component.

The picker is a paginated single-select: it loads nothing while closed, loads the first page on open, pages in more results on scroll or through a Load more button, and supports retry, keyboard navigation and screen readers. It is available in React, Vue, Next.js and Nuxt.

Loading users needs a signed-in user's access token with the system:user:view permission, because the backend does not expose GET /users on a public path. For that reason USER_SELECT is rendered only where a signed-in user exists (InviteUser). Sign-in, sign-up, recovery and accept-invite skip it with a warning, the same way OU_SELECT is handled today, and never fall back to a free-text field. An application that needs a paginated picker with its own data source uses the generic PagedSelect.

Approach

The feature is split into a generic paged-select layer and a thin user-specific wrapper, so a future EMAIL_SELECT or any other paginated picker can reuse the paging and accessibility work.

  • Core (@thunderid/javascript): adds the USER_SELECT enum member and the framework-neutral paged-select contract (PagedSelectOption, PagedSelectRequest, PagedSelectPage, FetchPagedOptions) with pure helpers for next-offset computation, de-duplication, option mapping and error mapping. User options are built from the existing management API: the picker reuses getUsers, ManagedUser, ManagedUserListResponse and the endpoints.users override rather than adding a second directory call. Only ThunderIDError messages are shown to the user; any other failure falls back to a translatable generic message.
  • Browser (@thunderid/browser): createHttpClientFetcher now forwards the caller's AbortSignal, and FetchHttpClient reports an aborted request as ABORT_ERROR instead of a generic network error, so cancelling a superseded page request actually cancels it and is not shown as a failure.
  • React and Vue: PagedSelect is the generic picker (public, takes a fetchOptions function) and UserSelect is a wrapper that builds the user-directory loader itself and takes no data source. In React the loader uses the same management endpoint resolution and authenticated fetcher as the other management hooks (the fetcher now forwards the request's AbortSignal); in Vue it uses the SDK's authenticated HTTP client. BaseUserSelect is the building block that takes a loader, used by InviteUser through the component factory, in line with the other Base* components. Stale results from a superseded request are dropped with a generation guard, and the panel keeps focus management and ARIA roles consistent with the existing popover components.
  • Flow wiring: InviteUser supplies the loader and enforces a required USER_SELECT like any other required field. The pre-auth components are unchanged and skip the type with a warning.
  • Node, Next.js and Nuxt: ThunderIDNodeClient.getUsers makes one authenticated request on behalf of a session (Express inherits it). Next.js and Nuxt each provide a UserSelect that loads through the server-held session, using a server action and a GET /api/auth/users route respectively. The browser sends only limit, offset and filter, never a session ID, token or URL, and receives only normalized {label, value} options, so the access token never reaches the client. Failures are returned as a plain result instead of thrown across the server-action boundary. PagedSelect is auto-registered in Nuxt.
  • React Router, TanStack Router, Express: no changes needed; they inherit the components and the Node client.

Related Issues

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.
  • 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: bf86bc91-f01a-4f59-892f-9c7cfc00398e

  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


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

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 => #30, #123
  • thunder-id/android-sdks, Android => #36
  • thunder-id/flutter-sdks, Flutter => #47

3 of 3 sibling SDKs accounted for.

The contract is in the SDK development specification.

@Dilusha-Madushan

Copy link
Copy Markdown
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 from 01071e7 to c96864b Compare September 30, 2026 13:57
Signed-off-by: Dilusha-Madushan <dilushamadushan9912@gmail.com>

# Conflicts:
#	packages/javascript/src/index.ts
#	packages/javascript/src/utils/resolveResourceEndpoint.ts
@Dilusha-Madushan
Dilusha-Madushan force-pushed the feature/user-select-input branch from c96864b to 3f3994a Compare October 1, 2026 04:47
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.

1 participant