Skip to content

Enhance DrumPicker with custom item rendering and new Row component - #16

Merged
scrollDynasty merged 10 commits into
mainfrom
feat/render-item
May 28, 2026
Merged

scrollDynasty merged 10 commits into
mainfrom
feat/render-item

Conversation

@scrollDynasty

@scrollDynasty scrollDynasty commented May 28, 2026 •

Copy link
Copy Markdown
Owner

Summary

What does this PR change and why?

Type of change

  • Bug fix
  • New feature
  • Documentation
  • Refactor / chore
  • Native change (Android Kotlin / iOS Swift)
  • Tests only

Checklist

  • I read CONTRIBUTING.md
  • I tested on Android and/or iOS (platforms I changed)
  • I ran yarn lint, yarn build, yarn typecheck, and yarn test from the repo root
  • I added or updated tests in the correct folder (see CONTRIBUTING.md — tests)
  • I updated docs if the public API changed
  • I committed yarn.lock if package.json dependencies changed
  • I did not add unnecessary dependencies
  • I removed debug logs from library code
  • I did not hardcode demo values in src/, android/, or ios/ (example app only)
  • For native changes: I considered memory leaks and scroll performance

CI

PRs to main run the full CI workflow automatically. New tests in standard locations are picked up without editing ci.yml.

Screenshots / recordings (UI changes)

If applicable, add before/after visuals.

Related issues

Fixes # (issue number)

Summary by CodeRabbit

  • New Features

    • Added renderItem prop to customize how items are rendered in DrumPicker.
    • Enhanced support for custom item types (objects) alongside strings.
  • Documentation

    • Updated CHANGELOG.md with improved formatting.
    • Expanded README.md with new examples, visual demos, and comprehensive API reference tables.
  • Chores

    • Added visual assets to npm package distribution.

Review Change Stack

Introduced a `renderItem` prop to the DrumPicker component, allowing users to customize the rendering of picker items with their own React components. This enhancement maintains native scroll physics and snap behavior while providing flexibility in item presentation. Updated README and types to reflect this new feature.
Updated the withVirtualized HOC to support a custom renderItem prop, allowing for more flexible item rendering in the VirtualizedDrumPicker. Added a new test case to verify that the renderItem function correctly updates the isSelected state based on the real index. Updated README to include performance recommendations for complex rows.
Introduced a new Row component for optimized rendering of picker items using React.memo. Updated the README to include the new Row component example and performance recommendations. Enhanced the DrumPicker test to verify the functionality of the renderItem prop, ensuring it correctly updates the selected state during interactions.
… image

Removed the outdated `DateDrumPicker` preview image and added multiple new demo images showcasing various picker functionalities, including basic, time, height/weight, controlled selection, and date picking. This enhances the documentation by providing clearer visual examples of the component's capabilities.
…tem handling

Replaced local image paths in the README with direct URLs for better accessibility. Updated the DrumPicker component to utilize a labels array for improved item handling, ensuring that the correct values are returned and clamped based on the labels instead of the original items. Added a new test case to verify the functionality of rendering items with object mappings.
… directory to package.json

Changed image paths in the README to reference local files instead of external URLs for better accessibility. Added the 'func-gif' directory to package.json to ensure it is included in the package distribution.
@coderabbitai

coderabbitai Bot commented May 28, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

@scrollDynasty, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 48 minutes and 10 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

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.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 5399b089-f1e7-48c7-8110-5b4a586f9eb4

📥 Commits

Reviewing files that changed from the base of the PR and between 5ab4cc3 and a9a32e0.

📒 Files selected for processing (5)
  • CHANGELOG.md
  • README.md
  • src/DrumPicker.native.tsx
  • src/DrumPicker.tsx
  • src/DrumPickerWithRenderItem.tsx
📝 Walkthrough

Walkthrough

Adds support for custom item rendering via a new renderItem prop. The PR makes DrumPickerProps generic, introduces label extraction, refactors native and web pickers to operate on computed labels, wraps native pickers with an overlay component for custom rendering, virtualizes index mappings, includes comprehensive tests, and expands documentation with examples and GIFs.

Changes

Custom item rendering with renderItem prop

