diff --git a/app/components/concerns/op_turbo/streamable.rb b/app/components/concerns/op_turbo/streamable.rb
index 5d8030f6789..858eb81e8c8 100644
--- a/app/components/concerns/op_turbo/streamable.rb
+++ b/app/components/concerns/op_turbo/streamable.rb
@@ -36,7 +36,7 @@ module OpTurbo
class_methods do
def wrapper_key
- name.underscore.gsub("/", "-").gsub("_", "-")
+ name.underscore.tr("/", "-").tr("_", "-")
end
end
@@ -61,7 +61,7 @@ module OpTurbo
"Wrap your component in a `component_wrapper` block in order to use turbo-stream methods"
end
- OpTurbo::StreamWrapperComponent.new(
+ OpTurbo::StreamComponent.new(
action:,
target: wrapper_key,
template:
@@ -79,7 +79,7 @@ module OpTurbo
"Wrap your component in a `component_wrapper` block in order to use turbo-stream methods"
end
- OpTurbo::StreamWrapperComponent.new(
+ OpTurbo::StreamComponent.new(
action:,
target: insert_target_modified? ? insert_target_modifier_id : wrapper_key,
template:
diff --git a/app/components/op_turbo/frame_component.html.erb b/app/components/op_turbo/frame_component.html.erb
new file mode 100644
index 00000000000..64adfb7f55d
--- /dev/null
+++ b/app/components/op_turbo/frame_component.html.erb
@@ -0,0 +1,31 @@
+<%#-- copyright
+OpenProject is an open source project management software.
+Copyright (C) 2012-2023 the OpenProject GmbH
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License version 3.
+
+OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
+Copyright (C) 2006-2013 Jean-Philippe Lang
+Copyright (C) 2010-2013 the ChiliProject Team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+See COPYRIGHT and LICENSE files for more details.
+
+++#%>
+
+ <%= content %>
+
diff --git a/app/components/op_turbo/frame_component.rb b/app/components/op_turbo/frame_component.rb
new file mode 100644
index 00000000000..6f3876a84ad
--- /dev/null
+++ b/app/components/op_turbo/frame_component.rb
@@ -0,0 +1,35 @@
+#-- copyright
+# OpenProject is an open source project management software.
+# Copyright (C) 2012-2023 the OpenProject GmbH
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License version 3.
+#
+# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
+# Copyright (C) 2006-2013 Jean-Philippe Lang
+# Copyright (C) 2010-2013 the ChiliProject Team
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# See COPYRIGHT and LICENSE files for more details.
+#++
+
+module OpTurbo
+ class FrameComponent < ApplicationComponent
+ def turbo_frame_id
+ ActionView::RecordIdentifier.dom_id(model, options[:context])
+ end
+ end
+end
diff --git a/app/components/op_turbo/stream_wrapper_component.html.erb b/app/components/op_turbo/stream_component.html.erb
similarity index 100%
rename from app/components/op_turbo/stream_wrapper_component.html.erb
rename to app/components/op_turbo/stream_component.html.erb
diff --git a/app/components/op_turbo/stream_wrapper_component.rb b/app/components/op_turbo/stream_component.rb
similarity index 96%
rename from app/components/op_turbo/stream_wrapper_component.rb
rename to app/components/op_turbo/stream_component.rb
index 6ad310353fc..2a1149d9465 100644
--- a/app/components/op_turbo/stream_wrapper_component.rb
+++ b/app/components/op_turbo/stream_component.rb
@@ -27,7 +27,7 @@
#++
module OpTurbo
- class StreamWrapperComponent < ApplicationComponent
+ class StreamComponent < ApplicationComponent
def initialize(template:, action:, target:)
super()
diff --git a/lookbook/previews/op_turbo/frame_component_preview.rb b/lookbook/previews/op_turbo/frame_component_preview.rb
new file mode 100644
index 00000000000..cbacdabbe6f
--- /dev/null
+++ b/lookbook/previews/op_turbo/frame_component_preview.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module OpTurbo
+ # @logical_path OpenProject/OpTurbo
+ class FrameComponentPreview < Lookbook::Preview
+ # Renders a turbo-frame tag with a unique id
+ # @param context text
+ def default(context: nil)
+ model = FactoryBot.build_stubbed(:user)
+ render_with_template(locals: { model:, context: })
+ end
+ end
+end
diff --git a/lookbook/previews/op_turbo/frame_component_preview/default.html.erb b/lookbook/previews/op_turbo/frame_component_preview/default.html.erb
new file mode 100644
index 00000000000..36894e579fd
--- /dev/null
+++ b/lookbook/previews/op_turbo/frame_component_preview/default.html.erb
@@ -0,0 +1,8 @@
+
+ Default
+ <%= render(OpTurbo::FrameComponent.new(model)) %>
+
+
+ Context
+ <%= render(OpTurbo::FrameComponent.new(model, context: context)) %>
+
diff --git a/lookbook/previews/op_turbo/stream_component_preview.rb b/lookbook/previews/op_turbo/stream_component_preview.rb
new file mode 100644
index 00000000000..d58958d4465
--- /dev/null
+++ b/lookbook/previews/op_turbo/stream_component_preview.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+module OpTurbo
+ # @logical_path OpenProject/OpTurbo
+ class StreamComponentPreview < Lookbook::Preview
+ # Renders a turbo-stream tag with given action and target
+ # @param _action select { choices: [append, prepend, replace, update, remove, before, after] }
+ # @param target text
+ def default(_action: 'append', target: 'model_id')
+ template = template_example_from_action(_action, target)
+ render_with_template(locals: { template:, action: _action, target: })
+ end
+
+ private
+
+ def template_example_from_action(action, target)
+ <<~HTML
+
+ This div will #{action} to the element with the DOM ID "#{target}".
+
+ HTML
+ end
+ end
+end
diff --git a/lookbook/previews/op_turbo/stream_component_preview/default.html.erb b/lookbook/previews/op_turbo/stream_component_preview/default.html.erb
new file mode 100644
index 00000000000..ae50d59624e
--- /dev/null
+++ b/lookbook/previews/op_turbo/stream_component_preview/default.html.erb
@@ -0,0 +1,4 @@
+
+ Default
+ <%= render(OpTurbo::StreamComponent.new(template:, action:, target:)) %>
+
diff --git a/spec/components/op_turbo/frame_component_spec.rb b/spec/components/op_turbo/frame_component_spec.rb
new file mode 100644
index 00000000000..9a056f96810
--- /dev/null
+++ b/spec/components/op_turbo/frame_component_spec.rb
@@ -0,0 +1,51 @@
+#-- copyright
+# OpenProject is an open source project management software.
+# Copyright (C) 2012-2023 the OpenProject GmbH
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License version 3.
+#
+# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
+# Copyright (C) 2006-2013 Jean-Philippe Lang
+# Copyright (C) 2010-2013 the ChiliProject Team
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# See COPYRIGHT and LICENSE files for more details.
+#++
+#
+require 'spec_helper'
+
+RSpec.describe OpTurbo::FrameComponent, type: :component do
+ describe '#turbo_frame_id' do
+ context 'with `context:` option' do
+ it 'returns the turbo frame id' do
+ storage = build_stubbed(:nextcloud_storage, id: 1)
+ component = described_class.new(storage, context: :general_info)
+
+ expect(component.turbo_frame_id).to eq('general_info_storages_nextcloud_storage_1')
+ end
+ end
+
+ context 'without `context:` option' do
+ it 'returns just the model dom id' do
+ storage = build_stubbed(:nextcloud_storage, id: 1)
+ component = described_class.new(storage)
+
+ expect(component.turbo_frame_id).to eq('storages_nextcloud_storage_1')
+ end
+ end
+ end
+end