Skip to content

Feat/tier 1 improvements - #3

Open
Abdullajon1881 wants to merge 6 commits into
scrollDynasty:mainfrom
Abdullajon1881:feat/tier-1-improvements
Open

Feat/tier 1 improvements#3
Abdullajon1881 wants to merge 6 commits into
scrollDynasty:mainfrom
Abdullajon1881:feat/tier-1-improvements

Conversation

@Abdullajon1881

@Abdullajon1881 Abdullajon1881 commented May 25, 2026

Copy link
Copy Markdown

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

Release Notes

  • New Features

    • Added TimeDrumPicker component for flexible time selection with 12/24-hour format support and configurable minute/second intervals.
    • Introduced web fallback platform support with SSR-safe HTML <select> rendering.
    • Extended drum picker items to support labeled objects with separate display labels and typed values, while maintaining backward compatibility with string arrays.
    • Added utility functions for time conversions and formatting.
  • Documentation

    • Expanded README with TimeDrumPicker documentation, web fallback guidance, and labeled items usage examples.
  • Tests

    • Added comprehensive test coverage for labeled items, web fallback, and time picker functionality.
  • Chores

    • Cleaned up local CodeQL database artifacts.

Review Change Stack

The codeql-db-js/ directory is a local CodeQL CLI artifact (database
snapshot produced by `codeql database create`). It is not source and
should not be in version control — it bloats clones and re-creating
it is the standard local workflow.

- Add codeql-db-js/ and codeql-db-*/ to .gitignore
- Untrack the currently checked-in copy
DrumPicker now accepts either string items (existing behavior) or labeled
items of the form { label, value } where `value` can be any typed payload
(string, number, enum, object). The label is what the wheel displays; the
resolved `value` is surfaced on `onChange` as `nativeEvent.item`, fully
typed via the new generic parameter on DrumPicker<T>.

Why: callers currently have to maintain a parallel lookup array to map the
selected index/label back to a domain id. This is the single most common
ergonomics ask for picker components and removes a class of off-by-one
bugs when the items list changes.

Implementation notes:
- The native Fabric spec still receives string[] only. The JS wrapper
  extracts labels via getItemLabel() before handing to native and looks
  up the resolved value via getItemValue() when the native onValueChange
  fires.
- Fully back-compatible: existing string[] callers see no change at
  runtime. The native event payload gains an `item` field; for plain
  strings, `item === value`, so any code reading either keeps working.
- Date/Time wrappers are unchanged — they already pass strings.

Tests: 6 new behavior tests cover string back-compat, labeled items,
typed object values, mixed arrays, and out-of-bounds index fallback.
Full suite: 42/42 green.
Previously, importing DrumPicker on web (Expo Web, react-native-web, or
any SSR context) threw at module load. This made the package unsafe to
import from cross-platform code without a Platform.OS branch at every
import site.

The web variant (src/DrumPicker.tsx) now renders a real HTML <select>
element:

- SSR-safe: no throw at module evaluation
- Keyboard-navigable and screen-reader-friendly by default (browser
  semantics)
- Honors the same value/onChange contract as native — callers read
  event.nativeEvent.index and event.nativeEvent.value identically
- Mirrors native's "no duplicate-index re-emit" behavior so controlled
  state loops are avoided

Props that translate to web (items, selectedIndex, textColor,
selectedTextColor, textSize, backgroundColor, visibleItemCount as
<select size>, testID, onChange, style.height/width) are honored.
Props that do not translate (haptics, custom indicators) are accepted
and ignored — no warnings on the hot path.

A full drum-style scroll wheel on web is a separate, larger feature.
This patch establishes a correct baseline so the lib is usable from
shared cross-platform components today.

