activeadmin-tom_select 4.1.1 → 4.2.0.beta1

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 (56) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +10 -7
  3. data/.gitignore +1 -0
  4. data/Appraisals +2 -2
  5. data/CHANGELOG.md +12 -0
  6. data/Gemfile +1 -1
  7. data/Gemfile.lock +137 -104
  8. data/README.md +1 -1
  9. data/docs/activeadmin-4-asset-setup.md +171 -0
  10. data/docs/activeadmin-tailwind-4.md +125 -0
  11. data/docs/guide-update-your-app.md +89 -356
  12. data/docs/setup-activeadmin-app.md +69 -492
  13. data/docs/setup-activeadmin-gem.md +83 -449
  14. data/gemfiles/rails_7.x_active_admin_4.x.gemfile +2 -1
  15. data/gemfiles/rails_7.x_active_admin_4.x.gemfile.lock +158 -116
  16. data/gemfiles/rails_8.x_active_admin_4.x.gemfile +2 -1
  17. data/gemfiles/rails_8.x_active_admin_4.x.gemfile.lock +148 -104
  18. data/lib/activeadmin/tom_select/option_collection.rb +4 -6
  19. data/lib/activeadmin/tom_select/select_input_extension.rb +42 -20
  20. data/lib/activeadmin/tom_select/version.rb +1 -1
  21. data/npm-package/package.json +1 -1
  22. data/sonar-project.properties +11 -11
  23. data/spec/features/ajax_false_input_spec.rb +74 -0
  24. data/spec/features/end_to_end_spec.rb +33 -20
  25. data/spec/features/input_errors_spec.rb +1 -3
  26. data/spec/features/options_dsl_spec.rb +6 -6
  27. data/spec/internal/Gemfile +1 -0
  28. data/spec/internal/Gemfile.lock +152 -107
  29. data/spec/internal/app/admin/option_values.rb +6 -6
  30. data/spec/internal/app/admin/posts.rb +4 -4
  31. data/spec/internal/app/admin/test_ajax_false.rb +18 -0
  32. data/spec/internal/app/admin/variants.rb +2 -2
  33. data/spec/internal/app/assets/stylesheets/active_admin.tailwind.css +8 -5
  34. data/spec/internal/app/models/option_value.rb +8 -0
  35. data/spec/internal/bin/tailwindcss +27 -0
  36. data/spec/internal/lib/tasks/active_admin.rake +9 -4
  37. data/spec/internal/package-lock.json +15 -1371
  38. data/spec/internal/package.json +1 -2
  39. data/{docs/tailwind-4/tailwind-active_admin.config.js → spec/internal/tailwind-active_admin.config.mjs} +9 -4
  40. data/spec/internal/yarn.lock +128 -728
  41. metadata +9 -23
  42. data/docs/activeadmin-4-detailed-reference.md +0 -932
  43. data/docs/activeadmin-4-gem-migration-guide.md +0 -313
  44. data/docs/combustion.md +0 -213
  45. data/docs/fail.png +0 -0
  46. data/docs/normal.png +0 -0
  47. data/docs/propshaft-readme.md +0 -320
  48. data/docs/propshaft-upgrade.md +0 -484
  49. data/docs/tailwind/blog-page.md +0 -341
  50. data/docs/tailwind/upgrade-guide-enhanced.md +0 -438
  51. data/docs/tailwind/upgrade-guide.md +0 -416
  52. data/docs/tailwind-4/active_admin.rake +0 -38
  53. data/docs/tailwind-4/active_admin.tailwind.css +0 -415
  54. data/docs/test-app-change.md +0 -154
  55. data/docs/test-environment-fixes.md +0 -58
  56. data/docs/update-tom-select.md +0 -348
