Skip to content

feat(core, react): sync combobox with uds and limit role selection - #476

Merged
chakrihacker merged 4 commits into
feat/my-org-ea-branchfrom
feat/role-selection-limit
Aug 13, 2026
Merged

feat(core, react): sync combobox with uds and limit role selection#476
chakrihacker merged 4 commits into
feat/my-org-ea-branchfrom
feat/role-selection-limit

Conversation

@chakrihacker

@chakrihacker chakrihacker commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Summary

Limits role selection to 10 in the assign-roles and invitation-create modals, and
re-syncs the vendored Combobox with the four features added upstream in
universal-components.

Why

The role pickers let admins select any number of roles, but the API rejects requests
over 10 — the failure only surfaced after submit. Separately, the vendored Combobox had
drifted from universal-components, so downstream consumers were missing the debounce,
loading state, and keyboard fixes that had already shipped upstream. Role search also
had no in-flight indicator: keystrokes debounced at the hook level left a visible gap
where the list looked stale with no feedback.

What

  • Combobox gains six props: retainQueryOnSelect, debounceMs (default 300),
    loading, loadingMessage, maxSelections, maxSelectionsMessage. All optional and
    defaulted, so existing call sites are unaffected.
  • At maxSelections, unselected options render disabled + aria-disabled="true",
    arrow navigation skips them, and Enter is guarded so keyboard input cannot bypass the
    limit. Already-selected options stay interactive so deselecting always works.
  • Role selection is capped at MAX_ROLES_PER_REQUEST (10) in
    OrganizationMemberAssignRolesModal and OrganizationInvitationCreateModal. One
    component backs both the member-table and member-detail entry points, so the limit
    holds in both contexts.
  • isSearchingRoles is a new optional prop on both modals, threaded from
    useMemberManagementService / useMemberDetail. It maps to
    rolesSearchQuery.isFetching rather than isLoading, so with React Query's
    keepPreviousData a refetch still shows the spinner instead of silently serving stale
    options.
  • Debounce ownership moved into the Combobox. useDebouncedValue and its registry
    entries are removed; the service hook now keys its query on the raw search term and the
    Combobox coalesces keystrokes. This avoids the double-debounce that stacking the two
    would have produced.
  • Two i18n keys added — member.detail.roles.assign_modal.max_selection_message and
    invitation.create.roles_max_selection_message — with matching optional entries in the
    custom-messages types. fr/ja carry English placeholders pending translation, matching
    the 12 and 18 keys already in that state in those files. fr gets one key, not two: it
    has no invitation.create block at all yet.

Packages

  • packages/core
  • packages/react
  • examples

References

Testing

How can this be verified? Note anything intentionally not covered by tests and why.

  1. Start an example app and open Organization → Members → any member → Roles →
    Assign roles.
  2. Open the role picker and select 10 roles. The limit message appears and every
    unselected role greys out; already-selected ones stay clickable.
  3. Press repeatedly — focus skips the disabled rows. Press Enter
    on a disabled row: nothing is added.
  4. Deselect one role — the message clears and the remaining options re-enable.
  5. Type in the picker. Requests fire once per 300ms pause, not per keystroke, and the
    spinner row shows for the whole in-flight window including the debounce delay.
  6. Repeat steps 2-5 in Members → Invite member → Roles.
  • This change adds unit test coverage
  • Tested for both SPA and RWA flows, all example apps working
  • All existing and new tests complete without errors

Checklist

  • Breaking change
  • Requires docs update
  • Backward compatible

Contributing

Summary by CodeRabbit

  • New Features

    • Added role-selection limits to member invitations and role assignments.
    • Displays localized messages when the maximum of 10 roles is selected.
    • Added loading indicators during role searches.
    • Preserves search terms after selecting roles and improves keyboard navigation.
    • Disabled unavailable role options while the selection limit is reached.
  • Bug Fixes

    • Improved role-search responsiveness and handling of repeated searches.
  • Tests

    • Added coverage for selection limits, loading states, searching, deselection, and keyboard navigation.

- port four combobox features from universal-components: retainQueryOnSelect,
  debounceMs + loading, maxSelections + maxSelectionsMessage, keyboard/blur fixes
- move debounce ownership from the service hook into the combobox, deleting
  use-debounced-value and its registry entries
- cap role selection at 10 in the assign-roles and invitation-create modals,
  disabling unselected options and surfacing a limit message
- thread isSearchingRoles from the member-management and member-detail hooks so
  the role picker shows a spinner while a search is in flight
- add max_selection_message and roles_max_selection_message across en-US/fr/ja
@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@chakrihacker, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 37 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 679e245b-e946-4271-a914-4b81118950b1

📥 Commits

Reviewing files that changed from the base of the PR and between 3bc022a and 1754aed.

