Skip to content

Commit 3fe165a

Browse files
committed
Introduce trix/actiontext module
In an effort to absorb responsibilities from `@rails/actiontext` (namely the [app/javascript/actiontext/index.js][] and [app/javascript/actiontext/attachment_upload.js][] files), this commit introduces a new `trix/actiontext` file that the build process will output to be consumable from the `action_text-trix` engine's asset directory. The `trix/actiontext` module will depend on two in-browser dependencies: * `trix` expected to be imported separately * `@rails/activestorage` expected to available for import. Since there is a direct dependency on `@rails/activestorage` through the `@rails/acitontext` package, the dependency will dependably (😉) be present. In support of this change, this commit also expands the system test coverage introduced by [#1258][] to also account for `direct-upload:`-prefixed events dispatched during file uploads. [app/javascript/actiontext/index.js]: https://github.com/rails/rails/blob/v8.0.3/actiontext/app/javascript/actiontext/index.js [app/javascript/actiontext/attachment_upload.js]: https://github.com/rails/rails/blob/v8.0.3/actiontext/app/javascript/actiontext/attachment_upload.js [#1258]: #1258
1 parent 835d7d5 commit 3fe165a

File tree

13 files changed

+200
-5
lines changed

13 files changed

+200
-5
lines changed

action_text-trix/app/assets/javascripts/trix.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,9 @@ Copyright © 2025 37signals, LLC
8989
var engines = {
9090
node: ">= 18"
9191
};
92+
var peerDependencies = {
93+
"@rails/actiontext": "^8.1.100"
94+
};
9295
var _package = {
9396
name: name,
9497
version: version,
@@ -107,7 +110,8 @@ Copyright © 2025 37signals, LLC
107110
resolutions: resolutions,
108111
scripts: scripts,
109112
dependencies: dependencies,
110-
engines: engines
113+
engines: engines,
114+
peerDependencies: peerDependencies
111115
};
112116

113117
const attachmentSelector = "[data-trix-attachment]";
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/*
2+
trix/actiontext 2.1.15
3+
Copyright © 2025 37signals, LLC
4+
*/
5+
import { AttachmentUpload } from '@rails/actiontext';
6+
7+
addEventListener("trix-attachment-add", event => {
8+
const {
9+
attachment,
10+
target
11+
} = event;
12+
if (attachment.file) {
13+
const upload = new AttachmentUpload(attachment, target, attachment.file);
14+
const onProgress = event => attachment.setUploadProgress(event.detail.progress);
15+
target.addEventListener("direct-upload:progress", onProgress);
16+
upload.start().then(attributes => attachment.setAttributes(attributes)).catch(error => alert(error)).finally(() => target.removeEventListener("direct-upload:progress", onProgress));
17+
}
18+
});
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/*
2+
trix/actiontext 2.1.15
3+
Copyright © 2025 37signals, LLC
4+
*/
5+
import{AttachmentUpload as t}from"@rails/actiontext";addEventListener("trix-attachment-add",(e=>{const{attachment:r,target:a}=e;if(r.file){const e=new t(r,a,r.file),s=t=>r.setUploadProgress(t.detail.progress);a.addEventListener("direct-upload:progress",s),e.start().then((t=>r.setAttributes(t))).catch((t=>alert(t))).finally((()=>a.removeEventListener("direct-upload:progress",s)))}}));

action_text-trix/lib/action_text/trix/engine.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ module Trix
22
class Engine < ::Rails::Engine
33
initializer "trix.asset" do |app|
44
if app.config.respond_to?(:assets)
5-
app.config.assets.precompile += %w[ trix.js trix.css ]
5+
app.config.assets.precompile += %w[ trix.js trix.css trix/actiontext.esm.js trix/actiontext.esm.min.js ]
66
end
77
end
88
end

action_text-trix/test/application_system_test_case.rb

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,34 @@ class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
77
js_errors: true,
88
headless: ENV["HEADLESS"] != "0"
99
}
10+
11+
def capture_events(event_names)
12+
execute_script <<~JS, *event_names
13+
window.capturedEvents = []
14+
15+
function capture({ target: { id }, type, detail }) {
16+
for (const name in detail) {
17+
detail[name] = detail[name].constructor.name
18+
}
19+
20+
capturedEvents.push({ id, type, detail })
21+
}
22+
23+
for (const eventName of arguments) {
24+
addEventListener(eventName, capture, { once: true })
25+
}
26+
JS
27+
28+
yield
29+
30+
evaluate_script("window.capturedEvents").each do |event|
31+
event["target"] = find(id: event.delete("id"))
32+
end
33+
end
34+
35+
def go_offline!
36+
page.driver.browser.network.emulate_network_conditions(offline: true)
37+
end
1038
end
1139

