activeadmin-tom_select 4.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.
Files changed (196) hide show
  1. checksums.yaml +7 -0
  2. data/.actrc +20 -0
  3. data/.claude/commands/fix-tests.md +203 -0
  4. data/.github/workflows/ci.yml +174 -0
  5. data/.github/workflows/npm-publish.yml +50 -0
  6. data/.gitignore +35 -0
  7. data/.npmignore +58 -0
  8. data/.rspec +1 -0
  9. data/.rubocop.yml +75 -0
  10. data/.yardopts +2 -0
  11. data/AGENTS.md +39 -0
  12. data/Appraisals +9 -0
  13. data/CHANGELOG.md +64 -0
  14. data/CLAUDE.md +157 -0
  15. data/Gemfile +12 -0
  16. data/Gemfile.lock +368 -0
  17. data/LICENSE.txt +25 -0
  18. data/README.md +483 -0
  19. data/Rakefile +4 -0
  20. data/activeadmin-tom_select.gemspec +43 -0
  21. data/bin/rspec +17 -0
  22. data/config/database.yml +16 -0
  23. data/docs/activeadmin-4-detailed-reference.md +932 -0
  24. data/docs/activeadmin-4-gem-migration-guide.md +313 -0
  25. data/docs/combustion.md +213 -0
  26. data/docs/fail.png +0 -0
  27. data/docs/guide-update-your-app.md +283 -0
  28. data/docs/normal.png +0 -0
  29. data/docs/propshaft-readme.md +320 -0
  30. data/docs/propshaft-upgrade.md +484 -0
  31. data/docs/setup-activeadmin-app.md +552 -0
  32. data/docs/setup-activeadmin-gem.md +535 -0
  33. data/docs/tailwind/blog-page.md +341 -0
  34. data/docs/tailwind/upgrade-guide-enhanced.md +438 -0
  35. data/docs/tailwind/upgrade-guide.md +416 -0
  36. data/docs/tailwind-4/active_admin.rake +38 -0
  37. data/docs/tailwind-4/active_admin.tailwind.css +415 -0
  38. data/docs/tailwind-4/tailwind-active_admin.config.js +18 -0
  39. data/docs/test-app-change.md +154 -0
  40. data/docs/test-environment-fixes.md +58 -0
  41. data/docs/update-tom-select.md +184 -0
  42. data/docs/upload-system.md +225 -0
  43. data/gemfiles/rails_7.x_active_admin_4.x.gemfile +10 -0
  44. data/gemfiles/rails_7.x_active_admin_4.x.gemfile.lock +377 -0
  45. data/gemfiles/rails_8.x_active_admin_4.x.gemfile +10 -0
  46. data/gemfiles/rails_8.x_active_admin_4.x.gemfile.lock +372 -0
  47. data/lefthook.yml +17 -0
  48. data/lib/activeadmin/inputs/filters/searchable_select_input.rb +19 -0
  49. data/lib/activeadmin/inputs/searchable_select_input.rb +16 -0
  50. data/lib/activeadmin/tom_select/engine.rb +17 -0
  51. data/lib/activeadmin/tom_select/option_collection.rb +128 -0
  52. data/lib/activeadmin/tom_select/resource_dsl_extension.rb +56 -0
  53. data/lib/activeadmin/tom_select/resource_extension.rb +10 -0
  54. data/lib/activeadmin/tom_select/select_input_extension.rb +168 -0
  55. data/lib/activeadmin/tom_select/version.rb +5 -0
  56. data/lib/activeadmin/tom_select.rb +20 -0
  57. data/lib/activeadmin-tom_select.rb +5 -0
  58. data/lib/generators/active_admin/tom_select/install/install_generator.rb +180 -0
  59. data/npm-package/package-lock.json +51 -0
  60. data/npm-package/package.json +43 -0
  61. data/npm-package/src/index.js +153 -0
  62. data/npm-package/src/tom-select-tailwind.css +392 -0
  63. data/sonar-project.properties +25 -0
  64. data/spec/features/ajax_params_spec.rb +31 -0
  65. data/spec/features/asset_pipeline_diagnostic_spec.rb +155 -0
  66. data/spec/features/end_to_end_spec.rb +273 -0
  67. data/spec/features/filter_input_spec.rb +144 -0
  68. data/spec/features/form_input_spec.rb +122 -0
  69. data/spec/features/inline_ajax_setting_spec.rb +26 -0
  70. data/spec/features/input_errors_spec.rb +76 -0
  71. data/spec/features/input_html_options_spec.rb +30 -0
  72. data/spec/features/options_dsl_spec.rb +230 -0
  73. data/spec/features/production_build_spec.rb +108 -0
  74. data/spec/internal/.node-version +1 -0
  75. data/spec/internal/Gemfile +43 -0
  76. data/spec/internal/Gemfile.lock +333 -0
  77. data/spec/internal/Procfile.dev +3 -0
  78. data/spec/internal/README.md +24 -0
  79. data/spec/internal/Rakefile +6 -0
  80. data/spec/internal/app/admin/categories.rb +26 -0
  81. data/spec/internal/app/admin/dashboard.rb +29 -0
  82. data/spec/internal/app/admin/option_types.rb +19 -0
  83. data/spec/internal/app/admin/option_values.rb +30 -0
  84. data/spec/internal/app/admin/posts.rb +27 -0
  85. data/spec/internal/app/admin/products.rb +22 -0
  86. data/spec/internal/app/admin/rgb_colors.rb +25 -0
  87. data/spec/internal/app/admin/tag_names.rb +21 -0
  88. data/spec/internal/app/admin/test_ajax_params_category.rb +10 -0
  89. data/spec/internal/app/admin/test_ajax_params_post.rb +20 -0
  90. data/spec/internal/app/admin/test_form_post_class.rb +7 -0
  91. data/spec/internal/app/admin/test_form_post_custom.rb +11 -0
  92. data/spec/internal/app/admin/test_form_post_resource.rb +11 -0
  93. data/spec/internal/app/admin/test_form_post_resource_custom.rb +12 -0
  94. data/spec/internal/app/admin/test_inline_ajax_post.rb +9 -0
  95. data/spec/internal/app/admin/test_input_html_post.rb +11 -0
  96. data/spec/internal/app/admin/test_posts_display_text.rb +9 -0
  97. data/spec/internal/app/admin/test_posts_filter.rb +9 -0
  98. data/spec/internal/app/admin/test_posts_named.rb +9 -0
  99. data/spec/internal/app/admin/test_posts_pagination.rb +9 -0
  100. data/spec/internal/app/admin/test_posts_payload_lambda.rb +11 -0
  101. data/spec/internal/app/admin/test_posts_payload_proc.rb +9 -0
  102. data/spec/internal/app/admin/test_posts_scope_lambda.rb +8 -0
  103. data/spec/internal/app/admin/test_posts_scope_params.rb +8 -0
  104. data/spec/internal/app/admin/test_posts_scope_user.rb +8 -0
  105. data/spec/internal/app/admin/test_posts_text_attr.rb +5 -0
  106. data/spec/internal/app/admin/users.rb +23 -0
  107. data/spec/internal/app/admin/variants.rb +31 -0
  108. data/spec/internal/app/assets/config/manifest.js +2 -0
  109. data/spec/internal/app/assets/images/.keep +0 -0
  110. data/spec/internal/app/assets/stylesheets/active_admin.tailwind.css +16 -0
  111. data/spec/internal/app/assets/stylesheets/application.tailwind.css +15 -0
  112. data/spec/internal/app/controllers/application_controller.rb +9 -0
  113. data/spec/internal/app/controllers/concerns/.keep +0 -0
  114. data/spec/internal/app/helpers/application_helper.rb +2 -0
  115. data/spec/internal/app/javascript/active_admin.js +19 -0
  116. data/spec/internal/app/javascript/application.js +2 -0
  117. data/spec/internal/app/jobs/application_job.rb +7 -0
  118. data/spec/internal/app/mailers/application_mailer.rb +4 -0
  119. data/spec/internal/app/models/admin_user.rb +9 -0
  120. data/spec/internal/app/models/application_record.rb +3 -0
  121. data/spec/internal/app/models/article.rb +12 -0
  122. data/spec/internal/app/models/category.rb +12 -0
  123. data/spec/internal/app/models/color.rb +9 -0
  124. data/spec/internal/app/models/concerns/.keep +0 -0
  125. data/spec/internal/app/models/internal/tag_name.rb +14 -0
  126. data/spec/internal/app/models/internal_tag_name.rb +11 -0
  127. data/spec/internal/app/models/option_type.rb +12 -0
  128. data/spec/internal/app/models/option_value.rb +4 -0
  129. data/spec/internal/app/models/post.rb +15 -0
  130. data/spec/internal/app/models/product.rb +12 -0
  131. data/spec/internal/app/models/rgb_color.rb +16 -0
  132. data/spec/internal/app/models/tag.rb +12 -0
  133. data/spec/internal/app/models/tagging.rb +12 -0
  134. data/spec/internal/app/models/user.rb +12 -0
  135. data/spec/internal/app/models/variant.rb +12 -0
  136. data/spec/internal/app/views/layouts/application.html.erb +28 -0
  137. data/spec/internal/app/views/layouts/mailer.html.erb +13 -0
  138. data/spec/internal/app/views/layouts/mailer.text.erb +1 -0
  139. data/spec/internal/app/views/pwa/manifest.json.erb +22 -0
  140. data/spec/internal/app/views/pwa/service-worker.js +26 -0
  141. data/spec/internal/bin/bundle +117 -0
  142. data/spec/internal/bin/dev +11 -0
  143. data/spec/internal/bin/rackup +27 -0
  144. data/spec/internal/bin/rails +4 -0
  145. data/spec/internal/bin/rake +4 -0
  146. data/spec/internal/bin/setup +37 -0
  147. data/spec/internal/config/application.rb +50 -0
  148. data/spec/internal/config/boot.rb +3 -0
  149. data/spec/internal/config/credentials.yml.enc +1 -0
  150. data/spec/internal/config/database.yml +32 -0
  151. data/spec/internal/config/environment.rb +5 -0
  152. data/spec/internal/config/environments/development.rb +63 -0
  153. data/spec/internal/config/environments/production.rb +86 -0
  154. data/spec/internal/config/environments/test.rb +50 -0
  155. data/spec/internal/config/initializers/active_admin.rb +54 -0
  156. data/spec/internal/config/initializers/assets.rb +8 -0
  157. data/spec/internal/config/initializers/content_security_policy.rb +25 -0
  158. data/spec/internal/config/initializers/devise.rb +315 -0
  159. data/spec/internal/config/initializers/filter_parameter_logging.rb +8 -0
  160. data/spec/internal/config/initializers/inflections.rb +16 -0
  161. data/spec/internal/config/initializers/searchable_select.rb +6 -0
  162. data/spec/internal/config/locales/devise.en.yml +65 -0
  163. data/spec/internal/config/locales/en.yml +31 -0
  164. data/spec/internal/config/master.key +1 -0
  165. data/spec/internal/config/puma.rb +38 -0
  166. data/spec/internal/config/routes.rb +17 -0
  167. data/spec/internal/config.ru +6 -0
  168. data/spec/internal/db/schema.rb +174 -0
  169. data/spec/internal/db/seeds.rb +167 -0
  170. data/spec/internal/esbuild.config.js +34 -0
  171. data/spec/internal/lib/tasks/.keep +0 -0
  172. data/spec/internal/lib/tasks/active_admin.rake +55 -0
  173. data/spec/internal/log/.keep +0 -0
  174. data/spec/internal/package-lock.json +1954 -0
  175. data/spec/internal/package.json +21 -0
  176. data/spec/internal/public/400.html +114 -0
  177. data/spec/internal/public/404.html +114 -0
  178. data/spec/internal/public/406-unsupported-browser.html +114 -0
  179. data/spec/internal/public/422.html +114 -0
  180. data/spec/internal/public/500.html +114 -0
  181. data/spec/internal/public/icon.png +0 -0
  182. data/spec/internal/public/icon.svg +3 -0
  183. data/spec/internal/public/robots.txt +1 -0
  184. data/spec/internal/script/.keep +0 -0
  185. data/spec/internal/storage/.keep +0 -0
  186. data/spec/internal/tailwind.config.js +23 -0
  187. data/spec/internal/vendor/.keep +0 -0
  188. data/spec/internal/yarn.lock +824 -0
  189. data/spec/rails_helper.rb +62 -0
  190. data/spec/spec_helper.rb +138 -0
  191. data/spec/support/active_admin_helpers.rb +17 -0
  192. data/spec/support/capybara.rb +8 -0
  193. data/spec/support/models.rb +11 -0
  194. data/spec/support/pluck_polyfill.rb +12 -0
  195. data/spec/support/reset_settings.rb +5 -0
  196. metadata +497 -0