📒 Files selected for processing (19)
  • packages/core/src/i18n/custom-messages/my-organization/member-management/invitation-tab-types.ts
  • packages/core/src/i18n/custom-messages/my-organization/member-management/member-management-types.ts
  • packages/core/src/i18n/translations/en-US.json
  • packages/core/src/i18n/translations/fr.json
  • packages/core/src/i18n/translations/ja.json
  • packages/react/src/components/auth0/my-organization/__tests__/organization-member-detail.test.tsx
  • packages/react/src/components/auth0/my-organization/organization-member-detail.tsx
  • packages/react/src/components/auth0/my-organization/organization-member-management.tsx
  • packages/react/src/components/auth0/my-organization/shared/member-management/members/organization-member-roles/__tests__/organization-member-assign-roles-modal.test.tsx
  • packages/react/src/components/auth0/my-organization/shared/member-management/members/organization-member-roles/organization-member-assign-roles-modal.tsx
  • packages/react/src/components/auth0/my-organization/shared/member-management/organization-member-detail/organization-member-roles-tab.tsx
  • packages/react/src/components/auth0/my-organization/shared/member-management/shared/invitation-create/__tests__/organization-invitation-create-modal.test.tsx
  • packages/react/src/components/auth0/my-organization/shared/member-management/shared/invitation-create/organization-invitation-create-modal.tsx
  • packages/react/src/hooks/my-organization/__tests__/use-member-management-service.test.ts
  • packages/react/src/hooks/my-organization/shared/services/use-member-management-service.ts
  • packages/react/src/hooks/my-organization/use-member-detail.ts
  • packages/react/src/hooks/my-organization/use-organization-member-management.ts
  • packages/react/src/types/my-organization/member-management/organization-member-detail-types.ts
  • packages/react/src/types/my-organization/member-management/organization-member-management-types.ts
📝 Walkthrough

Walkthrough

The PR adds a ten-role selection limit, localized limit messages, loading states for role searches, query retention after selection, and direct role-search queries. It removes the shared debounced-value hook and updates related types, wiring, registry entries, and tests.

Changes

Organization role selection

