Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow pasting into parent element with only one nested element type #2292

Merged
merged 2 commits into from
Apr 21, 2022
Merged
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
29 changes: 18 additions & 11 deletions app/controllers/alchemy/admin/elements_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ def index
elements = @page_version.elements.order(:position).includes(*element_includes)
@elements = elements.not_nested.unfixed
@fixed_elements = elements.not_nested.fixed
load_clipboard_items
end

def new
Expand All @@ -20,8 +21,7 @@ def new
@parent_element = Element.find_by(id: params[:parent_element_id])
@elements = @page.available_elements_within_current_scope(@parent_element)
@element = @page_version.elements.build
@clipboard = get_clipboard("elements")
@clipboard_items = Element.all_from_clipboard_for_page(@clipboard, @page)
load_clipboard_items
end

# Creates a element as discribed in config/alchemy/elements.yml on page via AJAX.
Expand All @@ -44,8 +44,7 @@ def create
else
@element.page_version = @page_version
@elements = @page.available_element_definitions
@clipboard = get_clipboard("elements")
@clipboard_items = Element.all_from_clipboard_for_page(@clipboard, @page)
load_clipboard_items
render :new
end
end
Expand Down Expand Up @@ -126,19 +125,27 @@ def load_element
@element = Element.find(params[:id])
end

def load_clipboard_items
@clipboard = get_clipboard("elements")
@clipboard_items = Element.all_from_clipboard_for_page(@clipboard, @page)
end

def element_from_clipboard
@element_from_clipboard ||= begin
@clipboard = get_clipboard("elements")
@clipboard.detect { |item| item["id"].to_i == params[:paste_from_clipboard].to_i }
end
@clipboard = get_clipboard("elements")
@clipboard.detect { |item| item["id"].to_i == params[:paste_from_clipboard].to_i }
end
end

def paste_element_from_clipboard
@source_element = Element.find(element_from_clipboard["id"])
element = Element.copy(@source_element, {
parent_element_id: create_element_params[:parent_element_id],
page_version_id: @page_version.id,
})
element = Element.copy(
@source_element,
{
parent_element_id: create_element_params[:parent_element_id],
page_version_id: @page_version.id,
}
)
if element_from_clipboard["action"] == "cut"
@cut_element_id = @source_element.id
@clipboard.delete_if { |item| item["id"] == @source_element.id.to_s }
Expand Down
15 changes: 10 additions & 5 deletions app/models/alchemy/element.rb
Original file line number Diff line number Diff line change
Expand Up @@ -159,19 +159,24 @@ def copy(source_element, differences = {})
end

def all_from_clipboard(clipboard)
return [] if clipboard.nil?
return none if clipboard.nil?

where(id: clipboard.collect { |e| e["id"] })
end

# All elements in clipboard that could be placed on page
#
def all_from_clipboard_for_page(clipboard, page)
return [] if clipboard.nil? || page.nil?
return none if clipboard.nil? || page.nil?

all_from_clipboard(clipboard).select { |ce|
page.available_element_names.include?(ce.name)
}
all_from_clipboard(clipboard).where(name: page.available_element_names)
end

# All elements in clipboard that could be placed as a child of `parent_element`
def all_from_clipboard_for_parent_element(clipboard, parent_element)
return none if clipboard.nil? || parent_element.nil?

all_from_clipboard(clipboard).where(name: parent_element.definition["nestable_elements"])
end
end

Expand Down
13 changes: 13 additions & 0 deletions app/views/alchemy/admin/clipboard/insert.js.erb
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,16 @@
Alchemy.growl('<%= j Alchemy.t("item copied to clipboard", name: @item.class.name == "Alchemy::Element" ? @item.display_name_with_preview_text : @item.name) %>')
<% end -%>
$('#clipboard_button .icon').removeClass('fa-clipboard').addClass('fa-paste');

