Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ jobs:
- name: Install Dependencies
run: yarn install --frozen-lockfile
- run: bin/ci
- name: Fail when generated npm changes are not checked-in
run: |
git update-index --refresh && git diff-index --quiet HEAD --

rails-tests:
name: Downstream Rails integration tests
runs-on: ubuntu-latest
Expand Down Expand Up @@ -88,6 +92,10 @@ jobs:
rails_branch: main
experimental: true
steps:
- uses: ruby/setup-ruby-pkgs@v1
with:
ruby-version: ${{ matrix.ruby }}
apt-get: libvips-tools
- uses: actions/checkout@v4
- uses: actions/setup-node@v3
with:
Expand Down
1 change: 1 addition & 0 deletions action_text-trix/Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ gemspec

branch = ENV.fetch("RAILS_BRANCH", "main")
gem "rails", github: "rails/rails", branch: branch
gem "image_processing"
gem "importmap-rails"
gem "propshaft"
gem "puma"
Expand Down
37 changes: 34 additions & 3 deletions action_text-trix/app/assets/javascripts/trix.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ Copyright © 2025 37signals, LLC
var engines = {
node: ">= 18"
};
var peerDependencies = {
"@rails/actiontext": "^8.1.100"
};
var _package = {
name: name,
version: version,
Expand All @@ -107,7 +110,8 @@ Copyright © 2025 37signals, LLC
resolutions: resolutions,
scripts: scripts,
dependencies: dependencies,
engines: engines
engines: engines,
peerDependencies: peerDependencies
};

const attachmentSelector = "[data-trix-attachment]";
Expand Down Expand Up @@ -3451,12 +3455,16 @@ $\
}
const width = this.attachment.getWidth();
const height = this.attachment.getHeight();
const alt = this.attachment.getAttribute("alt");
if (width != null) {
image.width = width;
}
if (height != null) {
image.height = height;
}
if (alt != null) {
image.alt = alt;
}
const storeKey = ["imageElement", this.attachment.id, image.src, image.width, image.height].join("/");
image.dataset.trixStoreKey = storeKey;
}
Expand Down Expand Up @@ -6616,6 +6624,11 @@ $\
this.attributes = Hash.box(attributes);
this.didChangeAttributes();
}
setAttribute(attribute, value) {
this.setAttributes({
[attribute]: value
});
}
getAttribute(attribute) {
return this.attributes.get(attribute);
}
Expand Down Expand Up @@ -8855,6 +8868,8 @@ $\
ManagedAttachment.proxyMethod("attachment.isPending");
ManagedAttachment.proxyMethod("attachment.isPreviewable");
ManagedAttachment.proxyMethod("attachment.getURL");
ManagedAttachment.proxyMethod("attachment.getPreviewURL");
ManagedAttachment.proxyMethod("attachment.setPreviewURL");
ManagedAttachment.proxyMethod("attachment.getHref");
ManagedAttachment.proxyMethod("attachment.getFilename");
ManagedAttachment.proxyMethod("attachment.getFilesize");
Expand Down Expand Up @@ -12465,12 +12480,12 @@ $\
this.attributes = {};
this.actions = {};
this.resetDialogInputs();
handleEvent("mousedown", {
handleEvent("click", {
onElement: this.element,
matchingSelector: actionButtonSelector,
withCallback: this.didClickActionButton
});
handleEvent("mousedown", {
handleEvent("click", {
onElement: this.element,
matchingSelector: attributeButtonSelector,
withCallback: this.didClickAttributeButton
Expand Down Expand Up @@ -13248,6 +13263,22 @@ $\
this.innerHTML = toolbar.getDefaultHTML();
}
}

// Properties

get editorElements() {
if (this.id) {
var _this$ownerDocument;
const nodeList = (_this$ownerDocument = this.ownerDocument) === null || _this$ownerDocument === void 0 ? void 0 : _this$ownerDocument.querySelectorAll("trix-editor[toolbar=\"".concat(this.id, "\"]"));
return Array.from(nodeList);
} else {
return [];
}
}
get editorElement() {
const [editorElement] = this.editorElements;
return editorElement;
}
}

let id = 0;
Expand Down
18 changes: 18 additions & 0 deletions action_text-trix/app/assets/javascripts/trix/actiontext.esm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
trix/actiontext 2.1.15
Copyright © 2025 37signals, LLC
*/
import { AttachmentUpload } from '@rails/actiontext';

addEventListener("trix-attachment-add", event => {
const {
attachment,
target
} = event;
if (attachment.file) {
const upload = new AttachmentUpload(attachment, target, attachment.file);
const onProgress = event => attachment.setUploadProgress(event.detail.progress);
target.addEventListener("direct-upload:progress", onProgress);
upload.start().then(attributes => attachment.setAttributes(attributes)).catch(error => alert(error)).finally(() => target.removeEventListener("direct-upload:progress", onProgress));
}
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/*
trix/actiontext 2.1.15
Copyright © 2025 37signals, LLC
*/
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)))}}));
2 changes: 1 addition & 1 deletion action_text-trix/lib/action_text/trix/engine.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ module Trix
class Engine < ::Rails::Engine
initializer "trix.asset" do |app|
if app.config.respond_to?(:assets)
app.config.assets.precompile += %w[ trix.js trix.css ]
app.config.assets.precompile += %w[ trix.js trix.css trix/actiontext.esm.js trix/actiontext.esm.min.js ]
end
end
end
Expand Down
28 changes: 28 additions & 0 deletions action_text-trix/test/application_system_test_case.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,34 @@ class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
js_errors: true,
headless: ENV["HEADLESS"] != "0"
}

