uchi 0.1.3 → 0.1.4

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.
Files changed (283) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +222 -0
  3. data/app/assets/config/uchi_manifest.js +2 -0
  4. data/app/assets/javascripts/uchi/application.js +6095 -0
  5. data/app/assets/javascripts/uchi.js +4 -0
  6. data/app/assets/stylesheets/uchi/application.css +3971 -0
  7. data/app/assets/stylesheets/uchi/uchi.css +17 -0
  8. data/app/assets/tailwind/uchi.css +21 -0
  9. data/app/components/flowbite/breadcrumb.rb +33 -0
  10. data/app/components/flowbite/breadcrumb_home.rb +26 -0
  11. data/app/components/flowbite/breadcrumb_item/current.rb +33 -0
  12. data/app/components/flowbite/breadcrumb_item/first.rb +35 -0
  13. data/app/components/flowbite/breadcrumb_item.rb +48 -0
  14. data/app/components/flowbite/breadcrumb_separator.rb +30 -0
  15. data/app/components/flowbite/button/outline.rb +22 -0
  16. data/app/components/flowbite/button/pill.rb +40 -0
  17. data/app/components/flowbite/button.rb +92 -0
  18. data/app/components/flowbite/card.rb +45 -0
  19. data/app/components/flowbite/input/checkbox.rb +73 -0
  20. data/app/components/flowbite/input/date.rb +11 -0
  21. data/app/components/flowbite/input/date_time.rb +11 -0
  22. data/app/components/flowbite/input/email.rb +12 -0
  23. data/app/components/flowbite/input/field.rb +117 -0
  24. data/app/components/flowbite/input/file.rb +30 -0
  25. data/app/components/flowbite/input/hint.rb +57 -0
  26. data/app/components/flowbite/input/label.rb +82 -0
  27. data/app/components/flowbite/input/number.rb +11 -0
  28. data/app/components/flowbite/input/password.rb +11 -0
  29. data/app/components/flowbite/input/phone.rb +11 -0
  30. data/app/components/flowbite/input/radio_button.rb +50 -0
  31. data/app/components/flowbite/input/select.rb +49 -0
  32. data/app/components/flowbite/input/textarea.rb +42 -0
  33. data/app/components/flowbite/input/url.rb +12 -0
  34. data/app/components/flowbite/input/validation_error.rb +11 -0
  35. data/app/components/flowbite/input_field/checkbox.html.erb +14 -0
  36. data/app/components/flowbite/input_field/checkbox.rb +54 -0
  37. data/app/components/flowbite/input_field/date.rb +13 -0
  38. data/app/components/flowbite/input_field/date_time.rb +13 -0
  39. data/app/components/flowbite/input_field/email.rb +13 -0
  40. data/app/components/flowbite/input_field/file.rb +13 -0
  41. data/app/components/flowbite/input_field/input_field.html.erb +8 -0
  42. data/app/components/flowbite/input_field/number.rb +13 -0
  43. data/app/components/flowbite/input_field/password.rb +13 -0
  44. data/app/components/flowbite/input_field/phone.rb +13 -0
  45. data/app/components/flowbite/input_field/radio_button.html.erb +14 -0
  46. data/app/components/flowbite/input_field/radio_button.rb +86 -0
  47. data/app/components/flowbite/input_field/select.rb +31 -0
  48. data/app/components/flowbite/input_field/text.rb +8 -0
  49. data/app/components/flowbite/input_field/textarea.rb +13 -0
  50. data/app/components/flowbite/input_field/url.rb +13 -0
  51. data/app/components/flowbite/input_field.rb +192 -0
  52. data/app/components/flowbite/link.rb +21 -0
  53. data/app/components/flowbite/style.rb +13 -0
  54. data/app/components/flowbite/toast/icon.html.erb +5 -0
  55. data/app/components/flowbite/toast/icon.rb +57 -0
  56. data/app/components/flowbite/toast/toast.html.erb +11 -0
  57. data/app/components/flowbite/toast.rb +34 -0
  58. data/app/components/uchi/field/base.rb +57 -0
  59. data/app/components/uchi/field/belongs_to/edit.html.erb +1 -0
  60. data/app/components/uchi/field/belongs_to/index.html.erb +1 -0
  61. data/app/components/uchi/field/belongs_to/show.html.erb +3 -0
  62. data/app/components/uchi/field/belongs_to.rb +105 -0
  63. data/app/components/uchi/field/blank/edit.html.erb +1 -0
  64. data/app/components/uchi/field/blank/index.html.erb +1 -0
  65. data/app/components/uchi/field/blank/show.html.erb +1 -0
  66. data/app/components/uchi/field/blank.rb +16 -0
  67. data/app/components/uchi/field/boolean/edit.html.erb +1 -0
  68. data/app/components/uchi/field/boolean/index.html.erb +9 -0
  69. data/app/components/uchi/field/boolean/show.html.erb +9 -0
  70. data/app/components/uchi/field/boolean.rb +27 -0
  71. data/app/components/uchi/field/date/edit.html.erb +1 -0
  72. data/app/components/uchi/field/date/index.html.erb +1 -0
  73. data/app/components/uchi/field/date/show.html.erb +1 -0
  74. data/app/components/uchi/field/date.rb +27 -0
  75. data/app/components/uchi/field/date_time/edit.html.erb +1 -0
  76. data/app/components/uchi/field/date_time/index.html.erb +1 -0
  77. data/app/components/uchi/field/date_time/show.html.erb +1 -0
  78. data/app/components/uchi/field/date_time.rb +27 -0
  79. data/app/components/uchi/field/file/edit.html.erb +1 -0
  80. data/app/components/uchi/field/file/index.html.erb +6 -0
  81. data/app/components/uchi/field/file/show.html.erb +8 -0
  82. data/app/components/uchi/field/file.rb +37 -0
  83. data/app/components/uchi/field/has_and_belongs_to_many/edit.html.erb +9 -0
  84. data/app/components/uchi/field/has_and_belongs_to_many/index.html.erb +1 -0
  85. data/app/components/uchi/field/has_and_belongs_to_many/show.html.erb +28 -0
  86. data/app/components/uchi/field/has_and_belongs_to_many.rb +131 -0
  87. data/app/components/uchi/field/has_many/edit.html.erb +1 -0
  88. data/app/components/uchi/field/has_many/index.html.erb +1 -0
  89. data/app/components/uchi/field/has_many/show.html.erb +28 -0
  90. data/app/components/uchi/field/has_many.rb +107 -0
  91. data/app/components/uchi/field/id/index.html.erb +4 -0
  92. data/app/components/uchi/field/id/show.html.erb +4 -0
  93. data/app/components/uchi/field/id.rb +26 -0
  94. data/app/components/uchi/field/image/edit.html.erb +1 -0
  95. data/app/components/uchi/field/image/index.html.erb +6 -0
  96. data/app/components/uchi/field/image/show.html.erb +6 -0
  97. data/app/components/uchi/field/image.rb +38 -0
  98. data/app/components/uchi/field/number/edit.html.erb +1 -0
  99. data/app/components/uchi/field/number/index.html.erb +1 -0
  100. data/app/components/uchi/field/number/show.html.erb +1 -0
  101. data/app/components/uchi/field/number.rb +32 -0
  102. data/app/components/uchi/field/string/edit.html.erb +1 -0
  103. data/app/components/uchi/field/string/index.html.erb +1 -0
  104. data/app/components/uchi/field/string/show.html.erb +1 -0
  105. data/app/components/uchi/field/string.rb +33 -0
  106. data/app/components/uchi/field/text/edit.html.erb +1 -0
  107. data/app/components/uchi/field/text/index.html.erb +1 -0
  108. data/app/components/uchi/field/text/show.html.erb +1 -0
  109. data/app/components/uchi/field/text.rb +38 -0
  110. data/app/components/uchi/ui/breadcrumb/breadcrumb.html.erb +13 -0
  111. data/app/components/uchi/ui/breadcrumb.rb +14 -0
  112. data/app/components/uchi/ui/form/footer/footer.html.erb +5 -0
  113. data/app/components/uchi/ui/form/footer.rb +15 -0
  114. data/app/components/uchi/ui/form/input/collection_checkboxes.html.erb +32 -0
  115. data/app/components/uchi/ui/form/input/collection_checkboxes.rb +125 -0
  116. data/app/components/uchi/ui/frame/frame.html.erb +3 -0
  117. data/app/components/uchi/ui/frame.rb +10 -0
  118. data/app/components/uchi/ui/index/records_table/records_table.html.erb +67 -0
  119. data/app/components/uchi/ui/index/records_table/search_form/search_form.html.erb +21 -0
  120. data/app/components/uchi/ui/index/records_table/search_form.rb +49 -0
  121. data/app/components/uchi/ui/index/records_table.rb +29 -0
  122. data/app/components/uchi/ui/index/turbo_frame.rb +50 -0
  123. data/app/components/uchi/ui/page_header/page_header.html.erb +24 -0
  124. data/app/components/uchi/ui/page_header.rb +18 -0
  125. data/app/components/uchi/ui/pagination/current_link.html.erb +3 -0
  126. data/app/components/uchi/ui/pagination/current_link.rb +10 -0
  127. data/app/components/uchi/ui/pagination/gap.html.erb +3 -0
  128. data/app/components/uchi/ui/pagination/gap.rb +10 -0
  129. data/app/components/uchi/ui/pagination/item.rb +24 -0
  130. data/app/components/uchi/ui/pagination/link.html.erb +3 -0
  131. data/app/components/uchi/ui/pagination/link.rb +10 -0
  132. data/app/components/uchi/ui/pagination/next_link.html.erb +8 -0
  133. data/app/components/uchi/ui/pagination/next_link.rb +22 -0
  134. data/app/components/uchi/ui/pagination/pagination.html.erb +15 -0
  135. data/app/components/uchi/ui/pagination/previous_link.html.erb +8 -0
  136. data/app/components/uchi/ui/pagination/previous_link.rb +22 -0
  137. data/app/components/uchi/ui/pagination.rb +48 -0
  138. data/app/components/uchi/ui/show/attribute_fields/attribute_fields.html.erb +14 -0
  139. data/app/components/uchi/ui/show/attribute_fields.rb +18 -0
  140. data/app/components/uchi/ui/spinner/spinner.html.erb +7 -0
  141. data/app/components/uchi/ui/spinner.rb +15 -0
  142. data/app/controllers/uchi/application_controller.rb +4 -0
  143. data/app/controllers/uchi/controller.rb +13 -0
  144. data/app/controllers/uchi/repository_controller.rb +166 -0
  145. data/app/helpers/uchi/application_helper.rb +17 -0
  146. data/app/jobs/uchi/application_job.rb +4 -0
  147. data/app/mailers/uchi/application_mailer.rb +6 -0
  148. data/app/views/layouts/uchi/_flash_messages.html.erb +10 -0
  149. data/app/views/layouts/uchi/application.html.erb +33 -0
  150. data/app/views/uchi/repository/edit.html.erb +40 -0
  151. data/app/views/uchi/repository/index.html.erb +49 -0
  152. data/app/views/uchi/repository/new.html.erb +37 -0
  153. data/app/views/uchi/repository/show.html.erb +41 -0
  154. data/lib/generators/uchi/controller/controller_generator.rb +16 -0
  155. data/lib/generators/uchi/controller/templates/controller.rb.tt +11 -0
  156. data/lib/generators/uchi/install/install_generator.rb +13 -0
  157. data/lib/generators/uchi/repository/repository_generator.rb +16 -0
  158. data/lib/generators/uchi/repository/templates/repository.rb.tt +11 -0
  159. data/lib/tasks/uchi_tasks.rake +4 -0
  160. data/lib/uchi/application_record.rb +5 -0
  161. data/lib/uchi/engine.rb +24 -0
  162. data/lib/uchi/field/configuration.rb +142 -0
  163. data/lib/uchi/field.rb +80 -0
  164. data/lib/uchi/i18n.rb +13 -0
  165. data/lib/uchi/pagination/controller.rb +26 -0
  166. data/lib/uchi/pagination/page.rb +20 -0
  167. data/lib/uchi/pagy/LICENSE.txt +21 -0
  168. data/lib/uchi/pagy/classes/exceptions.rb +35 -0
  169. data/lib/uchi/pagy/classes/offset/offset.rb +56 -0
  170. data/lib/uchi/pagy/classes/request.rb +38 -0
  171. data/lib/uchi/pagy/modules/abilities/configurable.rb +38 -0
  172. data/lib/uchi/pagy/modules/abilities/linkable.rb +62 -0
  173. data/lib/uchi/pagy/modules/abilities/rangeable.rb +17 -0
  174. data/lib/uchi/pagy/modules/abilities/shiftable.rb +14 -0
  175. data/lib/uchi/pagy/modules/console.rb +40 -0
  176. data/lib/uchi/pagy/toolbox/helpers/loader.rb +19 -0
  177. data/lib/uchi/pagy/toolbox/helpers/page_url.rb +25 -0
  178. data/lib/uchi/pagy/toolbox/paginators/method.rb +21 -0
  179. data/lib/uchi/pagy/toolbox/paginators/offset.rb +35 -0
  180. data/lib/uchi/pagy.rb +60 -0
  181. data/lib/uchi/repository/routes.rb +62 -0
  182. data/lib/uchi/repository/translate.rb +284 -0
  183. data/lib/uchi/repository.rb +156 -0
  184. data/lib/uchi/sort_order.rb +35 -0
  185. data/lib/uchi/version.rb +5 -0
  186. data/lib/uchi.rb +18 -0
  187. data/uchi.gemspec +35 -0
  188. metadata +189 -97
  189. data/.github/dependabot.yml +0 -17
  190. data/.github/workflows/build.yml +0 -23
  191. data/.github/workflows/lint.yml +0 -30
  192. data/CHANGELOG.md +0 -29
  193. data/docs/fields.md +0 -82
  194. data/docs/repositories.md +0 -63
  195. data/package.json +0 -31
  196. data/sig/uchi.rbs +0 -4
  197. data/test/components/uchi/field/belongs_to_test.rb +0 -134
  198. data/test/components/uchi/field/blank_test.rb +0 -119
  199. data/test/components/uchi/field/boolean_test.rb +0 -163
  200. data/test/components/uchi/field/date_test.rb +0 -163
  201. data/test/components/uchi/field/date_time_test.rb +0 -152
  202. data/test/components/uchi/field/has_and_belongs_to_many_test.rb +0 -144
  203. data/test/components/uchi/field/has_many_test.rb +0 -138
  204. data/test/components/uchi/field/id_test.rb +0 -113
  205. data/test/components/uchi/field/number_test.rb +0 -163
  206. data/test/components/uchi/field/string_test.rb +0 -159
  207. data/test/components/uchi/field/text_test.rb +0 -160
  208. data/test/components/uchi/ui/form/input/collection_checkboxes_test.rb +0 -171
  209. data/test/controllers/uchi/authors_controller_test.rb +0 -120
  210. data/test/controllers/uchi/repository_controller_test.rb +0 -93
  211. data/test/controllers/uchi/scoped_repository_controller_test.rb +0 -73
  212. data/test/dummy/Rakefile +0 -6
  213. data/test/dummy/app/assets/images/.keep +0 -0
  214. data/test/dummy/app/assets/stylesheets/application.css +0 -15
  215. data/test/dummy/app/controllers/application_controller.rb +0 -4
  216. data/test/dummy/app/controllers/concerns/.keep +0 -0
  217. data/test/dummy/app/controllers/uchi/authors_controller.rb +0 -7
  218. data/test/dummy/app/controllers/uchi/books_controller.rb +0 -7
  219. data/test/dummy/app/controllers/uchi/titles_controller.rb +0 -7
  220. data/test/dummy/app/helpers/application_helper.rb +0 -2
  221. data/test/dummy/app/jobs/application_job.rb +0 -7
  222. data/test/dummy/app/mailers/application_mailer.rb +0 -4
  223. data/test/dummy/app/models/application_record.rb +0 -3
  224. data/test/dummy/app/models/author.rb +0 -5
  225. data/test/dummy/app/models/book.rb +0 -4
  226. data/test/dummy/app/models/concerns/.keep +0 -0
  227. data/test/dummy/app/models/title.rb +0 -3
  228. data/test/dummy/app/uchi/repositories/author.rb +0 -20
  229. data/test/dummy/app/uchi/repositories/book.rb +0 -16
  230. data/test/dummy/app/uchi/repositories/title.rb +0 -17
  231. data/test/dummy/app/views/layouts/application.html.erb +0 -27
  232. data/test/dummy/app/views/layouts/mailer.html.erb +0 -13
  233. data/test/dummy/app/views/layouts/mailer.text.erb +0 -1
  234. data/test/dummy/app/views/pwa/manifest.json.erb +0 -22
  235. data/test/dummy/app/views/pwa/service-worker.js +0 -26
  236. data/test/dummy/bin/dev +0 -2
  237. data/test/dummy/bin/rails +0 -4
  238. data/test/dummy/bin/rake +0 -4
  239. data/test/dummy/bin/setup +0 -34
  240. data/test/dummy/config/application.rb +0 -29
  241. data/test/dummy/config/boot.rb +0 -5
  242. data/test/dummy/config/cable.yml +0 -10
  243. data/test/dummy/config/database.yml +0 -32
  244. data/test/dummy/config/environment.rb +0 -5
  245. data/test/dummy/config/environments/development.rb +0 -69
  246. data/test/dummy/config/environments/production.rb +0 -89
  247. data/test/dummy/config/environments/test.rb +0 -53
  248. data/test/dummy/config/initializers/content_security_policy.rb +0 -25
  249. data/test/dummy/config/initializers/filter_parameter_logging.rb +0 -8
  250. data/test/dummy/config/initializers/inflections.rb +0 -16
  251. data/test/dummy/config/locales/da.yml +0 -52
  252. data/test/dummy/config/locales/en.yml +0 -31
  253. data/test/dummy/config/puma.rb +0 -38
  254. data/test/dummy/config/routes.rb +0 -9
  255. data/test/dummy/config/storage.yml +0 -34
  256. data/test/dummy/config.ru +0 -6
  257. data/test/dummy/db/migrate/20251002183635_create_authors.rb +0 -11
  258. data/test/dummy/db/migrate/20251005131726_create_books.rb +0 -9
  259. data/test/dummy/db/migrate/20251005131811_create_titles.rb +0 -11
  260. data/test/dummy/db/migrate/20251031140958_add_author_books_join_table.rb +0 -9
  261. data/test/dummy/db/schema.rb +0 -44
  262. data/test/dummy/log/.keep +0 -0
  263. data/test/dummy/public/400.html +0 -114
  264. data/test/dummy/public/404.html +0 -114
  265. data/test/dummy/public/406-unsupported-browser.html +0 -114
  266. data/test/dummy/public/422.html +0 -114
  267. data/test/dummy/public/500.html +0 -114
  268. data/test/dummy/public/icon.png +0 -0
  269. data/test/dummy/public/icon.svg +0 -3
  270. data/test/dummy/storage/.keep +0 -0
  271. data/test/dummy/test/fixtures/authors.yml +0 -11
  272. data/test/dummy/test/models/author_test.rb +0 -7
  273. data/test/dummy/tmp/.keep +0 -0
  274. data/test/dummy/tmp/pids/.keep +0 -0
  275. data/test/dummy/tmp/storage/.keep +0 -0
  276. data/test/test_helper.rb +0 -15
  277. data/test/uchi/field_test.rb +0 -77
  278. data/test/uchi/i18n_test.rb +0 -18
  279. data/test/uchi/repository/routes_test.rb +0 -49
  280. data/test/uchi/repository/translate_test.rb +0 -272
  281. data/test/uchi/repository_test.rb +0 -137
  282. data/test/uchi/sort_order_test.rb +0 -47
  283. data/test/uchi_test.rb +0 -7