@@ -0,0 +1,74 @@
1
+ require 'rails_helper'
2
+
3
+ RSpec.describe 'ajax: false input behavior', type: :feature do
4
+ before do
5
+ # Create test data
6
+ @users = 3.times.map { |i| User.create!(name: "User #{i + 1}") }
7
+ @categories = 3.times.map { |i| Category.create!(name: "Category #{i + 1}") }
8
+
9
+ @post = Post.create!(
10
+ title: 'Test Post',
11
+ body: 'Test content',
12
+ category: @categories.first,
13
+ user: @users.first
14
+ )
15
+ end
16
+
17
+ describe 'filter inputs with ajax: false' do
18
+ it 'renders all options inline without ajax URL' do
19
+ visit '/admin/ajax_false_posts'
20
+
21
+ # Should have tom-select-input class
22
+ expect(page).to have_selector('.tom-select-input')
23
+
24
+ # Should NOT have data-ajax-url attribute
25
+ page_content = page.html
26
+ expect(page_content).not_to match(/<select[^>]*name="q\[category_id_eq\]"[^>]*data-ajax-url/)
27
+ expect(page_content).not_to match(/<select[^>]*name="q\[user_id_eq\]"[^>]*data-ajax-url/)
28
+
29
+ # Should have options rendered inline
30
+ within('select[name="q[category_id_eq]"]', visible: :all) do
31
+ @categories.each do |category|
32
+ expect(page).to have_selector("option[value='#{category.id}']", text: category.name,
33
+ visible: :all)
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ describe 'form inputs with ajax: false' do
40
+ it 'renders all options inline in new form' do
41
+ visit '/admin/ajax_false_posts/new'
42
+
43
+ expect(page).to have_selector('.tom-select-input')
44
+
45
+ # Check that options are rendered inline
46
+ within('select#post_category_id', visible: :all) do
47
+ @categories.each do |category|
48
+ expect(page).to have_selector("option[value='#{category.id}']", text: category.name,
49
+ visible: :all)
50
+ end
51
+ end
52
+ end
53
+
54
+ it 'preselects value in edit form' do
55
+ visit "/admin/ajax_false_posts/#{@post.id}/edit"
56
+
57
+ # Should have the selected option
58
+ within('select#post_category_id', visible: :all) do
59
+ selected_option = find("option[value='#{@categories.first.id}']", visible: :all)
60
+ expect(selected_option).to be_selected
61
+ end
62
+ end
63
+ end
64
+
65
+ describe 'comparison with ajax: true (default)' do
66
+ it 'default inputs use ajax' do
67
+ visit '/admin/posts'
68
+
69
+ # Default searchable_select should have data-ajax-url
70
+ page_content = page.html
71
+ expect(page_content).to match(/<select[^>]*name="q\[category_id_eq\]"[^>]*data-ajax-url/)
72
+ end
73
+ end
74
+ end
@@ -80,17 +80,16 @@ RSpec.describe 'end to end', type: :feature, js: true do
80
80
  end
81
81
  end
82
82
 
83
- context 'class with nested belongs_to association',
84
- skip: 'Nested routes issue with belongs_to in test environment' do
83
+ context 'class with nested belongs_to association' do
85
84
  # Using static admin files: option_types.rb, products.rb, option_values.rb, variants.rb
86
85
 
87
86
  describe 'new page with searchable select filter' do
88
87
  it 'loads filter input options' do
89
88
  option_type = OptionType.create(name: 'Color')
90
89
  ot = OptionType.create(name: 'Size')
91
- OptionValue.create(value: 'Black', option_type: option_type)
92
- OptionValue.create(value: 'Orange', option_type: option_type)
93
- OptionValue.create(value: 'M', option_type: ot)
90
+ OptionValue.create(name: 'Black', option_type: option_type)
91
+ OptionValue.create(name: 'Orange', option_type: option_type)
92
+ OptionValue.create(name: 'M', option_type: ot)
94
93
  product = Product.create(name: 'Cap', option_type: option_type)
95
94
 
96
95
  visit "/admin/products/#{product.id}/variants/new"
