From ce1e46544a31b8ed674f95ae70860920e30fe69f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean=20Mendon=C3=A7a?= Date: Thu, 9 Jul 2026 12:39:55 -0300 Subject: [PATCH 1/4] feat(data_table): support custom label and initial column visibility in DataTableColumnToggle Adds a `label:` option (defaults to "Columns") so the trigger text can be localized/customized, and reads `:visible` per column so a column can start hidden (checkbox unchecked). Also adds `checked:` styling to the checkbox. Co-Authored-By: Claude Opus 4.8 --- .../data_table/data_table_column_toggle.rb | 12 +++++++---- .../ruby_ui/data_table_column_toggle_test.rb | 20 +++++++++++++++++++ 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/gem/lib/ruby_ui/data_table/data_table_column_toggle.rb b/gem/lib/ruby_ui/data_table/data_table_column_toggle.rb index ad20b217..b694b522 100644 --- a/gem/lib/ruby_ui/data_table/data_table_column_toggle.rb +++ b/gem/lib/ruby_ui/data_table/data_table_column_toggle.rb @@ -2,8 +2,9 @@ module RubyUI class DataTableColumnToggle < Base - def initialize(columns:, **attrs) + def initialize(columns:, label: "Columns", **attrs) @columns = columns + @label = label super(**attrs) end @@ -12,7 +13,7 @@ def view_template render RubyUI::DropdownMenu.new do render RubyUI::DropdownMenuTrigger.new do render RubyUI::Button.new(variant: :outline, size: :sm) do - plain "Columns" + plain @label # inline chevron-down SVG (lucide 24px, 1px stroke) svg( xmlns: "http://www.w3.org/2000/svg", @@ -35,8 +36,11 @@ def view_template label(class: "flex items-center gap-2 rounded-sm px-2 py-1.5 text-sm cursor-pointer hover:bg-accent") do input( type: "checkbox", - checked: true, - class: "h-4 w-4 rounded border border-input accent-primary cursor-pointer", + checked: col.fetch(:visible, true), + class: [ + "h-4 w-4 rounded border border-input accent-primary cursor-pointer", + "checked:bg-primary checked:text-primary-foreground dark:checked:bg-secondary checked:text-primary checked:border-primary" + ], data: { column_key: col[:key].to_s, action: "change->ruby-ui--data-table-column-visibility#toggle" diff --git a/gem/test/ruby_ui/data_table_column_toggle_test.rb b/gem/test/ruby_ui/data_table_column_toggle_test.rb index ef3c4527..5210d6b7 100644 --- a/gem/test/ruby_ui/data_table_column_toggle_test.rb +++ b/gem/test/ruby_ui/data_table_column_toggle_test.rb @@ -17,4 +17,24 @@ def test_renders_dropdown_with_checkbox_per_column assert_match(/Email/, output) assert_match(/Salary/, output) end + + def test_renders_a_custom_trigger_label + output = phlex do + RubyUI.DataTableColumnToggle(label: "Colunas", columns: [ + {key: :email, label: "Email"} + ]) + end + assert_match(/Colunas/, output) + end + + def test_column_can_start_hidden + output = phlex do + RubyUI.DataTableColumnToggle(columns: [ + {key: :email, label: "Email"}, + {key: :salary, label: "Salary", visible: false} + ]) + end + # only the visible column renders the `checked` attribute on its checkbox + assert_equal 1, output.scan("checked class=").length + end end From bf60b9864565c189d890c036049387387e787b3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean=20Mendon=C3=A7a?= Date: Thu, 9 Jul 2026 14:55:06 -0300 Subject: [PATCH 2/4] chore(mcp): rebuild registry.json for DataTableColumnToggle --- mcp/data/registry.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mcp/data/registry.json b/mcp/data/registry.json index 0c83744e..ab1f3879 100644 --- a/mcp/data/registry.json +++ b/mcp/data/registry.json @@ -1182,7 +1182,7 @@ }, { "path": "data_table_column_toggle.rb", - "content": "# frozen_string_literal: true\n\nmodule RubyUI\n class DataTableColumnToggle < Base\n def initialize(columns:, **attrs)\n @columns = columns\n super(**attrs)\n end\n\n def view_template\n div(**attrs) do\n render RubyUI::DropdownMenu.new do\n render RubyUI::DropdownMenuTrigger.new do\n render RubyUI::Button.new(variant: :outline, size: :sm) do\n plain \"Columns\"\n # inline chevron-down SVG (lucide 24px, 1px stroke)\n svg(\n xmlns: \"http://www.w3.org/2000/svg\",\n width: \"16\",\n height: \"16\",\n viewBox: \"0 0 24 24\",\n fill: \"none\",\n stroke: \"currentColor\",\n stroke_width: \"2\",\n stroke_linecap: \"round\",\n stroke_linejoin: \"round\",\n class: \"w-4 h-4 ml-1\"\n ) do |s|\n s.polyline(points: \"6 9 12 15 18 9\")\n end\n end\n end\n render RubyUI::DropdownMenuContent.new do\n @columns.each do |col|\n label(class: \"flex items-center gap-2 rounded-sm px-2 py-1.5 text-sm cursor-pointer hover:bg-accent\") do\n input(\n type: \"checkbox\",\n checked: true,\n class: \"h-4 w-4 rounded border border-input accent-primary cursor-pointer\",\n data: {\n column_key: col[:key].to_s,\n action: \"change->ruby-ui--data-table-column-visibility#toggle\"\n }\n )\n span { plain col[:label] }\n end\n end\n end\n end\n end\n end\n\n private\n\n def default_attrs\n {\n class: \"relative\",\n data: {controller: \"ruby-ui--data-table-column-visibility\"}\n }\n end\n end\nend\n" + "content": "# frozen_string_literal: true\n\nmodule RubyUI\n class DataTableColumnToggle < Base\n def initialize(columns:, label: \"Columns\", **attrs)\n @columns = columns\n @label = label\n super(**attrs)\n end\n\n def view_template\n div(**attrs) do\n render RubyUI::DropdownMenu.new do\n render RubyUI::DropdownMenuTrigger.new do\n render RubyUI::Button.new(variant: :outline, size: :sm) do\n plain @label\n # inline chevron-down SVG (lucide 24px, 1px stroke)\n svg(\n xmlns: \"http://www.w3.org/2000/svg\",\n width: \"16\",\n height: \"16\",\n viewBox: \"0 0 24 24\",\n fill: \"none\",\n stroke: \"currentColor\",\n stroke_width: \"2\",\n stroke_linecap: \"round\",\n stroke_linejoin: \"round\",\n class: \"w-4 h-4 ml-1\"\n ) do |s|\n s.polyline(points: \"6 9 12 15 18 9\")\n end\n end\n end\n render RubyUI::DropdownMenuContent.new do\n @columns.each do |col|\n label(class: \"flex items-center gap-2 rounded-sm px-2 py-1.5 text-sm cursor-pointer hover:bg-accent\") do\n input(\n type: \"checkbox\",\n checked: col.fetch(:visible, true),\n class: [\n \"h-4 w-4 rounded border border-input accent-primary cursor-pointer\",\n \"checked:bg-primary checked:text-primary-foreground dark:checked:bg-secondary checked:text-primary checked:border-primary\"\n ],\n data: {\n column_key: col[:key].to_s,\n action: \"change->ruby-ui--data-table-column-visibility#toggle\"\n }\n )\n span { plain col[:label] }\n end\n end\n end\n end\n end\n end\n\n private\n\n def default_attrs\n {\n class: \"relative\",\n data: {controller: \"ruby-ui--data-table-column-visibility\"}\n }\n end\n end\nend\n" }, { "path": "data_table_column_visibility_controller.js", From ddc4733b0ab1e3497a95ccd6583784ca80d6aec4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean=20Mendon=C3=A7a?= Date: Thu, 9 Jul 2026 19:04:40 -0300 Subject: [PATCH 3/4] fix(data_table): apply initial column visibility on connect in DataTableColumnToggle Co-Authored-By: Claude Opus 4.8 --- .../data_table_column_visibility_controller.js | 14 ++++++++++++-- .../data_table_column_visibility_controller.js | 14 ++++++++++++-- mcp/data/registry.json | 2 +- 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/docs/app/javascript/controllers/ruby_ui/data_table_column_visibility_controller.js b/docs/app/javascript/controllers/ruby_ui/data_table_column_visibility_controller.js index d3cb0584..c4b3f629 100644 --- a/docs/app/javascript/controllers/ruby_ui/data_table_column_visibility_controller.js +++ b/docs/app/javascript/controllers/ruby_ui/data_table_column_visibility_controller.js @@ -2,9 +2,19 @@ import { Controller } from "@hotwired/stimulus"; export default class extends Controller { + connect() { + this.element + .querySelectorAll("input[type=checkbox][data-column-key]") + .forEach((checkbox) => + this._apply(checkbox.dataset.columnKey, checkbox.checked) + ); + } + toggle(event) { - const key = event.target.dataset.columnKey; - const visible = event.target.checked; + this._apply(event.target.dataset.columnKey, event.target.checked); + } + + _apply(key, visible) { const root = this.element.closest('[data-controller~="ruby-ui--data-table"]'); if (!root) return; root diff --git a/gem/lib/ruby_ui/data_table/data_table_column_visibility_controller.js b/gem/lib/ruby_ui/data_table/data_table_column_visibility_controller.js index d3cb0584..c4b3f629 100644 --- a/gem/lib/ruby_ui/data_table/data_table_column_visibility_controller.js +++ b/gem/lib/ruby_ui/data_table/data_table_column_visibility_controller.js @@ -2,9 +2,19 @@ import { Controller } from "@hotwired/stimulus"; export default class extends Controller { + connect() { + this.element + .querySelectorAll("input[type=checkbox][data-column-key]") + .forEach((checkbox) => + this._apply(checkbox.dataset.columnKey, checkbox.checked) + ); + } + toggle(event) { - const key = event.target.dataset.columnKey; - const visible = event.target.checked; + this._apply(event.target.dataset.columnKey, event.target.checked); + } + + _apply(key, visible) { const root = this.element.closest('[data-controller~="ruby-ui--data-table"]'); if (!root) return; root diff --git a/mcp/data/registry.json b/mcp/data/registry.json index ab1f3879..f94f438a 100644 --- a/mcp/data/registry.json +++ b/mcp/data/registry.json @@ -1186,7 +1186,7 @@ }, { "path": "data_table_column_visibility_controller.js", - "content": "// app/javascript/controllers/ruby_ui/data_table_column_visibility_controller.js\nimport { Controller } from \"@hotwired/stimulus\";\n\nexport default class extends Controller {\n toggle(event) {\n const key = event.target.dataset.columnKey;\n const visible = event.target.checked;\n const root = this.element.closest('[data-controller~=\"ruby-ui--data-table\"]');\n if (!root) return;\n root\n .querySelectorAll(`[data-column=\"${key}\"]`)\n .forEach((el) => el.classList.toggle(\"hidden\", !visible));\n }\n}\n" + "content": "// app/javascript/controllers/ruby_ui/data_table_column_visibility_controller.js\nimport { Controller } from \"@hotwired/stimulus\";\n\nexport default class extends Controller {\n connect() {\n this.element\n .querySelectorAll(\"input[type=checkbox][data-column-key]\")\n .forEach((checkbox) =>\n this._apply(checkbox.dataset.columnKey, checkbox.checked)\n );\n }\n\n toggle(event) {\n this._apply(event.target.dataset.columnKey, event.target.checked);\n }\n\n _apply(key, visible) {\n const root = this.element.closest('[data-controller~=\"ruby-ui--data-table\"]');\n if (!root) return;\n root\n .querySelectorAll(`[data-column=\"${key}\"]`)\n .forEach((el) => el.classList.toggle(\"hidden\", !visible));\n }\n}\n" }, { "path": "data_table_controller.js", From c2f03adc0e5a2c9abee13a396f195efa944e8cdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean=20Mendon=C3=A7a?= Date: Thu, 9 Jul 2026 20:55:01 -0300 Subject: [PATCH 4/4] test(data_table): match checked boolean attribute regardless of order in column toggle test Co-Authored-By: Claude Opus 4.8 --- gem/test/ruby_ui/data_table_column_toggle_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gem/test/ruby_ui/data_table_column_toggle_test.rb b/gem/test/ruby_ui/data_table_column_toggle_test.rb index 5210d6b7..e4311279 100644 --- a/gem/test/ruby_ui/data_table_column_toggle_test.rb +++ b/gem/test/ruby_ui/data_table_column_toggle_test.rb @@ -34,7 +34,7 @@ def test_column_can_start_hidden {key: :salary, label: "Salary", visible: false} ]) end - # only the visible column renders the `checked` attribute on its checkbox - assert_equal 1, output.scan("checked class=").length + # only the visible column renders the `checked` boolean attribute on its checkbox + assert_equal 1, output.scan(/\bchecked(?:\s|>)/).length end end