def capture_events(event_names)
execute_script <<~JS, *event_names
window.capturedEvents = []
function capture({ target: { id }, type, detail }) {
for (const name in detail) {
detail[name] = detail[name].constructor.name
}
capturedEvents.push({ id, type, detail })
}
for (const eventName of arguments) {
addEventListener(eventName, capture, { once: true })
}
JS

yield

evaluate_script("window.capturedEvents").each do |event|
event["target"] = find(id: event.delete("id"))
end
end

def go_offline!
page.driver.browser.network.emulate_network_conditions(offline: true)
end
end

Capybara.server = :puma, { Silent: true }
1 change: 0 additions & 1 deletion action_text-trix/test/dummy/app/javascript/application.js
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
// Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails
import "trix"
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,15 @@
<%# Includes all stylesheet files in app/assets/stylesheets %>
<%= stylesheet_link_tag :app %>
<%= javascript_importmap_tags %>
<script type="module">
import "trix"

<% if ActionText.version < "8.2.0" %>
import "@rails/actiontext"
<% else %>
import "trix/actiontext"
<% end %>
</script>
</head>

<body>
Expand Down
2 changes: 1 addition & 1 deletion action_text-trix/test/dummy/config/application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@ class Application < Rails::Application
# config.time_zone = "Central Time (US & Canada)"
# config.eager_load_paths << Rails.root.join("extras")

config.active_storage.variant_processor = :disabled
config.active_storage.variant_processor = :vips
end
end
3 changes: 3 additions & 0 deletions action_text-trix/test/dummy/config/importmap.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@
pin "application"

pin "trix"
pin "trix/actiontext", to: "trix/actiontext.esm.js"
pin "@rails/activestorage", to: "activestorage.esm.js"
pin "@rails/actiontext", to: "actiontext.esm.js"
69 changes: 69 additions & 0 deletions action_text-trix/test/system/action_text_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,73 @@ class ActionTextTest < ApplicationSystemTestCase

assert_element class: "trix-content", text: "Hello, world!"
end

test "attaches and uploads image file" do
visit new_message_url
attach_fixture_file "racecar.jpg"