@@ -139,9 +138,9 @@ RSpec.describe 'end to end', type: :feature, js: true do
139
138
  has_searchable = page.has_css?(searchable_select_css)
140
139
  puts "Page HTML includes searchable-select-input: #{has_searchable}"
141
140
 
142
- # Debug: Print the actual HTML around the select
141
+ # Debug: Print HTML around the select - use first match if there are multiple
143
142
  if page.has_css?(searchable_select_css)
144
- select_element = find(searchable_select_css, visible: :all)
143
+ select_element = all(searchable_select_css, visible: :all).first
145
144
  parent_html = begin
146
145
  select_element.find(:xpath,
147
146
  '..')['outerHTML'][0..500]
@@ -151,7 +150,10 @@ RSpec.describe 'end to end', type: :feature, js: true do
151
150
  puts "Parent element HTML: #{parent_html}..."
152
151
  end
153
152
 
154
- expand_select_box
153
+ # Target the option_value select specifically
154
+ within('#variant_option_value_input') do
155
+ find('.ts-control', match: :first).click
156
+ end
155
157
  wait_for_ajax
156
158
 
157
159
  expect(select_box_items).to eq(%w[Black Orange])
@@ -160,15 +162,18 @@ RSpec.describe 'end to end', type: :feature, js: true do
160
162
  it 'allows filtering options by term' do
161
163
  option_type = OptionType.create(name: 'Color')
162
164
  ot = OptionType.create(name: 'Size')
163
- OptionValue.create(value: 'Black', option_type: option_type)
164
- OptionValue.create(value: 'Orange', option_type: option_type)
165
- OptionValue.create(value: 'M', option_type: ot)
165
+ OptionValue.create(name: 'Black', option_type: option_type)
166
+ OptionValue.create(name: 'Orange', option_type: option_type)
167
+ OptionValue.create(name: 'M', option_type: ot)
166
168
  product = Product.create(name: 'Cap', option_type: option_type)
167
169
 
168
170
  visit "/admin/products/#{product.id}/variants/new"
169
171
 
170
- expand_select_box
171
- enter_search_term('O')
172
+ # Target the option_value select specifically
173
+ within('#variant_option_value_input') do
174
+ find('.ts-control', match: :first).click
175
+ find('input[type="text"]', match: :first).send_keys('O')
176
+ end
172
177
  wait_for_ajax
173
178
 
174
179
  expect(select_box_items).to eq(%w[Orange])
@@ -176,17 +181,22 @@ RSpec.describe 'end to end', type: :feature, js: true do
176
181
 
177
182
  it 'loads more items when scrolling down' do
178
183
  option_type = OptionType.create(name: 'Color')
179
- 15.times { |i| OptionValue.create(value: "Black #{i}", option_type: option_type) }
184
+ 15.times { |i| OptionValue.create(name: "Black #{i}", option_type: option_type) }
180
185
  product = Product.create(name: 'Cap', option_type: option_type)
181
186
 
182
187
  visit "/admin/products/#{product.id}/variants/new"
183
188
 
184
- expand_select_box
189
+ # Target the option_value select specifically
190
+ within('#variant_option_value_input') do
191
+ find('.ts-control', match: :first).click
192
+ end
185
193
  wait_for_ajax
186
194
  scroll_select_box_list
187
195
  wait_for_ajax
188
196
 
189
- expect(select_box_items.size).to eq(15)
197
+ # Default page size is 10, scrolling might load more pages
198
+ expect(select_box_items.size).to be >= 10
199
+ expect(select_box_items.size).to be <= 20
190
200
  end
191
201
  end
192
202
 
@@ -194,15 +204,18 @@ RSpec.describe 'end to end', type: :feature, js: true do
194
204
  it 'preselects item' do
195
205
  option_type = OptionType.create(name: 'Color')
196
206
  ot = OptionType.create(name: 'Size')
