Remove new_nested action from documents controller. Use cocoon gem to manage new nested fields creation on documents.

This commit is contained in:
Senén Rodero Rodríguez
2017-09-22 16:30:32 +02:00
parent 3745e76c07
commit 88a7a29d27
24 changed files with 175 additions and 194 deletions

View File

@@ -1,11 +1,14 @@
App.Documentable =
initialize: ->
inputFiles = $('input.js-document-attachment[type=file]')
$.each inputFiles, (index, input) ->
$('#nested-documents').on 'cocoon:after-insert', (e, nested_document) ->
input = $(nested_document).find('.js-document-attachment')
App.Documentable.initializeDirectUploadInput(input)
if $(nested_document).closest('#nested-documents').find('.document').length >= $('#nested-documents').data('max-documents-allowed')
$('#max-documents-notice').removeClass('hide')
$('#new_document_link').addClass('hide')
initializeDirectUploadInput: (input) ->
inputData = @buildData([], input)
@@ -75,8 +78,8 @@ App.Documentable =
data.fileNameContainer = $(wrapper).find('p.file-name')
data.destroyAttachmentLinkContainer = $(wrapper).find('.action-remove')
data.addAttachmentLabel = $(wrapper).find('.action-add label')
data.cachedAttachmentField = $(wrapper).find("#" + $(input).data('cached-attachment-input-field'))
data.titleField = $(wrapper).find("#" + $(input).data('title-input-field'))
data.cachedAttachmentField = $(wrapper).find("input[name$='[cached_attachment]']")
data.titleField = $(wrapper).find("input[name$='[title]']")
$(wrapper).find('.progress-bar-placeholder').css('display', 'block')
return data
@@ -119,12 +122,13 @@ App.Documentable =
App.Documentable.clearInputErrors(data)
App.Documentable.clearProgressBar(data)
$('#new_document_link').removeClass('hide')
$('#max-documents-notice').addClass('hide')
$(data.wrapper).find(".attachment-actions").addClass('small-12').removeClass('small-6 float-right')
$(data.wrapper).find(".attachment-actions .action-remove").addClass('small-3').removeClass('small-12')
if $(data.input).data('nested-document') == true
$(data.wrapper).remove()
$('#new_document_link').show()
$('.max-documents-notice').hide()
else
$(data.destroyAttachmentLinkContainer).find('a.delete').remove()
initializeRemoveDocumentLink: (input) ->
wrapper = $(input).closest(".direct-upload")
@@ -132,8 +136,8 @@ App.Documentable =
$(remove_document_link).on 'click', (e) ->
e.preventDefault()
$(wrapper).remove()
$('#new_document_link').show()
$('.max-documents-notice').hide()
$('#new_document_link').removeClass('hide')
$('#max-documents-notice').addClass('hide')
initializeRemoveCachedDocumentLink: (input, data) ->
wrapper = $(input).closest(".direct-upload")
@@ -143,23 +147,5 @@ App.Documentable =
e.stopPropagation()
App.Documentable.doDeleteCachedAttachmentRequest(this.href, data)
new: (nested_field) ->
nested_field = $(nested_field)
$(".documents-list").append(nested_field)
input = nested_field.find("input[type='file']")
@initializeDirectUploadInput(input)
destroyNestedDocument: (id) ->
$('#' + id).remove()
updateNewDocumentButton: (link) ->
if $('.document').length >= $('.documents').data('max-documents')
$('#new_document_link').hide()
$('.max-documents-notice').removeClass('hide')
$('.max-documents-notice').show()
else if $('#new_document_link').length > 0
$('#new_document_link').replaceWith(link)
$('.max-documents-notice').hide()
else
$('.max-documents-notice').hide()
$(link).insertBefore('.documents hr:last')

View File

@@ -114,6 +114,7 @@ App.Imageable =
$(data.errorContainer).append(errors)
setPreview: (data) ->
console.log 'App.Imageable.setPreview'
image_preview = '<div class="small-12 column text-center image-preview"><figure><img src="' + data.result.attachment_url + '" class="cached-image"/></figure></div>'
if $(data.preview).length > 0
$(data.preview).replaceWith(image_preview)