within :rich_text_area do
assert_selector :element, "img", src: %r{/rails/active_storage/blobs/redirect/.*/racecar.jpg\Z}
end

click_button "Create Message"

within class: "trix-content" do
assert_selector :element, "img", src: %r{/rails/active_storage/representations/redirect/.*/racecar.jpg\Z}
end
end

if ActionText.version >= "8.0.0"
test "dispatches direct-upload:-prefixed events when uploading a File" do
visit new_message_url
events = capture_direct_upload_events do
attach_fixture_file "racecar.jpg"

assert_selector :element, "img", src: %r{/rails/active_storage/blobs/redirect/.*/racecar.jpg\Z}
end

assert_equal 1, ActiveStorage::Blob.where(filename: "racecar.jpg").count
assert_equal direct_upload_event("start"), events[0]
assert_equal direct_upload_event("progress", progress: "Number"), events[1]
assert_equal direct_upload_event("end"), events[2]
end

test "dispatches direct-upload:error event when uploading fails" do
visit new_message_url
events = capture_direct_upload_events do
accept_alert 'Error creating Blob for "racecar.jpg". Status: 0' do
go_offline!
attach_fixture_file "racecar.jpg"
end

assert_no_selector :element, "img", src: /racecar.jpg\Z/
end

assert_empty ActiveStorage::Blob.where(filename: "racecar.jpg")
assert_equal direct_upload_event("error", error: "String"), events.last
end
end

def attach_fixture_file(path)
attach_file(file_fixture(path)) { click_button "Attach Files" }
end

def capture_direct_upload_events(&block)
capture_events %w[
direct-upload:start
direct-upload:progress
direct-upload:error
direct-upload:end
], &block
end

def direct_upload_event(name, target: find(:rich_text_area), **detail)
ActiveSupport::HashWithIndifferentAccess.new(
type: "direct-upload:#{name}",
target: target,
detail: detail.with_defaults(
attachment: "ManagedAttachment"
)
)
end
end
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,5 +79,8 @@
},
"engines": {
"node": ">= 18"
},
"peerDependencies": {
"@rails/actiontext": "^8.1.100"
}
}
33 changes: 33 additions & 0 deletions rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { version } from "./package.json"

const year = new Date().getFullYear()
const banner = `/*\nTrix ${version}\nCopyright © ${year} 37signals, LLC\n */`
const actionTextBanner = `/*\ntrix/actiontext ${version}\nCopyright © ${year} 37signals, LLC\n */`

const plugins = [
json(),
Expand Down Expand Up @@ -92,6 +93,38 @@ export default [
],
...compressedConfig,
},
{
input: "src/trix/actiontext.js",
output: [
{
file: "dist/trix/actiontext.esm.js",
format: "es",
banner: actionTextBanner
},
{
file: "action_text-trix/app/assets/javascripts/trix/actiontext.esm.js",
format: "es",
banner: actionTextBanner
}
],
...defaultConfig,
},
{
input: "src/trix/actiontext.js",
output: [
{
file: "dist/trix/actiontext.esm.js",
format: "es",
banner: actionTextBanner
},
{
file: "action_text-trix/app/assets/javascripts/trix/actiontext.esm.min.js",
format: "es",
banner: actionTextBanner
}
],
...compressedConfig,
},
{
input: "src/test/test.js",
output: {
Expand Down
17 changes: 17 additions & 0 deletions src/trix/actiontext.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { AttachmentUpload } from "@rails/actiontext"

addEventListener("trix-attachment-add", event => {
const { attachment, target } = event

if (attachment.file) {
const upload = new AttachmentUpload(attachment, target, attachment.file)
const onProgress = event => attachment.setUploadProgress(event.detail.progress)

target.addEventListener("direct-upload:progress", onProgress)

upload.start()
.then(attributes => attachment.setAttributes(attributes))
.catch(error => alert(error))
.finally(() => target.removeEventListener("direct-upload:progress", onProgress))
}
})