Layer / File(s) Summary
Combobox selection and loading behavior
packages/react/src/components/ui/combobox.tsx
Combobox now supports debounced input, loading indicators, retained queries, maximum selections, disabled options, and updated keyboard and focus behavior.
Role search state plumbing
packages/react/src/hooks/my-organization/..., packages/react/src/types/my-organization/..., packages/react/src/components/auth0/my-organization/organization-member-*.tsx, packages/react/src/components/auth0/my-organization/shared/member-management/organization-member-detail/..., packages/react/src/tests/utils/..., packages/react/registry.json
Role searches use the direct query term. Hooks expose isSearchingRoles, and views pass it to role modals. The removed debounced-value hook is no longer registered or tested.
Role modal configuration and localization
packages/react/src/components/auth0/my-organization/shared/member-management/.../organization-member-*-modal.tsx, packages/core/src/i18n/custom-messages/..., packages/core/src/i18n/translations/*.json
Member assignment and invitation modals configure the ten-role limit, loading state, retained queries, and localized limit messages.
Role flow validation
packages/react/src/components/auth0/my-organization/**/__tests__/*.tsx, packages/react/src/hooks/my-organization/__tests__/use-member-management-service.test.ts
Tests cover search timing, selection limits, disabled options, deselection, keyboard navigation, loading rows, and member-detail integration.

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

Possibly related PRs

Suggested labels: enhancement

Suggested reviewers: grandmaester, rax7389

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main changes: synchronizing the Combobox with UDS and limiting role selection.
Docstring Coverage ✅ Passed Docstring coverage is 88.89% which is sufficient. The required threshold is 80.00%.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/role-selection-limit

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
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (2)
packages/react/src/components/auth0/my-organization/__tests__/organization-member-detail.test.tsx (1)

837-842: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use typed Vitest mocks instead of assertions.

Lines 837 and 840 coerce API methods with as ReturnType<typeof vi.fn>. Use vi.mocked(...) if the repository Vitest version supports it. This preserves the mocked method type without unchecked assertions.

As per coding guidelines: “Avoid type assertions (as) unless there is no other option.”

Proposed fix
- (apiService.organization.roles.list as ReturnType<typeof vi.fn>).mockResolvedValue({
+ vi.mocked(apiService.organization.roles.list).mockResolvedValue({
    data: manyRoles,
  });
- (apiService.organization.members.roles.list as ReturnType<typeof vi.fn>).mockResolvedValue({
+ vi.mocked(apiService.organization.members.roles.list).mockResolvedValue({
    data: [],
  });
🤖 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/auth0/my-organization/__tests__/organization-member-detail.test.tsx`
around lines 837 - 842, Replace the ReturnType<typeof vi.fn> assertions on
apiService.organization.roles.list and
apiService.organization.members.roles.list with vi.mocked(...) and configure the
resulting typed mocks with mockResolvedValue, preserving the existing response
data.

Source: Coding guidelines

packages/react/src/components/ui/combobox.tsx (1)

18-35: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Document the expanded public API.

Add JSDoc for ComboboxProps and Combobox. Document debounce behavior, query retention, loading behavior, and selection-limit behavior. Add @module, @param, and @returns tags where applicable.

As per coding guidelines: “Document public APIs with JSDoc; use @module, @internal, @param, and @returns tags where applicable.”

🤖 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/ui/combobox.tsx` around lines 18 - 35, Add
JSDoc to the public ComboboxProps interface and Combobox component, using
`@module` for the module and `@param/`@returns where applicable. Describe debounceMs
behavior, retainQueryOnSelect query retention, loading/loadingMessage behavior,
and maxSelections/maxSelectionsMessage selection limits, while documenting any
internal-only symbols with `@internal`.

Source: Coding guidelines

🤖 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/core/src/i18n/translations/fr.json`:
- Line 1177: Update the max_selection_message value in the French translations
to a natural French translation of the selection-limit message, preserving the
existing key and meaning.

In `@packages/core/src/i18n/translations/ja.json`:
- Line 1240: Translate the English values for max_selection_message at both
referenced entries in the Japanese locale file into natural Japanese, preserving
the existing JSON keys and valid syntax.

In `@packages/react/src/components/ui/combobox.tsx`:
- Around line 654-658: Replace the hardcoded fallback in the Combobox loading
state with a translated loading-message key, using useTranslator for the
user-facing text. Update every role modal that passes loading to also provide
the localized loadingMessage, preserving the existing custom-message behavior
when supplied.

---

Nitpick comments:
In
`@packages/react/src/components/auth0/my-organization/__tests__/organization-member-detail.test.tsx`:
- Around line 837-842: Replace the ReturnType<typeof vi.fn> assertions on
apiService.organization.roles.list and
apiService.organization.members.roles.list with vi.mocked(...) and configure the
resulting typed mocks with mockResolvedValue, preserving the existing response
data.

In `@packages/react/src/components/ui/combobox.tsx`:
- Around line 18-35: Add JSDoc to the public ComboboxProps interface and
Combobox component, using `@module` for the module and `@param/`@returns where
applicable. Describe debounceMs behavior, retainQueryOnSelect query retention,
loading/loadingMessage behavior, and maxSelections/maxSelectionsMessage
selection limits, while documenting any internal-only symbols with `@internal`.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 46faf136-f334-4aa0-8626-90f845948d80

📥 Commits

Reviewing files that changed from the base of the PR and between 81f4606 and 3bc022a.

📒 Files selected for processing (25)
  • packages/core/src/i18n/custom-messages/my-organization/member-management/invitation-tab-types.ts
  • packages/core/src/i18n/custom-messages/my-organization/member-management/member-management-types.ts
  • packages/core/src/i18n/translations/en-US.json
  • packages/core/src/i18n/translations/fr.json
  • packages/core/src/i18n/translations/ja.json
  • packages/react/registry.json
  • packages/react/src/components/auth0/my-organization/__tests__/organization-member-detail.test.tsx
  • packages/react/src/components/auth0/my-organization/organization-member-detail.tsx
  • packages/react/src/components/auth0/my-organization/organization-member-management.tsx
  • packages/react/src/components/auth0/my-organization/shared/member-management/members/organization-member-roles/__tests__/organization-member-assign-roles-modal.test.tsx
  • packages/react/src/components/auth0/my-organization/shared/member-management/members/organization-member-roles/organization-member-assign-roles-modal.tsx
  • packages/react/src/components/auth0/my-organization/shared/member-management/organization-member-detail/organization-member-roles-tab.tsx
  • packages/react/src/components/auth0/my-organization/shared/member-management/shared/invitation-create/__tests__/organization-invitation-create-modal.test.tsx
  • packages/react/src/components/auth0/my-organization/shared/member-management/shared/invitation-create/organization-invitation-create-modal.tsx
  • packages/react/src/components/ui/combobox.tsx
  • packages/react/src/hooks/my-organization/__tests__/use-member-management-service.test.ts
  • packages/react/src/hooks/my-organization/shared/services/use-member-management-service.ts
  • packages/react/src/hooks/my-organization/use-member-detail.ts
  • packages/react/src/hooks/my-organization/use-organization-member-management.ts
  • packages/react/src/hooks/shared/__tests__/use-debounced-value.test.ts
  • packages/react/src/hooks/shared/use-debounced-value.ts
  • packages/react/src/tests/utils/__mocks__/my-organization/member-management/member.mocks.ts
  • packages/react/src/types/my-organization/member-management/organization-invitation-table-types.ts
  • packages/react/src/types/my-organization/member-management/organization-member-detail-types.ts
  • packages/react/src/types/my-organization/member-management/organization-member-management-types.ts
💤 Files with no reviewable changes (3)
  • packages/react/src/hooks/shared/tests/use-debounced-value.test.ts
  • packages/react/registry.json
  • packages/react/src/hooks/shared/use-debounced-value.ts

Comment thread packages/core/src/i18n/translations/fr.json Outdated
Comment thread packages/core/src/i18n/translations/ja.json Outdated
Comment thread packages/react/src/components/ui/combobox.tsx
@chakrihacker
chakrihacker merged commit 1836951 into feat/my-org-ea-branch Aug 13, 2026
2 checks passed
@chakrihacker
chakrihacker deleted the feat/role-selection-limit branch August 13, 2026 09:07
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