View File

@@ -47,6 +47,10 @@
.document-form,
.image-form {
.title{
margin-bottom: $line-height;
}
.document,
.image-form {
.file-name {

View File

@@ -315,7 +315,9 @@
}
.budget-investment-new,
.proposal-form {
.proposal-form,
.proposal-edit,
.image-form {
@include upload-image-documents;
}

View File

@@ -1,7 +1,7 @@
class DocumentsController < ApplicationController
before_action :authenticate_user!
before_action :find_documentable, except: :destroy
before_action :prepare_new_document, only: [:new, :new_nested]
before_action :prepare_new_document, only: [:new]
before_action :prepare_document_for_creation, only: :create
load_and_authorize_resource
@@ -9,9 +9,6 @@ class DocumentsController < ApplicationController
def new
end
def new_nested
end
def create
recover_attachments_from_cache

View File

@@ -17,24 +17,14 @@ module DocumentsHelper
bytes / Numeric::MEGABYTE
end
def document_nested_field_name(document, index, field)
parent = document.documentable_type.parameterize.underscore
"#{parent.parameterize}[documents_attributes][#{index}][#{field}]"
end
def document_nested_field_id(document, index, field)
parent = document.documentable_type.parameterize.underscore
"#{parent.parameterize}_documents_attributes_#{index}_#{field}"
end
def document_nested_field_wrapper_id(index)
"document_#{index}"
end
def render_destroy_document_link(document, index)
def render_destroy_document_link(document)
if document.persisted?
link_to t('documents.form.delete_button'),
document_path(document, index: index, nested_document: true),
document_path(document, nested_document: true),
method: :delete,
remote: true,
data: { confirm: t('documents.actions.destroy.confirm') },
@@ -55,25 +45,18 @@ module DocumentsHelper
end
end
def render_attachment(document, index)
html = file_field_tag :attachment,
def render_attachment(builder, document)
html = builder.file_field :attachment,
label: false,
accept: accepted_content_types_extensions(document.documentable_type.constantize),
class: 'js-document-attachment',
data: {
url: document_direct_upload_url(document),
cached_attachment_input_field: document_nested_field_id(document, index, :cached_attachment),
title_input_field: document_nested_field_id(document, index, :title),
multiple: false,
index: index,
nested_document: true
},
name: document_nested_field_name(document, index, :attachment),
id: document_nested_field_id(document, index, :attachment)
}
if document.attachment.blank? && document.cached_attachment.blank?
klass = document.errors[:attachment].any? ? "error" : ""
html += label_tag document_nested_field_id(document, index, :attachment),
t("documents.form.attachment_label"),
class: "button hollow #{klass}"
html += builder.label :attachment, t("documents.upload_document"), class: "button hollow"
if document.errors[:attachment].any?
html += content_tag :small, class: "error" do
document_errors_on_attachment(document)

View File

@@ -38,7 +38,7 @@ module Abilities
can [:create, :destroy], Follow
can [:create, :destroy, :new], Document, documentable: { author_id: user.id }
can [:new_nested, :upload, :destroy_upload], Document
can [:upload, :destroy_upload], Document
can [:create, :destroy, :new], Image, imageable: { author_id: user.id }
can [:new_nested, :upload, :destroy_upload], Image

View File

@@ -12,7 +12,6 @@ class Budget
documentable max_documents_allowed: 3,
max_file_size: 3.megabytes,
accepted_content_types: [ "application/pdf" ]
accepts_nested_attributes_for :documents, allow_destroy: true
acts_as_votable
acts_as_paranoid column: :hidden_at

View File

@@ -3,6 +3,7 @@ module Documentable
included do
has_many :documents, as: :documentable, dependent: :destroy
accepts_nested_attributes_for :documents, allow_destroy: true
end
module ClassMethods

View File

@@ -15,7 +15,6 @@ class Proposal < ActiveRecord::Base
documentable max_documents_allowed: 3,
max_file_size: 3.megabytes,
accepted_content_types: [ "application/pdf" ]
accepts_nested_attributes_for :documents, allow_destroy: true
include EmbedVideosHelper
acts_as_votable

View File

@@ -25,8 +25,8 @@
<%= render 'images/nested_images', imageable: @investment %>
</div>
<div class="documents small-12 column" data-max-documents="<%= Budget::Investment.max_documents_allowed %>">
<%= render 'documents/nested_documents', documentable: @investment %>
<div class="documents small-12 column">
<%= render 'documents/nested_documents', documentable: @investment, f: f %>
</div>
<div class="small-12 column">

View File

@@ -0,0 +1,31 @@
<div id="<%= dom_id(f.object) %>" class="document direct-upload">
<%= f.hidden_field :id %>
<%= f.hidden_field :user_id, value: current_user.id %>
<%= f.hidden_field :cached_attachment %>
<div class="small-12 column title">
<%= f.text_field :title, placeholder: t("documents.new.form.title_placeholder") %>
</div>
<div class="small-12 column attachment-actions">
<div class="small-9 column action-add attachment-errors document-attachment">
<%= render_attachment(f, f.object) %>
</div>
<div class="small-3 column action-remove text-right">
<%= render_destroy_document_link(f.object) %>
</div>
</div>
<div class="small-6 column">
<p class="file-name">
<%= document_attachment_file_name(f.object) %>
</p>
</div>
<div class="small-12 column">
<div class="progress-bar-placeholder"><div class="loading-bar"></div></div>
</div>
<hr>
</div>

View File

@@ -12,7 +12,7 @@
<%= f.hidden_field :cached_attachment %>
<div class="small-12 column">
<div class="small-12 column title">
<%= f.text_field :title, placeholder: t("documents.new.form.title_placeholder") %>
</div>

View File

@@ -1,23 +1,31 @@
<div class="documents-list">
<%= label_tag :documents, t("documents.form.title") %>
<%= f.label :documents, t("documents.form.title") %>
<p class="help-text"><%= documentables_note(documentable) %></p>
<% documentable.documents.each_with_index do |document, index| %>
<%= render 'documents/nested_fields', document: document, index: index %>
<div id="nested-documents" data-max-documents-allowed="<%= documentable.class.max_documents_allowed%>">
<%= f.fields_for :documents do |documents_builder| %>
<%= render 'documents/document_fields', f: documents_builder %>
<% end %>
</div>
</div>
<% unless max_documents_allowed?(documentable) %>
<%= link_to t("documents.form.add_new_document"),
new_nested_documents_path(documentable_type: documentable.class.name, index: documentable.documents.size),
remote: true,
<%= link_to_add_association t('documents.form.add_new_document'), f, :documents,
partial: "documents/document_fields",
render_options: {
locals: {
document: Document.new(documentable: documentable)
},
},
id: "new_document_link",
class: "button hollow" %>
<% end %>
class: "button hollow",
data: {
association_insertion_node: "#nested-documents",
association_insertion_method: "append"
} %>
<div class="max-documents-notice callout warning text-center <%= "hide" unless max_documents_allowed?(documentable) %>">
<div id="max-documents-notice" class="max-documents-notice callout warning text-center <%= "hide" unless max_documents_allowed?(documentable) %>">
<%= t "documents.max_documents_allowed_reached_html" %>
</div>
</div>
<hr>
<hr>
</div>

View File

@@ -1,48 +0,0 @@
<div id="<%= document_nested_field_wrapper_id(index) %>" class="document direct-upload">
<%= hidden_field_tag :id,
document.id,
name: document_nested_field_name(document, index, :id),
id: document_nested_field_id(document, index, :id) if document.persisted? %>
<%= hidden_field_tag :user_id,
current_user.id,
name: document_nested_field_name(document, index, :user_id),
id: document_nested_field_id(document, index, :user_id) %>
<%= hidden_field_tag :cached_attachment,
document.cached_attachment,
name: document_nested_field_name(document, index, :cached_attachment),
id: document_nested_field_id(document, index, :cached_attachment) %>
<div class="small-12 column">
<%= label_tag :title, t("activerecord.attributes.document.title") %>
<%= text_field_tag :title,
document.title,
placeholder: t("documents.new.form.title_placeholder"),
name: document_nested_field_name(document, index, :title),
id: document_nested_field_id(document, index, :title),
class: "document-title" %>
<% if document.errors[:title].any? %>
<small class="error"><%= document.errors[:title].join(", ") %></small>
<% end %>
</div>
<div class="small-12 column attachment-actions">
<div class="small-9 column action-add attachment-errors document-attachment">
<%= render_attachment(document, index) %>
</div>
<div class="small-3 column action-remove text-right">
<%= render_destroy_document_link(document, index) %>
</div>
</div>
<div class="small-6 column">
<p class="file-name">
<%= document_attachment_file_name(document) %>
</p>
</div>
<div class="small-12 column">
<div class="progress-bar-placeholder"><div class="loading-bar"></div></div>
</div>
<hr>
</div>

View File

@@ -1,7 +1 @@
App.Documentable.destroyNestedDocument("<%= document_nested_field_wrapper_id(params[:index]) %>")
<% new_document_link = link_to t("documents.form.add_new_document"),
new_nested_documents_path(documentable_type: @document.documentable_type, index: params[:index]),
remote: true,
id: "new_document_link",
class: "button hollow" %>
App.Documentable.updateNewDocumentButton("<%= j new_document_link %>")
App.Documentable.destroyNestedDocument("<%= dom_id(@document) %>")

View File

@@ -1,9 +0,0 @@
<%
new_document_link = link_to t("documents.form.add_new_document"),
new_nested_documents_path(documentable_type: params[:documentable_type], index: params[:index].to_i + 1),
remote: true,
id: "new_document_link",
class: "button hollow"
%>
App.Documentable.new("<%= j render('documents/nested_fields', document: @document, index: params[:index]) %>")
App.Documentable.updateNewDocumentButton("<%= j new_document_link %>")

View File

@@ -12,7 +12,7 @@
<%= f.hidden_field :cached_attachment %>
<div class="small-12 column">
<div class="small-12 column title">
<%= f.text_field :title, placeholder: t("images.new.form.title_placeholder") %>
</div>

View File

@@ -12,15 +12,17 @@
name: image_nested_field_name(image, :cached_attachment),
id: image_nested_field_id(image, :cached_attachment) %>
<div class="small-12 column">
<%= label_tag :title, t("activerecord.attributes.image.title") %>
<div class="small-12 column title">
<%= label_tag :title,
t("activerecord.attributes.image.title"),
class: image.errors[:title].any? ? "error" : "" %>
<%= text_field_tag :title,
image.title,
placeholder: t("images.new.form.title_placeholder"),
name: image_nested_field_name(image, :title),
id: image_nested_field_id(image, :title),
class: "image-title" %>
class: image.errors[:title].any? ? "error" : "" %>
<% if image.errors[:title].any? %>
<small class="error"><%= image.errors[:title].join(", ") %></small>
<% end %>

View File

@@ -50,8 +50,8 @@
<%= render 'images/nested_images', imageable: @proposal %>
</div>
<div class="documents small-12 column" data-max-documents="<%= Proposal.max_documents_allowed %>">
<%= render 'documents/nested_documents', documentable: @proposal %>
<div class="documents small-12 column">
<%= render 'documents/nested_documents', documentable: @proposal, f: f %>
</div>
<div class="small-12 medium-6 column">

View File

@@ -100,9 +100,7 @@ Rails.application.routes.draw do
resources :follows, only: [:create, :destroy]
resources :documents, only: [:new, :create, :destroy] do
get :new_nested, on: :collection
end
resources :documents, only: [:new, :create, :destroy]
resources :images, only: [:new, :create, :destroy] do
get :new_nested, on: :collection

View File

@@ -1290,6 +1290,8 @@ feature 'Proposals' do
it_behaves_like "followable", "proposal", "proposal_path", { "id": "id" }
describe "xxx" do
it_behaves_like "imageable", "proposal", "proposal_path", { "id": "id" }
it_behaves_like "nested imageable",
@@ -1325,6 +1327,7 @@ feature 'Proposals' do
nil,
"Save changes",
"Proposal updated successfully"
end
scenario 'Erased author' do
user = create(:user)

View File

@@ -92,7 +92,6 @@ describe "Abilities::Common" do
it { should_not be_able_to(:create, DirectMessage) }
it { should_not be_able_to(:show, DirectMessage) }
it { should be_able_to(:new_nested, Document) }
it { should be_able_to(:destroy_upload, Document) }
it { should be_able_to(:new, own_proposal_document) }

View File

@@ -30,9 +30,7 @@ shared_examples "nested documentable" do |documentable_factory_name, path, docum
visit send(path, arguments)
click_link "Add new document"
expect(page).to have_css "##{documentable_factory_name}_documents_attributes_0_title"
click_link "Add new document"
expect(page).to have_css "##{documentable_factory_name}_documents_attributes_1_title"
click_link "Add new document"
expect(page).to have_css "#new_document_link", visible: false
@@ -49,11 +47,9 @@ shared_examples "nested documentable" do |documentable_factory_name, path, docum
login_as user
visit send(path, arguments)
documentable.class.max_documents_allowed.times.each do
click_link "Add new document"
expect(page).to have_css "##{documentable_factory_name}_documents_attributes_0_title"
click_link "Add new document"
expect(page).to have_css "##{documentable_factory_name}_documents_attributes_1_title"
click_link "Add new document"
end
expect(page).to have_css ".max-documents-notice", visible: true
end
@@ -63,14 +59,10 @@ shared_examples "nested documentable" do |documentable_factory_name, path, docum
visit send(path, arguments)
click_link "Add new document"
expect(page).to have_css "##{documentable_factory_name}_documents_attributes_0_title"
click_link "Add new document"
expect(page).to have_css "##{documentable_factory_name}_documents_attributes_1_title"
click_link "Add new document"
expect(page).to have_css "##{documentable_factory_name}_documents_attributes_2_title"
within "#document_0" do
find("a", text: "Remove document").click
end
all("a", text: "Remove document").last.click
expect(page).to have_css ".max-documents-notice", visible: false
end
@@ -80,7 +72,10 @@ shared_examples "nested documentable" do |documentable_factory_name, path, docum
visit send(path, arguments)
click_link "Add new document"
attach_file("#{documentable_factory_name}[documents_attributes][0][attachment]", "spec/fixtures/files/empty.pdf", make_visible: true)
within "#nested-documents" do
document = find(".document input[type=file]", visible: false)
attach_file(document[:id], "spec/fixtures/files/empty.pdf", make_visible: true)
end
expect(page).to have_css ".file-name", text: "empty.pdf"
end
@@ -91,7 +86,7 @@ shared_examples "nested documentable" do |documentable_factory_name, path, docum
documentable_attach_new_file(documentable_factory_name, 0, "spec/fixtures/files/empty.pdf")
expect(find("##{documentable_factory_name}_documents_attributes_0_title").value).to eq('empty.pdf')
expect_document_has_title(0, "empty.pdf")
end
scenario "Should not update nested document file title with file name after choosing a file when title already defined", :js do
@@ -99,11 +94,13 @@ shared_examples "nested documentable" do |documentable_factory_name, path, docum
visit send(path, arguments)
click_link "Add new document"
fill_in "#{documentable_factory_name}[documents_attributes][0][title]", with: "Title"
within "#nested-documents .document" do
input = find("input[name$='[title]']")
fill_in input[:id], with: "My Title"
end
documentable_attach_new_file(documentable_factory_name, 0, "spec/fixtures/files/empty.pdf")
expect(find("##{documentable_factory_name}_documents_attributes_0_title").value).to eq "Title"
expect_document_has_title(0, "My Title")
end
scenario "Should update loading bar style after valid file upload", :js do
@@ -111,7 +108,6 @@ shared_examples "nested documentable" do |documentable_factory_name, path, docum
visit send(path, arguments)
documentable_attach_new_file(documentable_factory_name, 0, "spec/fixtures/files/empty.pdf")
fill_in "#{documentable_factory_name}[documents_attributes][0][title]", with: "Title"
expect(page).to have_css ".loading-bar.complete"
end
@@ -131,7 +127,7 @@ shared_examples "nested documentable" do |documentable_factory_name, path, docum
documentable_attach_new_file(documentable_factory_name, 0, "spec/fixtures/files/empty.pdf")
expect(page).to have_css("input[name='#{documentable_factory_name}[documents_attributes][0][cached_attachment]'][value$='.pdf']", visible: false)
expect_document_has_cached_attachment(0, ".pdf")
end
scenario "Should not update document cached_attachment field after unvalid file upload", :js do
@@ -140,7 +136,7 @@ shared_examples "nested documentable" do |documentable_factory_name, path, docum
documentable_attach_new_file(documentable_factory_name, 0, "spec/fixtures/files/logo_header.png", false)
expect(find("input[name='#{documentable_factory_name}[documents_attributes][0][cached_attachment]']", visible: false).value).to eq("")
expect_document_has_cached_attachment(0, "")
end
scenario "Should show document errors after documentable submit with empty document fields", :js do
@@ -148,10 +144,9 @@ shared_examples "nested documentable" do |documentable_factory_name, path, docum
visit send(path, arguments)
click_link "Add new document"
expect(page).to have_css("input[name='#{documentable_factory_name}[documents_attributes][0][title]']")
click_on submit_button
within "#document_0" do
within "#nested-documents .document" do
expect(page).to have_content("can't be blank", count: 2)
end
end
@@ -161,11 +156,9 @@ shared_examples "nested documentable" do |documentable_factory_name, path, docum
visit send(path, arguments)
documentable_attach_new_file(documentable_factory_name, 0, "spec/fixtures/files/empty.pdf")
within "#document_0" do
click_link "Remove document"
end
expect(page).not_to have_css("#document_0")
expect(page).not_to have_css("#nested-documents .document")
end
scenario "Should show successful notice when resource filled correctly without any nested documents", :js do
@@ -202,7 +195,7 @@ shared_examples "nested documentable" do |documentable_factory_name, path, docum
end
scenario "Should show resource with new document after successful creation with maximum allowed uploaded files", :js do
skip "due to unknown error"
skip "weird behavior"
# page.driver.resize_window 1200, 2500
login_as user
visit send(path, arguments)
@@ -216,9 +209,32 @@ shared_examples "nested documentable" do |documentable_factory_name, path, docum
click_on submit_button
documentable_redirected_to_resource_show_or_navigate_to
save_screenshot
expect(page).to have_content "Documents (#{documentable.class.max_documents_allowed})"
end
if path.include? "edit"
scenario "Should show persisted documents and remove nested_field" do
login_as user
create(:document, documentable: documentable)
visit send(path, arguments)
expect(page).to have_css ".document", count: 1
end
scenario "Should remove nested field after remove document", :js do
login_as user
create(:document, documentable: documentable)
visit send(path, arguments)
click_on "Remove document"
expect(page).not_to have_css ".document"
end
end
end
end
@@ -232,11 +248,10 @@ end
def documentable_attach_new_file(documentable_factory_name, index, path, success = true)
click_link "Add new document"
input_file_id = "#{documentable_factory_name}_documents_attributes_#{index}_attachment"
expect(page).to have_css("##{input_file_id}", visible: false)
attach_file(input_file_id, path, make_visible: true)
within "#document_#{index}" do
document = all(".document")[index]
document_input = document.find("input[type=file]", visible: false)
attach_file(document_input[:id], path, make_visible: true)
within document do
if success
expect(page).to have_css ".loading-bar.complete"
else
@@ -245,6 +260,22 @@ def documentable_attach_new_file(documentable_factory_name, index, path, success
end
end
def expect_document_has_title(index, title)
document = all(".document")[index]
within document do
expect(find("input[name$='[title]']").value).to eq title
end
end
def expect_document_has_cached_attachment(index, extension)
document = all(".document")[index]
within document do
expect(find("input[name$='[cached_attachment]']", visible: false).value).to end_with(extension)
end
end
def documentable_fill_new_valid_proposal
fill_in :proposal_title, with: "Proposal title #{rand(9999)}"
fill_in :proposal_summary, with: "Proposal summary"