diff --git a/app/views/fields/attached/many/_form.html.erb b/app/views/fields/attached/many/_form.html.erb new file mode 100644 index 0000000000..e69de29bb2 diff --git a/app/views/fields/attached/many/_show.html.erb b/app/views/fields/attached/many/_show.html.erb new file mode 100644 index 0000000000..e69de29bb2 diff --git a/app/views/fields/attached/one/_attachment.html.erb b/app/views/fields/attached/one/_attachment.html.erb new file mode 100644 index 0000000000..a85fce7ca4 --- /dev/null +++ b/app/views/fields/attached/one/_attachment.html.erb @@ -0,0 +1,3 @@ +
+ <%= image_tag field.data %> +
diff --git a/app/views/fields/attached/one/_form.html.erb b/app/views/fields/attached/one/_form.html.erb new file mode 100644 index 0000000000..de1940d693 --- /dev/null +++ b/app/views/fields/attached/one/_form.html.erb @@ -0,0 +1,24 @@ +<%# + # Attached One Form Partial + + This partial renders an input element for a single attached attribute. + By default, the input is a file field for the attached file. + + ## Local variables: + + - `f`: + A Rails form generator, used to help create the appropriate input fields. + - `field`: + An instance of [Administrate::Field::Attached::One][1]. + A wrapper around the attachment pulled from the database. + + [1]: http://www.rubydoc.info/gems/administrate/Administrate/Field/Attached/One +%> + + +
+ <%= f.label field.attribute %> +
+
+ <%= f.file_field(field.attribute) %> +
diff --git a/app/views/fields/attached/one/_show.html.erb b/app/views/fields/attached/one/_show.html.erb new file mode 100644 index 0000000000..2bfc2119ce --- /dev/null +++ b/app/views/fields/attached/one/_show.html.erb @@ -0,0 +1,22 @@ +<%# + # Attached One Show Partial + + This partial renders an attachment attribute, + to be displayed on a resource's show page. + + By default, the attribute is rendered as an attachment tag. + + ## Local variables: + + - `field`: + An instance of [Administrate::Field::Attached::One][1]. + A wrapper around the attachment pulled from the database. + + [1]: http://www.rubydoc.info/gems/administrate/Administrate/Field/Attached::One +%> + +<% if field.attached? %> + <%= render partial: "fields/attached/one/attachment", locals: { field: field } %> +<% else %> + No attachment +<% end %> diff --git a/lib/administrate/base_dashboard.rb b/lib/administrate/base_dashboard.rb index 61ef6abed8..22db1acac0 100644 --- a/lib/administrate/base_dashboard.rb +++ b/lib/administrate/base_dashboard.rb @@ -1,3 +1,5 @@ +require "administrate/field/attached/one" +require "administrate/field/attached/many" require "administrate/field/belongs_to" require "administrate/field/boolean" require "administrate/field/date_time" diff --git a/lib/administrate/field/attached/many.rb b/lib/administrate/field/attached/many.rb new file mode 100644 index 0000000000..1b3e86519c --- /dev/null +++ b/lib/administrate/field/attached/many.rb @@ -0,0 +1,15 @@ +require "administrate/field/base" + +module Administrate + module Field + module Attached + class Many < Administrate::Field::Base + delegate :attached?, to: :data, allow_nil: true + + def to_partial_path + "fields/attached/many/#{page}" + end + end + end + end +end diff --git a/lib/administrate/field/attached/one.rb b/lib/administrate/field/attached/one.rb new file mode 100644 index 0000000000..212d9f7b55 --- /dev/null +++ b/lib/administrate/field/attached/one.rb @@ -0,0 +1,15 @@ +require "administrate/field/base" + +module Administrate + module Field + module Attached + class One < Administrate::Field::Base + delegate :attached?, to: :data, allow_nil: true + + def to_partial_path + "fields/attached/one/#{page}" + end + end + end + end +end diff --git a/spec/administrate/views/fields/attached/one/_form_spec.rb b/spec/administrate/views/fields/attached/one/_form_spec.rb new file mode 100644 index 0000000000..e812193192 --- /dev/null +++ b/spec/administrate/views/fields/attached/one/_form_spec.rb @@ -0,0 +1,24 @@ +require "rails_helper" +require "administrate/field/attached/one" + +describe "field/attached/one/_form", type: :view do + it "renders a singular file field" do + model = build(:product, hero_image: nil) + field = instance_double( + "Administrate::Field::Attached::One", + attribute: :hero_image, + data: nil + ) + + fields model: model do |f| + concat render( + partial: "fields/attached/one/form", + locals: {field: field, f: f} + ) + end + + expect(rendered).to have_field( + "product[hero_image]", type: "file" + ) + end +end diff --git a/spec/administrate/views/fields/attached/one/_show_spec.rb b/spec/administrate/views/fields/attached/one/_show_spec.rb new file mode 100644 index 0000000000..cdf374de0f --- /dev/null +++ b/spec/administrate/views/fields/attached/one/_show_spec.rb @@ -0,0 +1,29 @@ +require "rails_helper" +require "administrate/field/attached/one" + +describe "fields/attached/one/_show", type: :view do + it "renders a 'No attachment' when missing a value" do + field = instance_double( + "Administrate::Field::Attached::One", + attached?: false + ) + + render(partial: "fields/attached/one/show", locals: {field: field}) + + expect(rendered).to have_text("No attachment") + end + + it "renders a preview when attached" do + model = build(:product, hero_image: file_fixture("image.png")) + field = instance_double( + "Administrate::Field::Attached::One", + data: model.hero_image, + attached?: true + ) + + render(partial: "fields/attached/one/show", locals: {field: field}) + + expect(rendered).to have_element("img", src: rails_blob_url(model.hero_image)) + .and(have_no_text("No attachment")) + end +end diff --git a/spec/example_app/app/dashboards/product_dashboard.rb b/spec/example_app/app/dashboards/product_dashboard.rb index c6e0d76814..039ec2a82e 100644 --- a/spec/example_app/app/dashboards/product_dashboard.rb +++ b/spec/example_app/app/dashboards/product_dashboard.rb @@ -9,7 +9,9 @@ class ProductDashboard < Administrate::BaseDashboard :image_url, :product_meta_tag, :release_year, - :banner + :banner, + :hero_image, + :thumbnails ] ATTRIBUTE_TYPES = { @@ -24,7 +26,9 @@ class ProductDashboard < Administrate::BaseDashboard product_meta_tag: Field::HasOne.with_options(order: "meta_title"), release_year: Field::Select.with_options( collection: -> { (Time.current.year - 10)..Time.current.year } - ) + ), + hero_image: Field::Attached::One, + thumbnails: Field::Attached::Many, } COLLECTION_ATTRIBUTES = ATTRIBUTES diff --git a/spec/example_app/app/models/product.rb b/spec/example_app/app/models/product.rb index fbbc10cc10..0f015d9f75 100644 --- a/spec/example_app/app/models/product.rb +++ b/spec/example_app/app/models/product.rb @@ -1,5 +1,7 @@ class Product < ApplicationRecord has_rich_text :banner + has_one_attached :hero_image + has_many_attached :thumbnails def self.policy_class=(policy) @policy_class = policy diff --git a/spec/factories.rb b/spec/factories.rb index 981f3daf01..aaf1b4c270 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -45,6 +45,16 @@ end product_meta_tag release_year { [2018, 2019, 2020].sample } + + transient do + hero_image { nil } + end + + after :build do |record, factory| + if (pathname = factory.hero_image) + record.hero_image.attach io: pathname.open, filename: pathname.to_s + end + end end factory :product_meta_tag do diff --git a/spec/features/products_form_spec.rb b/spec/features/products_form_spec.rb index 4eddddfb48..2b8a99d467 100644 --- a/spec/features/products_form_spec.rb +++ b/spec/features/products_form_spec.rb @@ -3,21 +3,23 @@ describe "product form has_one relationship" do include ActiveSupport::Testing::TimeHelpers - it "saves product and meta tag data correctly" do + it "saves product and meta tag data correctly", :js do visit new_admin_product_path fill_in "Name", with: "Example" fill_in "Price", with: "0" fill_in "Description", with: "Example" fill_in "Image url", with: "http://imageurlthatdoesnotexist" - fill_in "Meta title", with: "Example meta title" - fill_in "Meta description", with: "Example meta description" - - expect(page).to have_css("legend", text: "Product Meta Tag") + within_fieldset "Product Meta Tag" do + fill_in "Meta title", with: "Example meta title" + fill_in "Meta description", with: "Example meta description" + end + attach_file "Hero image", file_fixture("image.png") click_on "Create Product" expect(page).to have_link("Example meta title") + .and(have_element("img", src: /image.png/)) expect(page).to have_flash( t("administrate.controller.create.success", resource: "Product") ) @@ -53,17 +55,21 @@ ProductDashboard::ATTRIBUTE_TYPES[:release_year] = old_release_year end - it "edits product and meta tag data correctly" do - product = create(:product) + it "edits product and meta tag data correctly", :js do + product = create(:product, hero_image: file_fixture("image.svg")) + hero_image = product.hero_image visit edit_admin_product_path(product) - + attach_file "Hero image", file_fixture("image.png") click_on "Update Product" expect(page).to have_link(product.product_meta_tag.meta_title.to_s) + .and(have_element("img", src: /image.png/)) + .and(have_no_element("img", src: /image.svg/)) expect(page).to have_flash( t("administrate.controller.update.success", resource: "Product") ) + expect { hero_image.reload }.to raise_error(ActiveRecord::RecordNotFound) end describe "has_one relationships" do diff --git a/spec/fixtures/files/image.png b/spec/fixtures/files/image.png new file mode 100644 index 0000000000..96d1aa048b Binary files /dev/null and b/spec/fixtures/files/image.png differ diff --git a/spec/fixtures/files/image.svg b/spec/fixtures/files/image.svg new file mode 100644 index 0000000000..2ecf7b5071 --- /dev/null +++ b/spec/fixtures/files/image.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/spec/support/factory_girl.rb b/spec/support/factory_bot.rb similarity index 100% rename from spec/support/factory_girl.rb rename to spec/support/factory_bot.rb