24 Коммитов

Автор SHA1 Сообщение Дата
Mattia Roccoberton
e6e6742753 Merge pull request #15 from blocknotes/feature/webpacker-support
Webpacker support
2020-10-01 07:56:57 +02:00
Mattia Roccoberton
c2c8d48795 Webpacker support 2020-10-01 07:56:03 +02:00
Mattia Roccoberton
72062c1a30 v0.2.14 2020-09-10 07:40:35 +02:00
Mattia Roccoberton
c4aa91225b Minor specs improvement 2020-09-09 12:58:14 +02:00
Mattia Roccoberton
3cc6edb302 JS strict directive 2020-09-09 07:48:45 +02:00
Mattia Roccoberton
d78fb2eb9c v0.2.12 2020-09-08 18:35:23 +02:00
Mattia Roccoberton
e5db64af96 JS refactoring 2020-09-08 18:26:24 +02:00
Mattia Roccoberton
c0d37ad94e v0.2.10 2020-09-05 16:08:46 +02:00
Mattia Roccoberton
96c70106a4 Add a new example: upload plugin using Active Storage 2020-09-05 16:07:19 +02:00
Mattia Roccoberton
4ec6e80760 Editor default options refactoring + include imageUploader plugin
In order to use imageUploader plugin:
- js/css files must included in the app;
- an upload method must be provided in Active Admin entity;
- the image_uploader server_url field option needs to be set.
2020-09-04 10:53:29 +02:00
Mattia Roccoberton
707f4a5cc7 README improvements 2020-09-04 09:14:18 +02:00
Mattia Roccoberton
dc97316303 v0.2.9 2020-09-04 08:49:20 +02:00
Mattia Roccoberton
e9b8abc88e Merge pull request #14 from blocknotes/fix/text-alignment
Reduce the importance of the reset rules
2020-09-03 13:02:47 +02:00
Mattia Roccoberton
582667dc59 Reduce the importance of the reset rules
This allows to let Quill features to override the reset styles.

Closes #8
2020-09-03 13:00:42 +02:00
m.pestov
76c06f5346 Don't send paragraph tag if editor blank 2020-09-03 12:45:42 +02:00
Mattia Roccoberton
4e20e658ac v0.2.8 2020-09-03 11:53:48 +02:00
Mattia Roccoberton
135a56dc03 Add specs for editor in nested resources 2020-09-03 11:50:23 +02:00
Mattia Roccoberton
668bd642e5 Add Rubocop 2020-09-03 11:26:54 +02:00
Mattia Roccoberton
ab8c4fd73f Remove SimpleCov 2020-09-03 11:24:55 +02:00
Mattia Roccoberton
d60ed0f723 Update screenshot 2020-09-03 11:20:35 +02:00
Mattia Roccoberton
98b29d6f69 Minor style improvements 2020-09-03 11:20:12 +02:00
Mattia Roccoberton
962b48bb30 README improvements 2020-09-01 11:48:47 +02:00
Mattia Roccoberton
03b2d0e4e8 Merge pull request #13 from blocknotes/circleci-project-setup
Add CircleCI setup
2020-09-01 11:45:00 +02:00
Mattia Roccoberton
593ad3841a Add .circleci/config.yml 2020-09-01 11:40:23 +02:00
29 изменённых файлов: 455 добавлений и 146 удалений

33
.circleci/config.yml Обычный файл
Просмотреть файл

@@ -0,0 +1,33 @@
version: 2.1
orbs:
ruby: circleci/ruby@0.1.2
jobs:
build:
docker:
- image: circleci/ruby:2.6.6-stretch-node-browsers
executor: ruby/default
steps:
- checkout
- restore_cache:
key: gem-cache-{{ checksum "activeadmin_quill_editor.gemspec" }}
- run:
name: Setup Bundler
command: gem install bundler
- run:
name: Bundle Install
command: bundle install --path vendor/bundle
- save_cache:
key: gem-cache-{{ checksum "activeadmin_quill_editor.gemspec" }}
paths: vendor/bundle
- run:
name: Run Specs
command: |
bin/rspec \
--profile 10 \
--format RspecJunitFormatter \
--out test_results/rspec.xml \
--format progress \
$(circleci tests glob "spec/**/*_spec.rb")
- store_test_results:
path: test_results