Tests:
- 8 new tests in DrumPickerWeb.test.tsx exercise the fallback via
  react-test-renderer (imported with explicit .tsx extension to bypass
  the react-native preset's .native.tsx resolution).
- Full suite: 44/44 green (36 prior + 8 new).

No native code changed; android-build, ios-build, android-instrumented,
and ios-unit-tests CI jobs are unaffected.
Composes existing DrumPicker primitives (no native changes) to provide a
time-of-day picker that mirrors UIDatePicker.time behavior:

- Six modes: hour, minute, hour-minute, hour-minute-second,
  hour-minute-period (12h), hour-minute-second-period
- Always-24h value contract on value / onChange; component handles 12h
  display + AM/PM internally
- minuteInterval and secondInterval (1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 30)
  matching iOS UIDatePicker
- padWithZero, custom amLabel / pmLabel for localization
- Controlled + uncontrolled, with the same clamp-and-notify contract as
  DateDrumPicker (out-of-range minute snaps and emits once)
- Per-column testIDs and styles (hour / minute / second / period)

Pure TypeScript, reuses DrumPicker so no Android or iOS code is touched.
Adds 50 jest tests covering logic and component behavior; full suite is
86/86 green.
@coderabbitai

coderabbitai Bot commented May 25, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

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

More reviews will be available in 58 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: 5cd48d0a-c03e-4725-b799-59bf8d31d445

📥 Commits

Reviewing files that changed from the base of the PR and between fb50e98 and 85d5395.

⛔ Files ignored due to path filters (1)
  • src/__tests__/snapshots/__snapshots__/DateDrumPicker.snapshot.test.tsx.snap is excluded by !**/*.snap
📒 Files selected for processing (12)
  • README.md
  • example/src/App.tsx
  • src/DateDrumPicker.tsx
  • src/DrumPicker.native.tsx
  • src/DrumPicker.tsx
  • src/TimeDrumPicker.tsx
  • src/__tests__/DrumPickerWeb.test.tsx
  • src/__tests__/accessibility.test.tsx
  • src/__tests__/timeDrumPickerLogic.test.ts
  • src/drumPickerDefaults.ts
  • src/timeDrumPickerLogic.ts
  • src/types.ts
📝 Walkthrough

Walkthrough

The PR extends the drum picker library with generic typed items, a web fallback for SSR environments, and a new composed TimeDrumPicker component for time selection. DrumPicker now accepts generic item arrays with optional labeled objects, and both native and web implementations emit resolved typed values. TimeDrumPicker combines multiple drum columns for hour/minute/second/AM-PM selection with 12h↔24h conversion.

Changes

Drum Picker Generics + Time Picker Feature

Layer / File(s) Summary
Type system: generic items and labeled item model
src/types.ts
DrumPickerItem<T> union supports both string items and labeled objects { label, value }. Generic DrumPickerChangeEvent<T> carries the resolved item: T. Helpers getItemLabel and getItemValue normalize items into display text and typed values.
DrumPicker native: generic items and labeled item resolution
src/DrumPicker.native.tsx
Native DrumPicker becomes generic <T = string>, memoizes display labels from items, stores items in a ref, and emits change events where nativeEvent.item is resolved to type T via getItemValue.
DrumPicker web: HTML select fallback
src/DrumPicker.tsx
New web implementation renders an HTML <select> with <option> items instead of throwing. Handles selection clamping, de-duplication by index, and synthesizes native-like nativeEvent payloads with resolved item values. Supports SSR and accessibility via aria-label and data-testid.
Time logic: conversion, clamping, normalization, and builders
src/timeDrumPickerLogic.ts
Pure utility functions for time: normalizing intervals, clamping hours/minutes/seconds, snapping to intervals, converting between 24-hour and 12-hour (AM/PM) formats, and building picker item labels and index mappings.
TimeDrumPicker: composed time picker component
src/TimeDrumPicker.tsx
Composed wrapper rendering a row of DrumPicker columns (hour/minute/second/period) with configurable intervals, hour format, padding, and haptics. Manages controlled/uncontrolled state, clamps time values, handles 12h↔24h display conversion, and emits clamped updates via onChange.
Tests: DrumPicker labeled items, web fallback, TimeDrumPicker, time logic
src/__tests__/DrumPicker.labeledItems.test.tsx, src/__tests__/DrumPicker.test.tsx, src/__tests__/DrumPickerWeb.test.tsx, src/__tests__/TimeDrumPicker.test.tsx, src/__tests__/timeDrumPickerLogic.test.ts
Comprehensive test coverage: labeled item resolution for native/web, web <select> rendering and interaction, TimeDrumPicker per-mode rendering and hour conversion logic, and all time logic helpers (intervals, clamping, snapping, 12/24-hour round-trips).
Documentation and public API exports
README.md, src/index.tsx, .gitignore
README expanded with web fallback section, labeled items usage, and TimeDrumPicker documentation. Barrel exports now include TimeDrumPicker component, time utility functions, and broader type surface. Generated CodeQL database artifacts added to gitignore.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant DrumPicker
  participant Types as getItemLabel/<br/>getItemValue
  participant NativeModule as Native Module<br/>(or HTMLSelect)
  
  User->>DrumPicker: Pass items: { label, value }[]
  DrumPicker->>Types: Extract labels for display
  DrumPicker->>NativeModule: Render with labels
  User->>NativeModule: Select item at index
  NativeModule-->>DrumPicker: Emit index + native value
  DrumPicker->>Types: Resolve item value via getItemValue
  DrumPicker-->>User: onChange with nativeEvent.item: T
Loading
sequenceDiagram
  participant User
  participant TimeDrumPicker
  participant TimeLogic
  participant DrumPicker
  
  User->>TimeDrumPicker: Set value: { hour, minute, second }
  TimeDrumPicker->>TimeLogic: Clamp and normalize hour/minute/second
  TimeLogic-->>TimeDrumPicker: Clamped values + period (for 12h)
  TimeDrumPicker->>DrumPicker: Render columns with clamped indices
  User->>DrumPicker: Spin hour column
  DrumPicker-->>TimeDrumPicker: onValueChange with index
  TimeDrumPicker->>TimeLogic: Convert index to 24h hour + snap minute
  TimeLogic-->>TimeDrumPicker: Updated hour/minute/second
  TimeDrumPicker-->>User: onChange with clamped 24h value
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🥁 A drum spins round with value's grace,
No label left behind in its place,
On web a humble <select> does rest,
While Time drums click: 12, 24, and blessed,
Generic beats now make the melody!

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Feat/tier 1 improvements' is vague and does not convey meaningful information about the primary changes in the changeset. Replace with a specific title describing the main features added, such as 'Add TimeDrumPicker component with labeled item support and web fallback' or 'Add generic DrumPicker with TimeDrumPicker and web implementation'.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
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.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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: 3

Caution

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

⚠️ Outside diff range comments (1)
README.md (1)

281-297: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Document nativeEvent.item in the API table to match the new contract.

The onChange row still shows nativeEvent: { index, value }, but the new labeled-items contract also exposes nativeEvent.item. Please update this row so the API reference is consistent.

🤖 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 `@README.md` around lines 281 - 297, Update the README API table row for
onChange to reflect the new event contract: change the nativeEvent shape from {
index, value } to { index, value, item } so labeled items expose the full item
object; edit the `onChange` row in the props table (the line currently showing
`nativeEvent: { index, value }`) to `nativeEvent: { index, value, item }` and
adjust any brief description if present to mention the new `item` field.
🤖 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 `@src/DrumPicker.tsx`:
- Around line 176-183: The hardcoded 'aria-label': 'Picker' makes every
DrumPicker column identical to assistive tech; update the select element to use
a per-instance accessibility prop (e.g. accept an accessibilityLabel or
ariaLabel prop on the DrumPicker component) and set the element's
aria-label/aria-labelledby to that prop (falling back to the existing testID or
a sensible default) instead of the constant string; locate the select render in
DrumPicker.tsx (the block using safeIndex, handleChange, visibleItemCount,
testID) and wire the new prop through to the select so each
DateDrumPicker/TimeDrumPicker column can provide a distinct label.

In `@src/timeDrumPickerLogic.ts`:
- Around line 69-83: The function snapToInterval currently uses Math.round which
breaks the documented "ties round down" behavior (e.g., 3 with interval 2 goes
to 4); change the snapping logic in snapToInterval to compute the nearest
multiple using floor-based tie-breaking instead of Math.round — e.g., divide
safe by interval and use Math.floor with an adjustment that treats exact halves
as rounding down (for example subtracting a tiny epsilon or using an
integer-safe formula) before multiplying back by interval, then keep the
existing clamping to 0..max; leave clampMinute, interval checks and final
min/max logic intact.

In `@src/types.ts`:
- Around line 16-17: DrumPickerItem<T> currently permits raw string items for
all T, which lets DrumPicker<number> accept plain strings and breaks the
onChange types; update the DrumPickerItem generic so that raw string items are
only allowed when T is string (e.g. conditionally allow string |
DrumPickerLabeledItem<T> only for T = string), otherwise require
DrumPickerLabeledItem<T>; change the type alias named DrumPickerItem and verify
usages in DrumPicker and any onChange/nativeEvent typing still reflect the
tighter constraint.

---

Outside diff comments:
In `@README.md`:
- Around line 281-297: Update the README API table row for onChange to reflect
the new event contract: change the nativeEvent shape from { index, value } to {
index, value, item } so labeled items expose the full item object; edit the
`onChange` row in the props table (the line currently showing `nativeEvent: {
index, value }`) to `nativeEvent: { index, value, item }` and adjust any brief
description if present to mention the new `item` field.
🪄 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: dc336822-0c51-4d4a-8bb9-482f0c1d0152

📥 Commits

Reviewing files that changed from the base of the PR and between 0e2eed1 and fb50e98.

⛔ Files ignored due to path filters (1)
  • codeql-db-js/log/database-create-20260523.162940.875.log is excluded by !**/*.log
📒 Files selected for processing (18)
  • .gitignore
  • README.md
  • codeql-db-js/baseline-info.json
  • codeql-db-js/codeql-database.yml
  • codeql-db-js/diagnostic/cli-diagnostics-add-20260523T112944.961Z.json
  • codeql-db-js/diagnostic/cli-diagnostics-add-20260523T120803.711Z.json
  • codeql-db-js/diagnostic/extractors/javascript/autobuilder-1.jsonl
  • src/DrumPicker.native.tsx
  • src/DrumPicker.tsx
  • src/TimeDrumPicker.tsx
  • src/__tests__/DrumPicker.labeledItems.test.tsx
  • src/__tests__/DrumPicker.test.tsx
  • src/__tests__/DrumPickerWeb.test.tsx
  • src/__tests__/TimeDrumPicker.test.tsx
  • src/__tests__/timeDrumPickerLogic.test.ts
  • src/index.tsx
  • src/timeDrumPickerLogic.ts
  • src/types.ts
💤 Files with no reviewable changes (5)
  • codeql-db-js/baseline-info.json
  • codeql-db-js/diagnostic/cli-diagnostics-add-20260523T120803.711Z.json
  • codeql-db-js/codeql-database.yml
  • codeql-db-js/diagnostic/cli-diagnostics-add-20260523T112944.961Z.json
  • codeql-db-js/diagnostic/extractors/javascript/autobuilder-1.jsonl

Comment thread src/DrumPicker.tsx
Comment thread src/timeDrumPickerLogic.ts
Comment thread src/types.ts Outdated
@scrollDynasty

Copy link
Copy Markdown
Owner

Code review — Tier 1 improvements

Thanks for the work on labeled items, web fallback, and TimeDrumPicker. The JS layer, tests, and README updates are generally in good shape. Below is structured feedback before merge.


Summary

Area Assessment
Labeled items ({ label, value }) Correct design — labels to native, item resolved in JS
Web fallback (DrumPicker.tsx) Standard .native.tsx / .tsx split; SSR-safe <select>
TimeDrumPicker + logic module Solid separation; good unit test coverage
Native Android / iOS Unchanged for tier-1 — expected
Example app Not updated — hard to verify on device from the repo

Overall the PR delivers what it claims; the main gaps are example demos, line endings / lint, and PR metadata.


What looks good

  • Labeled itemsitems.map(getItemLabel) to native and enriched onChange.nativeEvent.item in DrumPicker.native.tsx is the right approach without bridge changes.
  • Web — explicit tests importing DrumPicker.tsx avoid Jest resolving only .native.tsx.
  • TimeDrumPickertimeDrumPickerLogic.ts is testable and well covered.
  • README — platform table and honest web fallback description (<select>, not a drum wheel).
  • Repo hygiene — CodeQL DB removed from git + .gitignore.

High priority

1. Example app has no demos for new APIs

Changes are almost entirely under src/, tests, and README. There is nothing in example/ for:

  • labeled DrumPicker items
  • TimeDrumPicker

Ask: Please add a small demo (e.g. a tab or section with one labeled picker and one TimeDrumPicker) so maintainers can verify on a real device without writing custom code.


2. Lint — CRLF line endings

Several files under src/** fail Prettier with Delete ␍ (CRLF vs LF). This can break lint-and-typecheck on CI.

Fix:

yarn eslint "**/*.{js,ts,tsx}" --fix

Then commit with LF line endings (editor: LF for the repo).


3. PR description and checklist

The PR body is empty and checklist items (tested on Android/iOS, etc.) are unchecked.

Ask: Please fill in:

  • what was added (labeled items, web fallback, TimeDrumPicker)
  • what was tested manually
  • what was not tested yet (e.g. no example app update)

Medium priority

4. Web UX expectations

On web, DrumPicker renders a native HTML <select>, not a scroll wheel. README documents this — please keep it that way in docs so users do not expect drum animation on web.


5. Labeled items scope

TimeDrumPicker and DateDrumPicker still use plain string columns only. Labeled { label, value } items apply to the base DrumPicker only.

Suggestion: Add one sentence in README under labeled items so consumers do not expect labeled API on date/time pickers yet.


6. Comment drift in types.ts

A comment suggests Date/Time wrappers use getItemLabel, but DateDrumPicker still passes strings from builders like buildMonthItems without labeled items.

Suggestion: Align the comment with actual usage or use getItemLabel consistently if you plan to support labeled items there later.


7. TimeDrumPicker controlled value

When value is controlled, clamping in useEffect may call onChange on mount if the passed value is out of range. This may be intentional but can surprise callers.

Suggestion: Document this behavior in README or prop docs.


8. Duplicated DEFAULTS

Default prop values are duplicated in DrumPicker.tsx and DrumPicker.native.tsx. They match today; a shared constants module would prevent web/native drift later.


Low priority

  • Full drum-style scroll on web is correctly scoped as a future enhancement.
  • No native Kotlin/Swift changes for tier-1 labeled items — no action needed; design is correct.

Pre-merge checklist

Please confirm before merge:

  • yarn lint passes (LF / Prettier fixed)
  • yarn test passes
  • yarn build and yarn typecheck pass
  • Example demo for new APIs or a linked follow-up issue
  • PR description and testing notes filled in

Re-review

Happy to take another look after the example demo and lint fixes are pushed.

CodeRabbit:
- snapToInterval now rounds half-step ties DOWN as documented (was
  Math.round, which rounds halves up); added explicit tie-break tests.
- DrumPickerItem<T> tightened so plain string items are only allowed when
  T = string. DrumPicker<number> now requires {label, value} pairs, making
  the generic onChange.item contract sound.
- aria-label is no longer hardcoded "Picker": new accessibilityLabel prop
  flows to the native view and the web <select>. DateDrumPicker and
  TimeDrumPicker give each column a distinct default label
  (Day/Month/Year, Hour/Minute/Second/AM-PM) via columnAccessibilityLabels.

Maintainer (scrollDynasty):
- Example app: added "Labeled" (typed-value DrumPicker) and "TimePicker"
  (TimeDrumPicker) demo tabs so the new APIs are verifiable on device.
- Extracted shared DRUM_PICKER_DEFAULTS so web and native cannot drift.
- Fixed misleading getItemLabel comment in types.ts.
- README: documented labeled-items scope (DrumPicker only), the
  TimeDrumPicker controlled clamp-and-notify behavior, and added an
  Accessibility section.

Verification: typecheck, build, and 110 jest tests across 11 suites all
pass locally (added accessibility + tie-break coverage). eslint clean on
src (the only local failures are CRLF from the Windows working copy; the
committed blobs are LF).
- Remove stray scratch text that leaked into the labeled-items section
  during an earlier edit.
- Add the labeled-items scope note (DrumPicker only; Date/Time pickers use
  their own structured onChange).
- Add an Accessibility section documenting accessibilityLabel and per-column
  columnAccessibilityLabels.
@scrollDynasty

Copy link
Copy Markdown
Owner

@copilot resolve the merge conflicts in this pull request

@scrollDynasty

Copy link
Copy Markdown
Owner

@Abdullajon1881 исправь конфликты

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