197
- option_value = OptionValue.create(value: 'Black', option_type: option_type)
198
- OptionValue.create(value: 'Orange', option_type: option_type)
199
- OptionValue.create(value: 'M', option_type: ot)
207
+ option_value = OptionValue.create(name: 'Black', option_type: option_type)
208
+ OptionValue.create(name: 'Orange', option_type: option_type)
209
+ OptionValue.create(name: 'M', option_type: ot)
200
210
  product = Product.create(name: 'Cap', option_type: option_type)
201
211
  variant = Variant.create(product: product, option_value: option_value)
202
212
 
203
213
  visit "/admin/products/#{product.id}/variants/#{variant.id}/edit"
204
214
 
205
- expect(select_box_selected_item_text).to eq('Black')
215
+ # Target the option_value select specifically
216
+ within('#variant_option_value_input') do
217
+ expect(page).to have_css('.ts-control .item', text: 'Black')
218
+ end
206
219
  end
207
220
  end
208
221
  end
@@ -38,9 +38,7 @@ RSpec.describe 'searchable select', type: :feature do
38
38
  end
39
39
 
40
40
  visit '/admin/test_error_post2s'
41
- expect(page).to have_content(
42
- "No option collection named 'nonexistent_collection' defined in 'Category' admin."
43
- )
41
+ expect(page).to have_content('The required ajax endpoint is missing')
44
42
  end
45
43
 
46
44
  it 'shows helpful error message if ajax resource does not have an admin' do
@@ -192,7 +192,7 @@ RSpec.describe 'searchable_select_options dsl', type: :request do
192
192
  expect(json_response).to include(results: array_including(a_hash_including(text: 'A post')))
193
193
  end
194
194
 
195
- it 'fails with helpful message if scope option is missing' do
195
+ it 'raises error when scope option is missing' do
196
196
  expect do
197
197
  ActiveAdminHelpers.setup do
198
198
  ActiveAdmin.register(Post) do
@@ -202,7 +202,7 @@ RSpec.describe 'searchable_select_options dsl', type: :request do
202
202
  end.to raise_error(/Missing option: scope/)
203
203
  end
204
204
 
205
- it 'fails with helpful message if display_text are missing' do
205
+ it 'raises error when display_text and text_attribute are missing' do
206
206
  expect do
207
207
  ActiveAdminHelpers.setup do
208
208
  ActiveAdmin.register(Post) do
@@ -210,18 +210,18 @@ RSpec.describe 'searchable_select_options dsl', type: :request do
210
210
  filter: ->(_term, scope) { scope })
211
211
  end
212
212
  end
213
- end.to raise_error(/Missing option: display_text/)
213
+ end.to raise_error(/Missing option: text_attribute/)
214
214
  end
215
215
 
216
- it 'fails with helpful message if filter option is missing' do
216
+ it 'raises error when filter and text_attribute are missing' do
217
217
  expect do
218
218
  ActiveAdminHelpers.setup do
219
219
  ActiveAdmin.register(Post) do
220
220
  searchable_select_options(scope: Post,
221
- display_text: ->(_term, scope) { scope })
221
+ display_text: ->(record) { record.title }) # rubocop:disable Style/SymbolProc
222
222
  end
223
223
  end
224
- end.to raise_error(/Missing option: filter/)
224
+ end.to raise_error(/Missing option: text_attribute/)
225
225
  end
226
226
 
227
227
  def json_response
@@ -12,6 +12,7 @@ gem 'puma', '>= 5.0'
12
12
  gem 'jsbundling-rails'
13
13
  # Bundle and process CSS [https://github.com/rails/cssbundling-rails]
14
14
  gem 'cssbundling-rails'
15
+ gem 'tailwindcss-rails', '~> 4.4.0'
15
16
 
16
17
  # ActiveAdmin and dependencies
17
18
  gem 'activeadmin', '~> 4.0.0.beta'