2
.gitignore поставляемый
Просмотреть файл

@@ -2,10 +2,10 @@
*.orig
/.rspec_failures
/.rubocop-*
/Gemfile.lock
/_misc/
/coverage/
/spec/dummy/db/*.sqlite3*
/spec/dummy/log/
/spec/dummy/storage/

Просмотреть файл

@@ -1,73 +1,27 @@
require:
- rubocop-rspec
Rails:
Enabled: true
inherit_from:
- https://relaxed.ruby.style/rubocop.yml
AllCops:
TargetRubyVersion: 2.3.8
TargetRailsVersion: 5.2
Exclude:
- db/schema.rb
- bin/*
- node_modules/**/*
# Temporary files
- tmp/**/*
- spec/dummy/**/*
NewCops: enable
Rails/InverseOf:
Gemspec/RequiredRubyVersion:
Enabled: false
Style/Documentation:
Naming/FileName:
Enabled: false
Metrics/ClassLength:
# Default value is 100
Max: 150
Metrics/LineLength:
# Default is 80
Layout/LineLength:
Enabled: true
Max: 120
Metrics/ModuleLength:
# Default is 100
Max: 150
Style/HashEachMethods:
Enabled: true
Metrics/ParameterLists:
# Default is 5
Max: 6
Style/HashTransformKeys:
Enabled: true
RSpec/ExampleLength:
# Default is 10
Max: 20
Style/FrozenStringLiteralComment:
# Deface DOES edit strings in place
Exclude:
- 'app/overrides/**/*'
RSpec/MultipleExpectations:
# Default is 3
Max: 5
RSpec/NestedGroups:
# Default is 3
Max: 6
Metrics/AbcSize:
Max: 25
Metrics/BlockLength:
# This value double the rubocop default
Max: 50
Metrics/CyclomaticComplexity:
# This value double the rubocop default
Max: 12
Metrics/MethodLength:
# This value double the rubocop default
Max: 20
Metrics/PerceivedComplexity:
# Default is 7
Max: 10
Style/HashTransformValues:
Enabled: true

Просмотреть файл

@@ -1,3 +1,5 @@
# frozen_string_literal: true
source 'https://rubygems.org'
gemspec

Просмотреть файл

@@ -1,4 +1,4 @@
Copyright (c) 2017 Mattia Roccoberton
Copyright (c) 2017-2020 Mattia Roccoberton
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the

Просмотреть файл

