Skip to content

Commit e1f0824

Browse files
authored
fix(date-picker): add installable component (#380)
1 parent b56b7d8 commit e1f0824

6 files changed

Lines changed: 153 additions & 13 deletions

File tree

docs/app/views/docs/date_picker.rb

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,7 @@ def view_template
1111

1212
render Docs::VisualCodeExample.new(title: "Single Date", context: self) do
1313
<<~RUBY
14-
div(class: 'space-y-4 w-[260px]') do
15-
Popover(options: { trigger: 'click' }) do
16-
PopoverTrigger(class: 'w-full') do
17-
div(class: 'grid w-full max-w-sm items-center gap-1.5') do
18-
label(for: "date") { "Select a date" }
19-
Input(type: 'string', placeholder: "Select a date", class: 'rounded-md border shadow', id: 'date', data_controller: 'ruby-ui--calendar-input')
20-
end
21-
end
22-
PopoverContent do
23-
Calendar(input_id: '#date')
24-
end
25-
end
26-
end
14+
DatePicker(id: "date")
2715
RUBY
2816
end
2917

gem/lib/generators/ruby_ui/dependencies.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,12 @@ context_menu:
5252
js_packages:
5353
- "tippy.js"
5454

55+
date_picker:
56+
components:
57+
- "Input"
58+
- "Popover"
59+
- "Calendar"
60+
5561
data_table:
5662
components:
5763
- "Button"
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# frozen_string_literal: true
2+
3+
require "securerandom"
4+
5+
module RubyUI
6+
class DatePicker < Base
7+
def initialize(
8+
id: nil,
9+
name: nil,
10+
label: "Select a date",
11+
value: nil,
12+
placeholder: "Select a date",
13+
selected_date: value,
14+
date_format: "yyyy-MM-dd",
15+
popover_options: {},
16+
input_attrs: {},
17+
calendar_attrs: {},
18+
trigger_attrs: {},
19+
content_attrs: {},
20+
**attrs
21+
)
22+
@id = id || "date-picker-#{SecureRandom.hex(4)}"
23+
@name = name
24+
@label = label
25+
@value = value || selected_date&.to_s
26+
@placeholder = placeholder
27+
@selected_date = selected_date
28+
@date_format = date_format
29+
@popover_options = {trigger: "click"}.merge(popover_options)
30+
@input_attrs = input_attrs
31+
@calendar_attrs = calendar_attrs
32+
@trigger_attrs = trigger_attrs
33+
@content_attrs = content_attrs
34+
super(**attrs)
35+
end
36+
37+
def view_template
38+
div(**attrs) do
39+
RubyUI.Popover(options: @popover_options) do
40+
RubyUI.PopoverTrigger(**trigger_attrs) do
41+
div(class: "grid w-full max-w-sm items-center gap-1.5") do
42+
label(for: @id) { @label } if @label
43+
RubyUI.Input(**input_attrs)
44+
end
45+
end
46+
RubyUI.PopoverContent(**content_attrs) do
47+
RubyUI.Calendar(input_id: "##{@id}", selected_date: @selected_date, date_format: @date_format, **calendar_attrs)
48+
end
49+
end
50+
end
51+
end
52+
53+
private
54+
55+
def default_attrs
56+
{
57+
class: "space-y-4 w-[260px]"
58+
}
59+
end
60+
61+
def trigger_attrs
62+
mix({class: "w-full"}, @trigger_attrs)
63+
end
64+
65+
def input_attrs
66+
mix({
67+
type: "string",
68+
placeholder: @placeholder,
69+
id: @id,
70+
name: @name,
71+
value: @value,
72+
data_controller: "ruby-ui--calendar-input",
73+
class: "rounded-md border shadow"
74+
}.compact, @input_attrs)
75+
end
76+
77+
def calendar_attrs
78+
mix({}, @calendar_attrs)
79+
end
80+
81+
def content_attrs
82+
mix({}, @content_attrs)
83+
end
84+
end
85+
end
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# frozen_string_literal: true
2+
3+
class Views::Docs::DatePicker < Views::Base
4+
def view_template
5+
component = "DatePicker"
6+
7+
div(class: "max-w-2xl mx-auto w-full py-10 space-y-10") do
8+
render Docs::Header.new(title: "Date Picker", description: "A date picker component with input.")
9+
10+
Heading(level: 2) { "Usage" }
11+
12+
render Docs::VisualCodeExample.new(title: "Single Date", context: self) do
13+
<<~RUBY
14+
DatePicker(id: "date")
15+
RUBY
16+
end
17+
18+
render Components::ComponentSetup::Tabs.new(component_name: component)
19+
20+
render Docs::ComponentsTable.new(component_files(component))
21+
end
22+
end
23+
end

gem/test/generators/component_generator_test.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
require "test_helper"
44
require "fileutils"
55
require "tmpdir"
6+
require "yaml"
67

78
# Tests the file filtering logic used in ComponentGenerator#components_file_paths
89
# to ensure documentation files (*_docs.rb) are excluded from component generation.
@@ -67,4 +68,11 @@ def test_includes_docs_file_when_with_docs_is_true
6768
assert_includes file_names, "link_docs.rb"
6869
end
6970
end
71+
72+
def test_date_picker_installs_composed_components
73+
dependencies_path = File.expand_path("../../lib/generators/ruby_ui/dependencies.yml", __dir__)
74+
dependencies = YAML.load_file(dependencies_path)
75+
76+
assert_equal ["Input", "Popover", "Calendar"], dependencies.fetch("date_picker").fetch("components")
77+
end
7078
end
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# frozen_string_literal: true
2+
3+
require "test_helper"
4+
5+
class RubyUI::DatePickerTest < ComponentTest
6+
def test_render
7+
output = phlex do
8+
RubyUI.DatePicker(id: "event-date", name: "event[date]", value: "2026-05-15")
9+
end
10+
11+
assert_match(/<label for="event-date">Select a date<\/label>/, output)
12+
assert_match(/<input type="string"/, output)
13+
assert_match(/id="event-date"/, output)
14+
assert_match(/name="event\[date\]"/, output)
15+
assert_match(/value="2026-05-15"/, output)
16+
assert_match(/data-controller="ruby-ui--calendar-input"/, output)
17+
assert_match(/data-controller="ruby-ui--popover"/, output)
18+
assert_match(/data-controller="ruby-ui--calendar"/, output)
19+
assert_match(/data-ruby-ui--calendar-ruby-ui--calendar-input-outlet="#event-date"/, output)
20+
end
21+
22+
def test_render_without_label
23+
output = phlex do
24+
RubyUI.DatePicker(id: "event-date", label: nil)
25+
end
26+
27+
refute_match(/<label/, output)
28+
assert_match(/id="event-date"/, output)
29+
end
30+
end

0 commit comments

Comments
 (0)