<%# Update add nested element forms for any elements that accept ONLY this as a nested element %>
<% if @item.class == Alchemy::Element %>
if (window.location.pathname == "<%= edit_admin_page_path(@item.page.id) %>") {
<%
@item.page.draft_version.elements.expanded.select do |element|
element.definition["nestable_elements"] == [@item.name]
end.each do |element|
%>
$(".add-nested-element[data-element-id='<%= element.id %>']").replaceWith('<%= j render "alchemy/admin/elements/add_nested_element_form", element: element %>')
<% end %>
}
<% end %>
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<%= content_tag :div, class: 'add-nested-element', data: { element_id: element.id } do %>
<% if element.expanded? || element.fixed? %>
<% if element.nestable_elements.length == 1 &&
(nestable_element = element.nestable_elements.first) &&
Alchemy::Element.all_from_clipboard_for_parent_element(get_clipboard("elements"), element).none?
%>
<%= form_for [:admin, Alchemy::Element.new(name: nestable_element)],
remote: true, html: { class: 'add-nested-element-form', id: nil } do |f| %>
<%= f.hidden_field :name %>
<%= f.hidden_field :page_version_id, value: element.page_version_id %>
<%= f.hidden_field :parent_element_id, value: element.id %>
<button class="button add-nestable-element-button" data-alchemy-button>
<%= Alchemy.t(:add_nested_element, name: Alchemy.t(nestable_element, scope: 'element_names')) %>
</button>
<% end %>
<% else %>
<%= link_to_dialog (nestable_element ? Alchemy.t(:add_nested_element, name: Alchemy.t(nestable_element, scope: 'element_names')) : Alchemy.t("New Element")),
alchemy.new_admin_element_path(
parent_element_id: element.id,
page_version_id: element.page_version_id
), {
size: "320x125",
title: Alchemy.t("New Element")
}, class: "button add-nestable-element-button" %>
<% end %>
<% end %>
<% end %>
24 changes: 1 addition & 23 deletions app/views/alchemy/admin/elements/_element.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -55,29 +55,7 @@
} %>
<% end %>

<% if element.expanded? || element.fixed? %>
<% if element.nestable_elements.length == 1 %>
<% nestable_element = element.nestable_elements.first %>
<%= form_for [:admin, Alchemy::Element.new(name: nestable_element)],
remote: true, html: { class: 'add-nested-element-form', id: nil } do |f| %>
<%= f.hidden_field :name %>
<%= f.hidden_field :page_version_id, value: element.page_version_id %>
<%= f.hidden_field :parent_element_id, value: element.id %>
<button class="button add-nestable-element-button" data-alchemy-button>
<%= Alchemy.t(:add_nested_element) % { name: Alchemy.t(nestable_element, scope: 'element_names') } %>
</button>
<% end %>
<% else %>
<%= link_to_dialog Alchemy.t("New Element"),
alchemy.new_admin_element_path(
parent_element_id: element.id,
page_version_id: element.page_version_id
), {
size: "320x125",
title: Alchemy.t("New Element")
}, class: "button add-nestable-element-button" %>
<% end %>
<% end %>
<%= render "alchemy/admin/elements/add_nested_element_form", element: element %>
</div>
<% end %>
<% end %>
6 changes: 5 additions & 1 deletion app/views/alchemy/admin/elements/_form.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@
label: Alchemy.t(:element_of_type),
collection: elements_for_select(@elements),
prompt: Alchemy.t(:select_element),
input_html: {class: 'alchemy_selectbox', autofocus: true} %>
selected: (@elements.first if @elements.count == 1),
input_html: {class: 'alchemy_selectbox', autofocus: true, disabled: @elements.count == 1} %>
<% if @elements.count == 1 %>
<%= form.hidden_field :name, value: @elements.first[:name] %>
<% end %>
<%= form.hidden_field :parent_element_id, value: @parent_element.try(:id) %>
<%= form.submit Alchemy.t(:add) %>
<%- end -%>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ module Alchemy
let(:page_version) { create(:alchemy_page_version) }

it "assign variable for all available element definitions" do
expect_any_instance_of(Alchemy::Page).to receive(:available_element_definitions)
expect_any_instance_of(Alchemy::Page).to receive(:available_element_definitions).twice { [] }
get :new, params: { page_version_id: page_version.id }
end

Expand Down
51 changes: 44 additions & 7 deletions spec/features/admin/edit_elements_feature_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,16 +53,53 @@
create(:alchemy_element, :with_nestable_elements, page_version: a_page.draft_version)
end