@@ -1,163 +0,0 @@
1
- require "test_helper"
2
- require "ostruct"
3
-
4
- module Uchi
5
- class Field
6
- class NumberTest < ActiveSupport::TestCase
7
- def setup
8
- @field = Uchi::Field::Number.new(:age)
9
- @form = OpenStruct.new(object: OpenStruct.new(age: 25))
10
- @repository = Uchi::Repositories::Author.new
11
- end
12
-
13
- test "inherits from Uchi::Field" do
14
- assert_kind_of Uchi::Field, @field
15
- end
16
-
17
- test "has default options" do
18
- assert_equal [:edit, :index, :show], @field.on
19
- assert_not @field.searchable?
20
- assert @field.sortable?
21
- end
22
-
23
- test "#edit_component returns an instance of Edit component" do
24
- component = @field.edit_component(form: @form, hint: "Custom hint", label: "Custom label", repository: @repository)
25
- assert_equal "Custom hint", component.hint
26
- assert_equal "Custom label", component.label
27
- assert_equal @field, component.field
28
- assert_equal @form, component.form
29
- assert_equal @repository, component.repository
30
- assert_kind_of Uchi::Field::Number::Edit, component
31
- end
32
-
33
- test "#index_component returns an instance of Index component" do
34
- component = @field.index_component(record: @form.object, repository: @repository)
35
- assert_equal @field, component.field
36
- assert_equal @form.object, component.record
37
- assert_equal @repository, component.repository
38
- assert_kind_of Uchi::Field::Number::Index, component
39
- end
40
-
41
- test "#searchable? returns false when explicitly set" do
42
- field = Uchi::Field::Number.new(:age).searchable(false)
43
- assert_not field.searchable?
44
- end
45
-
46
- test "#show_component returns an instance of Show component" do
47
- component = @field.show_component(record: @form.object, repository: @repository)
48
- assert_equal @field, component.field
49
- assert_equal @form.object, component.record
50
- assert_equal @repository, component.repository
51
- assert_kind_of Uchi::Field::Number::Show, component
52
- end
53
-
54
- test "#sortable? returns false when explicitly set" do
55
- field = Uchi::Field::Number.new(:age).sortable(false)
56
- assert_not field.sortable?
57
- end
58
- end
59
-
60
- class NumberEditTest < ViewComponent::TestCase
61
- def setup
62
- @field = Uchi::Field::Number.new(:age)
63
- @record = Author.new(name: "Test Author")
64
- @record.define_singleton_method(:age) { 25 }
65
- @record.define_singleton_method(:age=) { |val| @age = val }
66
- @repository = Uchi::Repositories::Author.new
67
- @view_context = ActionController::Base.new.view_context
68
-
69
- @form = ActionView::Helpers::FormBuilder.new(:author, @record, @view_context, {})
70
-
71
- @component = Uchi::Field::Number::Edit.new(
72
- field: @field,
73
- form: @form,
74
- hint: "Custom hint",
75
- label: "Custom label",
76
- repository: @repository
77
- )
78
- end
79
-
80
- test "inherits from Base component" do
81
- assert_kind_of Uchi::Field::Base::Edit, @component
82
- end
83
-
84
- test "renders a number input field with the field content" do
85
- render_inline(@component)
86
-
87
- assert_selector("input[name='author[age]']")
88
- end
89
-
90
- test "renders label with specified text" do
91
- render_inline(@component)
92
-
93
- assert_selector("label", text: "Custom label")
94
- end
95
-
96
- test "renders hint when provided" do
97
- render_inline(@component)
98
-
99
- assert_selector("p", text: "Custom hint")
100
- end
101
-
102
- test "initializes the input component with the correct options" do
103
- expected_options = {
104
- attribute: :age,
105
- form: @form,
106
- label: {content: "Custom label"},
107
- hint: {content: "Custom hint"}
108
- }
109
- assert_equal expected_options, @component.send(:options)
110
- end
111
- end
112
-
113
- class NumberIndexTest < ViewComponent::TestCase
114
- def setup
115
- @field = Uchi::Field::Number.new(:age)
116
- @record = Author.new(name: "Test Author")
117
- @record.define_singleton_method(:age) { 25 }
118
- @repository = Uchi::Repositories::Author.new
119
-
120
- @component = Uchi::Field::Number::Index.new(
121
- field: @field,
122
- record: @record,
123
- repository: @repository
124
- )
125
- end
126
-
127
- test "inherits from Base component" do
128
- assert_kind_of Uchi::Field::Base::Index, @component
129
- end
130
-
131
- test "renders the field content" do
132
- result = render_inline(@component)
133
-
134
- assert_includes result.to_html, "25"
135
- end
136
- end
137
-
138
- class NumberShowTest < ViewComponent::TestCase
139
- def setup
140
- @field = Uchi::Field::Number.new(:age)
141
- @record = Author.new(name: "Test Author")
142
- @record.define_singleton_method(:age) { 25 }
143
- @repository = Uchi::Repositories::Author.new
144
-
145
- @component = Uchi::Field::Number::Show.new(
146
- field: @field,
147
- record: @record,
148
- repository: @repository
149
- )
150
- end
151
-
152
- test "inherits from Base component" do
153
- assert_kind_of Uchi::Field::Base::Show, @component
154
- end
155
-
156
- test "renders the field content" do
157
- result = render_inline(@component)
158
-
159
- assert_includes result.to_html, "25"
160
- end
161
- end
162
- end
163
- end
@@ -1,159 +0,0 @@
1
- require "test_helper"
2
- require "ostruct"
3
-
4
- module Uchi
5
- class Field
6
- class StringTest < ActiveSupport::TestCase
7
- def setup
8
- @field = Uchi::Field::String.new(:name)
9
- @form = OpenStruct.new(object: OpenStruct.new(name: "Test Name"))
10
- @repository = Uchi::Repositories::Author.new
11
- end
12
-
13
- test "inherits from Uchi::Field" do
14
- assert_kind_of Uchi::Field, @field
15
- end
16
-
17
- test "has default options" do
18
- assert_equal [:edit, :index, :show], @field.on
19
- assert @field.searchable?
20
- assert @field.sortable?
21
- end
22
-
23
- test "#edit_component returns an instance of Edit component" do
24
- component = @field.edit_component(form: @form, hint: "Custom hint", label: "Custom label", repository: @repository)
25
- assert_equal "Custom hint", component.hint
26
- assert_equal "Custom label", component.label
27
- assert_equal @field, component.field
28
- assert_equal @form, component.form
29
- assert_equal @repository, component.repository
30
- assert_kind_of Uchi::Field::String::Edit, component
31
- end
32
-
33
- test "#index_component returns an instance of Index component" do
34
- component = @field.index_component(record: @form.object, repository: @repository)
35
- assert_equal @field, component.field
36
- assert_equal @form.object, component.record
37
- assert_equal @repository, component.repository
38
- assert_kind_of Uchi::Field::String::Index, component
39
- end
40
-
41
- test "#searchable? returns false when explicitly set" do
42
- field = Uchi::Field::String.new(:name).searchable(false)
43
- assert_not field.searchable?
44
- end
45
-
46
- test "#show_component returns an instance of Show component" do
47
- component = @field.show_component(record: @form.object, repository: @repository)
48
- assert_equal @field, component.field
49
- assert_equal @form.object, component.record
50
- assert_equal @repository, component.repository
51
- assert_kind_of Uchi::Field::String::Show, component
52
- end
53
-
54
- test "#sortable? returns false when explicitly set" do
55
- field = Uchi::Field::String.new(:name).sortable(false)
56
- assert_not field.sortable?
57
- end
58
- end
59
-
60
- class StringEditTest < ViewComponent::TestCase
61
- def setup
62
- @field = Uchi::Field::String.new(:name)
63
- @record = Author.new(name: "J.R.R. Tolkien")
64
- @repository = Uchi::Repositories::Author.new
65
- @view_context = ActionController::Base.new.view_context
66
-
67
- @form = ActionView::Helpers::FormBuilder.new(:author, @record, @view_context, {})
68
-
69
- @component = Uchi::Field::String::Edit.new(
70
- field: @field,
71
- form: @form,
72
- hint: "Custom hint",
73
- label: "Custom label",
74
- repository: @repository
75
- )
76
- end
77
-
78
- test "inherits from Base component" do
79
- assert_kind_of Uchi::Field::Base::Edit, @component
80
- end
81
-
82
- test "renders an input field with the field content" do
83
- render_inline(@component)
84
-
85
- assert_selector("input[type='text'][name='author[name]'][value='J.R.R Tolkien']")
86
- end
87
-
88
- test "renders label with specified text" do
89
- render_inline(@component)
90
-
91
- assert_selector("label[for='author_name']", text: "Custom label")
92
- end
93
-
94
- test "renders hint when provided" do
95
- render_inline(@component)
96
-
97
- assert_selector("p[id=author_name_hint]", text: "Custom hint")
98
- end
99
-
100
- test "initializes the input component with the correct options" do
101
- expected_options = {
102
- attribute: :name,
103
- form: @form,
104
- label: {content: "Custom label"},
105
- hint: {content: "Custom hint"}
106
- }
107
- assert_equal expected_options, @component.send(:options)
108
- end
109
- end
110
-
111
- class StringIndexTest < ViewComponent::TestCase
112
- def setup
113
- @field = Uchi::Field::String.new(:name)
114
- @record = Author.new(name: "J.R.R. Tolkien")
115
- @repository = Uchi::Repositories::Author.new
116
-
117
- @component = Uchi::Field::String::Index.new(
118
- field: @field,
119
- record: @record,
120
- repository: @repository
121
- )
122
- end
123
-
124
- test "inherits from Base component" do
125
- assert_kind_of Uchi::Field::Base::Index, @component
126
- end
127
-
128
- test "renders the field content" do
129
- result = render_inline(@component)
130
-
131
- assert_includes result.to_html, "J.R.R. Tolkien"
132
- end
133
- end
134
-
135
- class StringShowTest < ViewComponent::TestCase
136
- def setup
137
- @field = Uchi::Field::String.new(:name)
138
- @record = Author.new(name: "J.R.R. Tolkien")
139
- @repository = Uchi::Repositories::Author.new
140
-
141
- @component = Uchi::Field::String::Show.new(
142
- field: @field,
143
- record: @record,
144
- repository: @repository
145
- )
146
- end
147
-
148
- test "inherits from Base component" do
149
- assert_kind_of Uchi::Field::Base::Show, @component
150
- end
151
-
152
- test "renders the field content" do
153
- result = render_inline(@component)
154
-
155
- assert_includes result.to_html, "J.R.R. Tolkien"
156
- end
157
- end
158
- end
159
- end
@@ -1,160 +0,0 @@
1
- require "test_helper"
2
- require "ostruct"
3
-
4
- module Uchi
5
- class Field
6
- class TextTest < ActiveSupport::TestCase
7
- def setup
8
- @field = Uchi::Field::Text.new(:biography)
9
- @form = OpenStruct.new(object: OpenStruct.new(biography: "Test Biography"))
10
- @repository = Uchi::Repositories::Author.new
11
- end
12
-
13
- test "inherits from Uchi::Field" do
14
- assert_kind_of Uchi::Field, @field
15
- end
16
-
17
- test "has default options" do
18
- assert_equal [:edit, :show], @field.on
19
- assert @field.searchable?
20
- assert @field.sortable?
21
- end
22
-
23
- test "#edit_component returns an instance of Edit component" do
24
- component = @field.edit_component(form: @form, hint: "Custom hint", label: "Custom label", repository: @repository)
25
- assert_equal "Custom hint", component.hint
26
- assert_equal "Custom label", component.label
27
- assert_equal @field, component.field
28
- assert_equal @form, component.form
29
- assert_equal @repository, component.repository
30
- assert_kind_of Uchi::Field::Text::Edit, component
31
- end
32
-
33
- test "#index_component returns an instance of Index component" do
34
- component = @field.index_component(record: @form.object, repository: @repository)
35
- assert_equal @field, component.field
36
- assert_equal @form.object, component.record
37
- assert_equal @repository, component.repository
38
- assert_kind_of Uchi::Field::Text::Index, component
39
- end
40
-
41
- test "#searchable? returns false when explicitly set" do
42
- field = Uchi::Field::Text.new(:biography).searchable(false)
43
- assert_not field.searchable?
44
- end
45
-
46
- test "#show_component returns an instance of Show component" do
47
- component = @field.show_component(record: @form.object, repository: @repository)
48
- assert_equal @field, component.field
49
- assert_equal @form.object, component.record
50
- assert_equal @repository, component.repository
51
- assert_kind_of Uchi::Field::Text::Show, component
52
- end
53
-
54
- test "#sortable? returns false when explicitly set" do
55
- field = Uchi::Field::Text.new(:biography).sortable(false)
56
- assert_not field.sortable?
57
- end
58
- end
59
-
60
- class TextEditTest < ViewComponent::TestCase
61
- def setup
62
- @field = Uchi::Field::Text.new(:biography)
63
- @record = Author.new(name: "J.R.R. Tolkien", biography: "Famous author")
64
- @repository = Uchi::Repositories::Author.new
65
- @view_context = ActionController::Base.new.view_context
66
-
67
- @form = ActionView::Helpers::FormBuilder.new(:author, @record, @view_context, {})
68
-
69
- @component = Uchi::Field::Text::Edit.new(
70
- field: @field,
71
- form: @form,
72
- hint: "Custom hint",
73
- label: "Custom label",
74
- repository: @repository
75
- )
76
- end
77
-
78
- test "inherits from Base component" do
79
- assert_kind_of Uchi::Field::Base::Edit, @component
80
- end
81
-
82
- test "renders a textarea field with the field content" do
83
- render_inline(@component)
84
-
85
- assert_selector("textarea[name='author[biography]'][rows='8']", text: "Famous author")
86
- end
87
-
88
- test "renders label with specified text" do
89
- render_inline(@component)
90
-
91
- assert_selector("label[for='author_biography']", text: "Custom label")
92
- end
93
-
94
- test "renders hint when provided" do
95
- render_inline(@component)
96
-
97
- assert_selector("p[id=author_biography_hint]", text: "Custom hint")
98
- end
99
-
100
- test "initializes the input component with the correct options" do
101
- expected_options = {
102
- attribute: :biography,
103
- form: @form,
104
- label: {content: "Custom label"},
105
- input: {options: {rows: 8}},
106
- hint: {content: "Custom hint"}
107
- }
108
- assert_equal expected_options, @component.send(:options)
109
- end
110
- end
111
-
112
- class TextIndexTest < ViewComponent::TestCase
113
- def setup
114
- @field = Uchi::Field::Text.new(:biography)
115
- @record = Author.new(name: "J.R.R. Tolkien", biography: "Famous author of The Lord of the Rings")
116
- @repository = Uchi::Repositories::Author.new
117
-
118
- @component = Uchi::Field::Text::Index.new(
119
- field: @field,
120
- record: @record,
121
- repository: @repository
122
- )
123
- end
124
-
125
- test "inherits from Base component" do
126
- assert_kind_of Uchi::Field::Base::Index, @component
127
- end
128
-
129
- test "renders the field content" do
130
- result = render_inline(@component)
131
-
132
- assert_includes result.to_html, "Famous author of The Lord of the Rings"
133
- end
134
- end
135
-
136
- class TextShowTest < ViewComponent::TestCase
137
- def setup
138
- @field = Uchi::Field::Text.new(:biography)
139
- @record = Author.new(name: "J.R.R. Tolkien", biography: "Famous author of The Lord of the Rings")
140
- @repository = Uchi::Repositories::Author.new
141
-
142
- @component = Uchi::Field::Text::Show.new(
143
- field: @field,
144
- record: @record,
145
- repository: @repository
146
- )
147
- end
148
-
149
- test "inherits from Base component" do
150
- assert_kind_of Uchi::Field::Base::Show, @component
151
- end
152
-
153
- test "renders the field content" do
154
- result = render_inline(@component)
155
-
156
- assert_includes result.to_html, "Famous author of The Lord of the Rings"
157
- end
158
- end
159
- end
160
- end
@@ -1,171 +0,0 @@
1
- require "test_helper"
2
-
3
- module Uchi
4
- module Ui
5
- module Form
6
- module Input
7
- class CollectionCheckboxesTest < ViewComponent::TestCase
8
- class Tag
9
- attr_accessor :id, :name
10
-
11
- def initialize(id, name)
12
- @id = id
13
- @name = name
14
- end
15
- end
16
-
17
- def setup
18
- @tags = [
19
- Tag.new(1, "Ruby"),
20
- Tag.new(2, "Rails"),
21
- Tag.new(3, "JavaScript")
22
- ]
23
-
24
- @record = Author.new(name: "Test Author")
25
- @record.define_singleton_method(:tag_ids) { [1, 3] }
26
- @record.define_singleton_method(:tag_ids=) { |val| @tag_ids = val }
27
-
28
- @view_context = ActionController::Base.new.view_context
29
- @form = ActionView::Helpers::FormBuilder.new(:author, @record, @view_context, {})
30
-
31
- @component = Uchi::Ui::Form::Input::CollectionCheckboxes.new(
32
- attribute: :tag_ids,
33
- collection: @tags,
34
- form: @form,
35
- label: "Select Tags",
36
- hint: "Choose one or more tags",
37
- value_method: :id,
38
- text_method: :name
39
- )
40
- end
41
-
42
- test "renders collection checkboxes" do
43
- render_inline(@component)
44
-
45
- assert_selector("input[type='hidden'][name='author[tag_ids][]']", visible: :all, count: 1)
46
- assert_selector("input[type='checkbox'][name='author[tag_ids][]']", count: 3)
47
- end
48
-
49
- test "renders label when provided" do
50
- render_inline(@component)
51
-
52
- assert_selector("label", text: "Select Tags")
53
- end
54
-
55
- test "does not render label when not provided" do
56
- component = Uchi::Ui::Form::Input::CollectionCheckboxes.new(
57
- attribute: :tag_ids,
58
- collection: @tags,
59
- form: @form
60
- )
61
- render_inline(component)
62
-
63
- assert_no_selector("label", text: "Select Tags")
64
- end
65
-
66
- test "renders hint when provided" do
67
- render_inline(@component)
68
-
69
- assert_selector("p", text: "Choose one or more tags")
70
- end
71
-
72
- test "does not render hint when not provided" do
73
- component = Uchi::Ui::Form::Input::CollectionCheckboxes.new(
74
- attribute: :tag_ids,
75
- collection: @tags,
76
- form: @form
77
- )
78
- render_inline(component)
79
-
80
- assert_no_selector("p", text: "Choose one or more tags")
81
- end
82
-
83
- test "renders checkbox labels for each item" do
84
- render_inline(@component)
85
-
86
- assert_selector("label", text: "Ruby")
87
- assert_selector("label", text: "Rails")
88
- assert_selector("label", text: "JavaScript")
89
- end
90
-
91
- test "renders checkboxes with correct values" do
92
- render_inline(@component)
93
-
94
- assert_selector("input[type='checkbox'][value='1']")
95
- assert_selector("input[type='checkbox'][value='2']")
96
- assert_selector("input[type='checkbox'][value='3']")
97
- end
98
-
99
- test "applies disabled state to all checkboxes" do
100
- component = Uchi::Ui::Form::Input::CollectionCheckboxes.new(
101
- attribute: :tag_ids,
102
- collection: @tags,
103
- form: @form,
104
- disabled: true
105
- )
106
- render_inline(component)
107
-
108
- assert_selector("input[type='checkbox'][disabled]", count: 3)
109
- end
110
-
111
- test "renders errors when present" do
112
- @record.errors.add(:tag_ids, "must be selected")
113
- render_inline(@component)
114
-
115
- assert_selector("p", text: /MUST BE SELECTED/i)
116
- end
117
-
118
- test "applies error styling when errors present" do
119
- @record.errors.add(:tag_ids, "must be selected")
120
- component = Uchi::Ui::Form::Input::CollectionCheckboxes.new(
121
- attribute: :tag_ids,
122
- collection: @tags,
123
- form: @form
124
- )
125
-
126
- render_inline(component)
127
-
128
- # Check that error classes are applied to checkboxes
129
- assert component.errors?
130
- end
131
-
132
- test "uses custom value and text methods" do
133
- custom_tags = [
134
- OpenStruct.new(identifier: "ruby", title: "Ruby Programming"),
135
- OpenStruct.new(identifier: "rails", title: "Rails Framework")
136
- ]
137
-
138
- component = Uchi::Ui::Form::Input::CollectionCheckboxes.new(
139
- attribute: :tag_ids,
140
- collection: custom_tags,
141
- form: @form,
142
- value_method: :identifier,
143
- text_method: :title
144
- )
145
-
146
- render_inline(component)
147
-
148
- assert_selector("input[type='checkbox'][value='ruby']")
149
- assert_selector("input[type='checkbox'][value='rails']")
150
- assert_selector("label", text: "Ruby Programming")
151
- assert_selector("label", text: "Rails Framework")
152
- end
153
-
154
- test "handles empty collection" do
155
- component = Uchi::Ui::Form::Input::CollectionCheckboxes.new(
156
- attribute: :tag_ids,
157
- collection: [],
158
- form: @form
159
- )
160
-
161
- render_inline(component)
162
-
163
- # Should only have the hidden field, no checkboxes
164
- assert_selector("input[type='hidden'][name='author[tag_ids][]']", visible: :all, count: 1)
165
- assert_selector("input[type='checkbox']", count: 0)
166
- end
167
- end
168
- end
169
- end
170
- end
171
- end