1240
Capybara.server = :puma, { Silent: true }
Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
11
// Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails
2-
import "trix"

action_text-trix/test/dummy/app/views/layouts/application.html.erb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,15 @@
2020
<%# Includes all stylesheet files in app/assets/stylesheets %>
2121
<%= stylesheet_link_tag :app %>
2222
<%= javascript_importmap_tags %>
23+
<script type="module">
24+
import "trix"
25+
26+
<% if ActionText.version < "8.2" %>
27+
import "@rails/actiontext"
28+
<% else %>
29+
import "trix/actiontext"
30+
<% end %>
31+
</script>
2332
</head>
2433

2534
<body>

action_text-trix/test/dummy/config/application.rb

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,5 @@ class Application < Rails::Application
2525
#
2626
# config.time_zone = "Central Time (US & Canada)"
2727
# config.eager_load_paths << Rails.root.join("extras")
28-
29-
config.active_storage.variant_processor = :disabled
3028
end
3129
end

action_text-trix/test/dummy/config/importmap.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,6 @@
33
pin "application"
44

55
pin "trix"
6+
pin "trix/actiontext", to: "trix/actiontext.esm.js"
7+
pin "@rails/activestorage", to: "activestorage.esm.js"
8+
pin "@rails/actiontext", to: "actiontext.esm.js"

action_text-trix/test/system/action_text_test.rb

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,82 @@ class ActionTextTest < ApplicationSystemTestCase
1010

1111
assert_element class: "trix-content", text: "Hello, world!"
1212
end
13+
14+
test "attaches and uploads image file" do
15+
visit new_message_url
16+
attach_fixture_file "racecar.jpg"
17+
18+
within :rich_text_area do
19+
assert_selector :element, "img", src: %r{/rails/active_storage/blobs/redirect/.*/racecar.jpg\Z}
20+
end
21+
22+
click_button "Create Message"
23+
24+
within class: "trix-content" do
25+
assert_selector :element, "img", src: %r{/rails/active_storage/representations/redirect/.*/racecar.jpg\Z}
26+
end
27+
end
28+
29+
test "dispatches direct-upload:-prefixed events when uploading a File" do
30+
visit new_message_url
31+
events = capture_direct_upload_events do
32+
attach_fixture_file "racecar.jpg"
33+
34+
assert_selector :element, "img", src: %r{/rails/active_storage/blobs/redirect/.*/racecar.jpg\Z}
35+
end
36+
37+
assert_equal 1, ActiveStorage::Blob.where(filename: "racecar.jpg").count
38+
if ActionText.version > "7.2.0"
39+
assert_equal direct_upload_event("start"), events[0]
40+
assert_equal direct_upload_event("progress", progress: "Number"), events[1]
41+
assert_equal direct_upload_event("end"), events[2]
42+
end
43+
end
44+
45+
test "dispatches direct-upload:error event when uploading fails" do
46+
visit new_message_url
47+
events = capture_direct_upload_events do
48+
accept_alert 'Error creating Blob for "racecar.jpg". Status: 0' do
49+
go_offline!
50+
attach_fixture_file "racecar.jpg"
51+
end
52+
53+
assert_no_selector :element, "img", src: /racecar.jpg\Z/
54+
end
55+
56+
assert_empty ActiveStorage::Blob.where(filename: "racecar.jpg")
57+
if ActionText.version > "7.2.0"
58+
assert_equal direct_upload_event("error", error: "String"), events.last
59+
end
60+
end
61+
62+
def attach_fixture_file(path)
63+
attach_file(file_fixture(path)) { click_button "Attach Files" }
64+
rescue Ferrum::JavaScriptError => error
65+
case error.message
66+
when /Direct upload failed/
67+
# ignore the error in Ruby, let it bubble up on the browser side
68+
else
69+
raise
70+
end
71+
end
72+
73+
def capture_direct_upload_events(&block)
74+
capture_events %w[
75+
direct-upload:start
76+
direct-upload:progress
77+
direct-upload:error
78+
direct-upload:end
79+
], &block
80+
end
81+
82+
def direct_upload_event(name, target: find(:rich_text_area), **detail)
83+
ActiveSupport::HashWithIndifferentAccess.new(
84+
type: "direct-upload:#{name}",
85+
target: target,
86+
detail: detail.with_defaults(
87+
attachment: "ManagedAttachment"
88+
)
89+
)
90+
end
1391
end

0 commit comments

Comments
 (0)