@@ -0,0 +1,392 @@
1
+ /*
2
+ Tom Select Tailwind CSS styles for ActiveAdmin Searchable Select
3
+
4
+ Credits:
5
+ - Based on discussion at https://github.com/orchidjs/tom-select/discussions/693
6
+ - Original Tailwind styles by @LeZellus (https://github.com/LeZellus)
7
+ - Updated for Tailwind CSS v4 compatibility
8
+ */
9
+
10
+ /* Plugin: Drag & Drop */
11
+ .ts-wrapper.plugin-drag_drop.multi > .ts-control > div.ui-sortable-placeholder {
12
+ @apply visible border-none;
13
+ }
14
+
15
+ .ts-wrapper.plugin-drag_drop .ui-sortable-placeholder::after {
16
+ content: "!";
17
+ visibility: hidden;
18
+ }
19
+
20
+ /* Plugin: Checkbox Options */
21
+ .plugin-checkbox_options .option input {
22
+ margin-right: 0.5rem;
23
+ }
24
+
25
+ /* Plugin: Clear Button */
26
+ .plugin-clear_button .clear-button {
27
+ @apply opacity-0 absolute top-1/2 transform -translate-y-1/2 right-0 cursor-pointer;
28
+ }
29
+
30
+ .plugin-clear_button.single .clear-button {
31
+ @apply right-2;
32
+ }
33
+
34
+ .plugin-clear_button.focus.has-items .clear-button,
35
+ .plugin-clear_button:hover.has-items .clear-button {
36
+ @apply opacity-100;
37
+ }
38
+
39
+ /* Plugin: Dropdown Header */
40
+ .ts-wrapper .dropdown-header {
41
+ @apply relative p-2.5 rounded-t border-b border-gray-300 bg-gray-100;
42
+ }
43
+
44
+ .ts-wrapper .dropdown-header-close {
45
+ @apply absolute right-2 top-1/2 opacity-40 -mt-3 leading-5 text-xl;
46
+ }
47
+
48
+ .ts-wrapper .dropdown-header-close:hover {
49
+ @apply text-black;
50
+ }
51
+
52
+ /* Plugin: Dropdown Input */
53
+ .plugin-dropdown_input .dropdown-input {
54
+ @apply block p-2 shadow-none w-full bg-transparent border-b border-gray-300;
55
+ }
56
+
57
+ /* Plugin: Input Autogrow */
58
+ .ts-wrapper.plugin-input_autogrow.has-items .ts-control > input {
59
+ @apply min-w-0;
60
+ }
61
+
62
+ .ts-wrapper.plugin-input_autogrow.has-items.focus .ts-control > input {
63
+ @apply flex;
64
+ min-width: 4px;
65
+ }
66
+
67
+ .ts-wrapper.plugin-input_autogrow.has-items.focus .ts-control > input::-webkit-input-placeholder {
68
+ @apply text-transparent;
69
+ }
70
+
71
+ .ts-wrapper.plugin-input_autogrow.has-items.focus .ts-control > input::-ms-input-placeholder {
72
+ @apply text-transparent;
73
+ }
74
+
75
+ .ts-wrapper.plugin-input_autogrow.has-items.focus .ts-control > input::placeholder {
76
+ @apply text-transparent;
77
+ }
78
+
79
+ /* Plugin: Optgroup Columns */
80
+ .ts-dropdown.plugin-optgroup_columns .ts-dropdown-content {
81
+ @apply flex;
82
+ }
83
+
84
+ .ts-dropdown.plugin-optgroup_columns .optgroup {
85
+ @apply flex-grow border-t-0 border-r border-gray-100 basis-0 min-w-0;
86
+ }
87
+
88
+ .ts-dropdown.plugin-optgroup_columns .optgroup:last-child {
89
+ @apply border-r-0;
90
+ }
91
+
92
+ .ts-dropdown.plugin-optgroup_columns .optgroup:before {
93
+ @apply hidden;
94
+ }
95
+
96
+ .ts-dropdown.plugin-optgroup_columns .optgroup-header {
97
+ @apply border-t-0;
98
+ }
99
+
100
+ /* Plugin: Remove Button */
101
+ .ts-wrapper.plugin-remove_button .item {
102
+ @apply inline-flex items-center pr-0;
103
+ }
104
+
105
+ .ts-wrapper.plugin-remove_button .item .remove {
106
+ @apply text-inherit no-underline align-middle inline-block p-0 px-1.5 border-l border-gray-300 rounded-r-sm box-border ml-1.5;
107
+ }
108
+
109
+ .ts-wrapper.plugin-remove_button .item .remove:hover {
110
+ background: rgba(0, 0, 0, 0.05);
111
+ }
112
+
113
+ .ts-wrapper.plugin-remove_button .item.active .remove {
114
+ border-left-color: #cacaca;
115
+ }
116
+
117
+ .ts-wrapper.plugin-remove_button.disabled .item .remove:hover {
118
+ background: none;
119
+ }
120
+
121
+ .ts-wrapper.plugin-remove_button.disabled .item .remove {
122
+ border-left-color: white;
123
+ }
124
+
125
+ .ts-wrapper.plugin-remove_button .remove-single {
126
+ position: absolute;
127
+ right: 0;
128
+ top: 0;
129
+ font-size: 23px;
130
+ }
131
+
132
+ /* Core Tom Select Styles */
133
+ .ts-wrapper {
134
+ position: relative;
135
+ }
136
+
137
+ .ts-dropdown,
138
+ .ts-control,
139
+ .ts-control input {
140
+ color: #303030;
141
+ font-family: inherit;
142
+ font-size: 13px;
143
+ line-height: 18px;
144
+ }
145
+
146
+ /* Control (Input) Styles */
147
+ .ts-control,
148
+ .ts-wrapper.single.input-active .ts-control {
149
+ @apply bg-white dark:bg-gray-900;
150
+ cursor: text;
151
+ }
152
+
153
+ .ts-control {
154
+ @apply border border-gray-300 dark:border-gray-600 p-2 w-full overflow-hidden relative z-10 box-border shadow-none rounded-sm flex flex-wrap;
155
+ }
156
+
157
+ .ts-wrapper.multi.has-items .ts-control {
158
+ padding: 6px 8px 3px;
159
+ }
160
+
161
+ .full .ts-control {
162
+ @apply bg-gray-50 dark:bg-gray-900 dark:text-gray-100;
163
+ }
164
+
165
+ .disabled .ts-control,
166
+ .disabled .ts-control * {
167
+ @apply cursor-default;
168
+ }
169
+
170
+ .focus .ts-control {
171
+ @apply shadow-none border-indigo-500 ring-1 ring-indigo-500;
172
+ }
173
+
174
+ .ts-control > * {
175
+ @apply align-baseline inline-block;
176
+ }
177
+
178
+ /* Multi-select Items */
179
+ .ts-wrapper.multi .ts-control > div {
180
+ @apply cursor-pointer m-0 mr-1 my-1 px-2 py-1 bg-gray-100 text-gray-800 border-0 rounded-sm;
181
+ }
182
+
183
+ .ts-wrapper.multi .ts-control > div.active {
184
+ @apply bg-gray-200 text-gray-900;
185
+ }
186
+
187
+ .ts-wrapper.multi.disabled .ts-control > div,
188
+ .ts-wrapper.multi.disabled .ts-control > div.active {
189
+ @apply text-gray-400 bg-gray-50 border-0;
190
+ }
191
+
192
+ /* Selected items display */
193
+ .ts-control .item {
194
+ @apply inline-flex items-center;
195
+ }
196
+
197
+ /* Input Field */
198
+ .ts-control > input {
199
+ @apply inline-block p-0 min-h-0 max-w-full m-0 indent-0 border-0 bg-transparent flex-grow select-auto placeholder-gray-400 dark:placeholder-gray-600;
200
+ min-width: 7rem;
201
+ max-height: none !important;
202
+ box-shadow: none !important;
203
+ outline: none !important;
204
+ }
205
+
206
+ .ts-control > input::-ms-clear {
207
+ @apply hidden;
208
+ }
209
+
210
+ .ts-control > input:focus {
211
+ @apply outline-none;
212
+ }
213
+
214
+ .has-items .ts-control > input {
215
+ margin: 0 4px !important;
216
+ }
217
+
218
+ /* RTL Support */
219
+ .ts-control.rtl {
220
+ @apply text-right;
221
+ }
222
+
223
+ .ts-control.rtl.single .ts-control:after {
224
+ @apply absolute right-auto;
225
+ content: '';
226
+ left: 15px;
227
+ }
228
+
229
+ .ts-control.rtl .ts-control > input {
230
+ margin: 0 4px 0 -2px !important;
231
+ }
232
+
233
+ /* Disabled State */
234
+ .disabled .ts-control {
235
+ @apply opacity-50 bg-gray-50 cursor-not-allowed;
236
+ }
237
+
238
+ .input-hidden .ts-control > input {
239
+ @apply opacity-0 absolute;
240
+ left: -10000px;
241
+ }
242
+
243
+ /* Dropdown Styles */
244
+ .ts-dropdown {
245
+ @apply absolute top-full left-0 w-full z-50 border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-900 mt-1 box-border rounded-sm shadow-sm overflow-hidden;
246
+ }
247
+
248
+ .ts-dropdown [data-selectable] {
249
+ @apply cursor-pointer overflow-hidden;
250
+ }
251
+
252
+ .ts-dropdown [data-selectable] .highlight {
253
+ background: rgba(125, 168, 208, 0.2);
254
+ border-radius: 1px;
255
+ }
256
+
257
+ .ts-dropdown .option,
258
+ .ts-dropdown .optgroup-header,
259
+ .ts-dropdown .no-results,
260
+ .ts-dropdown .create {
261
+ @apply px-3 py-2;
262
+ }
263
+
264
+ .ts-dropdown .option,
265
+ .ts-dropdown [data-disabled],
266
+ .ts-dropdown [data-disabled] [data-selectable].option {
267
+ cursor: inherit;
268
+ opacity: 0.5;
269
+ }
270
+
271
+ .ts-dropdown [data-selectable].option {
272
+ @apply opacity-100 cursor-pointer text-gray-900 dark:text-gray-100;
273
+ }
274
+
275
+ .ts-dropdown .optgroup:first-child .optgroup-header {
276
+ @apply border-t-0;
277
+ }
278
+
279
+ .ts-dropdown .optgroup-header {
280
+ @apply cursor-default bg-gray-50 text-gray-700 font-semibold text-xs uppercase tracking-wider;
281
+ }
282
+
283
+ /* Hover and Active States */
284
+ .ts-dropdown .create:hover,
285
+ .ts-dropdown .option:hover,
286
+ .ts-dropdown .active {
287
+ @apply bg-indigo-50 dark:bg-gray-800 text-indigo-900 dark:text-gray-100;
288
+ }
289
+
290
+ .ts-dropdown .create:hover.create,
291
+ .ts-dropdown .option:hover.create,
292
+ .ts-dropdown .active.create {
293
+ @apply text-gray-600;
294
+ }
295
+
296
+ .ts-dropdown .create {
297
+ @apply text-gray-500;
298
+ }
299
+
300
+ /* Loading Spinner */
301
+ .ts-dropdown .spinner {
302
+ @apply inline-block w-7 h-7 m-1 mx-2;
303
+ }
304
+
305
+ .ts-dropdown .spinner:after {
306
+ @apply block w-6 h-6 m-1 rounded-full border-4 border-gray-300;
307
+ content: " ";
308
+ border-color: #3b82f6 transparent #3b82f6 transparent;
309
+ animation: lds-dual-ring 1.2s linear infinite;
310
+ }
311
+
312
+ @keyframes lds-dual-ring {
313
+ 0% {
314
+ transform: rotate(0deg);
315
+ }
316
+ 100% {
317
+ transform: rotate(360deg);
318
+ }
319
+ }
320
+
321
+ /* Dropdown Content Scrolling */
322
+ .ts-dropdown-content {
323
+ @apply overflow-y-auto overflow-x-hidden max-h-52;
324
+ -webkit-overflow-scrolling: touch;
325
+ scroll-behavior: smooth;
326
+ }
327
+
328
+ /* Accessibility Helper */
329
+ .ts-hidden-accessible {
330
+ @apply border-0 overflow-hidden p-0 absolute whitespace-nowrap;
331
+ width: 1px !important;
332
+ height: 1px !important;
333
+ clip: rect(0 0 0 0) !important;
334
+ clip-path: inset(50%) !important;
335
+ }
336
+
337
+ /* Single Select Arrow */
338
+ .ts-wrapper.single .ts-control:not(.dropdown-active) {
339
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");
340
+ background-position: right 0.5rem center;
341
+ background-repeat: no-repeat;
342
+ background-size: 1.5em 1.5em;
343
+ padding-right: 2.5rem;
344
+ }
345
+
346
+ /* Override ActiveAdmin's width: 100% on inputs inside Tom Select */
347
+ .ts-wrapper.single .ts-control,
348
+ .ts-wrapper.single .ts-control input {
349
+ width: auto !important;
350
+ }
351
+
352
+ /* ActiveAdmin Specific Overrides */
353
+ .searchable_select.input .ts-wrapper {
354
+ min-width: 30%;
355
+ display: inline-block;
356
+ }
357
+
358
+ .searchable_select.input .ts-control {
359
+ margin-top: 0;
360
+ }
361
+
362
+ /* Filter Form Specific Styles */
363
+ .filter_form .searchable_select.input .ts-wrapper {
364
+ width: 100%;
365
+ }
366
+
367
+ /* Hide Duplicate Elements */
368
+ .ts-wrapper + .ts-control {
369
+ display: none;
370
+ }
371
+
372
+ /* Dark Mode Support */
373
+ .dark .ts-control {
374
+ @apply bg-gray-800 border-gray-600 text-gray-100;
375
+ }
376
+
377
+ .dark .ts-dropdown {
378
+ @apply bg-gray-800 border-gray-600;
379
+ }
380
+
381
+ .dark .ts-dropdown .option {
382
+ @apply text-gray-100;
383
+ }
384
+
385
+ .dark .ts-dropdown .option:hover,
386
+ .dark .ts-dropdown [data-selectable].active {
387
+ @apply bg-gray-700 text-white;
388
+ }
389
+
390
+ .dark .ts-wrapper.multi .ts-control > div {
391
+ @apply bg-gray-700 text-gray-100;
392
+ }
@@ -0,0 +1,25 @@
1
+ # SonarQube project configuration
2
+ sonar.projectKey=glebtv_activeadmin-searchable_select
3
+ sonar.projectName=activeadmin-searchable_select
4
+ sonar.projectVersion=0.5.0
5
+
6
+ # Source code directories
7
+ sonar.sources=lib,app
8
+ sonar.tests=spec
9
+
10
+ # Language
11
+ sonar.language=ruby
12
+
13
+ # Encoding
14
+ sonar.sourceEncoding=UTF-8
15
+
16
+ # Ruby specific settings
17
+ sonar.ruby.file.suffixes=.rb,.rake
18
+ sonar.ruby.coverage.reportPaths=coverage/coverage.json
19
+
20
+ # Exclusions
21
+ sonar.exclusions=spec/**/*,vendor/**/*,coverage/**/*,tmp/**/*,*.gemspec,gemfiles/**/*,Appraisals
22
+ sonar.test.exclusions=spec/internal/app/assets/builds/**/*,spec/internal/public/**/*,spec/internal/node_modules/**/*
23
+
24
+ # Coverage exclusions (files that don't need coverage)
25
+ sonar.coverage.exclusions=spec/**/*,vendor/**/*,spec/internal/**/*,**/*_spec.rb
@@ -0,0 +1,31 @@
1
+ require 'rails_helper'
2
+
3
+ require 'support/models'
4
+ require 'support/capybara'
5
+
6
+ RSpec.describe 'ajax params', type: :request do
7
+ # Using static TestAjaxParamsPost and TestAjaxParamsCategory admins
8
+
9
+ it 'passes parameters when rendering selected item' do
10
+ user = User.create
11
+ category = Category.create(name: 'Travel', created_by: user)
12
+ post = Post.create(category: category)
13
+
14
+ ApplicationController.current_user = user
15
+ get "/admin/test_ajax_params_posts/#{post.id}/edit"
16
+
17
+ expect(response.body).to have_selector('.searchable-select-input option[selected]',
18
+ text: 'Travel')
19
+ end
20
+
21
+ it 'includes parameters in ajax url' do
22
+ user = User.create
23
+
24
+ ApplicationController.current_user = user
25
+ get '/admin/test_ajax_params_posts/new'
26
+
27
+ url_matcher = "?created_by=#{user.id}"
28
+ expect(response.body).to have_selector('.searchable-select-input' \
29
+ "[data-ajax-url*='#{url_matcher}']")
30
+ end
31
+ end
@@ -0,0 +1,155 @@
1
+ require 'rails_helper'
2
+
3
+ RSpec.describe 'Asset Pipeline Diagnostics', type: :feature do
4
+ describe 'Asset Configuration' do
5
+ it 'verifies all asset pipeline components are properly configured', js: true do
6
+ errors = []
7
+
8
+ # 1. Check Propshaft is loaded
9
+ if defined?(Rails.application.assets)
10
+ # 2. Check asset paths are configured
11
+ errors << 'No asset paths configured' if Rails.application.assets.load_path.paths.empty?
12
+
13
+ # 3. Check resolver type
14
+ unless Rails.application.assets.resolver.is_a?(Propshaft::Resolver::Dynamic)
15
+ errors << "Wrong resolver type: #{Rails.application.assets.resolver.class}"
16
+ end
17
+ else
18
+ errors << 'Rails.application.assets not defined - Propshaft not loaded'
19
+ end
20
+
21
+ # 4. Check built assets exist and contain expected content
22
+ builds_path = Rails.root.join('app/assets/builds')
23
+ if File.exist?(builds_path)
24
+ # Check JavaScript file
25
+ js_file = File.join(builds_path, 'active_admin.js')
26
+ if File.exist?(js_file)
27
+ js_content = File.read(js_file)
28
+ js_checks = {
29
+ 'TomSelect class' => js_content.include?('TomSelect'),
30
+ 'initSearchableSelects function' => js_content.include?('initSearchableSelects'),
31
+ 'window.TomSelect assignment' => js_content.include?('window.TomSelect'),
32
+ 'DOMContentLoaded listener' => js_content.include?('DOMContentLoaded'),
33
+ 'searchable-select-input selector' => js_content.include?('searchable-select-input')
34
+ }
35
+
36
+ js_checks.each do |check, present|
37
+ errors << "JavaScript missing: #{check}" unless present
38
+ end
39
+ else
40
+ errors << 'active_admin.js not found in builds directory'
41
+ end
42
+
43
+ # Check CSS file
44
+ css_file = File.join(builds_path, 'active_admin.css')
45
+ if File.exist?(css_file)
46
+ css_content = File.read(css_file)
47
+ css_checks = {
48
+ 'ts-wrapper styles' => css_content.include?('.ts-wrapper'),
49
+ 'ts-control styles' => css_content.include?('.ts-control'),
50
+ 'ts-dropdown styles' => css_content.include?('.ts-dropdown')
51
+ }
52
+
53
+ css_checks.each do |check, present|
54
+ errors << "CSS missing: #{check}" unless present
55
+ end
56
+ else
57
+ errors << 'active_admin.css not found in builds directory'
58
+ end
59
+ else
60
+ errors << "Builds directory does not exist at #{builds_path}"
61
+ end
62
+
63
+ # 5. Test actual asset serving in browser
64
+ visit '/admin/posts'
65
+
66
+ # Check if assets are loaded in browser
67
+ js_loaded = page.evaluate_script("typeof window.TomSelect !== 'undefined'")
68
+ errors << 'TomSelect not loaded in browser' unless js_loaded
69
+
70
+ init_loaded = page.evaluate_script("typeof window.initSearchableSelects !== 'undefined'")
71
+ errors << 'initSearchableSelects not loaded in browser' unless init_loaded
72
+
73
+ # Check if Tom Select initialized
74
+ inputs = page.evaluate_script("document.querySelectorAll('.searchable-select-input').length")
75
+ ts_wrappers = page.evaluate_script("document.querySelectorAll('.ts-wrapper').length")
76
+
77
+ if inputs > 0 && ts_wrappers == 0
78
+ errors << "Tom Select not initializing (#{inputs} inputs but 0 wrappers)"
79
+ end
80
+
81
+ # Only print diagnostic info if there are errors
82
+ if errors.any?
83
+ puts "\n#{'=' * 80}"
84
+ puts 'ASSET PIPELINE DIAGNOSTIC FAILURES'
85
+ puts '=' * 80
86
+
87
+ errors.each do |error|
88
+ puts " ✗ #{error}"
89
+ end
90
+
91
+ # Print additional debugging info
92
+ puts "\nDEBUG INFO:"
93
+ puts '-' * 40
94
+
95
+ if defined?(Rails.application.assets)
96
+ puts "Asset paths (#{Rails.application.assets.load_path.paths.count}):"
97
+ Rails.application.assets.load_path.paths.each { |path| puts " - #{path}" }
98
+
99
+ puts "\nResolver: #{Rails.application.assets.resolver.class}"
100
+ end
101
+
102
+ if File.exist?(builds_path)
103
+ puts "\nBuilt files:"
104
+ Dir.glob(File.join(builds_path, '*')).each do |file|
105
+ puts " - #{File.basename(file)}: #{File.size(file)} bytes"
106
+ end
107
+ end
108
+
109
+ # Check asset URLs in page
110
+ html = page.html
111
+ js_tags = html.scan(/<script[^>]*src=["']([^"']+)["'][^>]*>/).flatten
112
+ css_tags = html.scan(/<link[^>]*href=["']([^"']+\.css[^"']*)["'][^>]*>/).flatten
113
+
114
+ puts "\nAsset tags in HTML:"
115
+ puts " JS: #{js_tags.join(', ')}"
116
+ puts " CSS: #{css_tags.join(', ')}"
117
+
118
+ puts "\nBrowser state:"
119
+ puts " Inputs found: #{inputs}"
120
+ puts " Tom Select wrappers: #{ts_wrappers}"
121
+
122
+ puts '=' * 80
123
+ end
124
+
125
+ # Fail the test if there are any errors
126
+ expect(errors).to be_empty
127
+ end
128
+ end
129
+
130
+ describe 'Asset Serving in Test Mode', js: true do
131
+ it 'serves assets correctly through Propshaft' do
132
+ visit '/admin/posts'
133
+
134
+ # Check that assets are served with digest URLs
135
+ js_url = page.evaluate_script("document.querySelector('script[src*=\"active_admin\"]')?.src")
136
+ css_url = page.evaluate_script("document.querySelector('link[href*=\"active_admin\"]')?.href")
137
+
138
+ # Only check if URLs exist and use /assets/ path
139
+ expect(js_url).to match(%r{/assets/active_admin-[a-f0-9]+\.js})
140
+ expect(css_url).to match(%r{/assets/active_admin-[a-f0-9]+\.css})
141
+
142
+ # Check that assets actually load
143
+ if js_url
144
+ response = Net::HTTP.get_response(URI(js_url))
145
+ expect(response.code).to eq('200')
146
+ expect(response.body).to include('TomSelect')
147
+ end
148
+
149
+ if css_url
150
+ response = Net::HTTP.get_response(URI(css_url))
151
+ expect(response.code).to eq('200')
152
+ end
153
+ end
154
+ end
155
+ end