Skip to content

Commit d15167c

Browse files
pierry01claude
andauthored
feat(data_table): support custom label and initial column visibility in DataTableColumnToggle (#466)
* 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 <noreply@anthropic.com> * chore(mcp): rebuild registry.json for DataTableColumnToggle * fix(data_table): apply initial column visibility on connect in DataTableColumnToggle Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * test(data_table): match checked boolean attribute regardless of order in column toggle test Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 428fcac commit d15167c

5 files changed

Lines changed: 54 additions & 10 deletions

File tree

docs/app/javascript/controllers/ruby_ui/data_table_column_visibility_controller.js

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,19 @@
22
import { Controller } from "@hotwired/stimulus";
33

44
export default class extends Controller {
5+
connect() {
6+
this.element
7+
.querySelectorAll("input[type=checkbox][data-column-key]")
8+
.forEach((checkbox) =>
9+
this._apply(checkbox.dataset.columnKey, checkbox.checked)
10+
);
11+
}
12+
513
toggle(event) {
6-
const key = event.target.dataset.columnKey;
7-
const visible = event.target.checked;
14+
this._apply(event.target.dataset.columnKey, event.target.checked);
15+
}
16+
17+
_apply(key, visible) {
818
const root = this.element.closest('[data-controller~="ruby-ui--data-table"]');
919
if (!root) return;
1020
root

gem/lib/ruby_ui/data_table/data_table_column_toggle.rb

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22

33
module RubyUI
44
class DataTableColumnToggle < Base
5-
def initialize(columns:, **attrs)
5+
def initialize(columns:, label: "Columns", **attrs)
66
@columns = columns
7+
@label = label
78
super(**attrs)
89
end
910

@@ -12,7 +13,7 @@ def view_template
1213
render RubyUI::DropdownMenu.new do
1314
render RubyUI::DropdownMenuTrigger.new do
1415
render RubyUI::Button.new(variant: :outline, size: :sm) do
15-
plain "Columns"
16+
plain @label
1617
# inline chevron-down SVG (lucide 24px, 1px stroke)
1718
svg(
1819
xmlns: "http://www.w3.org/2000/svg",
@@ -35,8 +36,11 @@ def view_template
3536
label(class: "flex items-center gap-2 rounded-sm px-2 py-1.5 text-sm cursor-pointer hover:bg-accent") do
3637
input(
3738
type: "checkbox",
38-
checked: true,
39-
class: "h-4 w-4 rounded border border-input accent-primary cursor-pointer",
39+
checked: col.fetch(:visible, true),
40+
class: [
41+
"h-4 w-4 rounded border border-input accent-primary cursor-pointer",
42+
"checked:bg-primary checked:text-primary-foreground dark:checked:bg-secondary checked:text-primary checked:border-primary"
43+
],
4044
data: {
4145
column_key: col[:key].to_s,
4246
action: "change->ruby-ui--data-table-column-visibility#toggle"

gem/lib/ruby_ui/data_table/data_table_column_visibility_controller.js

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,19 @@
22
import { Controller } from "@hotwired/stimulus";
33

44
export default class extends Controller {
5+
connect() {
6+
this.element
7+
.querySelectorAll("input[type=checkbox][data-column-key]")
8+
.forEach((checkbox) =>
9+
this._apply(checkbox.dataset.columnKey, checkbox.checked)
10+
);
11+
}
12+
513
toggle(event) {
6-
const key = event.target.dataset.columnKey;
7-
const visible = event.target.checked;
14+
this._apply(event.target.dataset.columnKey, event.target.checked);
15+
}
16+
17+
_apply(key, visible) {
818
const root = this.element.closest('[data-controller~="ruby-ui--data-table"]');
919
if (!root) return;
1020
root

gem/test/ruby_ui/data_table_column_toggle_test.rb

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,24 @@ def test_renders_dropdown_with_checkbox_per_column
1717
assert_match(/Email/, output)
1818
assert_match(/Salary/, output)
1919
end
20+
21+
def test_renders_a_custom_trigger_label
22+
output = phlex do
23+
RubyUI.DataTableColumnToggle(label: "Colunas", columns: [
24+
{key: :email, label: "Email"}
25+
])
26+
end
27+
assert_match(/Colunas/, output)
28+
end
29+
30+
def test_column_can_start_hidden
31+
output = phlex do
32+
RubyUI.DataTableColumnToggle(columns: [
33+
{key: :email, label: "Email"},
34+
{key: :salary, label: "Salary", visible: false}
35+
])
36+
end
37+
# only the visible column renders the `checked` boolean attribute on its checkbox
38+
assert_equal 1, output.scan(/\bchecked(?:\s|>)/).length
39+
end
2040
end

mcp/data/registry.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1182,11 +1182,11 @@
11821182
},
11831183
{
11841184
"path": "data_table_column_toggle.rb",
1185-
"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"
1185+
"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"
11861186
},
11871187
{
11881188
"path": "data_table_column_visibility_controller.js",
1189-
"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"
1189+
"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"
11901190
},
11911191
{
11921192
"path": "data_table_controller.js",

0 commit comments

Comments
 (0)