Skip to content

feat(data_table): Hotwire-first DataTable component family#457

Open
djalmaaraujo wants to merge 81 commits intomainfrom
da/datatable-hotwire
Open

feat(data_table): Hotwire-first DataTable component family#457
djalmaaraujo wants to merge 81 commits intomainfrom
da/datatable-hotwire

Conversation

@djalmaaraujo
Copy link
Copy Markdown
Contributor

@djalmaaraujo djalmaaraujo commented Apr 24, 2026

Summary

WhatsApp.Video.2026-04-24.at.17.06.57.mp4

Adds a server-first DataTable inspired by shadcn's composition, powered entirely by Turbo Frames and Stimulus — no external JS table library.

  • 14 new components under app/components/ruby_ui/data_table/
  • 3 Stimulus controllers (selection/bulk-actions/expand, column visibility, debounced search)
  • 3 pagination adapters (Manual, Pagy, Kaminari — duck-typed, no gem deps)
  • Form-first bulk actions: row checkboxes are <input name="ids[]">, bulk submit buttons use HTML5 form= + formaction=
  • 6 documentation examples + pagination adapters reference at /docs/data_table
  • Demo controller/view at /docs/data_table_demo
  • 124 tests (components + adapters + controller integration)

Key decisions

  • Reuses existing Table, Checkbox, NativeSelect, Pagination, DropdownMenu, Input, Button, Badge, lucide-rails primitives — row + select-all checkboxes render RubyUI::Checkbox internally; per-page select renders RubyUI::NativeSelect.
  • Configurable query params per component (name:, sort_param:, direction_param:, page_param:).
  • Configurable pagination window (window: N) + adapter pattern (pagy: / kaminari: / with: custom_adapter).
  • Debounced search (300ms default, configurable via debounce:, false disables).
  • Focus + cursor restored after Turbo Frame swap via module-level map surviving Stimulus disconnect/reconnect.
  • Expand toggle delegated to root Stimulus controller (one listener handles all rows).
  • Sidebar label renders "Data Table" (matches "Alert Dialog" / "Date Picker" convention); Ruby class stays DataTable.

Video

Video walkthrough:

Test plan

  • bin/rails test green
  • bundle exec standardrb clean
  • Visit /docs/data_table — all 6 examples + pagination adapters section render
  • Live demo at /docs/data_table_demo: debounced search filters (focus survives swap), sort cycles, per-page switches, numbered pagination navigates, select-all works (indeterminate state), bulk Delete/Export submit, column toggle hides/shows, row expand reveals detail with arrow rotation, row actions dropdown opens

@djalmaaraujo djalmaaraujo requested a review from cirdes as a code owner April 24, 2026 19:36
@djalmaaraujo djalmaaraujo marked this pull request as draft April 24, 2026 19:51
@djalmaaraujo djalmaaraujo marked this pull request as ready for review April 24, 2026 20:33
@djalmaaraujo djalmaaraujo force-pushed the da/datatable-hotwire branch from 1e88333 to 7833312 Compare April 24, 2026 20:36
Adds the brainstorm-approved design document for the DataTable component
family. Covers components, Stimulus controllers, Turbo wiring, pagination
adapter pattern, configurable query params, icon strategy, test scope, and
six documentation examples.
TDD, incremental commits per task. Enforces subagent model
(claude-sonnet-4-6, low effort) for all implementation steps.
Adds /vendor/bundle to .gitignore (local BUNDLE_PATH install). Fixes
plan's dx helper node path to 25.8.2 and records baseline test count.
Reused from Cirdes's branch (commit 36a61e8) verbatim for demo
fixture parity.
Normalizes page/per_page/total_count inputs; total_pages >= 1.
Places adapter in app/components/ruby_ui/data_table_pagination/ (Option A)
and excludes that directory from the Zeitwerk collapse glob so that
RubyUI::DataTablePagination resolves as a proper module namespace.
After Task 6 landed, file paths for adapters are data_table_pagination/*
(not data_table/pagination/*) to produce RubyUI::DataTablePagination::X
without collapse issues. Patches Tasks 7/8 paths.
Duck-typed wrapper — does not add pagy gem as a dependency.
Duck-typed wrapper — no gem dependency added.
Renders <turbo-frame> wrapping a <form> with the ruby-ui--data-table
Stimulus controller. Form supports form-first bulk actions via button
formaction.
respond_to? check replaces bare rescue so unexpected errors surface
instead of being swallowed. New test guards against silent token drop.
Reverts raw <input> to RubyUI::Input for styling + behavior parity
with Cirdes's reference. Tests use simpler regexes that tolerate
Input's data-action attribute.
Wraps TableHead with sort link. Uses lucide-rails file-based icons
(chevron-up/chevron-down/chevrons-up-down). Configurable param names.
Native <input name=ids[] value=ID> so bulk actions submit via <form>
without custom fetch. Default aria-label="Select row N".
Reuses DropdownMenu + Button. Own Stimulus controller for column
visibility toggle.
Frees the DataTablePagination namespace for the component class.
Base Pagination has mx-auto w-full justify-center (sensible default
elsewhere). DataTablePagination overrides with mx-0 w-auto
justify-end so it sits flush on the right inside
DataTablePaginationBar.
@djalmaaraujo djalmaaraujo force-pushed the da/datatable-hotwire branch from 7833312 to ab6b111 Compare April 24, 2026 20:39
Copy link
Copy Markdown
Contributor

@cirdes cirdes left a comment

Choose a reason for hiding this comment

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

The PR looks amazing, let's merge it!

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