Layer / File(s) Summary
Type contract and label extraction
src/types.ts, src/itemLabel.ts, src/index.tsx
Introduces generic DrumPickerProps<T> and DrumPickerRenderItemInfo<T> with optional renderItem callback; adds getItemLabel utility to compute string labels from items.
Native implementation with renderItem branching
src/DrumPicker.native.tsx, src/DrumPickerWithRenderItem.tsx
DrumPickerNativeBase now uses computed labels for selection/scrolling; new DrumPickerWithRenderItem wraps native picker and overlays custom-rendered items by tracking current and "changing" indices.
Web fallback with label-based selection
src/DrumPicker.tsx
Web picker derives labels via getItemLabel, uses them for selection/onChange values, exposes label strings from ref API, and renders custom renderItem cells positioned absolutely.
Virtualization layer index remapping
src/withVirtualized.tsx
Maps local sliced indices to real/global indices and supplies real item data, label, and isSelected to custom renderItem callback.
Test coverage for renderItem behavior
src/__tests__/DrumPicker.web.test.tsx, src/__tests__/DrumPickerRenderItem.test.tsx, src/__tests__/withVirtualized.test.tsx
Validates renderItem receives correct item/label/index/isSelected; verifies selection tracking during onValueChanging/onChange events; tests labeled items and virtualized index remapping.
Documentation and package metadata
README.md, CHANGELOG.md, package.json
Expands Custom item rendering section with renderItem prop details and performance notes; updates API reference tables and adds demo GIFs for controlled picker, onChange, virtualization, and date constraints; reformats Troubleshooting; adds unreleased renderItem entry; includes func-gif in npm whitelist.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🥁 A drum rolls forth with custom art,
Each item rendered from the start,
With labels drawn and indices tight,
The picker spins in verdant light!
From virtual to overlay's embrace,
Custom rendering finds its place. 🎨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Enhance DrumPicker with custom item rendering and new Row component' accurately reflects the main changes: adding a renderItem prop for custom rendering and introducing support for custom row rendering.
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/render-item

Warning

Billing warning: we have not been able to collect payment for this subscription for more than 72 hours. Please update the payment method or pay any pending invoices in Billing to avoid service interruption.


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.

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/withVirtualized.tsx (1)

236-243: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Use label-based values in virtualized mode (not raw items[...]).

These paths still emit/resolve values using raw items. With non-string items, onChange/onValueChanging can return objects and scrollToValue can fail, which conflicts with DrumPickerChangeEvent.value and non-virtualized behavior.

Suggested direction
+const labels = useMemo(() => items.map((item) => getItemLabel(item)), [items]);

- value: items[clamped] ?? '',
+ value: labels[clamped] ?? '',

- const index = items.indexOf(value);
+ const index = labels.indexOf(value);

- return items[parentIndexRef.current] ?? '';
+ return labels[parentIndexRef.current] ?? '';

- value: items[realIdx] ?? event.nativeEvent.value,
+ value: labels[realIdx] ?? event.nativeEvent.value,

Also pass labels (not items) into resolveRealIndex(...) where label matching is required.

Also applies to: 341-353, 383-389, 410-416

🤖 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 `@src/withVirtualized.tsx` around lines 236 - 243, The code currently emits raw
items (items[...]) in virtualized mode; change these emissions and any
index-resolution to use label-based values instead: when calling
onChange/onValueChanging replace value: items[clamped] with value:
labels[clamped] (or the corresponding label lookup) and when calling
resolveRealIndex(...) pass labels (not items) so label matching is used; update
the same pattern in the other locations referenced (around the blocks that call
onValueChanging, onChange, scrollToValue, and resolveRealIndex) to ensure
virtualized mode uses labels consistently and avoids returning non-string
objects.
🤖 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 `@CHANGELOG.md`:
- Around line 31-35: Update the `[Unreleased]` comparison link so its base is
`v0.2.3` instead of including already released changes; locate the
`[Unreleased]` heading/link in CHANGELOG.md and change the compare URL/anchor to
start from `v0.2.3` (ensure subsequent released entries remain unchanged) so the
diff only shows changes after v0.2.3.

In `@README.md`:
- Around line 500-503: The props table incorrectly documents DrumPicker.items as
`string[]`; update the docs to reflect the generic contract by describing
`items` as a generic array (e.g., `T[]` or `Array<T>`) and note that consumers
can pass objects when using the component's generic type parameter; update the
`Prop`/`Type` column for `items` and adjust the `Description` to mention that
the shape is determined by the generic type `T` and mapping/render props (refer
to DrumPicker.items and any object-item examples in the README to ensure
consistency).

