rs-activeadmin-searchable_select 4.0.5

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 (145) 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 +170 -0
  5. data/.github/workflows/npm-publish.yml +47 -0
  6. data/.gitignore +27 -0
  7. data/.npmignore +58 -0
  8. data/.rspec +2 -0
  9. data/.rubocop.yml +67 -0
  10. data/.yardopts +2 -0
  11. data/AGENTS.md +39 -0
  12. data/Appraisals +15 -0
  13. data/CHANGELOG.md +24 -0
  14. data/CLAUDE.md +104 -0
  15. data/Gemfile +11 -0
  16. data/Gemfile.lock +366 -0
  17. data/LICENSE.txt +25 -0
  18. data/README.md +439 -0
  19. data/Rakefile +4 -0
  20. data/bin/rspec +17 -0
  21. data/config/database.yml +16 -0
  22. data/config.ru +28 -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/for-next-session.md +199 -0
  27. data/docs/guide-update-your-app.md +213 -0
  28. data/docs/propshaft-readme.md +121 -0
  29. data/docs/propshaft-upgrade.md +267 -0
  30. data/docs/rails-7-asset-pipeline.md +279 -0
  31. data/docs/setup-activeadmin-app.md +552 -0
  32. data/docs/setup-activeadmin-gem.md +535 -0
  33. data/docs/upload-system.md +225 -0
  34. data/gemfiles/rails_7.x_active_admin_4.x.gemfile +11 -0
  35. data/gemfiles/rails_7.x_active_admin_4.x.gemfile.lock +371 -0
  36. data/gemfiles/rails_8.x_active_admin_4.x.gemfile +11 -0
  37. data/gemfiles/rails_8.x_active_admin_4.x.gemfile.lock +366 -0
  38. data/lefthook.yml +17 -0
  39. data/lib/activeadmin/inputs/filters/searchable_select_input.rb +13 -0
  40. data/lib/activeadmin/inputs/searchable_select_input.rb +11 -0
  41. data/lib/activeadmin/searchable_select/engine.rb +17 -0
  42. data/lib/activeadmin/searchable_select/option_collection.rb +119 -0
  43. data/lib/activeadmin/searchable_select/resource_dsl_extension.rb +56 -0
  44. data/lib/activeadmin/searchable_select/resource_extension.rb +10 -0
  45. data/lib/activeadmin/searchable_select/select_input_extension.rb +159 -0
  46. data/lib/activeadmin/searchable_select/version.rb +5 -0
  47. data/lib/activeadmin/searchable_select.rb +20 -0
  48. data/lib/activeadmin-searchable_select.rb +4 -0
  49. data/lib/generators/active_admin/searchable_select/install/install_generator.rb +217 -0
  50. data/package-lock.json +18 -0
  51. data/package.json +45 -0
  52. data/rs-activeadmin-searchable_select.gemspec +38 -0
  53. data/sonar-project.properties +25 -0
  54. data/spec/features/ajax_params_spec.rb +31 -0
  55. data/spec/features/end_to_end_spec.rb +227 -0
  56. data/spec/features/filter_input_spec.rb +137 -0
  57. data/spec/features/form_input_spec.rb +122 -0
  58. data/spec/features/inline_ajax_setting_spec.rb +26 -0
  59. data/spec/features/input_errors_spec.rb +76 -0
  60. data/spec/features/input_html_options_spec.rb +30 -0
  61. data/spec/features/options_dsl_spec.rb +220 -0
  62. data/spec/features/production_build_spec.rb +108 -0
  63. data/spec/internal/app/admin/categories.rb +26 -0
  64. data/spec/internal/app/admin/dashboard.rb +29 -0
  65. data/spec/internal/app/admin/option_types.rb +19 -0
  66. data/spec/internal/app/admin/option_values.rb +30 -0
  67. data/spec/internal/app/admin/posts.rb +27 -0
  68. data/spec/internal/app/admin/products.rb +22 -0
  69. data/spec/internal/app/admin/rgb_colors.rb +25 -0
  70. data/spec/internal/app/admin/tag_names.rb +21 -0
  71. data/spec/internal/app/admin/test_ajax_params_category.rb +10 -0
  72. data/spec/internal/app/admin/test_ajax_params_post.rb +20 -0
  73. data/spec/internal/app/admin/test_form_post_class.rb +7 -0
  74. data/spec/internal/app/admin/test_form_post_custom.rb +11 -0
  75. data/spec/internal/app/admin/test_form_post_resource.rb +11 -0
  76. data/spec/internal/app/admin/test_form_post_resource_custom.rb +12 -0
  77. data/spec/internal/app/admin/test_inline_ajax_post.rb +9 -0
  78. data/spec/internal/app/admin/test_input_html_post.rb +11 -0
  79. data/spec/internal/app/admin/test_posts_display_text.rb +9 -0
  80. data/spec/internal/app/admin/test_posts_filter.rb +9 -0
  81. data/spec/internal/app/admin/test_posts_named.rb +9 -0
  82. data/spec/internal/app/admin/test_posts_pagination.rb +9 -0
  83. data/spec/internal/app/admin/test_posts_payload_lambda.rb +11 -0
  84. data/spec/internal/app/admin/test_posts_payload_proc.rb +9 -0
  85. data/spec/internal/app/admin/test_posts_scope_lambda.rb +8 -0
  86. data/spec/internal/app/admin/test_posts_scope_params.rb +8 -0
  87. data/spec/internal/app/admin/test_posts_scope_user.rb +8 -0
  88. data/spec/internal/app/admin/test_posts_text_attr.rb +5 -0
  89. data/spec/internal/app/admin/users.rb +23 -0
  90. data/spec/internal/app/admin/variants.rb +31 -0
  91. data/spec/internal/app/assets/config/manifest.js +1 -0
  92. data/spec/internal/app/assets/javascripts/active_admin.js +2 -0
  93. data/spec/internal/app/assets/javascripts/searchable_select_test.js +2 -0
  94. data/spec/internal/app/controllers/application_controller.rb +5 -0
  95. data/spec/internal/app/css/active_admin_source.css +81 -0
  96. data/spec/internal/app/js/active_admin.js +17 -0
  97. data/spec/internal/app/models/article.rb +12 -0
  98. data/spec/internal/app/models/category.rb +12 -0
  99. data/spec/internal/app/models/internal/tag_name.rb +14 -0
  100. data/spec/internal/app/models/internal_tag_name.rb +11 -0
  101. data/spec/internal/app/models/option_type.rb +12 -0
  102. data/spec/internal/app/models/option_value.rb +4 -0
  103. data/spec/internal/app/models/post.rb +15 -0
  104. data/spec/internal/app/models/product.rb +12 -0
  105. data/spec/internal/app/models/rgb_color.rb +16 -0
  106. data/spec/internal/app/models/user.rb +12 -0
  107. data/spec/internal/app/models/variant.rb +12 -0
  108. data/spec/internal/build_activeadmin_css.js +115 -0
  109. data/spec/internal/config/database.yml +7 -0
  110. data/spec/internal/config/environment.rb +48 -0
  111. data/spec/internal/config/initializers/active_admin.rb +53 -0
  112. data/spec/internal/config/initializers/assets.rb +9 -0
  113. data/spec/internal/config/initializers/searchable_select.rb +6 -0
  114. data/spec/internal/config/routes.rb +4 -0
  115. data/spec/internal/config.ru +4 -0
  116. data/spec/internal/db/schema.rb +63 -0
  117. data/spec/internal/db/seeds.rb +88 -0
  118. data/spec/internal/esbuild.config.js +30 -0
  119. data/spec/internal/inject-jquery.js +4 -0
  120. data/spec/internal/log/.gitignore +1 -0
  121. data/spec/internal/package/LICENSE.txt +25 -0
  122. data/spec/internal/package/README.md +439 -0
  123. data/spec/internal/package/package.json +45 -0
  124. data/spec/internal/package/src/index.js +1 -0
  125. data/spec/internal/package/src/searchable_select/init.js +1 -0
  126. data/spec/internal/package/src/searchable_select.css +1 -0
  127. data/spec/internal/package/src/searchable_select.scss +1 -0
  128. data/spec/internal/package-lock.json +1385 -0
  129. data/spec/internal/package.json +26 -0
  130. data/spec/internal/public/favicon.ico +0 -0
  131. data/spec/internal/spec/internal/app/css/active_admin_source.css +38 -0
  132. data/spec/internal/spec/internal/log/test.log +0 -0
  133. data/spec/internal/tailwind-active_admin.config.js +53 -0
  134. data/spec/rails_helper.rb +86 -0
  135. data/spec/spec_helper.rb +137 -0
  136. data/spec/support/active_admin_helpers.rb +17 -0
  137. data/spec/support/capybara.rb +8 -0
  138. data/spec/support/models.rb +88 -0
  139. data/spec/support/pluck_polyfill.rb +12 -0
  140. data/spec/support/reset_settings.rb +5 -0
  141. data/src/index.js +77 -0
  142. data/src/searchable_select/init.js +58 -0
  143. data/src/searchable_select.css +5 -0
  144. data/src/searchable_select.css.map +1 -0
  145. metadata +405 -0