@@ -1,34 +1,49 @@
# ActiveAdmin Quill Editor [![Gem Version](https://badge.fury.io/rb/activeadmin_quill_editor.svg)](https://badge.fury.io/rb/activeadmin_quill_editor)
# ActiveAdmin Quill Editor [![Gem Version](https://badge.fury.io/rb/activeadmin_quill_editor.svg)](https://badge.fury.io/rb/activeadmin_quill_editor) [![CircleCI](https://circleci.com/gh/blocknotes/activeadmin_quill_editor.svg?style=svg)](https://circleci.com/gh/blocknotes/activeadmin_quill_editor)
An Active Admin plugin to use [Quill Rich Text Editor](https://github.com/quilljs/quill)
An Active Admin plugin to use [Quill Rich Text Editor](https://github.com/quilljs/quill) in form fields.
![screenshot](screenshot.jpg)
![screenshot](screenshot.png)
## Install
After installing Active Admin, add to your Gemfile: `gem 'activeadmin_quill_editor'` (and execute *bundle*)
- Update your Gemfile: `gem 'activeadmin_quill_editor'` (and execute *bundle*)
- Add at the end of your ActiveAdmin styles (_app/assets/stylesheets/active_admin.scss_):
If you installed Active Admin without Webpacker support (default for now):
- Add at the end of your Active Admin styles (_app/assets/stylesheets/active_admin.scss_):
```scss
@import 'activeadmin/quill_editor/quill.snow';
@import 'activeadmin/quill_editor_input';
```
- Add at the end of your ActiveAdmin javascripts (_app/assets/javascripts/active_admin.js_):
- Add at the end of your Active Admin javascripts (_app/assets/javascripts/active_admin.js_):
```js
//= require activeadmin/quill_editor/quill
//= require activeadmin/quill_editor_input
```
- Use the input with `as: :quill_editor` in Active Admin model conf
Why 2 separated scripts? In this way you can include a different version of *quill editor* if you like.
> **UPDATE FROM VERSION <= 2.0**: please add to your _app/assets/stylesheets/active_admin.scss_ the line `@import 'activeadmin/quill_editor/quill.snow';`
## Options
If you installed Active Admin with Webpacker support:
- Execute in your project root:
```sh
yarn add blocknotes/activeadmin_quill_editor
```
- Add to your *app/javascript/packs/active_admin.js*:
```js
require('activeadmin_quill_editor')
```
## Usage
In your Active Admin models, form configuration, set the text inputs with `as: :quill_editor` where needed.
**data-options**: permits to set *quill editor* options directly - see [options list](https://quilljs.com/docs/configuration/)
## Examples
### Basic usage
```ruby
# ActiveAdmin article form conf:
# Active Admin article form conf:
form do |f|
f.inputs 'Article' do
f.input :title
@@ -39,26 +54,46 @@ Why 2 separated scripts? In this way you can include a different version of *qui
end
```
Toolbar buttons configuration:
### Toolbar buttons configuration
```ruby
f.input :description, as: :quill_editor, input_html: {data: {options: {modules: {toolbar: [['bold', 'italic', 'underline'], ['link']]}, placeholder: 'Type something...', theme: 'snow'}}}
f.input :description, as: :quill_editor, input_html: { data: { options: { modules: { toolbar: [['bold', 'italic', 'underline'], ['link']] }, placeholder: 'Type something...', theme: 'snow' } } }
```
## Notes
### ImageUploader plugin
This plugin allows to upload images to the server (instead of storing them in *base64* by default), reference [here](https://github.com/NoelOConnell/quill-image-uploader).
- Upload functions (Images, Documents, Files, etc.) are not implemented yet
```ruby
# Upload method (to be included in the admin entity configuration)
member_action :upload, method: [:post] do
result = { success: resource.images.attach(params[:file_upload]) }
result[:url] = url_for(resource.images.last) if result[:success]
render json: result
end
```
```ruby
# Form field
unless object.new_record?
plugin_opts = { image_uploader: { server_url: upload_admin_post_path(object.id), field_name: 'file_upload' } }
f.input :description, as: :quill_editor, input_html: { data: { plugins: plugin_opts } }
end
```
For the relevant files of the upload example see [here](examples/upload_plugin_using_activestorage/).
Consider that this is just a basic example: images are uploaded as soon as they are attached to the
editor (regardless of the form submit), it shows the editor only for an existing record (because of
the *upload_admin_post_path*) and it doesn't provide a way to remove images (just deleting them from
the editor will not destroy them, you'll need to implement a purge logic for that).
## Do you like it? Star it!
If you use this component just star it. A developer is more motivated to improve a project when there is some interest.
Take a look at [other ActiveAdmin components](https://github.com/blocknotes?utf8=✓&tab=repositories&q=activeadmin&type=source) that I made if you are curious.
Take a look at [other Active Admin components](https://github.com/blocknotes?utf8=✓&tab=repositories&q=activeadmin&type=source) that I made if you are curious.
## Contributors
- [Mattia Roccoberton](http://blocknot.es): author
- The good guys that opened issues and pull requests from time to time
## License
[MIT](LICENSE.txt)
The gem is available as open-source under the terms of the [MIT](LICENSE.txt).

Просмотреть файл

@@ -1,3 +1,13 @@
# frozen_string_literal: true
require 'bundler/gem_tasks'
begin
require 'rspec/core/rake_task'
RSpec::Core::RakeTask.new(:spec)
task default: :spec
rescue LoadError
puts '! LoadError: no RSpec available'
end

Просмотреть файл

@@ -25,7 +25,7 @@ Gem::Specification.new do |spec|
spec.add_development_dependency 'puma', '~> 4.3.5'
spec.add_development_dependency 'rspec_junit_formatter', '~> 0.4.1'
spec.add_development_dependency 'rspec-rails', '~> 4.0.1'
spec.add_development_dependency 'rubocop', '~> 0.90.0'
spec.add_development_dependency 'selenium-webdriver', '~> 3.142.7'
spec.add_development_dependency 'simplecov', '~> 0.19.0'
spec.add_development_dependency 'sqlite3', '~> 1.4.2'
end

1
app/assets/javascripts/activeadmin/quill.imageUploader.min.js поставляемый Обычный файл

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

@@ -1,45 +1,95 @@
function initQuillEditors() {
var editors = document.querySelectorAll('.quill-editor');
var default_options = {
modules: {
toolbar: [
['bold', 'italic', 'underline'],
['link', 'blockquote', 'code-block'],
[{ 'script': 'sub'}, { 'script': 'super' }],
[{ 'align': [] }, { list: 'ordered' }, { list: 'bullet' }],
[{ 'color': [] }, { 'background': [] }],
['clean'],
]
},
placeholder: '',
theme: 'snow'
};
(function () {
'use strict'
for(var i = 0; i < editors.length; i++) {
var content = editors[i].querySelector('.quill-editor-content');
var isActive = editors[i].classList.contains('quill-editor--active');
if(content && !isActive) {
var options = editors[i].getAttribute('data-options') ? JSON.parse(editors[i].getAttribute('data-options')) : default_options;
editors[i]['_quill-editor'] = new Quill(content, options);
editors[i].classList += ' quill-editor--active';
// --- functions ---------------------------------------------------------------
function initQuillEditors() {
let default_theme = 'snow'
let default_toolbar = [
['bold', 'italic', 'underline'],
['link', 'blockquote', 'code-block'],
[{ 'script': 'sub' }, { 'script': 'super' }],
[{ 'align': [] }, { list: 'ordered' }, { list: 'bullet' }],
[{ 'color': [] }, { 'background': [] }],
['image'],
['clean'],
]
let editors = document.querySelectorAll('[data-aa-quill-editor]')
let registered_plugins = {}
for (let i = 0; i < editors.length; i++) {
let content = editors[i].querySelector('[data-aa-quill-content]')
let isActive = editors[i].classList.contains('quill-editor--active')
if (content && !isActive) {
// Setup editor options
let options = editors[i].getAttribute('data-options') ? JSON.parse(editors[i].getAttribute('data-options')) : {}
if (!options.theme) options.theme = default_theme
if (!options.modules) options.modules = {}
if (!options.modules.toolbar) options.modules.toolbar = default_toolbar
// Setup plugin options
let plugin_options = editors[i].getAttribute('data-plugins') ? JSON.parse(editors[i].getAttribute('data-plugins')) : {}
if (plugin_options.image_uploader && plugin_options.image_uploader.server_url) {
if (!registered_plugins.image_uploader) {
Quill.register('modules/imageUploader', ImageUploader)
registered_plugins.image_uploader = true
}
let opts = plugin_options.image_uploader
options.modules.imageUploader = setupImageUploader(opts.server_url, opts.field_name)
}
// Init editor
editors[i]['_quill-editor'] = new Quill(content, options)
editors[i].classList += ' quill-editor--active'
}
}
let formtastic = document.querySelector('form.formtastic')
if (formtastic) {
formtastic.onsubmit = () => {
for (let i = 0; i < editors.length; i++) {
let input = editors[i].querySelector('input[type="hidden"]')
if (editors[i]['_quill-editor'].editor.isBlank()) {
input.value = ''
} else {
input.value = editors[i]['_quill-editor'].root.innerHTML
}
}
};
}
}
var formtastic = document.querySelector('form.formtastic');
if(formtastic) {
formtastic.onsubmit = function() {
for(var i = 0; i < editors.length; i++) {
var input = editors[i].querySelector('input[type="hidden"]');
input.value = editors[i]['_quill-editor'].root.innerHTML;
function setupImageUploader(server_url, field_name) {
return {
upload: file => {
return new Promise((resolve, reject) => {
const formData = new FormData()
formData.append(field_name || 'file_upload', file)
fetch(server_url, {
body: formData,
headers: {
'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').getAttribute('content')
},
method: 'POST'
}).then(response => response.json())
.then(result => {
resolve(result.url);
})
.catch(error => {
reject('Upload failed')
console.error('Error: ', error)
})
})
}
};
}
}
}
$(document).ready( function() {
initQuillEditors();
});
// --- events ------------------------------------------------------------------
$(document).ready(() => {
initQuillEditors()
})
$(document).on('has_many_add:after', function() {
initQuillEditors();
});
$(document).on('has_many_add:after', '.has_many_container', () => {
initQuillEditors()
})
})()

Просмотреть файл

@@ -1,10 +1,27 @@
body.active_admin .quill-editor {
display: inline-block;
width: calc(80% - 22px);
// reset internal elements
.ql-editor * {
margin: initial;
padding: initial;
text-align: initial;
}
button:hover {
background-image: none;
background-color: rgba(0, 0, 0, 0.1);
body.active_admin [data-aa-quill-editor] {
display: inline-block;
width: calc(80% - 2px);
button {
text-shadow: none;
box-shadow: none;
&:hover {
background-image: none;
background-color: transparent;
}
}
.ql-container {
border: 1px solid #c9d0d6;
border-radius: 0 0 3px 3px;
}
.ql-editor {
@@ -13,13 +30,6 @@ body.active_admin .quill-editor {
min-height: 150px;
padding: 10px;
// reset internal elements
* {
margin: initial;
padding: initial;
text-align: initial;
}
ol {
list-style-type: decimal;
}
@@ -37,4 +47,9 @@ body.active_admin .quill-editor {
padding-left: 1.5em;
}
}
.ql-toolbar {
border: 1px solid #c9d0d6;
border-radius: 3px 3px 0 0;
}
}

33
app/assets/stylesheets/activeadmin/quill.imageUploader.min.css поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,33 @@
.image-uploading {
position: relative;
display: inline-block;
}
.image-uploading img {
max-width: 98% !important;
filter: blur(5px);
opacity: 0.3;
}
.image-uploading::before {
content: "";
box-sizing: border-box;
position: absolute;
top: 50%;
left: 50%;
width: 30px;
height: 30px;
margin-top: -15px;
margin-left: -15px;
border-radius: 50%;
border: 3px solid #ccc;
border-top-color: #1e986c;
z-index: 1;
animation: spinner 0.6s linear infinite;
}
@keyframes spinner {
to {
transform: rotate(360deg);
}
}

29
bin/rubocop Исполняемый файл
Просмотреть файл

@@ -0,0 +1,29 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
#
# This file was generated by Bundler.
#
# The application 'rubocop' is installed as part of a gem, and
# this file is here to facilitate running it.
#
require "pathname"
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
Pathname.new(__FILE__).realpath)
bundle_binstub = File.expand_path("../bundle", __FILE__)
if File.file?(bundle_binstub)
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
load(bundle_binstub)
else
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
end
end
require "rubygems"
require "bundler/setup"
load Gem.bin_path("rubocop", "rubocop")

Просмотреть файл

@@ -0,0 +1,73 @@
# frozen_string_literal: true
ActiveAdmin.register Post do
permit_params :author_id,
:title,
:description,
:category,
:dt,
:position,
:published,
tag_ids: []
member_action :upload, method: [:post] do
result = { success: resource.images.attach(params[:file_upload]) }
result[:url] = url_for(resource.images.last) if result[:success]
render json: result
end
index do
selectable_column
id_column
column :title
column :author
column :published
column :created_at
actions
end
show do
attributes_table do
row :author
row :title
row :description
row :category
row :dt
row :position
row :published
row :tags
row :created_at
row :updated_at
row :images do |resurce|
resurce.images.each do |image|
div do
link_to image.filename, image, target: '_blank'
end
end
nil
end
end
active_admin_comments
end
form do |f|
f.inputs 'Post' do
f.input :author
f.input :title
unless object.new_record?
plugin_opts = { image_uploader: { server_url: upload_admin_post_path(object.id), field_name: 'file_upload' } }
f.input :description, as: :quill_editor, input_html: { data: { plugins: plugin_opts } }
end
f.input :category
f.input :dt
f.input :position
f.input :published
end
f.inputs 'Tags' do
f.input :tags
end
f.actions
end
end

Просмотреть файл

@@ -0,0 +1,6 @@
//= require active_admin/base
//= require activeadmin/quill_editor/quill
//= require activeadmin/quill_editor_input
//= require activeadmin/quill.imageUploader.min

Просмотреть файл

@@ -0,0 +1,7 @@
@import 'active_admin/mixins';
@import 'active_admin/base';
@import 'activeadmin/quill_editor/quill.snow';
@import 'activeadmin/quill_editor_input';
@import 'activeadmin/quill.imageUploader.min';

Просмотреть файл

@@ -0,0 +1,7 @@
# frozen_string_literal: true
class Post < ApplicationRecord
has_many_attached :images
validates :title, allow_blank: false, presence: true
end

7
index.js Обычный файл
Просмотреть файл

@@ -0,0 +1,7 @@
// Styles
import 'activeadmin_quill_editor/app/assets/stylesheets/activeadmin/quill_editor/quill.snow'
import 'activeadmin_quill_editor/app/assets/stylesheets/activeadmin/_quill_editor_input'
// JS
window.Quill = require('activeadmin_quill_editor/app/assets/javascripts/activeadmin/quill_editor/quill')
require('activeadmin_quill_editor/app/assets/javascripts/activeadmin/quill_editor_input')

Просмотреть файл

@@ -2,6 +2,6 @@
module ActiveAdmin
module QuillEditor
VERSION = '0.2.4'
VERSION = '0.3.0'
end
end

Просмотреть файл

@@ -6,9 +6,9 @@ module Formtastic
def to_html
input_wrapping do
label_html <<
template.content_tag(:div, input_html_options.merge(class: 'quill-editor')) do
template.content_tag(:div, input_html_options.merge('data-aa-quill-editor': '1')) do
builder.hidden_field(input_name) <<
template.content_tag(:div, class: 'quill-editor-content') do
template.content_tag(:div, 'data-aa-quill-content': '1') do
object.send(method).try :html_safe
end
end

9
package.json Обычный файл
Просмотреть файл

@@ -0,0 +1,9 @@
{
"name": "activeadmin_quill_editor",
"version": "0.3.0",
"description": "Quill Editor for ActiveAdmin",
"author": "Mattia Roccoberton <mat@blocknot.es>",
"license": "MIT",
"homepage": "https://github.com/blocknotes/activeadmin_quill_editor",
"main": "index.js"
}

Двоичные данные
screenshot.jpg

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 33 KiB

Двоичные данные
screenshot.png Обычный файл

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 52 KiB

Просмотреть файл

@@ -1,9 +1,12 @@
# frozen_string_literal: true
ActiveAdmin.register Author do
menu priority: 1
permit_params :name, :email, :age, :avatar, profile_attributes: %i[id description _destroy]
permit_params :name,
:email,
:age,
:avatar,
profile_attributes: %i[id description _destroy],
posts_attributes: %i[id title description]
index do
selectable_column
@@ -28,6 +31,7 @@ ActiveAdmin.register Author do
row :created_at
row :updated_at
row :profile
row :posts
end
active_admin_comments
end
@@ -44,6 +48,10 @@ ActiveAdmin.register Author do
f.has_many :profile, allow_destroy: true do |ff|
ff.input :description
end
f.has_many :posts do |fp|
fp.input :title
fp.input :description, as: :quill_editor
end
f.actions
end
end

Просмотреть файл

@@ -25,15 +25,24 @@ ActiveAdmin.register Post do
row :tags
row :created_at
row :updated_at
row :images do |resurce|
resurce.images.each do |image|
div do
link_to image.filename, image, target: '_blank'
end
end
nil
end
end
active_admin_comments
end
form do |f|
toolbar = %w[bold italic underline link]
f.inputs 'Post' do
f.input :author
f.input :title
f.input :description, as: :quill_editor
f.input :description, as: :quill_editor, input_html: { data: { options: { modules: { toolbar: toolbar } } } }
f.input :category
f.input :dt
f.input :position

Просмотреть файл

@@ -12,6 +12,7 @@ class Author < ApplicationRecord
has_one_attached :avatar
accepts_nested_attributes_for :profile, allow_destroy: true
accepts_nested_attributes_for :posts, allow_destroy: true
validates :email, format: { with: /\A[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\z/i, message: 'Invalid email' }

Просмотреть файл

@@ -10,6 +10,10 @@ class Post < ApplicationRecord
has_many :post_tags, inverse_of: :post, dependent: :destroy
has_many :tags, through: :post_tags
has_many_attached :images
accepts_nested_attributes_for :post_tags, allow_destroy: true
validates :title, allow_blank: false, presence: true
scope :published, -> { where(published: true) }

Просмотреть файл

@@ -13,12 +13,6 @@ require 'capybara/rails'
Dir[File.expand_path('support/**/*.rb', __dir__)].sort.each { |f| require f }
require 'simplecov'
SimpleCov.start :rails do
filters.clear
add_filter %r{^/spec/}
end
# Force deprecations to raise an exception.
ActiveSupport::Deprecation.behavior = :raise

Просмотреть файл

@@ -9,7 +9,7 @@ RSpec.describe 'Quill editor', type: :system do
end
after do
post.destroy
Post.destroy_all
author.destroy
end
@@ -17,7 +17,12 @@ RSpec.describe 'Quill editor', type: :system do
it 'updates some HTML content' do
visit "/admin/posts/#{post.id}/edit"
%w[bold italic underline link].each do |button|
expect(page).to have_css(".ql-toolbar button.ql-#{button}")
end
expect(page).to have_css('#post_description[data-aa-quill-editor]')
expect(page).to have_css('#post_description_input .ql-editor', text: 'Some content...')
find('#post_description_input .ql-editor').click
find('#post_description_input .ql-editor').base.send_keys('more text')
find('[type="submit"]').click
@@ -25,4 +30,21 @@ RSpec.describe 'Quill editor', type: :system do
expect(post.reload.description).to eq '<p>Some content...more text</p>'
end
end
context 'with a Quill editor in a nested resource' do
it 'updates some HTML content of a new nested resource' do
visit "/admin/authors/#{author.id}/edit"
expect(page).to have_css('.posts.has_many_container .ql-editor', text: 'Some content...')
find('.posts.has_many_container .has_many_add').click
expect(page).to have_css('.posts.has_many_container .ql-editor', count: 2)
fill_in('author[posts_attributes][1][title]', with: 'A new post')
find('#author_posts_attributes_1_description_input .ql-editor').base.send_keys('new post text')
find('[type="submit"]').click
expect(page).to have_content('was successfully updated')
expect(author.posts.last.description).to eq '<p>new post text</p>'
end
end
end