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,415 @@
1
+ @import "tailwindcss";
2
+
3
+ @config "../../../config/tailwind-active_admin.config.js";
4
+
5
+ @custom-variant dark (&:where(.dark, .dark *));
6
+
7
+ @utility ring-opacity-5 {
8
+ --tw-ring-opacity: 0.05;
9
+ }
10
+
11
+ @layer base {
12
+ *,
13
+ ::after,
14
+ ::before,
15
+ ::backdrop,
16
+ ::file-selector-button {
17
+ border-color: var(--color-gray-200, currentColor);
18
+ }
19
+
20
+ /* Form Inputs */
21
+ [type='text'],
22
+ [type='email'],
23
+ [type='url'],
24
+ [type='password'],
25
+ [type='number'],
26
+ [type='date'],
27
+ [type='datetime-local'],
28
+ [type='month'],
29
+ [type='search'],
30
+ [type='tel'],
31
+ [type='time'],
32
+ [type='week'],
33
+ textarea,
34
+ select {
35
+ @apply appearance-none bg-gray-50 dark:bg-gray-700 border-gray-300 dark:border-gray-600 border rounded-md px-3 py-2 text-gray-900 dark:text-white w-full;
36
+ --tw-shadow: 0 0 #0000;
37
+ }
38
+
39
+ /* Form Input Focus States */
40
+ [type='text']:focus,
41
+ [type='email']:focus,
42
+ [type='url']:focus,
43
+ [type='password']:focus,
44
+ [type='number']:focus,
45
+ [type='date']:focus,
46
+ [type='datetime-local']:focus,
47
+ [type='month']:focus,
48
+ [type='search']:focus,
49
+ [type='tel']:focus,
50
+ [type='time']:focus,
51
+ [type='week']:focus,
52
+ textarea:focus,
53
+ select:focus {
54
+ @apply outline-none ring-2 ring-blue-500 dark:ring-blue-500 border-blue-500 dark:border-blue-500;
55
+ }
56
+
57
+ /* Placeholders */
58
+ input::placeholder,
59
+ textarea::placeholder {
60
+ @apply text-gray-500 dark:text-gray-400;
61
+ }
62
+
63
+ /* Checkbox and Radio */
64
+ [type='checkbox'],
65
+ [type='radio'] {
66
+ @apply appearance-none p-0 inline-block align-middle bg-white dark:bg-gray-700 border-gray-300 dark:border-gray-600 border rounded-none h-4 w-4 text-blue-600 dark:text-blue-500;
67
+ print-color-adjust: exact;
68
+ }
69
+
70
+ [type='radio'] {
71
+ @apply rounded-full;
72
+ }
73
+
74
+ /* Checkbox and Radio Focus States */
75
+ [type='checkbox']:focus,
76
+ [type='radio']:focus {
77
+ @apply outline-none ring-2 ring-blue-500 dark:ring-blue-500;
78
+ }
79
+
80
+ /* Checkbox and Radio Checked States */
81
+ [type='checkbox']:checked,
82
+ [type='radio']:checked {
83
+ @apply border-transparent bg-current bg-no-repeat bg-center;
84
+ background-size: 0.65rem 0.65rem;
85
+ }
86
+
87
+ [type='checkbox']:checked {
88
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 12'%3E%3Cpath stroke='white' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M1 5.917 5.724 10.5 15 1.5'/%3E%3C/svg%3E");
89
+ }
90
+
91
+ [type='radio']:checked {
92
+ background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3E%3Ccircle cx='8' cy='8' r='3'/%3E%3C/svg%3E");
93
+ background-size: 1rem 1rem;
94
+ }
95
+
96
+ /* File Input */
97
+ [type='file'] {
98
+ @apply bg-transparent border-0 p-0;
99
+ }
100
+
101
+ [type='file']:focus {
102
+ @apply outline-none;
103
+ }
104
+
105
+ /* File Selector Button */
106
+ input[type=file]::file-selector-button {
107
+ @apply text-white bg-gray-800 dark:bg-gray-600 border-0 font-medium text-sm cursor-pointer px-8 py-2.5 -ml-4 mr-4;
108
+ }
109
+
110
+ input[type=file]::file-selector-button:hover {
111
+ @apply bg-gray-700 dark:bg-gray-500;
112
+ }
113
+ }
114
+
115
+ @layer components {
116
+ /* Action Item Button */
117
+ .action-item-button {
118
+ @apply py-2 px-3 text-sm font-medium no-underline text-gray-900 focus:outline-none bg-white rounded-md border border-gray-200 hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-4 focus:ring-gray-200 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700;
119
+ }
120
+
121
+ /* Index Data Table Toolbar */
122
+ .index-data-table-toolbar {
123
+ @apply flex flex-col lg:flex-row gap-4 mb-4;
124
+ }
125
+
126
+ /* Scopes */
127
+ .scopes {
128
+ @apply flex flex-wrap gap-1.5;
129
+ }
130
+
131
+ /* Index Button Group */
132
+ .index-button-group {
133
+ @apply inline-flex flex-wrap items-stretch rounded-md;
134
+ }
135
+
136
+ .index-button-group > :where(*:not(:first-child)) {
137
+ @apply -ms-px my-0;
138
+ }
139
+
140
+ /* Index Button */
141
+ .index-button {
142
+ @apply inline-flex items-center justify-center px-3 py-2 text-sm font-medium no-underline text-gray-900 bg-white border border-gray-200 hover:bg-gray-100 focus:z-10 focus:ring-2 focus:ring-blue-700 focus:text-blue-700 first:rounded-s-md last:rounded-e-md dark:bg-gray-900 dark:border-gray-700 dark:text-gray-100 dark:hover:text-gray-200 dark:hover:bg-gray-800 dark:focus:ring-blue-500 dark:focus:text-white;
143
+ }
144
+
145
+ .index-button-selected {
146
+ @apply bg-gray-100 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-800;
147
+ }
148
+
149
+ /* Scopes Count */
150
+ .scopes-count {
151
+ @apply inline-flex items-center justify-center rounded-full bg-indigo-200/80 text-indigo-800 dark:bg-indigo-800 dark:text-indigo-200 px-1.5 py-1 text-xs font-normal ms-2 leading-none;
152
+ }
153
+
154
+ /* Paginated Collection */
155
+ .paginated-collection {
156
+ @apply border border-gray-200 dark:border-gray-800 rounded-md shadow-sm overflow-hidden;
157
+ }
158
+
159
+ .paginated-collection-contents {
160
+ @apply overflow-x-auto;
161
+ }
162
+
163
+ .paginated-collection-pagination {
164
+ @apply p-2 lg:p-3 flex flex-col-reverse lg:flex-row gap-4 items-center justify-between;
165
+ }
166
+
167
+ .paginated-collection-footer {
168
+ @apply p-3 flex gap-2 items-center justify-between text-sm border-t border-gray-200 dark:border-gray-800;
169
+ }
170
+
171
+ /* Data Table */
172
+ .data-table {
173
+ @apply w-full text-sm text-gray-800 dark:text-gray-300;
174
+ }
175
+
176
+ .data-table :where(thead > tr > th) {
177
+ @apply px-3 py-3.5 whitespace-nowrap font-semibold text-start text-xs uppercase border-b text-gray-700 bg-gray-50 dark:bg-gray-950/50 dark:border-gray-800 dark:text-white;
178
+ }
179
+
180
+ .data-table :where(tbody > tr) {
181
+ @apply border-b dark:border-gray-800;
182
+ }
183
+
184
+ .data-table :where(td) {
185
+ @apply px-3 py-4;
186
+ }
187
+
188
+ /* Data Table Resource Actions */
189
+ .data-table-resource-actions {
190
+ @apply flex gap-2 items-center;
191
+ }
192
+
193
+ .data-table-resource-actions > a {
194
+ @apply text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white;
195
+ }
196
+
197
+ /* Data Sortable */
198
+ .data-table :where(thead > tr > th[data-sortable]) {
199
+ @apply cursor-pointer;
200
+ }
201
+
202
+ .data-table-sorted-icon {
203
+ @apply invisible w-2 h-3;
204
+ }
205
+
206
+ .data-table :where(thead > tr > th[data-sort-direction]) .data-table-sorted-icon {
207
+ @apply visible;
208
+ }
209
+
210
+ .data-table :where(thead > tr > th[data-sort-direction="asc"]) .data-table-sorted-icon {
211
+ @apply rotate-180;
212
+ }
213
+
214
+ /* Filters Form */
215
+ .filters-form {
216
+ @apply text-sm mb-6;
217
+ }
218
+
219
+ .filters-form-title {
220
+ @apply text-gray-700 dark:text-gray-200 font-bold text-lg mb-4;
221
+ }
222
+
223
+ .filters-form :where(.label) {
224
+ @apply block mb-1.5 text-sm;
225
+ }
226
+
227
+ .filters-form-input-group {
228
+ @apply grid grid-cols-1 sm:grid-cols-2 gap-4;
229
+ }
230
+
231
+ .filters-form-input-group :where(.input) {
232
+ @apply w-full;
233
+ }
234
+
235
+ .filters-form-input-group :where(.label) {
236
+ @apply block mb-1 text-sm font-medium text-gray-700 dark:text-gray-300;
237
+ }
238
+
239
+ .filters-form-input-group :where(.hint) {
240
+ @apply mt-1 text-sm text-gray-500 dark:text-gray-400;
241
+ }
242
+
243
+ .filters-form-field {
244
+ @apply mb-4;
245
+ }
246
+
247
+ .filters-form-buttons {
248
+ @apply flex gap-2 items-center;
249
+ }
250
+
251
+ .filters-form-submit {
252
+ @apply min-w-[6rem] font-bold text-white bg-blue-600 hover:bg-blue-700 focus:ring-4 focus:outline-none focus:ring-blue-300 rounded-md px-3 py-2 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800 cursor-pointer;
253
+ }
254
+
255
+ .filters-form-clear {
256
+ @apply rounded-md px-3 py-2 font-semibold text-gray-700 hover:bg-gray-100 no-underline dark:text-gray-400 dark:hover:bg-inherit dark:hover:text-gray-100 dark:focus:ring-blue-800;
257
+ }
258
+
259
+ /* Panel */
260
+ .panel {
261
+ @apply mb-6 border border-gray-200 rounded-md shadow-sm dark:border-gray-800;
262
+ }
263
+
264
+ .panel-title {
265
+ @apply font-bold bg-gray-100 dark:bg-gray-900 rounded-t-md p-3;
266
+ }
267
+
268
+ .panel-body {
269
+ @apply py-5 px-3;
270
+ }
271
+
272
+ /* Attributes Table */
273
+ .attributes-table {
274
+ @apply overflow-hidden mb-6 border border-gray-200 rounded-md shadow-sm dark:border-gray-800;
275
+ }
276
+
277
+ .attributes-table :where(tbody > tr > th) {
278
+ @apply w-32 sm:w-40 text-start text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-800/60 dark:text-gray-300;
279
+ }
280
+
281
+ .attributes-table :where(tbody > tr > th, tbody > tr > td) {
282
+ @apply p-3;
283
+ }
284
+
285
+ /* Status Tag */
286
+ .status-tag {
287
+ @apply bg-gray-200 text-gray-600 dark:bg-gray-700 dark:text-gray-400 inline-flex items-center rounded-full text-sm font-medium px-2.5 py-0.5 whitespace-nowrap;
288
+ }
289
+
290
+ .status-tag:where([data-status=yes]) {
291
+ @apply bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-300;
292
+ }
293
+
294
+ /* Tabs */
295
+ .tabs-nav {
296
+ @apply flex flex-wrap mb-2 text-sm font-medium text-center border-b border-gray-200 dark:border-gray-700;
297
+ }
298
+
299
+ .tabs-nav > :where(a) {
300
+ @apply block p-4 border-b-2 border-transparent rounded-t-md hover:text-gray-600 dark:hover:text-gray-300 no-underline;
301
+ }
302
+
303
+ .tabs-content {
304
+ @apply p-4 mb-6;
305
+ }
306
+
307
+ /* Formtastic */
308
+ .formtastic {
309
+ @apply text-sm;
310
+ }
311
+
312
+ .formtastic :where(.fieldset-title, .has-many-fields-title) {
313
+ @apply block w-full mb-3 border-b font-bold text-lg;
314
+ }
315
+
316
+ .formtastic :where(.label) {
317
+ @apply block mb-1.5 text-gray-700 dark:text-gray-300;
318
+ }
319
+
320
+ .formtastic :where(.label abbr) {
321
+ @apply ms-1 no-underline;
322
+ }
323
+
324
+ .formtastic :where(.input) {
325
+ @apply py-3;
326
+ }
327
+
328
+ .formtastic :where(.choice) {
329
+ @apply mb-1;
330
+ }
331
+
332
+ .formtastic :where(.boolean label, .choice label) {
333
+ @apply flex gap-2 items-center;
334
+ }
335
+
336
+ .formtastic :where(.fragments-group) {
337
+ @apply inline-flex flex-wrap gap-1;
338
+ }
339
+
340
+ .formtastic :where(.fragment label) {
341
+ @apply sr-only;
342
+ }
343
+
344
+ .formtastic :where(.inline-hints) {
345
+ @apply text-gray-500 mt-2;
346
+ }
347
+
348
+ .formtastic :where(.errors) {
349
+ @apply p-4 mb-6 rounded-md space-y-2 bg-red-50 text-red-800 dark:bg-red-800 dark:text-red-300;
350
+ }
351
+
352
+ .formtastic :where(.errors > li) {
353
+ @apply list-disc ms-4;
354
+ }
355
+
356
+ .formtastic :where(.inline-errors) {
357
+ @apply font-bold mt-2 text-red-600 dark:text-red-300;
358
+ }
359
+
360
+ .formtastic :where(.error [type=email], .error [type=number], .error [type=password], .error [type=tel], .error [type=text], .error [type=url], .error textarea) {
361
+ @apply border-red-500;
362
+ }
363
+
364
+ .formtastic :where(.buttons, .actions) {
365
+ @apply mt-3;
366
+ }
367
+
368
+ .formtastic :where(.actions > ol) {
369
+ @apply flex items-center gap-6;
370
+ }
371
+
372
+ .formtastic :where([type=submit], [type=button], button) {
373
+ @apply font-bold text-white bg-blue-600 hover:bg-blue-700 focus:ring-4 focus:outline-none focus:ring-blue-300 rounded-lg px-4 py-2 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800 cursor-pointer;
374
+ }
375
+
376
+ .formtastic :where(.actions .cancel-link) {
377
+ @apply font-semibold leading-6 text-gray-900 dark:text-white no-underline;
378
+ }
379
+
380
+ .formtastic :where(.has-many-add) {
381
+ @apply inline-block py-3;
382
+ }
383
+
384
+ .formtastic :where(.has-many-container) {
385
+ @apply space-y-8;
386
+ }
387
+
388
+ .formtastic :where(.has-many-fields) {
389
+ @apply ps-3 border-s-4 border-s-gray-200 dark:border-s-gray-700;
390
+ }
391
+
392
+ /* Select Elements */
393
+ select {
394
+ @apply bg-no-repeat bg-[right_0.75rem_center] bg-[length:0.75em_0.75em] pr-8;
395
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 10 6'%3E%3Cpath stroke='%236B7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m1 1 4 4 4-4'/%3E%3C/svg%3E");
396
+ print-color-adjust: exact;
397
+ }
398
+
399
+ .dark select {
400
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 10 6'%3E%3Cpath stroke='%23D1D5DB' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m1 1 4 4 4-4'/%3E%3C/svg%3E");
401
+ }
402
+
403
+ /* Fix for Formtastic fragments (date/time selects) */
404
+ .fragments-group,
405
+ .fragments {
406
+ @apply flex flex-row gap-2 items-center;
407
+ }
408
+ .fragment {
409
+ @apply flex-shrink-0;
410
+ }
411
+ .fragments-group select,
412
+ .fragments select {
413
+ @apply w-auto min-w-[5rem];
414
+ }
415
+ }
@@ -0,0 +1,18 @@
1
+ const execSync = require("node:child_process").execSync;
2
+ const activeAdminPath = execSync("bundle show activeadmin", {
3
+ encoding: "utf-8",
4
+ }).trim();
5
+
6
+ module.exports = {
7
+ content: [
8
+ `${activeAdminPath}/vendor/javascript/flowbite.js`,
9
+ `${activeAdminPath}/plugin.js`,
10
+ `${activeAdminPath}/app/views/**/*.{arb,erb,html,rb}`,
11
+ "./app/admin/**/*.{arb,erb,html,rb}",
12
+ "./app/views/active_admin/**/*.{arb,erb,html,rb}",
13
+ "./app/views/admin/**/*.{arb,erb,html,rb}",
14
+ "./app/views/layouts/active_admin*.{erb,html}",
15
+ "./app/assets/controllers/active_admin/**/*.js",
16
+ ],
17
+ darkMode: "selector"
18
+ };
@@ -0,0 +1,154 @@
1
+ # Test App Migration from Combustion to Rails 8 (2025-09-11)
2
+
3
+ ## Summary
4
+ Migrated the test environment from Combustion-based minimal app to a full Rails 8 application with Propshaft, Tom Select, and Tailwind CSS v3.
5
+
6
+ ## Key Changes
7
+
8
+ ### 1. Removed Combustion
9
+ - Backed up old `spec/internal` to `spec/internal.backup`
10
+ - Created new full Rails 8 app in `spec/internal`
11
+ - Removed all Combustion references from specs
12
+ - Fixed RSpec configuration to load Rails app directly
13
+
14
+ ### 2. Rails 8 Test App Setup
15
+ ```ruby
16
+ # Created with:
17
+ rails new spec/internal \
18
+ --skip-action-cable \
19
+ --skip-action-mailbox \
20
+ --skip-action-text \
21
+ --skip-active-storage \
22
+ --skip-bootsnap \
23
+ --skip-brakeman \
24
+ --skip-ci \
25
+ --skip-docker \
26
+ --skip-git \
27
+ --skip-hotwire \
28
+ --skip-jbuilder \
29
+ --skip-kamal \
30
+ --skip-rubocop \
31
+ --skip-solid \
32
+ --skip-system-test \
33
+ --skip-test \
34
+ --skip-thruster \
35
+ --css=tailwind \
36
+ --javascript=esbuild
37
+ ```
38
+
39
+ ### 3. Asset Pipeline (Propshaft)
40
+ - Removed sassc-rails (using Tailwind only)
41
+ - Configured Propshaft for asset serving
42
+ - Built CSS outputs to `app/assets/builds/active_admin.css`
43
+ - Tom Select styles integrated via Tailwind build process
44
+
45
+ ### 4. Tom Select Integration
46
+ - Migrated from Select2 to Tom Select
47
+ - Full Tailwind CSS styling for Tom Select components
48
+ - Styles located in `/src/tom-select-tailwind.css`
49
+ - Credits: Based on https://github.com/orchidjs/tom-select/discussions/693 by @LeZellus
50
+
51
+ ### 5. Database Setup
52
+ ```ruby
53
+ # spec/internal/db/schema.rb includes:
54
+ - users (with name field)
55
+ - admin_users (for Devise/ActiveAdmin)
56
+ - categories, posts, colors, rgb_colors
57
+ - tags, taggings (many-to-many)
58
+ - option_types, internal_tag_names (for tests)
59
+ ```
60
+
61
+ ### 6. Build Process
62
+ ```bash
63
+ # CSS Build (Tailwind with Tom Select)
64
+ cd spec/internal
65
+ bundle exec rake active_admin:build
66
+
67
+ # JavaScript Build
68
+ npm run build
69
+
70
+ # Watch mode
71
+ bundle exec rake active_admin:watch # CSS
72
+ npm run watch:js # JavaScript
73
+ ```
74
+
75
+ ### 7. Running the Test App
76
+ ```bash
77
+ cd spec/internal
78
+ bundle exec rackup
79
+ # Available at http://localhost:9292
80
+ # Admin login: admin@example.com / password
81
+ ```
82
+
83
+ ### 8. Fixed Issues
84
+ - ✅ Removed duplicate config.ru from root
85
+ - ✅ Fixed empty schema.rb
86
+ - ✅ Added missing Devise gem
87
+ - ✅ Created missing models (AdminUser, Color, Tag, Tagging)
88
+ - ✅ Fixed seeds.rb to match schema
89
+ - ✅ Converted from Sprockets to Propshaft
90
+ - ✅ Downgraded Tailwind CSS from v4 to v3 for compatibility
91
+ - ✅ Fixed all RSpec/Capybara loading issues
92
+
93
+ ### 9. File Structure
94
+ ```
95
+ spec/internal/
96
+ ├── app/
97
+ │ ├── admin/ # ActiveAdmin resources
98
+ │ ├── assets/
99
+ │ │ ├── builds/ # Built CSS/JS (Propshaft serves these)
100
+ │ │ └── stylesheets/
101
+ │ │ └── active_admin.tailwind.css
102
+ │ └── models/ # Test models
103
+ ├── config/
104
+ │ ├── initializers/
105
+ │ │ ├── active_admin.rb
106
+ │ │ └── devise.rb
107
+ │ └── routes.rb
108
+ ├── db/
109
+ │ ├── schema.rb # Complete test schema
110
+ │ └── seeds.rb # Test data
111
+ ├── lib/
112
+ │ └── tasks/
113
+ │ └── active_admin.rake # Build tasks
114
+ ├── package.json # Node dependencies
115
+ ├── tailwind.config.js # Tailwind configuration
116
+ ├── Gemfile # Ruby dependencies (Propshaft, no Sprockets)
117
+ └── config.ru # Rack configuration
118
+ ```
119
+
120
+ ### 10. Dependencies
121
+ - Rails 8.0.2+
122
+ - ActiveAdmin 4.0.0.beta16
123
+ - Propshaft (not Sprockets)
124
+ - Tailwind CSS v3 (not v4)
125
+ - Tom Select 2.4.3
126
+ - esbuild for JavaScript
127
+ - cssbundling-rails for CSS
128
+
129
+ ### 11. Testing
130
+ ```bash
131
+ # Run all specs
132
+ bundle exec rspec
133
+
134
+ # Run specific spec
135
+ bundle exec rspec spec/features/form_input_spec.rb
136
+
137
+ # Tests now work with:
138
+ - No Combustion dependency
139
+ - Proper Rails 8 app loading
140
+ - ActiveAdmin integration
141
+ - Tom Select functionality
142
+ ```
143
+
144
+ ### 12. Documentation Updates
145
+ - Updated CLAUDE.md with new test app instructions
146
+ - Updated README.md with development/testing section
147
+ - Added `bundle exec rackup` instructions for manual testing
148
+
149
+ ## Important Notes
150
+ - Always use Tailwind v3 (v4 has breaking changes)
151
+ - Tom Select base CSS is concatenated during build
152
+ - All imports use proper @import directives
153
+ - Test app is a full Rails app, not a minimal Combustion app
154
+ - Propshaft serves assets from app/assets/builds/
@@ -0,0 +1,58 @@
1
+ # Test Environment Fixes for Tom Select Migration
2
+
3
+ ## Issue
4
+ After migrating from Select2 to Tom Select, tests were failing because Propshaft wasn't being loaded in the test environment, causing assets not to be served properly.
5
+
6
+ ## Root Cause
7
+ The Rails 8 test application wasn't loading Propshaft's railtie, which meant:
8
+ - No asset paths were configured
9
+ - Assets were being requested at wrong URLs (`/javascripts/` instead of `/assets/`)
10
+ - Tom Select JavaScript wasn't being loaded
11
+ - Tom Select wasn't initializing on searchable select inputs
12
+
13
+ ## Solution
14
+
15
+ ### 1. Load Propshaft Railtie
16
+ Added to `spec/internal/config/application.rb`:
17
+ ```ruby
18
+ # Require Propshaft railtie for asset pipeline
19
+ require 'propshaft/railtie'
20
+ ```
21
+
22
+ This ensures Propshaft is properly initialized with:
23
+ - Asset paths configured
24
+ - Dynamic resolver for test environment
25
+ - Proper asset serving with digest URLs
26
+
27
+ ### 2. Key Configuration Details
28
+ After the fix, Propshaft in test environment has:
29
+ - **Asset paths**: 7 configured paths including app/assets/builds
30
+ - **Resolver**: `Propshaft::Resolver::Dynamic` (no precompilation needed)
31
+ - **Asset serving**: Enabled (`config.assets.server = true`)
32
+ - **URLs**: Proper digest URLs like `/assets/active_admin-f967af7b.js`
33
+
34
+ ### 3. Test Adjustments
35
+ Minor adjustments to tests for Tom Select compatibility:
36
+ - Use `visible: :all` for finding dropdown options (Tom Select may create hidden options)
37
+ - More flexible selectors for Tom Select wrappers
38
+ - Removed hardcoded waits in favor of proper Capybara waiting
39
+
40
+ ## Verification
41
+ Created comprehensive diagnostic test (`spec/features/asset_pipeline_diagnostic_spec.rb`) that verifies:
42
+ - Rails and Propshaft configuration
43
+ - Asset paths and files
44
+ - Built asset contents (checks for Tom Select code)
45
+ - Asset serving in browser
46
+ - JavaScript loading and initialization
47
+
48
+ ## Results
49
+ - ✅ All production_build_spec tests passing
50
+ - ✅ Tom Select properly initializing
51
+ - ✅ Assets served with correct digest URLs
52
+ - ⚠️ Some end_to_end tests still need adjustment for Tom Select behavior differences
53
+
54
+ ## Lessons Learned
55
+ 1. Always verify that required railties are loaded in test environments
56
+ 2. Use diagnostic tests to troubleshoot asset pipeline issues
57
+ 3. Tom Select has slightly different DOM structure and behavior than Select2
58
+ 4. Propshaft requires explicit railtie loading unlike Sprockets which was auto-loaded