Skip to content

Conversation

@Agarwalchetan
Copy link

@Agarwalchetan Agarwalchetan commented Jan 25, 2026

This PR refactors the DevicePicker component to remove the react/no-multi-comp ESLint suppression by splitting it into smaller, focused files.

Previously, multiple components and helpers were defined in a single file, which required disabling the ESLint rule. This change restructures the code to follow existing patterns used in Keypad, Widget, and PeerInfo, while keeping behavior and public APIs unchanged.


What changed

  • Split DevicePicker.tsx into a dedicated directory:

    packages/ui-voip/src/components/DevicePicker/
    ├── index.tsx                 // main DevicePicker component
    ├── DevicePickerButton.tsx    // button wrapper component
    └── getDefaultDeviceItem.tsx  // helper for default device items
    
  • Extracted DevicePickerButton into its own component

  • Extracted getDefaultDeviceItem helper into a separate file

  • Removed react/no-multi-comp ESLint disable

  • Updated component exports to preserve the existing public API


Why

  • Removes technical debt (eslint-disable comment)
  • Improves readability and maintainability
  • Keeps components small and focused
  • Aligns with established patterns in the codebase

Scope & risk

  • Type: Refactor
  • Behavior changes: None
  • Public API changes: None
  • Risk: Low

Verification

  • ESLint suppression removed (react/no-multi-comp)
  • Imports and exports verified manually
  • No logic changes introduced
  • yarn lint / yarn typecheck passed

Related issue

Closes: fixes #38326 Refactor DevicePicker to remove react/no-multi-comp ESLint disable

Summary by CodeRabbit

  • New Features

    • Rebuilt Device Picker into modular pieces while preserving microphone and speaker selection UI, permission prompt on open, default-item handling, and updated button behavior.
  • Refactor

    • Component structure and public export path reorganized to a new module layout for improved maintainability.

✏️ Tip: You can customize this high-level summary in your review settings.

Resolves react/no-multi-comp ESLint violation by separating
DevicePickerButton and DevicePicker components into individual files.

Changes:
- Created DevicePicker/ directory structure
- Extracted DevicePickerButton to separate file
- Extracted getDefaultDeviceItem helper to separate file
- Updated component exports in index.ts
- Removed eslint-disable comment

BREAKING CHANGE: None (same public API)
@dionisio-bot
Copy link
Contributor

dionisio-bot bot commented Jan 25, 2026

Looks like this PR is not ready to merge, because of the following issues:

  • This PR is missing the 'stat: QA assured' label
  • This PR is missing the required milestone or project

Please fix the issues and try again

If you have any trouble, please check the PR guidelines

@changeset-bot
Copy link

changeset-bot bot commented Jan 25, 2026

⚠️ No Changeset found

Latest commit: 22544b2

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 25, 2026

Walkthrough

Refactors DevicePicker by removing the single-file implementation and introducing a modular folder with DevicePicker/index.tsx, DevicePicker/DevicePickerButton.tsx, and DevicePicker/getDefaultDeviceItem.tsx; updates the public export path to the new module entry and removes the old default export.

Changes

Cohort / File(s) Summary
Removed legacy component
\packages/ui-voip/src/components/DevicePicker.tsx``
Deleted the original multi-component DevicePicker file (≈157 lines).
New DevicePicker module
\packages/ui-voip/src/components/DevicePicker/index.tsx`, `packages/ui-voip/src/components/DevicePicker/DevicePickerButton.tsx`, `packages/ui-voip/src/components/DevicePicker/getDefaultDeviceItem.tsx``
Added modular implementation: main DevicePicker component, a DevicePickerButton wrapper for the menu trigger, and getDefaultDeviceItem factory for default menu items (radio addon + label).
Public export update
\packages/ui-voip/src/components/index.ts``
Updated export path: default as DevicePicker now resolves from ./DevicePicker/index instead of ./DevicePicker.

Sequence Diagram(s)

(No sequence diagrams generated — changes are a refactor/splitting of components without new multi-component control flows requiring visualization.)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

stat: ready to merge, stat: QA assured

Suggested reviewers

  • tassoevan

Poem

🐰 I hopped through code with nimble paws,

Split one file into tidy laws,
Buttons, getters, menus set apart,
Cleaner imports, a joyful heart,
A tiny hop for tidier parts.