@@ -0,0 +1,535 @@
1
+ # Setting Up ActiveAdmin 4 Extension Gem with Combustion
2
+
3
+ This guide shows how to set up a new Rails gem for ActiveAdmin 4 with Combustion testing, exactly as configured in the working activeadmin_trumbowyg gem.
4
+
5
+ ## CRITICAL: JavaScript Architecture for Gem Distribution
6
+
7
+ **Your gem's JavaScript code must live in the gem, NOT be copied to user apps!**
8
+
9
+ ✅ **Correct approach:**
10
+ - Main JS module in `app/assets/javascripts/your_gem.js`
11
+ - Generator adds single import: `import 'your_gem_name'`
12
+ - Users get updates when they update your gem
13
+ - Code is maintainable and reusable
14
+
15
+ ❌ **Wrong approach:**
16
+ - Generator copies large code blocks to user's app
17
+ - Users never get updates without re-running generator
18
+ - Code duplication across all installations
19
+ - Maintenance nightmare
20
+
21
+ ## 1. Create Gem Structure
22
+
23
+ ```bash
24
+ # Create new gem
25
+ bundle gem activeadmin_your_feature --test=rspec
26
+ cd activeadmin_your_feature
27
+ ```
28
+
29
+ ## 2. Update Gemspec
30
+
31
+ ```ruby
32
+ # activeadmin_your_feature.gemspec
33
+ Gem::Specification.new do |spec|
34
+ spec.name = "activeadmin_your_feature"
35
+ # ... other standard fields ...
36
+
37
+ spec.required_ruby_version = ">= 3.2"
38
+
39
+ # Runtime dependencies
40
+ spec.add_runtime_dependency "activeadmin", [">= 1.0", "< 5"]
41
+
42
+ # Development dependencies
43
+ spec.add_development_dependency "combustion", "~> 1.3"
44
+ spec.add_development_dependency "rspec-rails", "~> 6.0"
45
+ spec.add_development_dependency "capybara", "~> 3.39"
46
+ spec.add_development_dependency "importmap-rails", "~> 2.0"
47
+ spec.add_development_dependency "propshaft" # Required - Sprockets not supported
48
+ end
49
+ ```
50
+
51
+ ## 3. Set Up Gemfile
52
+
53
+ ```ruby
54
+ # Gemfile
55
+ source "https://rubygems.org"
56
+ gemspec
57
+
58
+ gem "rails", "~> 7.2"
59
+ gem "activeadmin", "~> 4.0.0.beta16"
60
+ gem "sqlite3"
61
+ gem "puma"
62
+
63
+ # Required for ActiveAdmin 4
64
+ gem "importmap-rails", "~> 2.0"
65
+ gem "propshaft" # Required - Sprockets not supported
66
+
67
+ group :development, :test do
68
+ gem "combustion", "~> 1.3"
69
+ gem "rspec-rails"
70
+ gem "capybara"
71
+ end
72
+ ```
73
+
74
+ ## 4. Generate Combustion Test App
75
+
76
+ ```bash
77
+ # This creates spec/internal directory
78
+ bundle exec combust
79
+ ```
80
+
81
+ ## 5. Configure config.ru (CRITICAL LOADING ORDER!)
82
+
83
+ ```ruby
84
+ # config.ru
85
+ require "rubygems"
86
+ require "bundler"
87
+
88
+ # DON'T use Bundler.require - loads gems too early!
89
+ Bundler.setup(:default, :development)
90
+
91
+ # Load Rails and combustion first
92
+ require 'combustion'
93
+
94
+ # Initialize Combustion with Rails components
95
+ Combustion.initialize! :active_record, :action_controller, :action_view do
96
+ config.load_defaults Rails::VERSION::STRING.to_f if Rails::VERSION::MAJOR >= 7
97
+ end
98
+
99
+ # NOW load ActiveAdmin and dependencies after Rails is initialized
100
+ require 'importmap-rails'
101
+ require 'active_admin'
102
+ require 'activeadmin_your_feature'
103
+
104
+ # If you have custom Formtastic inputs
105
+ # require 'formtastic/inputs/your_input'
106
+
107
+ run Combustion::Application
108
+ ```
109
+
110
+ ## 6. Set Up Test App Structure
111
+
112
+ ```bash
113
+ cd spec/internal
114
+
115
+ # Create necessary directories
116
+ mkdir -p app/admin
117
+ mkdir -p app/assets/{builds,config,stylesheets,javascripts}
118
+ mkdir -p app/js
119
+ mkdir -p app/models
120
+ mkdir -p config/initializers
121
+ mkdir -p db
122
+ ```
123
+
124
+ ## 7. Database Schema
125
+
126
+ ```ruby
127
+ # spec/internal/db/schema.rb
128
+ ActiveRecord::Schema.define do
129
+ create_table :active_admin_comments, force: true do |t|
130
+ t.string :namespace
131
+ t.text :body
132
+ t.references :resource, polymorphic: true
133
+ t.references :author, polymorphic: true
134
+ t.timestamps
135
+ end
136
+
137
+ # Add your test models
138
+ create_table :posts, force: true do |t|
139
+ t.string :title
140
+ t.text :body
141
+ t.timestamps
142
+ end
143
+ end
144
+ ```
145
+
146
+ ## 8. ActiveAdmin Configuration
147
+
148
+ ```ruby
149
+ # spec/internal/config/initializers/active_admin.rb
150
+ ActiveAdmin.setup do |config|
151
+ config.site_title = "Test App"
152
+ config.authentication_method = false
153
+ config.current_user_method = false
154
+ config.batch_actions = true
155
+ end
156
+ ```
157
+
158
+ ## 9. Routes Configuration
159
+
160
+ ```ruby
161
+ # spec/internal/config/routes.rb
162
+ Rails.application.routes.draw do
163
+ ActiveAdmin.routes(self)
164
+ root to: 'admin/dashboard#index'
165
+ end
166
+ ```
167
+
168
+ ## 10. Install NPM Dependencies
169
+
170
+ ```bash
171
+ cd spec/internal
172
+ npm init -y
173
+
174
+ # Install required packages
175
+ npm install --save-dev \
176
+ @activeadmin/activeadmin@^3.3.0 \
177
+ tailwindcss@^3.4.17 \
178
+ esbuild@^0.24.2
179
+
180
+ # Install runtime dependencies (if your gem needs jQuery plugins)
181
+ npm install \
182
+ jquery@^3.7.1 \
183
+ your-vendor-package
184
+ ```
185
+
186
+ ## 11. Create Package.json Scripts
187
+
188
+ ```json
189
+ {
190
+ "name": "internal",
191
+ "version": "1.0.0",
192
+ "scripts": {
193
+ "build:css": "node ./build_css.js",
194
+ "build:js": "node esbuild.config.js",
195
+ "build": "npm run build:js && npm run build:css",
196
+ "watch": "npm run build:js --watch & npm run build:css -- --watch"
197
+ },
198
+ "dependencies": {
199
+ "esbuild": "^0.24.2",
200
+ "jquery": "^3.7.1"
201
+ },
202
+ "devDependencies": {
203
+ "@activeadmin/activeadmin": "^3.3.0",
204
+ "tailwindcss": "^3.4.17"
205
+ }
206
+ }
207
+ ```
208
+
209
+ ## 12. Create Tailwind Config (WITH SAFELIST!)
210
+
211
+ ```javascript
212
+ // spec/internal/tailwind.config.mjs
213
+ import activeAdminPlugin from '@activeadmin/activeadmin/plugin'
214
+ import { execSync } from 'node:child_process'
215
+
216
+ let activeAdminPath = null
217
+ try {
218
+ activeAdminPath = execSync('bundle show activeadmin', { encoding: 'utf8' }).trim()
219
+ } catch (e) {
220
+ // Continue without scanning if bundler unavailable
221
+ }
222
+
223
+ export default {
224
+ content: [
225
+ './app/admin/**/*.{arb,erb,html,rb}',
226
+ './app/views/**/*.{arb,erb,html,rb}',
227
+ './app/javascript/**/*.js',
228
+ './app/js/**/*.js',
229
+ ...(activeAdminPath ? [
230
+ `${activeAdminPath}/vendor/javascript/flowbite.js`,
231
+ `${activeAdminPath}/plugin.js`,
232
+ `${activeAdminPath}/app/views/**/*.{arb,erb,html,rb}`,
233
+ ] : [])
234
+ ],
235
+ darkMode: 'selector',
236
+ plugins: [activeAdminPlugin],
237
+ // CRITICAL: Without safelist, ActiveAdmin layout breaks!
238
+ safelist: [
239
+ // Grid and layout
240
+ 'grid', 'gap-4', 'gap-6', 'lg:grid-cols-3', 'md:grid-cols-2',
241
+ 'col-span-2', 'col-span-3', 'lg:col-span-2', 'lg:col-span-1',
242
+ // Flexbox
243
+ 'flex', 'flex-col', 'flex-row', 'flex-wrap', 'items-center',
244
+ 'justify-between', 'justify-center', 'items-start', 'items-end',
245
+ // Spacing
246
+ 'p-4', 'p-6', 'px-4', 'px-6', 'py-2', 'py-4', 'm-0', 'mx-auto',
247
+ 'mt-4', 'mb-4', 'ml-auto', 'mr-auto', 'space-y-4', 'space-x-4',
248
+ // Display
249
+ 'block', 'inline-block', 'hidden', 'lg:hidden', 'lg:block', 'lg:flex',
250
+ // Width/Height
251
+ 'w-full', 'w-auto', 'w-64', 'h-full', 'min-h-screen', 'max-w-7xl',
252
+ // Typography
253
+ 'text-sm', 'text-base', 'text-lg', 'text-xl', 'font-medium', 'font-semibold',
254
+ // Colors
255
+ 'bg-white', 'bg-gray-50', 'bg-gray-100', 'text-gray-900', 'text-gray-600',
256
+ 'dark:bg-gray-800', 'dark:bg-gray-900', 'dark:text-white', 'dark:text-gray-300',
257
+ // Borders
258
+ 'border', 'border-gray-200', 'dark:border-gray-700', 'rounded-lg', 'rounded-md',
259
+ // Forms
260
+ 'form-input', 'form-select', 'form-checkbox',
261
+ // Position & Z-index
262
+ 'relative', 'absolute', 'fixed', 'sticky', 'top-0', 'left-0', 'right-0',
263
+ 'z-10', 'z-20', 'z-30', 'z-40', 'z-50',
264
+ // Shadows
265
+ 'shadow', 'shadow-md', 'shadow-lg'
266
+ ]
267
+ }
268
+ ```
269
+
270
+ ## 13. Create Build Script for CSS
271
+
272
+ ```javascript
273
+ // spec/internal/build_css.js
274
+ #!/usr/bin/env node
275
+ const fs = require('fs');
276
+ const path = require('path');
277
+ const { spawnSync } = require('child_process');
278
+
279
+ const root = __dirname;
280
+ const inputPath = path.join(root, 'app/assets/stylesheets/active_admin_source.css');
281
+ const tmpPath = path.join(root, 'app/assets/stylesheets/__aa_tmp.css');
282
+ const outPath = path.join(root, 'app/assets/builds/active_admin.css');
283
+
284
+ function build() {
285
+ // Read source file
286
+ const src = fs.readFileSync(inputPath, 'utf8');
287
+
288
+ // If you have vendor CSS to include:
289
+ // const vendorCssPath = path.join(root, 'node_modules/your-package/dist/styles.css');
290
+ // const vendorCss = fs.readFileSync(vendorCssPath, 'utf8');
291
+
292
+ // Ensure Tailwind directives are first
293
+ const tailwindDirectives = '@tailwind base;\n@tailwind components;\n@tailwind utilities;';
294
+
295
+ // Build final CSS
296
+ // With vendor: const tmpCss = `${tailwindDirectives}\n\n/* Vendor */\n${vendorCss}\n\n/* Custom */\n${src}`;
297
+ const tmpCss = `${tailwindDirectives}\n\n${src}`;
298
+
299
+ fs.writeFileSync(tmpPath, tmpCss, 'utf8');
300
+
301
+ // Run Tailwind
302
+ const res = spawnSync('npx', [
303
+ 'tailwindcss',
304
+ '-c', 'tailwind.config.mjs',
305
+ '-i', tmpPath,
306
+ '-o', outPath
307
+ ], { stdio: 'inherit', cwd: root });
308
+
309
+ if (res.status !== 0) {
310
+ process.exit(res.status);
311
+ }
312
+
313
+ fs.unlinkSync(tmpPath);
314
+ console.log(`CSS built: ${outPath}`);
315
+ }
316
+
317
+ build();
318
+ ```
319
+
320
+ ## 14. Create jQuery Injection (if needed)
321
+
322
+ ```javascript
323
+ // spec/internal/inject-jquery.js
324
+ // https://github.com/evanw/esbuild/issues/1681
325
+ export { default as $ } from 'jquery/dist/jquery.js'
326
+ export { default as jQuery } from 'jquery/dist/jquery.js'
327
+ ```
328
+
329
+ ## 15. Create JavaScript Entry Point
330
+
331
+ ```javascript
332
+ // spec/internal/app/js/active_admin.js
333
+ import '@activeadmin/activeadmin';
334
+
335
+ // Import your gem's JavaScript module - users will use this exact import
336
+ // NOTE: In development, this needs to be resolved via esbuild alias to your local gem
337
+ import 'your_gem_name';
338
+ ```
339
+
340
+ For development testing, create an esbuild config:
341
+
342
+ ```javascript
343
+ // spec/internal/esbuild.config.js
344
+ const esbuild = require('esbuild');
345
+ const path = require('path');
346
+
347
+ const config = {
348
+ entryPoints: ['app/js/active_admin.js'],
349
+ bundle: true,
350
+ sourcemap: true,
351
+ format: 'esm',
352
+ outdir: 'app/assets/builds',
353
+ publicPath: '/assets',
354
+ inject: ['./inject-jquery.js'],
355
+ alias: {
356
+ // Map your gem's module name to the actual file for development
357
+ 'your_gem_name': path.resolve(__dirname, '../../app/assets/javascripts/your_gem_main.js')
358
+ }
359
+ };
360
+
361
+ // Build logic...
362
+ ```
363
+
364
+ ## 16. Create CSS Source
365
+
366
+ ```css
367
+ /* spec/internal/app/assets/stylesheets/active_admin_source.css */
368
+ @tailwind base;
369
+ @tailwind components;
370
+ @tailwind utilities;
371
+
372
+ /* Your custom styles here */
373
+ ```
374
+
375
+ ## 17. Set Up Propshaft Assets
376
+
377
+ With Propshaft, assets in `app/assets/builds` are automatically served. No manifest configuration needed.
378
+
379
+ ```css
380
+ /* spec/internal/app/assets/stylesheets/active_admin.css */
381
+ /*
382
+ * This file can be empty or include custom overrides
383
+ * The real CSS is in builds/active_admin.css
384
+ */
385
+ ```
386
+
387
+ ## 19. Create Test Models
388
+
389
+ ```ruby
390
+ # spec/internal/app/models/post.rb
391
+ class Post < ActiveRecord::Base
392
+ def self.ransackable_attributes(_auth_object = nil)
393
+ %w[title body created_at updated_at]
394
+ end
395
+ end
396
+ ```
397
+
398
+ ## 20. Create Admin Resources
399
+
400
+ ```ruby
401
+ # spec/internal/app/admin/posts.rb
402
+ ActiveAdmin.register Post do
403
+ permit_params :title, :body
404
+
405
+ form do |f|
406
+ f.semantic_errors
407
+ f.inputs do
408
+ f.input :title
409
+ f.input :body, as: :text
410
+ # Use your custom input types here
411
+ end
412
+ f.actions
413
+ end
414
+ end
415
+ ```
416
+
417
+ ## 21. Build Assets
418
+
419
+ ```bash
420
+ cd spec/internal
421
+ npm install
422
+ npm run build
423
+
424
+ # Verify output
425
+ ls -lah app/assets/builds/
426
+ # Should see active_admin.css (100KB+) and active_admin.js
427
+ ```
428
+
429
+ ## 22. Test the Setup
430
+
431
+ ```bash
432
+ # From gem root
433
+ bundle exec rackup
434
+
435
+ # Visit http://localhost:9292/admin
436
+ # Should see styled ActiveAdmin interface
437
+ ```
438
+
439
+ ## 23. Set Up RSpec
440
+
441
+ ```ruby
442
+ # spec/rails_helper.rb
443
+ ENV['RAILS_ENV'] ||= 'test'
444
+
445
+ require 'combustion'
446
+
447
+ Combustion.path = 'spec/internal'
448
+ Combustion.initialize! :active_record, :action_controller, :action_view do
449
+ config.load_defaults Rails::VERSION::STRING.to_f
450
+ end
451
+
452
+ require 'rspec/rails'
453
+ require 'capybara/rails'
454
+
455
+ RSpec.configure do |config|
456
+ config.use_transactional_fixtures = true
457
+ config.infer_spec_type_from_file_location!
458
+ config.filter_rails_from_backtrace!
459
+ end
460
+ ```
461
+
462
+ ## 24. Create Basic Spec
463
+
464
+ ```ruby
465
+ # spec/features/admin_spec.rb
466
+ require 'rails_helper'
467
+
468
+ RSpec.describe 'Admin Interface', type: :feature do
469
+ it 'loads the admin dashboard' do
470
+ visit '/admin'
471
+ expect(page).to have_content('Dashboard')
472
+ end
473
+
474
+ it 'has properly styled interface' do
475
+ visit '/admin'
476
+ # Check for Tailwind classes indicating proper styling
477
+ expect(page).to have_css('.flex')
478
+ expect(page).to have_css('.grid')
479
+ end
480
+ end
481
+ ```
482
+
483
+ ## 25. Add to .gitignore
484
+
485
+ ```
486
+ # .gitignore
487
+ spec/internal/node_modules/
488
+ spec/internal/app/assets/builds/
489
+ spec/internal/tmp/
490
+ spec/internal/log/
491
+ spec/internal/db/*.sqlite3
492
+ spec/internal/package-lock.json
493
+ ```
494
+
495
+ ## Checklist
496
+
497
+ - [ ] Gem structure created with proper dependencies
498
+ - [ ] Combustion test app generated
499
+ - [ ] config.ru with correct loading order
500
+ - [ ] NPM packages installed
501
+ - [ ] Tailwind configured with safelist
502
+ - [ ] CSS build script created
503
+ - [ ] JavaScript entry point set up
504
+ - [ ] jQuery injection (if needed)
505
+ - [ ] Asset manifests configured
506
+ - [ ] Test models and admin resources created
507
+ - [ ] Assets built successfully (>100KB CSS file)
508
+ - [ ] Server starts and admin interface is styled
509
+ - [ ] RSpec tests pass
510
+
511
+ ## Common Issues
512
+
513
+ ### Issue: "uninitialized constant ActiveAdmin"
514
+ **Solution**: Check config.ru loading order - ActiveAdmin must load AFTER Combustion.initialize!
515
+
516
+ ### Issue: Unstyled admin pages
517
+ **Solution**: Ensure Tailwind safelist is included and CSS file is >100KB
518
+
519
+ ### Issue: jQuery not defined
520
+ **Solution**: Add inject-jquery.js and use with esbuild --inject flag
521
+
522
+ ### Issue: Vendor CSS not loading
523
+ **Solution**: Use build_css.js to concatenate vendor CSS before Tailwind processing
524
+
525
+ ### Issue: Assets not loading / 404 errors
526
+ **Solution**: Ensure you're using Propshaft, not Sprockets. Check that assets are in `app/assets/builds/`
527
+
528
+ ## Success Indicators
529
+
530
+ ✅ Admin interface fully styled with proper layout
531
+ ✅ CSS file size > 100KB
532
+ ✅ No JavaScript console errors
533
+ ✅ Dark mode toggle works
534
+ ✅ Custom features/inputs working
535
+ ✅ Tests passing