rails-datepicker 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +29 -0
- data/LICENSE.txt +21 -0
- data/README.md +166 -0
- data/app/assets/stylesheets/rails_datepicker.css +98 -0
- data/app/javascript/controllers/rails_datepicker_controller.js +412 -0
- data/app/javascript/rails-datepicker/calendar.js +236 -0
- data/app/javascript/rails-datepicker/index.js +1 -0
- data/config/importmap.rb +2 -0
- data/config/locales/rails_datepicker.am.yml +12 -0
- data/config/locales/rails_datepicker.en.yml +12 -0
- data/lib/generators/rails_datepicker/install_generator.rb +87 -0
- data/lib/generators/rails_datepicker/templates/initializer.rb.tt +9 -0
- data/lib/rails-datepicker.rb +4 -0
- data/lib/rails_datepicker/calendar.rb +150 -0
- data/lib/rails_datepicker/configuration.rb +55 -0
- data/lib/rails_datepicker/engine.rb +31 -0
- data/lib/rails_datepicker/form_helper.rb +267 -0
- data/lib/rails_datepicker/simple_form_input.rb +17 -0
- data/lib/rails_datepicker/version.rb +5 -0
- data/lib/rails_datepicker.rb +25 -0
- data/package.json +22 -0
- data/test/application_system_test_case.rb +11 -0
- data/test/dummy/app/controllers/application_controller.rb +4 -0
- data/test/dummy/app/controllers/students_controller.rb +27 -0
- data/test/dummy/app/javascript/application.js +6 -0
- data/test/dummy/app/models/application_record.rb +5 -0
- data/test/dummy/app/models/student.rb +5 -0
- data/test/dummy/app/views/layouts/application.html.erb +10 -0
- data/test/dummy/app/views/students/frame.html.erb +8 -0
- data/test/dummy/app/views/students/new.html.erb +21 -0
- data/test/dummy/config/application.rb +16 -0
- data/test/dummy/config/boot.rb +2 -0
- data/test/dummy/config/cable.yml +2 -0
- data/test/dummy/config/database.yml +3 -0
- data/test/dummy/config/environment.rb +4 -0
- data/test/dummy/config/environments/test.rb +11 -0
- data/test/dummy/config/importmap.rb +5 -0
- data/test/dummy/config/routes.rb +7 -0
- data/test/dummy/db/schema.rb +7 -0
- data/test/fixtures/tailwind.css +2 -0
- data/test/generators/install_generator_test.rb +53 -0
- data/test/helpers/form_helper_test.rb +77 -0
- data/test/integration/form_submission_test.rb +22 -0
- data/test/javascript/calendar_test.js +56 -0
- data/test/rails_datepicker/calendar_test.rb +57 -0
- data/test/rails_datepicker/configuration_test.rb +39 -0
- data/test/simple_form_input_test.rb +20 -0
- data/test/system/datepicker_test.rb +93 -0
- data/test/test_helper.rb +9 -0
- metadata +253 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "test_helper"
|
|
4
|
+
|
|
5
|
+
class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
|
|
6
|
+
if ENV["RUN_SYSTEM_TESTS"] == "1"
|
|
7
|
+
driven_by :selenium, using: :headless_chrome, screen_size: [375, 812]
|
|
8
|
+
else
|
|
9
|
+
driven_by :rack_test
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class StudentsController < ApplicationController
|
|
4
|
+
def new
|
|
5
|
+
@student = Student.new(birth_date: Date.new(2023, 9, 12))
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def create
|
|
9
|
+
@student = Student.new(student_params)
|
|
10
|
+
if @student.save
|
|
11
|
+
render plain: @student.birth_date.iso8601
|
|
12
|
+
else
|
|
13
|
+
render :new, status: :unprocessable_entity
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def frame
|
|
18
|
+
@student = Student.new(birth_date: Date.new(2024, 1, 1))
|
|
19
|
+
render :frame
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def student_params
|
|
25
|
+
params.require(:student).permit(:name, :birth_date)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<title>Stimulus Datepicker test</title>
|
|
5
|
+
<%= csrf_meta_tags %>
|
|
6
|
+
<%= javascript_importmap_tags if respond_to?(:javascript_importmap_tags) %>
|
|
7
|
+
<%= stylesheet_link_tag "rails_datepicker", "data-turbo-track": "reload" %>
|
|
8
|
+
</head>
|
|
9
|
+
<body><%= yield %></body>
|
|
10
|
+
</html>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<%= form_with model: @student do |form| %>
|
|
2
|
+
<%= form.label :name %>
|
|
3
|
+
<%= form.text_field :name, value: "Test Student" %>
|
|
4
|
+
<%= form.label :birth_date %>
|
|
5
|
+
<%= form.datepicker_field :birth_date,
|
|
6
|
+
calendar: :ethiopian,
|
|
7
|
+
locale: :am,
|
|
8
|
+
min: Date.new(2020, 1, 1),
|
|
9
|
+
max: Date.new(2030, 12, 31),
|
|
10
|
+
disabled_dates: [Date.new(2023, 9, 13)] %>
|
|
11
|
+
<%= form.submit %>
|
|
12
|
+
<% end %>
|
|
13
|
+
|
|
14
|
+
<%= rails_datepicker_field :student, :birth_date, object: @student, calendar: :gregorian, id: "standalone_birth_date" %>
|
|
15
|
+
|
|
16
|
+
<%= turbo_frame_tag "datepicker_frame" do %>
|
|
17
|
+
<%= link_to "Replace frame", datepicker_frame_path %>
|
|
18
|
+
<% end %>
|
|
19
|
+
|
|
20
|
+
<%= rails_datepicker_field :student, :birth_date, object: @student,
|
|
21
|
+
calendar: :ethiopian, display_calendar: :gregorian, locale: :am, id: "gregorian_result" %>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "boot"
|
|
4
|
+
require "rails/all"
|
|
5
|
+
Bundler.require(*Rails.groups)
|
|
6
|
+
require "rails_datepicker"
|
|
7
|
+
|
|
8
|
+
module Dummy
|
|
9
|
+
class Application < Rails::Application
|
|
10
|
+
config.root = File.expand_path("..", __dir__)
|
|
11
|
+
config.load_defaults 8.0
|
|
12
|
+
config.eager_load = false
|
|
13
|
+
config.secret_key_base = "test-secret-key-base-for-rails-datepicker"
|
|
14
|
+
config.hosts.clear
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
Rails.application.configure do
|
|
4
|
+
config.cache_classes = true
|
|
5
|
+
config.eager_load = false
|
|
6
|
+
config.consider_all_requests_local = true
|
|
7
|
+
config.action_controller.perform_caching = false
|
|
8
|
+
config.action_controller.allow_forgery_protection = false
|
|
9
|
+
config.action_dispatch.show_exceptions = :rescuable
|
|
10
|
+
config.active_support.deprecation = :stderr
|
|
11
|
+
end
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
pin "application"
|
|
2
|
+
pin "@hotwired/stimulus", to: "stimulus.min.js"
|
|
3
|
+
pin "@hotwired/turbo-rails", to: "turbo.min.js"
|
|
4
|
+
pin "rails-datepicker/calendar", to: "rails-datepicker/calendar.js"
|
|
5
|
+
pin "rails-datepicker/controller", to: "controllers/rails_datepicker_controller.js"
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "test_helper"
|
|
4
|
+
require "generators/rails_datepicker/install_generator"
|
|
5
|
+
|
|
6
|
+
class InstallGeneratorTest < Rails::Generators::TestCase
|
|
7
|
+
tests RailsDatepicker::Generators::InstallGenerator
|
|
8
|
+
destination File.expand_path("../tmp/generator", __dir__)
|
|
9
|
+
setup :prepare_destination
|
|
10
|
+
|
|
11
|
+
setup do
|
|
12
|
+
FileUtils.mkdir_p(File.join(destination_root, "config"))
|
|
13
|
+
File.write(File.join(destination_root, "config/importmap.rb"), "# pins\n")
|
|
14
|
+
FileUtils.mkdir_p(File.join(destination_root, "app/assets/tailwind"))
|
|
15
|
+
File.write(File.join(destination_root, "app/assets/tailwind/application.css"), '@import "tailwindcss";\n')
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
test "installs configuration, JavaScript, importmap pin, and Tailwind source" do
|
|
19
|
+
run_generator
|
|
20
|
+
|
|
21
|
+
assert_file "config/initializers/rails_datepicker.rb"
|
|
22
|
+
assert_file "app/javascript/controllers/rails_datepicker_controller.js"
|
|
23
|
+
assert_file "app/javascript/rails-datepicker/calendar.js"
|
|
24
|
+
assert_file "app/assets/tailwind/rails_datepicker.css"
|
|
25
|
+
assert_file "config/importmap.rb", /rails-datepicker\/calendar/
|
|
26
|
+
assert_file "app/assets/tailwind/application.css", /rails_datepicker/
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
test "can skip Tailwind integration" do
|
|
30
|
+
run_generator ["--skip-tailwind"]
|
|
31
|
+
|
|
32
|
+
assert_no_file "app/assets/tailwind/rails_datepicker.css"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
test "uses a relative calendar import for JavaScript bundlers" do
|
|
36
|
+
FileUtils.rm_f(File.join(destination_root, "config/importmap.rb"))
|
|
37
|
+
run_generator ["--skip-tailwind"]
|
|
38
|
+
|
|
39
|
+
assert_file "app/javascript/controllers/rails_datepicker_controller.js", /from "\.\.\/rails-datepicker\/calendar\.js"/
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
test "does not modify a non-Tailwind application stylesheet" do
|
|
43
|
+
FileUtils.rm_f(File.join(destination_root, "app/assets/tailwind/application.css"))
|
|
44
|
+
FileUtils.mkdir_p(File.join(destination_root, "app/assets/stylesheets"))
|
|
45
|
+
File.write(File.join(destination_root, "app/assets/stylesheets/application.css"), "body { color: black; }\n")
|
|
46
|
+
run_generator
|
|
47
|
+
|
|
48
|
+
assert_file "app/assets/tailwind/rails_datepicker.css"
|
|
49
|
+
assert_file "app/assets/stylesheets/application.css" do |content|
|
|
50
|
+
assert_equal "body { color: black; }\n", content
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "test_helper"
|
|
4
|
+
|
|
5
|
+
class FormHelperTest < ActionView::TestCase
|
|
6
|
+
include RailsDatepicker::FormHelper
|
|
7
|
+
|
|
8
|
+
test "preserves field attributes and nested names" do
|
|
9
|
+
student = Student.new(birth_date: Date.new(2023, 9, 12))
|
|
10
|
+
html = form_for(student, url: "/") do |form|
|
|
11
|
+
form.fields_for(:profile) do |nested|
|
|
12
|
+
nested.datepicker_field(:birth_date, required: true, disabled: true, class: "custom")
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
assert_includes html, 'name="student[profile][birth_date]"'
|
|
17
|
+
assert_includes html, "required=\"required\""
|
|
18
|
+
assert_includes html, "disabled=\"disabled\""
|
|
19
|
+
assert_includes html, "custom"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
test "calendar switching data keeps canonical value" do
|
|
23
|
+
student = Student.new(birth_date: Date.new(2023, 9, 12))
|
|
24
|
+
html = rails_datepicker_field(:student, :birth_date, object: student, calendar: :ethiopian)
|
|
25
|
+
|
|
26
|
+
assert_includes html, 'value="2023-09-12"'
|
|
27
|
+
assert_includes html, 'value="2016-01-01"'
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
test "displays Gregorian results when choosing from the Ethiopian calendar" do
|
|
31
|
+
html = rails_datepicker_field(:student, :birth_date,
|
|
32
|
+
object: Student.new(birth_date: Date.new(2023, 9, 12)),
|
|
33
|
+
calendar: :ethiopian, display_calendar: :gregorian)
|
|
34
|
+
fragment = Nokogiri::HTML.fragment(html)
|
|
35
|
+
assert_equal "2023-09-12", fragment.at_css(".rdp__input")["value"]
|
|
36
|
+
assert_equal "gregorian", fragment.at_css(".rdp")["data-rails-datepicker-display-calendar-value"]
|
|
37
|
+
assert fragment.at_css("[data-action='rails-datepicker#switchLocale']")
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
test "uses the field locale and preserves custom accessibility attributes" do
|
|
41
|
+
student = Student.new
|
|
42
|
+
student.errors.add(:birth_date, "is invalid")
|
|
43
|
+
html = rails_datepicker_field(
|
|
44
|
+
:student,
|
|
45
|
+
:birth_date,
|
|
46
|
+
object: student,
|
|
47
|
+
locale: :am,
|
|
48
|
+
aria: { label: "Birth date", describedby: "birth_date_hint" },
|
|
49
|
+
data: { action: "focus->hint#show" }
|
|
50
|
+
)
|
|
51
|
+
fragment = Nokogiri::HTML.fragment(html)
|
|
52
|
+
wrapper = fragment.at_css("[data-controller='rails-datepicker']")
|
|
53
|
+
input = fragment.at_css("input.rdp__input")
|
|
54
|
+
|
|
55
|
+
assert_equal "am", wrapper["lang"]
|
|
56
|
+
assert_equal "Birth date", input["aria-label"]
|
|
57
|
+
assert_equal "birth_date_hint student_birth_date_error", input["aria-describedby"]
|
|
58
|
+
assert_includes input["data-action"], "focus->hint#show"
|
|
59
|
+
assert_equal "ቀን መቁጠሪያ ክፈት", fragment.at_css(".rdp__trigger")["aria-label"]
|
|
60
|
+
assert_equal "ዛሬ", fragment.at_css(".rdp__footer-button").text
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
test "disables every interactive control in a disabled inline picker" do
|
|
64
|
+
html = rails_datepicker_field(
|
|
65
|
+
:student,
|
|
66
|
+
:birth_date,
|
|
67
|
+
object: Student.new,
|
|
68
|
+
inline: true,
|
|
69
|
+
disabled: true
|
|
70
|
+
)
|
|
71
|
+
fragment = Nokogiri::HTML.fragment(html)
|
|
72
|
+
controls = fragment.css(".rdp__popover button, .rdp__popover select")
|
|
73
|
+
|
|
74
|
+
assert controls.any?
|
|
75
|
+
assert controls.all? { |control| control.key?("disabled") }
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "test_helper"
|
|
4
|
+
|
|
5
|
+
class FormSubmissionTest < ActionDispatch::IntegrationTest
|
|
6
|
+
test "submits Gregorian ISO into an Active Record date column" do
|
|
7
|
+
post students_path, params: { student: { name: "Mimi", birth_date: "2023-09-12" } }
|
|
8
|
+
|
|
9
|
+
assert_response :success
|
|
10
|
+
assert_equal "2023-09-12", response.body
|
|
11
|
+
assert_equal Date.new(2023, 9, 12), Student.last.birth_date
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
test "renders builder and standalone datepickers with canonical names" do
|
|
15
|
+
get new_student_path
|
|
16
|
+
|
|
17
|
+
assert_response :success
|
|
18
|
+
assert_select "[data-controller='rails-datepicker']", count: 3
|
|
19
|
+
assert_select "input[type='hidden'][name='student[birth_date]'][value='2023-09-12']", count: 3
|
|
20
|
+
assert_select "input.rdp__input:not([name])", count: 3
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import test from "node:test"
|
|
2
|
+
import assert from "node:assert/strict"
|
|
3
|
+
import {
|
|
4
|
+
decodeEthiopicInteger, encodeEthiopicInteger, ethiopianLeapYear,
|
|
5
|
+
ethiopianMonthLength, ethiopianNewYear, ethiopianToGregorian, formatDate,
|
|
6
|
+
gregorianLeapYear, gregorianToEthiopian, normalizeEthiopicNumerals,
|
|
7
|
+
parseFormatted, parseIso, toIso
|
|
8
|
+
} from "../../app/javascript/rails-datepicker/calendar.js"
|
|
9
|
+
|
|
10
|
+
test("converts Ethiopian New Year boundaries", () => {
|
|
11
|
+
assert.equal(toIso(ethiopianToGregorian(2015, 1, 1)), "2022-09-11")
|
|
12
|
+
assert.equal(toIso(ethiopianToGregorian(2016, 1, 1)), "2023-09-12")
|
|
13
|
+
assert.deepEqual(gregorianToEthiopian(parseIso("2023-09-11")), { year: 2015, month: 13, day: 6 })
|
|
14
|
+
assert.deepEqual(gregorianToEthiopian(parseIso("2023-09-12")), { year: 2016, month: 1, day: 1 })
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
test("remains accurate across Gregorian century boundaries", () => {
|
|
18
|
+
assert.equal(toIso(ethiopianNewYear(2092)), "2099-09-12")
|
|
19
|
+
assert.deepEqual(gregorianToEthiopian(parseIso("2099-09-11")), { year: 2091, month: 13, day: 6 })
|
|
20
|
+
assert.deepEqual(gregorianToEthiopian(parseIso("2099-09-12")), { year: 2092, month: 1, day: 1 })
|
|
21
|
+
assert.equal(toIso(ethiopianNewYear(2096)), "2103-09-13")
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
test("handles Gregorian and Ethiopian leap years", () => {
|
|
25
|
+
assert.equal(gregorianLeapYear(2000), true)
|
|
26
|
+
assert.equal(gregorianLeapYear(2100), false)
|
|
27
|
+
assert.equal(ethiopianLeapYear(2011), true)
|
|
28
|
+
assert.equal(ethiopianMonthLength(2011, 13), 6)
|
|
29
|
+
assert.equal(ethiopianMonthLength(2012, 13), 5)
|
|
30
|
+
assert.equal(toIso(ethiopianToGregorian(2011, 13, 6)), "2019-09-11")
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
test("round trips dates over a broad reference range", () => {
|
|
34
|
+
for (let year = 1900; year <= 2100; year += 1) {
|
|
35
|
+
for (const month of [1, 3, 6, 9, 12]) {
|
|
36
|
+
const date = parseIso(`${year}-${String(month).padStart(2, "0")}-15`)
|
|
37
|
+
const ethiopian = gregorianToEthiopian(date)
|
|
38
|
+
assert.equal(toIso(ethiopianToGregorian(ethiopian.year, ethiopian.month, ethiopian.day)), toIso(date))
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
test("strictly parses configured formats", () => {
|
|
44
|
+
assert.equal(toIso(parseFormatted("2016-01-01", "YYYY-MM-DD", "ethiopian")), "2023-09-12")
|
|
45
|
+
assert.equal(toIso(parseFormatted("31/12/2024", "DD/MM/YYYY", "gregorian")), "2024-12-31")
|
|
46
|
+
assert.throws(() => parseFormatted("2024-2-01", "YYYY-MM-DD", "gregorian"))
|
|
47
|
+
assert.throws(() => parseFormatted("2024-02-30", "YYYY-MM-DD", "gregorian"))
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
test("formats and accepts Ethiopic numerals", () => {
|
|
51
|
+
assert.equal(encodeEthiopicInteger(2016), "፳፻፲፮")
|
|
52
|
+
assert.equal(decodeEthiopicInteger("፳፻፲፮"), 2016)
|
|
53
|
+
assert.equal(normalizeEthiopicNumerals("፳፻፲፮-፩-፩"), "2016-1-1")
|
|
54
|
+
assert.equal(formatDate(parseIso("2023-09-12"), "YYYY-MM-DD", "ethiopian", true), "፳፻፲፮-፩-፩")
|
|
55
|
+
assert.equal(toIso(parseFormatted("፳፻፲፮-፩-፩", "YYYY-MM-DD", "ethiopian")), "2023-09-12")
|
|
56
|
+
})
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "minitest/autorun"
|
|
4
|
+
require_relative "../../lib/rails_datepicker/calendar"
|
|
5
|
+
|
|
6
|
+
class RailsDatepickerCalendarTest < Minitest::Test
|
|
7
|
+
Calendar = RailsDatepicker::Calendar
|
|
8
|
+
|
|
9
|
+
def test_ethiopian_new_year_boundaries
|
|
10
|
+
assert_equal Date.new(2022, 9, 11), Calendar.ethiopian_to_gregorian(2015, 1, 1)
|
|
11
|
+
assert_equal Date.new(2023, 9, 12), Calendar.ethiopian_to_gregorian(2016, 1, 1)
|
|
12
|
+
assert_equal [2015, 13, 6], Calendar.gregorian_to_ethiopian(Date.new(2023, 9, 11))
|
|
13
|
+
assert_equal [2016, 1, 1], Calendar.gregorian_to_ethiopian(Date.new(2023, 9, 12))
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def test_conversion_remains_accurate_across_gregorian_century_boundaries
|
|
17
|
+
assert_equal Date.new(2099, 9, 12, Date::GREGORIAN), Calendar.ethiopian_new_year(2092)
|
|
18
|
+
assert_equal [2091, 13, 6], Calendar.gregorian_to_ethiopian(Date.new(2099, 9, 11, Date::GREGORIAN))
|
|
19
|
+
assert_equal [2092, 1, 1], Calendar.gregorian_to_ethiopian(Date.new(2099, 9, 12, Date::GREGORIAN))
|
|
20
|
+
assert_equal Date.new(2103, 9, 13, Date::GREGORIAN), Calendar.ethiopian_new_year(2096)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def test_pagume_and_ethiopian_leap_years
|
|
24
|
+
assert Calendar.ethiopian_leap_year?(2011)
|
|
25
|
+
refute Calendar.ethiopian_leap_year?(2012)
|
|
26
|
+
assert_equal 6, Calendar.ethiopian_month_length(2011, 13)
|
|
27
|
+
assert_equal 5, Calendar.ethiopian_month_length(2012, 13)
|
|
28
|
+
assert_equal Date.new(2019, 9, 11), Calendar.ethiopian_to_gregorian(2011, 13, 6)
|
|
29
|
+
assert_raises(RailsDatepicker::Calendar::InvalidDate) { Calendar.ethiopian_to_gregorian(2012, 13, 6) }
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def test_gregorian_leap_year_rules
|
|
33
|
+
assert Calendar.gregorian_leap_year?(2000)
|
|
34
|
+
refute Calendar.gregorian_leap_year?(2100)
|
|
35
|
+
assert Calendar.gregorian_leap_year?(2024)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def test_round_trip_over_reference_range
|
|
39
|
+
(1900..2100).each do |year|
|
|
40
|
+
[1, 3, 6, 9, 12].each do |month|
|
|
41
|
+
date = Date.new(year, month, 15)
|
|
42
|
+
ethiopian = Calendar.gregorian_to_ethiopian(date)
|
|
43
|
+
assert_equal date, Calendar.ethiopian_to_gregorian(*ethiopian)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def test_strict_iso_parsing
|
|
49
|
+
assert_equal Date.new(2024, 2, 29), Calendar.parse_iso("2024-02-29")
|
|
50
|
+
assert_raises(RailsDatepicker::Calendar::InvalidDate) { Calendar.parse_iso("2024-2-29") }
|
|
51
|
+
assert_raises(RailsDatepicker::Calendar::InvalidDate) { Calendar.parse_iso("2023-02-29") }
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def test_ethiopic_numeral_formatting
|
|
55
|
+
assert_equal "፳፻፲፮-፲፫-፮", Calendar.to_ethiopic_numerals("2016-13-06")
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "minitest/autorun"
|
|
4
|
+
require_relative "../../lib/rails_datepicker/configuration"
|
|
5
|
+
|
|
6
|
+
class RailsDatepickerConfigurationTest < Minitest::Test
|
|
7
|
+
def setup
|
|
8
|
+
@configuration = RailsDatepicker::Configuration.new
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def test_defaults
|
|
12
|
+
assert_equal :ethiopian, @configuration.default_calendar
|
|
13
|
+
assert_equal :am, @configuration.default_locale
|
|
14
|
+
assert_equal "YYYY-MM-DD", @configuration.date_format
|
|
15
|
+
assert_equal :auto, @configuration.theme
|
|
16
|
+
assert_equal :sunday, @configuration.week_start
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def test_accepts_supported_values
|
|
20
|
+
@configuration.default_calendar = "gregorian"
|
|
21
|
+
@configuration.default_locale = "en"
|
|
22
|
+
@configuration.theme = "dark"
|
|
23
|
+
@configuration.week_start = "monday"
|
|
24
|
+
|
|
25
|
+
assert_equal :gregorian, @configuration.default_calendar
|
|
26
|
+
assert_equal :en, @configuration.default_locale
|
|
27
|
+
assert_equal :dark, @configuration.theme
|
|
28
|
+
assert_equal :monday, @configuration.week_start
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def test_rejects_unsupported_values
|
|
32
|
+
assert_raises(ArgumentError) { @configuration.default_calendar = :julian }
|
|
33
|
+
assert_raises(ArgumentError) { @configuration.default_locale = :fr }
|
|
34
|
+
assert_raises(ArgumentError) { @configuration.theme = :neon }
|
|
35
|
+
assert_raises(ArgumentError) { @configuration.week_start = :tuesday }
|
|
36
|
+
assert_raises(ArgumentError) { @configuration.date_format = "YYYY-MM" }
|
|
37
|
+
assert_raises(ArgumentError) { @configuration.date_format = "YYYY-YYYY-MM-DD" }
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "test_helper"
|
|
4
|
+
require "simple_form"
|
|
5
|
+
require "rails_datepicker/simple_form_input"
|
|
6
|
+
|
|
7
|
+
class SimpleFormInputTest < ActionView::TestCase
|
|
8
|
+
include SimpleForm::ActionViewExtensions::FormHelper
|
|
9
|
+
|
|
10
|
+
test "Simple Form input delegates to the datepicker builder" do
|
|
11
|
+
student = Student.new(birth_date: Date.new(2023, 9, 12))
|
|
12
|
+
html = simple_form_for(student, url: "/") do |form|
|
|
13
|
+
form.input :birth_date, as: :rails_datepicker, calendar: :ethiopian, locale: :am
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
assert_includes html, 'data-controller="rails-datepicker"'
|
|
17
|
+
assert_includes html, 'name="student[birth_date]"'
|
|
18
|
+
assert_includes html, 'value="2016-01-01"'
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "application_system_test_case"
|
|
4
|
+
|
|
5
|
+
class DatepickerTest < ApplicationSystemTestCase
|
|
6
|
+
setup do
|
|
7
|
+
skip "set RUN_SYSTEM_TESTS=1 to run browser tests" unless ENV["RUN_SYSTEM_TESTS"] == "1"
|
|
8
|
+
visit datepicker_path
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
test "switches calendars without changing the canonical date" do
|
|
12
|
+
picker = find("[data-controller='rails-datepicker']", match: :first)
|
|
13
|
+
canonical = picker.find("[data-rails-datepicker-target='canonical']", visible: :all)
|
|
14
|
+
assert_equal "2023-09-12", canonical.value
|
|
15
|
+
|
|
16
|
+
picker.find("[data-rails-datepicker-target='trigger']").click
|
|
17
|
+
picker.find("[data-rails-datepicker-target='calendarToggle']").click
|
|
18
|
+
|
|
19
|
+
assert_equal "2023-09-12", canonical.value
|
|
20
|
+
assert_equal "2023-09-12", picker.find(".rdp__input").value
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
test "switches all calendar labels between Amharic and English" do
|
|
24
|
+
picker = find(".rdp", match: :first)
|
|
25
|
+
picker.find(".rdp__trigger").click
|
|
26
|
+
picker.find("[data-rails-datepicker-target='localeToggle']").click
|
|
27
|
+
assert_equal "en", picker["lang"]
|
|
28
|
+
assert_equal "Open calendar", picker.find(".rdp__trigger")["aria-label"]
|
|
29
|
+
assert_equal "Today", picker.find("[data-action='rails-datepicker#today']").text
|
|
30
|
+
assert_equal "Meskerem", picker.find("[data-rails-datepicker-target='monthSelect'] option:checked").text
|
|
31
|
+
picker.find("[data-rails-datepicker-target='localeToggle']").click
|
|
32
|
+
assert_equal "am", picker["lang"]
|
|
33
|
+
assert_equal "ዛሬ", picker.find("[data-action='rails-datepicker#today']").text
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
test "keeps Gregorian results after Ethiopian selection and calendar switching" do
|
|
37
|
+
picker = find(".rdp:has(#gregorian_result)")
|
|
38
|
+
picker.find(".rdp__trigger").click
|
|
39
|
+
width = picker.find(".rdp__popover").evaluate_script("this.getBoundingClientRect().width")
|
|
40
|
+
assert_operator width, :<=, 288
|
|
41
|
+
picker.find("[data-iso='2023-09-14']").click
|
|
42
|
+
assert_equal "2023-09-14", picker.find(".rdp__input").value
|
|
43
|
+
assert_equal "2023-09-14", picker.find("input[type='hidden']", visible: :all).value
|
|
44
|
+
picker.find(".rdp__trigger").click
|
|
45
|
+
picker.find("[data-rails-datepicker-target='calendarToggle']").click
|
|
46
|
+
assert_equal "2023-09-14", picker.find(".rdp__input").value
|
|
47
|
+
picker.find(".rdp__input").fill_in(with: "2024-02-29")
|
|
48
|
+
picker.find(".rdp__input").send_keys(:tab)
|
|
49
|
+
assert_equal "2024-02-29", picker.find("input[type='hidden']", visible: :all).value
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
test "supports independent instances and disabled dates" do
|
|
53
|
+
pickers = all("[data-controller='rails-datepicker']")
|
|
54
|
+
pickers.first.find("[data-rails-datepicker-target='trigger']").click
|
|
55
|
+
|
|
56
|
+
assert pickers.first.find("[data-iso='2023-09-13']", visible: :all).disabled?
|
|
57
|
+
assert pickers.first.find(".rdp__popover", visible: :visible)
|
|
58
|
+
assert pickers[1].find(".rdp__popover", visible: :hidden)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
test "provides keyboard navigation and Escape focus restoration" do
|
|
62
|
+
trigger = find("[data-rails-datepicker-target='trigger']", match: :first)
|
|
63
|
+
trigger.click
|
|
64
|
+
day = find("[data-rdp-day]:not([disabled])", match: :first)
|
|
65
|
+
day.send_keys(:arrow_right)
|
|
66
|
+
assert_selector "[data-rdp-day]:focus"
|
|
67
|
+
page.send_keys(:escape)
|
|
68
|
+
|
|
69
|
+
assert_equal trigger, page.evaluate_script("document.activeElement")
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
test "reconnects once after a Turbo Frame replacement and renders dark inline mode" do
|
|
73
|
+
click_link "Replace frame"
|
|
74
|
+
|
|
75
|
+
within("turbo-frame#datepicker_frame") do
|
|
76
|
+
assert_selector ".rdp--dark.rdp--inline", count: 1
|
|
77
|
+
assert_selector ".rdp__popover:not([hidden])", count: 1
|
|
78
|
+
find(".rdp__day:not(.rdp__day--empty):not([disabled])", match: :first).click
|
|
79
|
+
assert_selector "input[type='hidden'][value]:not([value=''])", visible: :all, count: 1
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
test "strictly validates manual input and respects range restrictions" do
|
|
84
|
+
input = find(".rdp__input", match: :first)
|
|
85
|
+
input.fill_in(with: "2016-1-1")
|
|
86
|
+
input.send_keys(:tab)
|
|
87
|
+
assert_equal "true", input["aria-invalid"]
|
|
88
|
+
|
|
89
|
+
input.fill_in(with: "2016-01-01")
|
|
90
|
+
input.send_keys(:tab)
|
|
91
|
+
assert_equal "false", input["aria-invalid"]
|
|
92
|
+
end
|
|
93
|
+
end
|
data/test/test_helper.rb
ADDED