🚥 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 accurately describes the main change: refactoring DevicePicker by splitting it into separate files as outlined in the PR objectives.
Linked Issues check ✅ Passed The PR successfully addresses all coding requirements from issue #38326: splits DevicePicker into separate files, removes react/no-multi-comp ESLint suppression, preserves public API, and maintains functionality.
Out of Scope Changes check ✅ Passed All changes are directly related to the refactoring objective. The PR only restructures the DevicePicker component and updates exports without introducing unrelated modifications.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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 and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@packages/ui-voip/src/components/DevicePicker/index.tsx`:
- Around line 84-89: The call to requestPermission in DevicePicker (the promise
currently invoked with void requestPermission(...).then(...)) lacks rejection
handling; add a .catch handler (or use try/catch if converting to async) on
requestPermission to handle user-denied or error cases, call stopTracks if a
stream is returned on success, call setIsOpen(true) on success, and on failure
log the error and/or set an error state or show user feedback so the rejection
isn't swallowed (refer to requestPermission, stopTracks, and setIsOpen to locate
where to attach the error handling).
🧹 Nitpick comments (1)
packages/ui-voip/src/components/DevicePicker/index.tsx (1)

36-36: Consider adding onChange handler to controlled RadioButton.

The RadioButton is controlled (checked prop set) but has no onChange handler. While the selection is handled via GenericMenu's onAction, this pattern may trigger React warnings about controlled components without change handlers in some React versions.

Proposed fix
-                addon: <RadioButton checked={device.id === selectedAudioDevices?.audioInput?.id} />,
+                addon: <RadioButton checked={device.id === selectedAudioDevices?.audioInput?.id} onChange={() => undefined} />,

Apply the same pattern at line 53 for output devices.

Resolves react/no-multi-comp ESLint violation by separating
DevicePickerButton and DevicePicker components into individual files.

Changes:
- Created DevicePicker/ directory structure
- Extracted DevicePickerButton to separate file
- Extracted getDefaultDeviceItem helper to separate file
- Updated component exports in index.ts
- Removed eslint-disable comment

BREAKING CHANGE: None (same public API)
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

No issues found across 5 files

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@packages/ui-voip/src/components/DevicePicker/index.tsx`:
- Around line 114-129: The parser for deviceId currently uses includes + replace
which can mangle IDs that contain “-input”/“-output” internally; change the
checks in the DevicePicker component to use endsWith('-input') and
endsWith('-output') and derive the base id with slice(0, deviceId.length -
'-input'.length) / slice(0, deviceId.length - '-output'.length) (and guard for
empty id) before calling availableDevices?.audioInput?.find /
availableDevices?.audioOutput?.find and onDeviceChange; update the branches that
reference deviceId, id, availableDevices, and onDeviceChange accordingly so only
suffixes are removed.
🧹 Nitpick comments (1)
packages/ui-voip/src/components/DevicePicker/index.tsx (1)

91-94: Remove inline comment to match TS/TSX implementation guidelines.

The log + state update already communicates intent; the comment can be dropped.

♻️ Proposed change
             .catch((error) => {
-                // Permission denied or error occurred, keep menu closed
                 console.warn('DevicePicker: Failed to request device permissions', error);
                 setIsOpen(false);
             });

As per coding guidelines, avoid code comments in implementation.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@packages/ui-voip/src/components/DevicePicker/index.tsx`:
- Around line 23-38: The current mapping for availableInputDevice uses
getDefaultDeviceItem whenever either device.id or device.label is missing,
losing the real id and collapsing devices; change the logic in the
availableInputDevice mapping so that you only call getDefaultDeviceItem when
device.id is falsy, otherwise preserve device.id (use `${device.id}-input` for
the id and RadioButton checked logic using selectedAudioDevices?.audioInput?.id)
and fall back to a default label/string when device.label is missing (e.g., use
t('Default') or similar for the Box content). Apply the same change to the
corresponding output mapping (the block around lines 41-59) so audioOutput keeps
real ids and only uses getDefaultDeviceItem when id is missing.
🧹 Nitpick comments (1)
packages/ui-voip/src/components/DevicePicker/index.tsx (1)

91-95: Prefer suffix constants over inline comments and magic numbers.

Inline comments in implementation aren’t needed here; constants convey intent and remove the need for comments and hardcoded lengths.

🧹 Suggested cleanup
+    const INPUT_SUFFIX = '-input';
+    const OUTPUT_SUFFIX = '-output';
@@
-                .catch((error) => {
-                    // Permission denied or error occurred, keep menu closed
+                .catch((error) => {
                     console.warn('DevicePicker: Failed to request device permissions', error);
                     setIsOpen(false);
                 });
@@
-                // Use endsWith to check suffix and slice to remove it
-                if (deviceId.endsWith('-input')) {
-                    const id = deviceId.slice(0, -6); // Remove '-input' suffix
+                if (deviceId.endsWith(INPUT_SUFFIX)) {
+                    const id = deviceId.slice(0, -INPUT_SUFFIX.length);
@@
-                if (deviceId.endsWith('-output')) {
-                    const id = deviceId.slice(0, -7); // Remove '-output' suffix
+                if (deviceId.endsWith(OUTPUT_SUFFIX)) {
+                    const id = deviceId.slice(0, -OUTPUT_SUFFIX.length);

As per coding guidelines, avoid code comments in implementation.

Also applies to: 114-116, 124-126

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.

Refactor DevicePicker to remove react/no-multi-comp ESLint disable

1 participant