зеркало из
https://github.com/rs-pro/activeadmin-quill_editor.git
synced 2026-09-07 20:15:51 +03:00
feat: Add ActiveAdmin 4 and Propshaft support
- Switch to ActiveAdmin 4 beta with Tailwind CSS - Replace Sprockets with Propshaft for Rails 8 - Migrate from jQuery to vanilla JavaScript - Implement proper module initialization without setTimeout hacks - Add esbuild for JavaScript bundling with clean import aliases - Configure Tailwind CSS build process with ActiveAdmin plugin - Update engine configuration for both Propshaft and Sprockets - Add comprehensive documentation for AA4 gem updates - Maintain backward compatibility with legacy AA versions 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
Этот коммит содержится в:
473
docs/activeadmin-4-propshaft-update.md
Обычный файл
473
docs/activeadmin-4-propshaft-update.md
Обычный файл
@@ -0,0 +1,473 @@
|
||||
# ActiveAdmin Quill Editor - Rails 8 & Propshaft Migration Guide
|
||||
|
||||
## Executive Summary
|
||||
|
||||
This document outlines the necessary changes to modernize the `activeadmin_quill_editor` gem for compatibility with:
|
||||
- **Rails 8.x** with Propshaft asset pipeline
|
||||
- **ActiveAdmin 4.x**
|
||||
- **Ruby 3.3+**
|
||||
- Modern JavaScript without jQuery dependencies
|
||||
|
||||
**Note**: This gem already has a full test application at `spec/dummy/` and comprehensive system tests. We'll update these existing resources rather than creating new ones from scratch.
|
||||
|
||||
## Key Changes Overview
|
||||
|
||||
### 1. Asset Pipeline Migration (Propshaft)
|
||||
|
||||
Propshaft automatically serves assets from `vendor/assets` and `app/assets` directories in gems. The key changes:
|
||||
|
||||
1. **Remove Sprockets directives** - No more `//= require` statements
|
||||
2. **Place precompiled assets in vendor/assets** - Propshaft serves them directly
|
||||
3. **Use ES6 modules** for JavaScript initialization
|
||||
4. **No asset compilation** - Serve Quill.js directly as minified files
|
||||
|
||||
### 2. File Structure Changes
|
||||
|
||||
```
|
||||
activeadmin_quill_editor/
|
||||
├── vendor/assets/ # Static assets served by Propshaft
|
||||
│ ├── javascripts/
|
||||
│ │ └── quill.min.js # Quill 2.x minified (no jQuery)
|
||||
│ └── stylesheets/
|
||||
│ ├── quill.snow.css # Theme CSS
|
||||
│ └── quill.bubble.css # Alternative theme
|
||||
├── app/assets/ # Gem-specific code
|
||||
│ ├── javascripts/
|
||||
│ │ └── activeadmin_quill_editor.js # ES6 initializer
|
||||
│ └── stylesheets/
|
||||
│ └── activeadmin_quill_editor.css # Custom styles
|
||||
└── lib/
|
||||
├── activeadmin/
|
||||
│ └── quill_editor/
|
||||
│ └── engine.rb # Rails engine configuration
|
||||
└── formtastic/
|
||||
└── inputs/
|
||||
└── quill_editor_input.rb # Form input class
|
||||
```
|
||||
|
||||
## Detailed Implementation
|
||||
|
||||
### 3. JavaScript Modernization
|
||||
|
||||
Replace jQuery-dependent code with vanilla JavaScript:
|
||||
|
||||
```javascript
|
||||
// app/assets/javascripts/activeadmin_quill_editor.js
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
const defaultToolbar = [
|
||||
['bold', 'italic', 'underline'],
|
||||
['link', 'blockquote', 'code-block'],
|
||||
[{ 'script': 'sub' }, { 'script': 'super' }],
|
||||
[{ 'align': [] }, { list: 'ordered' }, { list: 'bullet' }],
|
||||
[{ 'color': [] }, { 'background': [] }],
|
||||
['image'],
|
||||
['clean']
|
||||
];
|
||||
|
||||
function initQuillEditor(container) {
|
||||
if (!container || container.classList.contains('quill-editor--active')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const content = container.querySelector('[data-aa-quill-content]');
|
||||
if (!content) return;
|
||||
|
||||
// Parse options from data attributes
|
||||
const options = JSON.parse(container.dataset.options || '{}');
|
||||
const theme = options.theme || 'snow';
|
||||
const modules = options.modules || { toolbar: defaultToolbar };
|
||||
|
||||
// Initialize Quill
|
||||
const editor = new Quill(content, {
|
||||
theme: theme,
|
||||
modules: modules
|
||||
});
|
||||
|
||||
// Store reference and mark as active
|
||||
container._quillEditor = editor;
|
||||
container.classList.add('quill-editor--active');
|
||||
|
||||
// Handle form submission
|
||||
const form = container.closest('form.formtastic');
|
||||
if (form && !form._quillSubmitHandler) {
|
||||
form._quillSubmitHandler = true;
|
||||
form.addEventListener('submit', function(e) {
|
||||
updateQuillInputs(form);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function updateQuillInputs(form) {
|
||||
form.querySelectorAll('[data-aa-quill-editor]').forEach(container => {
|
||||
const editor = container._quillEditor;
|
||||
const input = container.querySelector('input[type="hidden"]');
|
||||
|
||||
if (editor && input) {
|
||||
input.value = editor.root.innerHTML;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function initAllEditors() {
|
||||
document.querySelectorAll('[data-aa-quill-editor]').forEach(initQuillEditor);
|
||||
}
|
||||
|
||||
// Initialize on various events
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', initAllEditors);
|
||||
} else {
|
||||
initAllEditors();
|
||||
}
|
||||
|
||||
// Support for Turbo/Turbolinks
|
||||
document.addEventListener('turbo:load', initAllEditors);
|
||||
document.addEventListener('turbolinks:load', initAllEditors);
|
||||
|
||||
// Support for ActiveAdmin has_many fields
|
||||
document.addEventListener('has_many_add:after', function(e) {
|
||||
e.detail?.container?.querySelectorAll('[data-aa-quill-editor]').forEach(initQuillEditor);
|
||||
});
|
||||
|
||||
// Public API
|
||||
window.QuillEditorHelpers = {
|
||||
initEditor: initQuillEditor,
|
||||
initAll: initAllEditors,
|
||||
getEditor: (elementOrId) => {
|
||||
const element = typeof elementOrId === 'string'
|
||||
? document.getElementById(elementOrId)
|
||||
: elementOrId;
|
||||
return element?._quillEditor;
|
||||
}
|
||||
};
|
||||
})();
|
||||
```
|
||||
|
||||
### 4. Updated Formtastic Input Class
|
||||
|
||||
```ruby
|
||||
# lib/formtastic/inputs/quill_editor_input.rb
|
||||
module Formtastic
|
||||
module Inputs
|
||||
class QuillEditorInput < Formtastic::Inputs::TextInput
|
||||
def to_html
|
||||
input_wrapping do
|
||||
label_html <<
|
||||
template.content_tag(:div, input_html_options) do
|
||||
builder.hidden_field(input_name) <<
|
||||
template.content_tag(:div, 'data-aa-quill-content': '1') do
|
||||
object.send(method).try(:html_safe)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def input_html_options
|
||||
super.tap do |options|
|
||||
options['data-aa-quill-editor'] = '1'
|
||||
# Merge any custom options
|
||||
if options[:data] && options[:data][:options]
|
||||
options['data-options'] = options[:data][:options].to_json
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
### 5. Engine Configuration for Propshaft
|
||||
|
||||
```ruby
|
||||
# lib/activeadmin/quill_editor/engine.rb
|
||||
require 'active_admin'
|
||||
|
||||
module ActiveAdmin
|
||||
module QuillEditor
|
||||
class Engine < ::Rails::Engine
|
||||
engine_name 'activeadmin_quill_editor'
|
||||
|
||||
# Propshaft automatically includes these paths
|
||||
# vendor/assets and app/assets are included by default
|
||||
|
||||
initializer 'activeadmin_quill_editor.assets' do |app|
|
||||
# Ensure our assets are in the precompile list
|
||||
if defined?(Propshaft)
|
||||
app.config.assets.precompile += %w[
|
||||
activeadmin_quill_editor.js
|
||||
activeadmin_quill_editor.css
|
||||
quill.min.js
|
||||
quill.snow.css
|
||||
quill.bubble.css
|
||||
]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
## Test Application Setup (Using Existing spec/dummy)
|
||||
|
||||
### 6. Update Existing Test App
|
||||
|
||||
The gem already has a full Rails test application at `spec/dummy/`. We need to update it for Rails 8 and Propshaft:
|
||||
|
||||
```ruby
|
||||
# spec/dummy/config/application.rb
|
||||
require_relative 'boot'
|
||||
require 'rails/all'
|
||||
|
||||
Bundler.require(*Rails.groups)
|
||||
|
||||
module Dummy
|
||||
class Application < Rails::Application
|
||||
config.load_defaults Rails::VERSION::STRING.to_f
|
||||
|
||||
# Add Propshaft configuration for Rails 8
|
||||
if Rails.version.to_f >= 8.0
|
||||
# Propshaft is default in Rails 8
|
||||
config.assets.paths << Rails.root.join("../../vendor/assets/javascripts")
|
||||
config.assets.paths << Rails.root.join("../../vendor/assets/stylesheets")
|
||||
else
|
||||
# For Rails 7.x, explicitly use Propshaft
|
||||
config.assets = Propshaft::Railtie.config.assets
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
### 7. Update Test App Asset Configuration
|
||||
|
||||
```ruby
|
||||
# spec/dummy/config/initializers/assets.rb
|
||||
# Add Quill editor assets to precompile list
|
||||
Rails.application.config.assets.precompile += %w[
|
||||
quill.min.js
|
||||
quill.snow.css
|
||||
quill.bubble.css
|
||||
activeadmin_quill_editor.js
|
||||
activeadmin_quill_editor.css
|
||||
]
|
||||
```
|
||||
|
||||
```javascript
|
||||
// spec/dummy/app/assets/javascripts/active_admin.js
|
||||
// For test app, import the gem's assets
|
||||
//= require activeadmin_quill_editor
|
||||
```
|
||||
|
||||
## CI/CD Configuration
|
||||
|
||||
### 8. GitHub Actions Workflow
|
||||
|
||||
```yaml
|
||||
# .github/workflows/tests.yml
|
||||
name: Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, master]
|
||||
pull_request:
|
||||
branches: [main, master]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
ruby: ['3.3', '3.4']
|
||||
gemfile:
|
||||
- rails_7.x_active_admin_4.x
|
||||
- rails_8.x_active_admin_4.x
|
||||
|
||||
env:
|
||||
BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/${{ matrix.gemfile }}.gemfile
|
||||
RAILS_ENV: test
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Ruby
|
||||
uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: ${{ matrix.ruby }}
|
||||
bundler-cache: true
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: 'npm'
|
||||
cache-dependency-path: spec/dummy/package-lock.json
|
||||
|
||||
- name: Install npm dependencies
|
||||
working-directory: spec/dummy
|
||||
run: npm ci
|
||||
|
||||
- name: Build assets
|
||||
working-directory: spec/dummy
|
||||
run: npm run build
|
||||
|
||||
- name: Setup test database
|
||||
run: bundle exec rake db:setup
|
||||
|
||||
- name: Run tests
|
||||
run: bundle exec rspec
|
||||
|
||||
- name: Upload coverage
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
file: ./coverage/coverage.json
|
||||
```
|
||||
|
||||
### 9. Appraisals Configuration
|
||||
|
||||
```ruby
|
||||
# Appraisals
|
||||
appraise 'rails-7.x-active-admin-4.x' do
|
||||
gem 'rails', '~> 7.0'
|
||||
gem 'activeadmin', '~> 4.0.0.beta'
|
||||
gem 'sqlite3', '~> 2.0'
|
||||
gem 'propshaft', '~> 1.0'
|
||||
end
|
||||
|
||||
appraise 'rails-8.x-active-admin-4.x' do
|
||||
gem 'rails', '~> 8.0'
|
||||
gem 'activeadmin', '~> 4.0.0.beta'
|
||||
gem 'sqlite3', '~> 2.0'
|
||||
# Propshaft is default in Rails 8
|
||||
end
|
||||
```
|
||||
|
||||
## Migration Steps
|
||||
|
||||
### 10. Step-by-Step Migration Process
|
||||
|
||||
1. **Update gemspec dependencies**
|
||||
```ruby
|
||||
spec.required_ruby_version = '>= 3.3'
|
||||
spec.add_dependency 'activeadmin', '~> 4.0.0.beta'
|
||||
spec.add_dependency 'rails', '>= 7.0'
|
||||
```
|
||||
|
||||
2. **Download and place Quill assets**
|
||||
```bash
|
||||
# Download Quill 2.x from CDN or npm
|
||||
curl -o vendor/assets/javascripts/quill.min.js \
|
||||
https://cdn.jsdelivr.net/npm/quill@2/dist/quill.min.js
|
||||
curl -o vendor/assets/stylesheets/quill.snow.css \
|
||||
https://cdn.jsdelivr.net/npm/quill@2/dist/quill.snow.css
|
||||
```
|
||||
|
||||
3. **Update JavaScript initialization**
|
||||
- Remove jQuery dependencies
|
||||
- Implement vanilla JS initialization
|
||||
- Add Turbo/Turbolinks support
|
||||
|
||||
4. **Update test suite**
|
||||
- Migrate to Combustion
|
||||
- Add modern browser tests
|
||||
- Configure CI matrix testing
|
||||
|
||||
5. **Update documentation**
|
||||
- Installation instructions for Propshaft
|
||||
- Usage examples without jQuery
|
||||
- Migration guide from old versions
|
||||
|
||||
## Usage in Rails 8 Application
|
||||
|
||||
### Installation
|
||||
|
||||
```ruby
|
||||
# Gemfile
|
||||
gem 'activeadmin_quill_editor', '~> 2.0'
|
||||
```
|
||||
|
||||
```scss
|
||||
// app/assets/stylesheets/active_admin.scss
|
||||
@import 'quill.snow';
|
||||
@import 'activeadmin_quill_editor';
|
||||
```
|
||||
|
||||
```javascript
|
||||
// app/javascript/active_admin.js (or equivalent)
|
||||
import 'activeadmin_quill_editor';
|
||||
```
|
||||
|
||||
### Form Usage
|
||||
|
||||
```ruby
|
||||
form do |f|
|
||||
f.inputs do
|
||||
f.input :title
|
||||
f.input :content, as: :quill_editor, input_html: {
|
||||
data: {
|
||||
options: {
|
||||
theme: 'snow',
|
||||
modules: {
|
||||
toolbar: [
|
||||
['bold', 'italic'],
|
||||
['link', 'image']
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
end
|
||||
f.actions
|
||||
end
|
||||
```
|
||||
|
||||
## Benefits of This Approach
|
||||
|
||||
1. **No NPM package needed** - Assets served directly from vendor directory
|
||||
2. **Propshaft compatible** - Works with Rails 8 default asset pipeline
|
||||
3. **No jQuery dependency** - Modern vanilla JavaScript
|
||||
4. **Smaller bundle size** - Only ~200KB for Quill vs 450KB+ for alternatives
|
||||
5. **Simple integration** - Drop-in replacement for text areas
|
||||
6. **Maintained compatibility** - Works with Rails 7.x and 8.x
|
||||
|
||||
## Testing Locally
|
||||
|
||||
```bash
|
||||
# Clone and setup
|
||||
git clone https://github.com/yourusername/activeadmin_quill_editor.git
|
||||
cd activeadmin_quill_editor
|
||||
bundle install
|
||||
|
||||
# Run tests for specific Rails version
|
||||
RAILS_VERSION=8.0 bundle exec rspec
|
||||
|
||||
# Or use appraisals
|
||||
bundle exec appraisal rails-8.x-active-admin-4.x rspec
|
||||
|
||||
# Start test app server
|
||||
cd spec/dummy
|
||||
bundle install
|
||||
bundle exec rails server
|
||||
```
|
||||
|
||||
## Existing Test Coverage
|
||||
|
||||
The gem already includes comprehensive system tests at `spec/system/`:
|
||||
- `quill_editor_spec.rb` - Editor functionality tests (formatting, links, etc.)
|
||||
- `quill_js_spec.rb` - JavaScript API tests
|
||||
|
||||
Additional test cases from activeadmin_trumbowyg to consider adding:
|
||||
- CSS loading verification tests
|
||||
- Dark mode support tests
|
||||
- Multiple editors on same page
|
||||
- Has-many nested forms
|
||||
- Turbo/Turbolinks compatibility tests
|
||||
|
||||
## Notes
|
||||
|
||||
- Quill.js file size: ~200KB minified (vs SunEditor's 450KB)
|
||||
- No compilation step needed with Propshaft
|
||||
- Assets are fingerprinted automatically by Propshaft
|
||||
- Compatible with both importmap-rails and jsbundling-rails setups
|
||||
Ссылка в новой задаче
Block a user