scenario "the add element button immediately creates the nested element.", :js do
visit alchemy.admin_elements_path(page_version_id: element.page_version_id)
button = page.find(".add-nestable-element-button")
expect(button).to have_content "Add slide"
button.click
expect(page).to have_selector(".element-editor[data-element-name='slide']")
context "when clipboard has a nestable element" do
before do
allow_any_instance_of(Alchemy::Admin::ElementsController).to receive(:get_clipboard) do
[
{ "id" => create(:alchemy_element, name: element.definition["nestable_elements"].first).id, "action" => "copy" },
]
end
end

scenario "the add button opens add element form with the clipboard tab" do
visit alchemy.admin_elements_path(page_version_id: element.page_version_id)
button = page.find(".add-nestable-element-button")
expect(button).to have_content "Add slide"
button.click
expect(page).to have_select("Element")
expect(page).to have_link("Paste from clipboard")
end
end

context "when clipboard does not have a nestable element", :js do
scenario "the add element button immediately creates the nested element." do
visit alchemy.admin_elements_path(page_version_id: element.page_version_id)
button = page.find("button.add-nestable-element-button")
expect(button).to have_content "Add slide"
button.click
expect(page).to have_selector(".element-editor[data-element-name='slide']")
end

context "when a nested element is copied to clipboard" do
before do
visit alchemy.edit_admin_page_path(element.page)
page.find(".add-nestable-element-button").click
new_element = Alchemy::Element.last
page.find("#element_#{new_element.id} .element-header").hover
page.first("a[href^='/admin/clipboard/insert?remarkable_id=#{new_element.id}&remarkable_type=elements']").click
end

scenario "the add button now opens add element form with the clipboard tab" do
find("a.add-nestable-element-button").click
expect(page).to have_select("Element")
expect(page).to have_link("Paste from clipboard")
end
end
end
end

context "With an element having multiple nestable element defined" do
context "With an element having multiple nestable elements defined" do
let!(:element) do
create(:alchemy_element,
:with_nestable_elements,
Expand Down
55 changes: 44 additions & 11 deletions spec/models/alchemy/element_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,9 @@ module Alchemy
let(:clipboard) { [{ "id" => element_1.id.to_s }, { "id" => element_2.id.to_s }] }

before do
allow(Element).to receive(:all_from_clipboard).and_return([element_1, element_2])
allow(Element).to receive(:all_from_clipboard).and_return(
Element.where(id: [element_1, element_2].map(&:id))
)
end

it "return all elements from clipboard that could be placed on page" do
Expand All @@ -411,6 +413,37 @@ module Alchemy
end
end

describe ".all_from_clipboard_for_parent_element" do
subject { Element.all_from_clipboard_for_parent_element(clipboard, parent_element) }

let(:element_1) { create(:alchemy_element) }
let(:element_2) { create(:alchemy_element, name: "slide") }
let(:clipboard) { [{ "id" => element_1.id.to_s }, { "id" => element_2.id.to_s }] }
let(:parent_element) { create :alchemy_element, name: "slider" }

before do
allow(Element).to receive(:all_from_clipboard).and_return(
Element.where(id: [element_1, element_2].map(&:id))
)
end

it "returns all elements from clipboard that can be nested in the parent element" do
expect(subject).to match_array [element_2]
end

context "when clipboard nil" do
let(:clipboard) { nil }

it { is_expected.to be_empty }
end

context "when parent_element nil" do
let(:parent_element) { nil }

it { is_expected.to be_empty }
end
end

# InstanceMethods

describe "#all_contents_by_type" do
Expand Down Expand Up @@ -480,19 +513,19 @@ module Alchemy

it "should return the translation with the translated content label" do
expect(Alchemy).to receive(:t)
.with("content_names.content", default: "Content")
.and_return("Content")
.with("content_names.content", default: "Content")
.and_return("Content")
expect(Alchemy).to receive(:t)
.with("content", scope: "content_names.article", default: "Content")
.and_return("Contenido")
.with("content", scope: "content_names.article", default: "Content")
.and_return("Contenido")
expect(Alchemy).to receive(:t)
.with("article.content.invalid", {
scope: "content_validations",
default: [:"fields.content.invalid", :"errors.invalid"],
field: "Contenido",
})
.with("article.content.invalid", {
scope: "content_validations",
default: [:"fields.content.invalid", :"errors.invalid"],
field: "Contenido",
})
expect(element).to receive(:essence_errors)
.and_return({ "content" => [:invalid] })
.and_return({ "content" => [:invalid] })

element.essence_error_messages
end
Expand Down