In `@src/DrumPicker.native.tsx`:
- Line 216: The exported DrumPicker currently erases generics by using
DrumPickerProps<any>; change the forwardRef signature to preserve the generic
type parameter so consumers retain type-safety: make DrumPicker a generic
forwardRef with a type parameter T (e.g., forwardRef<DrumPickerRef,
DrumPickerProps<T>>) or export a generically-typed wrapper/cast that returns a
React.ForwardRefExoticComponent<DrumPickerProps<T> &
React.RefAttributes<DrumPickerRef>>, ensuring DrumPickerProps<T> and
renderItem/item typings are preserved.

In `@src/DrumPickerWithRenderItem.tsx`:
- Around line 43-45: currentIndex and changingIndex are only set from
selectedIndex at initialization, so when selectedIndex changes externally the
overlay (changingIndex) can get out of sync; update both state values whenever
selectedIndex changes by adding an effect that sets
setCurrentIndex(selectedIndex) and setChangingIndex(selectedIndex) inside a
useEffect that depends on selectedIndex (so liveIndex = onValueChanging != null
? changingIndex : currentIndex stays correct). Ensure the effect targets the
existing state setters currentIndex/changingIndex and does not run on every
render unnecessarily.

---

Outside diff comments:
In `@src/withVirtualized.tsx`:
- Around line 236-243: The code currently emits raw items (items[...]) in
virtualized mode; change these emissions and any index-resolution to use
label-based values instead: when calling onChange/onValueChanging replace value:
items[clamped] with value: labels[clamped] (or the corresponding label lookup)
and when calling resolveRealIndex(...) pass labels (not items) so label matching
is used; update the same pattern in the other locations referenced (around the
blocks that call onValueChanging, onChange, scrollToValue, and resolveRealIndex)
to ensure virtualized mode uses labels consistently and avoids returning
non-string objects.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 0d0c389e-73a0-4130-9c1a-38f2b8e56b3d

📥 Commits

Reviewing files that changed from the base of the PR and between db40ff7 and 5ab4cc3.

⛔ Files ignored due to path filters (8)
  • func-gif/Booking.gif is excluded by !**/*.gif
  • func-gif/Controller.gif is excluded by !**/*.gif
  • func-gif/Date.gif is excluded by !**/*.gif
  • func-gif/Debounce.gif is excluded by !**/*.gif
  • func-gif/Large-List.gif is excluded by !**/*.gif
  • func-gif/basic.gif is excluded by !**/*.gif
  • func-gif/heigh-weight.gif is excluded by !**/*.gif
  • func-gif/time.gif is excluded by !**/*.gif
📒 Files selected for processing (13)
  • CHANGELOG.md
  • README.md
  • package.json
  • src/DrumPicker.native.tsx
  • src/DrumPicker.tsx
  • src/DrumPickerWithRenderItem.tsx
  • src/__tests__/DrumPicker.web.test.tsx
  • src/__tests__/DrumPickerRenderItem.test.tsx
  • src/__tests__/withVirtualized.test.tsx
  • src/index.tsx
  • src/itemLabel.ts
  • src/types.ts
  • src/withVirtualized.tsx

Comment thread CHANGELOG.md
Comment thread README.md
Comment thread src/DrumPicker.native.tsx Outdated
);
});

export const DrumPicker = forwardRef<DrumPickerRef, DrumPickerProps<any>>(

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major | 🏗️ Heavy lift

Preserve generic item typing on exported DrumPicker instead of any.

Line 216 hard-codes DrumPickerProps<any>, which drops type-safety for items and renderItem payloads in consumer code. Please keep the forwarded component generic (or cast to a generic callable signature) so the new DrumPickerProps<T> contract is actually enforced.

🤖 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 `@src/DrumPicker.native.tsx` at line 216, The exported DrumPicker currently
erases generics by using DrumPickerProps<any>; change the forwardRef signature
to preserve the generic type parameter so consumers retain type-safety: make
DrumPicker a generic forwardRef with a type parameter T (e.g.,
forwardRef<DrumPickerRef, DrumPickerProps<T>>) or export a generically-typed
wrapper/cast that returns a React.ForwardRefExoticComponent<DrumPickerProps<T> &
React.RefAttributes<DrumPickerRef>>, ensuring DrumPickerProps<T> and
renderItem/item typings are preserved.

Comment thread src/DrumPickerWithRenderItem.tsx
…ed display name

Updated the DrumPicker component to utilize generic typing for better type safety and flexibility. Introduced a new implementation for the DrumPicker, ensuring the display name is correctly set. This refactor improves the overall structure and maintainability of the code.
@scrollDynasty
scrollDynasty merged commit 1ebcc06 into main May 28, 2026
14 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.

1 participant