From 361f7ccc2f62a68bec312acd40e2883c685b1f76 Mon Sep 17 00:00:00 2001 From: Zee <50284+zspencer@users.noreply.github.com> Date: Sat, 17 Feb 2024 09:52:57 -0800 Subject: [PATCH] =?UTF-8?q?=E2=9A=99=EF=B8=8F=20`Gizmos`:=20Support=20`Act?= =?UTF-8?q?ionText`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - https://github.com/zinc-collective/convene/issues/709 - https://github.com/zinc-collective/convene/pull/2200#pullrequestreview-1879198110 - https://github.com/zinc-collective/convene/pull/2211 Markdown is cool, but it's nerd-forward. ActionText is a much more human-friendly way of implementing rich text in Rails applications. I don't know enough about [Trix] to know if it will work in multi-user contexts; or how it will work with rich-embeds, ala Notion or Google Docs But I think it's a good-enough-default for now; and if we get to the point where embeddables or multi-user editing of a field is important we can figure that out. [Trix]: https://trix-editor.org/ --- app/assets/stylesheets/actiontext.css | 36 +++++++++++++++++++ .../stylesheets/application.postcss.css | 1 + app/javascript/application.js | 3 ++ app/views/active_storage/blobs/_blob.html.erb | 14 ++++++++ .../action_text/contents/_content.html.erb | 3 ++ ...9_create_action_text_tables.action_text.rb | 27 ++++++++++++++ db/schema.rb | 12 ++++++- package.json | 2 ++ spec/rails_helper.rb | 13 +++---- spec/spec_helper.rb | 7 ++-- yarn.lock | 14 +++++++- 11 files changed, 120 insertions(+), 12 deletions(-) create mode 100644 app/assets/stylesheets/actiontext.css create mode 100644 app/views/active_storage/blobs/_blob.html.erb create mode 100644 app/views/layouts/action_text/contents/_content.html.erb create mode 100644 db/migrate/20240216213129_create_action_text_tables.action_text.rb diff --git a/app/assets/stylesheets/actiontext.css b/app/assets/stylesheets/actiontext.css new file mode 100644 index 000000000..f325379b3 --- /dev/null +++ b/app/assets/stylesheets/actiontext.css @@ -0,0 +1,36 @@ +/* + * Provides a drop-in pointer for the default Trix stylesheet that will format the toolbar and + * the trix-editor content (whether displayed or under editing). Feel free to incorporate this + * inclusion directly in any other asset bundle and remove this file. + * + *= require trix +*/ + +/* + * We need to override trix.css’s image gallery styles to accommodate the + * element we wrap around attachments. Otherwise, + * images in galleries will be squished by the max-width: 33%; rule. +*/ +.trix-content .attachment-gallery > action-text-attachment, +.trix-content .attachment-gallery > .attachment { + flex: 1 0 33%; + padding: 0 0.5em; + max-width: 33%; +} + +.trix-content + .attachment-gallery.attachment-gallery--2 + > action-text-attachment, +.trix-content .attachment-gallery.attachment-gallery--2 > .attachment, +.trix-content + .attachment-gallery.attachment-gallery--4 + > action-text-attachment, +.trix-content .attachment-gallery.attachment-gallery--4 > .attachment { + flex-basis: 50%; + max-width: 50%; +} + +.trix-content action-text-attachment .attachment { + padding: 0 !important; + max-width: 100% !important; +} diff --git a/app/assets/stylesheets/application.postcss.css b/app/assets/stylesheets/application.postcss.css index 43128e8b4..ff9af79db 100644 --- a/app/assets/stylesheets/application.postcss.css +++ b/app/assets/stylesheets/application.postcss.css @@ -7,3 +7,4 @@ @import "./utilities.scss"; @import "./components.scss"; +@import "actiontext.css"; diff --git a/app/javascript/application.js b/app/javascript/application.js index 341c48ae1..d170874c4 100644 --- a/app/javascript/application.js +++ b/app/javascript/application.js @@ -15,3 +15,6 @@ ActiveStorage.start(); import "@hotwired/turbo-rails"; import "./controllers/index.js"; + +import "trix"; +import "@rails/actiontext"; diff --git a/app/views/active_storage/blobs/_blob.html.erb b/app/views/active_storage/blobs/_blob.html.erb new file mode 100644 index 000000000..49ba357dd --- /dev/null +++ b/app/views/active_storage/blobs/_blob.html.erb @@ -0,0 +1,14 @@ +
attachment--<%= blob.filename.extension %>"> + <% if blob.representable? %> + <%= image_tag blob.representation(resize_to_limit: local_assigns[:in_gallery] ? [ 800, 600 ] : [ 1024, 768 ]) %> + <% end %> + +
+ <% if caption = blob.try(:caption) %> + <%= caption %> + <% else %> + <%= blob.filename %> + <%= number_to_human_size blob.byte_size %> + <% end %> +
+
diff --git a/app/views/layouts/action_text/contents/_content.html.erb b/app/views/layouts/action_text/contents/_content.html.erb new file mode 100644 index 000000000..9e3c0d0df --- /dev/null +++ b/app/views/layouts/action_text/contents/_content.html.erb @@ -0,0 +1,3 @@ +
+ <%= yield -%> +
diff --git a/db/migrate/20240216213129_create_action_text_tables.action_text.rb b/db/migrate/20240216213129_create_action_text_tables.action_text.rb new file mode 100644 index 000000000..c18655343 --- /dev/null +++ b/db/migrate/20240216213129_create_action_text_tables.action_text.rb @@ -0,0 +1,27 @@ +# This migration comes from action_text (originally 20180528164100) +class CreateActionTextTables < ActiveRecord::Migration[6.0] + def change + # Use Active Record's configured type for primary and foreign keys + primary_key_type, foreign_key_type = primary_and_foreign_key_types + + create_table :action_text_rich_texts, id: primary_key_type do |t| + t.string :name, null: false + t.text :body, size: :long + t.references :record, null: false, polymorphic: true, index: false, type: foreign_key_type + + t.timestamps + + t.index [:record_type, :record_id, :name], name: "index_action_text_rich_texts_uniqueness", unique: true + end + end + + private + + def primary_and_foreign_key_types + config = Rails.configuration.generators + setting = config.options[config.orm][:primary_key_type] + primary_key_type = setting || :primary_key + foreign_key_type = setting || :bigint + [primary_key_type, foreign_key_type] + end +end diff --git a/db/schema.rb b/db/schema.rb index 575ea565a..dfb44b130 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.1].define(version: 2024_02_07_040004) do +ActiveRecord::Schema[7.1].define(version: 2024_02_16_213129) do # These are extensions that must be enabled in order to support this database enable_extension "pgcrypto" enable_extension "plpgsql" @@ -30,6 +30,16 @@ "revoked", ], force: :cascade + create_table "action_text_rich_texts", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| + t.string "name", null: false + t.text "body" + t.string "record_type", null: false + t.uuid "record_id", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["record_type", "record_id", "name"], name: "index_action_text_rich_texts_uniqueness", unique: true + end + create_table "active_storage_attachments", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| t.string "name", null: false t.string "record_type", null: false diff --git a/package.json b/package.json index e2524551a..65f729313 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "@hotwired/stimulus-webpack-helpers": "^1.0.1", "@hotwired/turbo-rails": "^8.0.2", "@rails/actioncable": "^7.1.3", + "@rails/actiontext": "^7.1.3", "@rails/activestorage": "^7.1.3", "@sentry/browser": "^7.101.1", "@tailwindcss/forms": "^0.5.7", @@ -49,6 +50,7 @@ "postcss-import": "^16.0.1", "postcss-preset-env": "^9.3.0", "tailwindcss": "^3.4.1", + "trix": "^2.0.10", "webpack": "^5.76.0", "webpack-cli": "^5.1.4" } diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 08b3502c6..c6d16eb65 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -1,7 +1,7 @@ # This file is copied to spec/ when you run 'rails generate rspec:install' require "spec_helper" ENV["RAILS_ENV"] ||= "test" -require File.expand_path("../config/environment", __dir__) +require_relative "../config/environment" # Prevent database truncation if the environment is production abort("The Rails environment is running in production mode!") if Rails.env.production? require "rspec/rails" @@ -20,19 +20,20 @@ # directory. Alternatively, in the individual `*_spec.rb` files, manually # require only the support files necessary. # -Dir[Rails.root.join("spec", "support", "**", "*.rb")].sort.each { |f| require f } +Rails.root.glob("spec/support/**/*.rb").sort.each { |f| require f } # Checks for pending migrations and applies them before tests are run. # If you are not using ActiveRecord, you can remove these lines. begin ActiveRecord::Migration.maintain_test_schema! rescue ActiveRecord::PendingMigrationError => e - puts e.to_s.strip - exit 1 + abort e.to_s.strip end RSpec.configure do |config| # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures - config.fixture_paths = ["#{::Rails.root}/spec/fixtures"] + config.fixture_paths = [ + Rails.root.join("spec/fixtures") + ] # If you're not using ActiveRecord, or you'd prefer not to run each of your # examples within a transaction, remove the following line or assign false @@ -54,7 +55,7 @@ # end # # The different available types are documented in the features, such as in - # https://relishapp.com/rspec/rspec-rails/docs + # https://rspec.info/features/6-0/rspec-rails config.infer_spec_type_from_file_location! # Filter lines from Rails gems in backtraces. diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 7cd8f9340..23a18427c 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -23,7 +23,7 @@ # the additional setup, and require it from the spec files that actually need # it. # -# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration +# See https://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration RSpec.configure do |config| # rspec-expectations config goes here. You can use an alternate # assertion/expectation library such as wrong or the stdlib/minitest @@ -57,6 +57,7 @@ config.define_derived_metadata do |meta| meta[:aggregate_failures] = true end + # The settings below are suggested to provide a good initial experience # with RSpec, but feel free to customize to your heart's content. # # This allows you to limit a spec run to individual examples or groups @@ -73,9 +74,7 @@ # # # Limits the available syntax to the non-monkey patched syntax that is # # recommended. For more details, see: - # # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/ - # # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ - # # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode + # # https://rspec.info/features/3-12/rspec-core/configuration/zero-monkey-patching-mode/ # config.disable_monkey_patching! # # # Many RSpec users commonly either run the entire suite or an individual diff --git a/yarn.lock b/yarn.lock index f97588e96..2ba3a4503 100644 --- a/yarn.lock +++ b/yarn.lock @@ -373,7 +373,14 @@ resolved "https://registry.yarnpkg.com/@rails/actioncable/-/actioncable-7.1.3.tgz#4db480347775aeecd4dde2405659eef74a458881" integrity sha512-ojNvnoZtPN0pYvVFtlO7dyEN9Oml1B6IDM+whGKVak69MMYW99lC2NOWXWeE3bmwEydbP/nn6ERcpfjHVjYQjA== -"@rails/activestorage@^7.1.3": +"@rails/actiontext@^7.1.3": + version "7.1.3" + resolved "https://registry.yarnpkg.com/@rails/actiontext/-/actiontext-7.1.3.tgz#1b7aa2732c45cbc8a5708c54457c16fb81ce3da3" + integrity sha512-jwY0LJ0xpyIWkYs6BUMY4RcwVA3jZ+bFDPvv5KpKQnw+24BhJVtBLqF0XIsROaRCyabakueRyVuSG9NNhnLTvA== + dependencies: + "@rails/activestorage" ">= 7.1.0-alpha" + +"@rails/activestorage@>= 7.1.0-alpha", "@rails/activestorage@^7.1.3": version "7.1.3" resolved "https://registry.yarnpkg.com/@rails/activestorage/-/activestorage-7.1.3.tgz#e83ece6c5fd94b3ddf30a8cf3b8f78cad049e596" integrity sha512-B+RFYAU8vdTPFg0IJcRp2ey0Qw9hpcUOqHHcWqftDJ76ZMBi9+m/UUeMJlNsSd0l9eD+1HLlFSo1X//cY4yiDw== @@ -2109,6 +2116,11 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" +trix@^2.0.10: + version "2.0.10" + resolved "https://registry.yarnpkg.com/trix/-/trix-2.0.10.tgz#43f1ff7a94c42f708bd2bad3a2783147c0583698" + integrity sha512-a24w8rNVL+g9nDDdiDZwQVQ9AEWiXAmk9r0ZbwimczJi/xlaM+m0d6upAi0vysDNu0HsiYDFS1/VrR7HbX0Aig== + ts-interface-checker@^0.1.9: version "0.1.13" resolved "https://registry.yarnpkg.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz#784fd3d679722bc103b1b4b8030bcddb5db2a699"