Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 1 addition & 13 deletions docs/app/views/docs/date_picker.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,7 @@ def view_template

render Docs::VisualCodeExample.new(title: "Single Date", context: self) do
<<~RUBY
div(class: 'space-y-4 w-[260px]') do
Popover(options: { trigger: 'click' }) do
PopoverTrigger(class: 'w-full') do
div(class: 'grid w-full max-w-sm items-center gap-1.5') do
label(for: "date") { "Select a date" }
Input(type: 'string', placeholder: "Select a date", class: 'rounded-md border shadow', id: 'date', data_controller: 'ruby-ui--calendar-input')
end
end
PopoverContent do
Calendar(input_id: '#date')
end
end
end
DatePicker(id: "date")
RUBY
end

Expand Down
6 changes: 6 additions & 0 deletions gem/lib/generators/ruby_ui/dependencies.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@ context_menu:
js_packages:
- "tippy.js"

date_picker:
components:
- "Input"
- "Popover"
- "Calendar"

data_table:
components:
- "Button"
Expand Down
85 changes: 85 additions & 0 deletions gem/lib/ruby_ui/date_picker/date_picker.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# frozen_string_literal: true

require "securerandom"

module RubyUI
class DatePicker < Base
def initialize(
id: nil,
name: nil,
label: "Select a date",
value: nil,
placeholder: "Select a date",
selected_date: value,
date_format: "yyyy-MM-dd",
popover_options: {},
input_attrs: {},
calendar_attrs: {},
trigger_attrs: {},
content_attrs: {},
**attrs
)
@id = id || "date-picker-#{SecureRandom.hex(4)}"
@name = name
@label = label
@value = value || selected_date&.to_s
@placeholder = placeholder
@selected_date = selected_date
@date_format = date_format
@popover_options = {trigger: "click"}.merge(popover_options)
@input_attrs = input_attrs
@calendar_attrs = calendar_attrs
@trigger_attrs = trigger_attrs
@content_attrs = content_attrs
super(**attrs)
end

def view_template
div(**attrs) do
RubyUI.Popover(options: @popover_options) do
RubyUI.PopoverTrigger(**trigger_attrs) do
div(class: "grid w-full max-w-sm items-center gap-1.5") do
label(for: @id) { @label } if @label
RubyUI.Input(**input_attrs)
end
end
RubyUI.PopoverContent(**content_attrs) do
RubyUI.Calendar(input_id: "##{@id}", selected_date: @selected_date, date_format: @date_format, **calendar_attrs)
end
end
end
end

private

def default_attrs
{
class: "space-y-4 w-[260px]"
}
end

def trigger_attrs
mix({class: "w-full"}, @trigger_attrs)
end

def input_attrs
mix({
type: "string",
placeholder: @placeholder,
id: @id,
name: @name,
value: @value,
data_controller: "ruby-ui--calendar-input",
class: "rounded-md border shadow"
}.compact, @input_attrs)
end

def calendar_attrs
mix({}, @calendar_attrs)
end

def content_attrs
mix({}, @content_attrs)
end
end
end
23 changes: 23 additions & 0 deletions gem/lib/ruby_ui/date_picker/date_picker_docs.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# frozen_string_literal: true

class Views::Docs::DatePicker < Views::Base
def view_template
component = "DatePicker"

div(class: "max-w-2xl mx-auto w-full py-10 space-y-10") do
render Docs::Header.new(title: "Date Picker", description: "A date picker component with input.")

Heading(level: 2) { "Usage" }

render Docs::VisualCodeExample.new(title: "Single Date", context: self) do
<<~RUBY
DatePicker(id: "date")
RUBY
end

render Components::ComponentSetup::Tabs.new(component_name: component)

render Docs::ComponentsTable.new(component_files(component))
end
end
end
8 changes: 8 additions & 0 deletions gem/test/generators/component_generator_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
require "test_helper"
require "fileutils"
require "tmpdir"
require "yaml"

# Tests the file filtering logic used in ComponentGenerator#components_file_paths
# to ensure documentation files (*_docs.rb) are excluded from component generation.
Expand Down Expand Up @@ -67,4 +68,11 @@ def test_includes_docs_file_when_with_docs_is_true
assert_includes file_names, "link_docs.rb"
end
end

def test_date_picker_installs_composed_components
dependencies_path = File.expand_path("../../lib/generators/ruby_ui/dependencies.yml", __dir__)
dependencies = YAML.load_file(dependencies_path)

assert_equal ["Input", "Popover", "Calendar"], dependencies.fetch("date_picker").fetch("components")
end
end
30 changes: 30 additions & 0 deletions gem/test/ruby_ui/date_picker_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# frozen_string_literal: true

require "test_helper"

class RubyUI::DatePickerTest < ComponentTest
def test_render
output = phlex do
RubyUI.DatePicker(id: "event-date", name: "event[date]", value: "2026-05-15")
end

assert_match(/<label for="event-date">Select a date<\/label>/, output)
assert_match(/<input type="string"/, output)
assert_match(/id="event-date"/, output)
assert_match(/name="event\[date\]"/, output)
assert_match(/value="2026-05-15"/, output)
assert_match(/data-controller="ruby-ui--calendar-input"/, output)
assert_match(/data-controller="ruby-ui--popover"/, output)
assert_match(/data-controller="ruby-ui--calendar"/, output)
assert_match(/data-ruby-ui--calendar-ruby-ui--calendar-input-outlet="#event-date"/, output)
end

def test_render_without_label
output = phlex do
RubyUI.DatePicker(id: "event-date", label: nil)
end

refute_match(/<label/, output)
assert_match(/id="event-date"/, output)
end
end