diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 894d977..236ae6f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,8 +2,6 @@ name: "[CI] Test" on: push: - branches: - - main pull_request: env: diff --git a/README.md b/README.md index 1125354..46c1abc 100644 --- a/README.md +++ b/README.md @@ -31,12 +31,14 @@ And then execute: ```bash bundle +bundle exec rails decidim_alternative_landing:webpacker:install ``` Depending on your Decidim version, choose the corresponding version to ensure compatibility: | Alternative Landing version | Compatible Decidim versions | |-----------------------------|-----------------------------| +| 0.4.x | 0.27.x | | 0.3.x | 0.25.x, 0.26.x | | 0.2.x | 0.24.x | diff --git a/Rakefile b/Rakefile index 6414c20..61ba449 100644 --- a/Rakefile +++ b/Rakefile @@ -6,6 +6,7 @@ require "fileutils" desc "Generates a dummy app for testing" task test_app: "decidim:generate_external_test_app" do ENV["RAILS_ENV"] = "test" + fix_babel_config("spec/decidim_dummy_app") override_webpacker_config_files("spec/decidim_dummy_app") end @@ -21,6 +22,20 @@ def seed_db(path) end end +# Temporary fix to overcome the issue with babel plugin updates, see: +# https://github.com/decidim/decidim/pull/10916 +def fix_babel_config(path) + Dir.chdir(path) do + babel_config = "#{Dir.pwd}/babel.config.json" + File.delete(babel_config) if File.exist?(babel_config) + FileUtils.cp("#{__dir__}/babel.config.json", Dir.pwd) + + # Temporary fix to overcome the issue with sass-embedded, see: + # https://github.com/decidim/decidim/pull/11074 + system("npm i sass-embedded@~1.62.0") + end +end + desc "Generates a development app." task :development_app do Bundler.with_original_env do @@ -35,6 +50,7 @@ task :development_app do ) end + fix_babel_config("development_app") seed_db("development_app") override_webpacker_config_files("development_app") end diff --git a/app/cells/decidim/alternative_landing/content_blocks/alternative_upcoming_meetings_cell.rb b/app/cells/decidim/alternative_landing/content_blocks/alternative_upcoming_meetings_cell.rb index 45366fc..a2442cf 100644 --- a/app/cells/decidim/alternative_landing/content_blocks/alternative_upcoming_meetings_cell.rb +++ b/app/cells/decidim/alternative_landing/content_blocks/alternative_upcoming_meetings_cell.rb @@ -23,12 +23,16 @@ def meeting_path(meeting) def meetings @meetings ||= Meetings::Meeting.upcoming.where( - component: meeting_component + component: component || components ).limit(meetings_to_show).order(start_time: :asc) end private + def manifest_name + "meetings" + end + # A MD5 hash of model attributes because is needed because # it ensures the cache version value will always be the same size def cache_hash @@ -40,10 +44,6 @@ def cache_hash hash.join("/") end - def meeting_component - @meeting_component ||= (Component.find_by(id: model.settings.component_id) || Component.where(manifest_name: "meetings")) - end - def meetings_to_show model.settings.count || 3 end diff --git a/app/cells/decidim/alternative_landing/content_blocks/alternative_upcoming_meetings_settings_form/show.erb b/app/cells/decidim/alternative_landing/content_blocks/alternative_upcoming_meetings_settings_form/show.erb index 4b04869..dccf4a2 100644 --- a/app/cells/decidim/alternative_landing/content_blocks/alternative_upcoming_meetings_settings_form/show.erb +++ b/app/cells/decidim/alternative_landing/content_blocks/alternative_upcoming_meetings_settings_form/show.erb @@ -3,12 +3,10 @@ <%= settings_fields.translated :text_field, :link_text, label: t(".link_text") %> <%= settings_fields.translated :text_field, :link_url, label: t(".link_url") %> <%= settings_fields.number_field :count, label: t(".count") %> - <% if component %>

<%= t(".info", component: translated_attribute(component.name), space: translated_attribute(component.participatory_space.title)) %>

<% end %> - - <%= settings_fields.select :component_id, available_components("meetings") %> + <%= settings_fields.select :component_id, available_components %> <% end %> diff --git a/app/cells/decidim/alternative_landing/content_blocks/alternative_upcoming_meetings_settings_form_cell.rb b/app/cells/decidim/alternative_landing/content_blocks/alternative_upcoming_meetings_settings_form_cell.rb index 4a35ad5..3fcf49c 100644 --- a/app/cells/decidim/alternative_landing/content_blocks/alternative_upcoming_meetings_settings_form_cell.rb +++ b/app/cells/decidim/alternative_landing/content_blocks/alternative_upcoming_meetings_settings_form_cell.rb @@ -6,8 +6,8 @@ module ContentBlocks class AlternativeUpcomingMeetingsSettingsFormCell < BaseCell alias form model - def component - @component ||= Decidim::Component.find_by(id: form.object.settings.try(:component_id)) + def manifest_name + "meetings" end end end diff --git a/app/cells/decidim/alternative_landing/content_blocks/base_cell.rb b/app/cells/decidim/alternative_landing/content_blocks/base_cell.rb index 791a9bb..fce2123 100644 --- a/app/cells/decidim/alternative_landing/content_blocks/base_cell.rb +++ b/app/cells/decidim/alternative_landing/content_blocks/base_cell.rb @@ -18,24 +18,40 @@ def participatory_spaces ].flatten.compact end - def available_components(manifest_name) - Decidim::Component.published.where(participatory_space: participatory_spaces, manifest_name: manifest_name).map do |component| + def available_components + @available_components ||= components.where(manifest_name: manifest_name).map do |component| ["#{translated_attribute(component.name)} (#{translated_attribute(component.participatory_space.title)})", component.id] end.unshift [t(".all"), nil] end def component - @component ||= Decidim::Component.find_by(id: form.object.settings.try(:component_id)) + @component ||= components.find_by(id: (defined?(form) ? form.object : model).settings.try(:component_id)) + end + + def components + @components ||= Decidim::Component.where(participatory_space: participatory_spaces) + end + + def manifest_name + raise NotImplementedError end def colors model.settings.to_h.select { |k, _v| k.match?(/color_/) } end + def opacities + model.settings.to_h.select { |k, _v| k.match?(/opacity_/) } + end + def color_keys form.object.settings.to_h.keys.grep(/color_/) end + def opacity_keys + form.object.settings.to_h.keys.grep(/opacity_/) + end + # Renders a view with the customizable CSS variables in two flavours: # 1. as a hexadecimal valid CSS color (ie: #ff0000) # 2. as a disassembled RGB components (ie: 255,0,0) @@ -54,6 +70,12 @@ def color_keys # # background-color: rgba(var(--primary-rgb), 0.5) def css + colors_css + opacities_css + end + + private + + def colors_css colors.each.map do |k, v| if v.match?(/^#[0-9a-fA-F]{6}$/) "--#{k}: #{v};--#{k}-rgb: #{v[1..2].hex},#{v[3..4].hex},#{v[5..6].hex};" @@ -62,6 +84,12 @@ def css end end.join end + + def opacities_css + opacities.each.map do |k, v| + "--#{k}: #{v};" + end.join + end end end end diff --git a/app/cells/decidim/alternative_landing/content_blocks/cover_full_settings_form/show.erb b/app/cells/decidim/alternative_landing/content_blocks/cover_full_settings_form/show.erb index b1ef7b0..2fe101e 100644 --- a/app/cells/decidim/alternative_landing/content_blocks/cover_full_settings_form/show.erb +++ b/app/cells/decidim/alternative_landing/content_blocks/cover_full_settings_form/show.erb @@ -8,6 +8,13 @@ <% end %> +
+ <% opacity_keys.each do |opacity_key| %> +
+ <%= settings_fields.number_field opacity_key, label: t(".#{opacity_key}"), step: 0.1, min: 0, max: 1 %> +
+ <% end %> +
<% end %> <% form.fields_for :images, form.object.images do |images_fields| %> <%= images_fields.upload :background_image, label: t(".background_image") %> diff --git a/app/cells/decidim/alternative_landing/content_blocks/cover_half_settings_form/show.erb b/app/cells/decidim/alternative_landing/content_blocks/cover_half_settings_form/show.erb index 3dcca1d..3d6160d 100644 --- a/app/cells/decidim/alternative_landing/content_blocks/cover_half_settings_form/show.erb +++ b/app/cells/decidim/alternative_landing/content_blocks/cover_half_settings_form/show.erb @@ -10,6 +10,13 @@ <% end %> +
+ <% opacity_keys.each do |opacity_key| %> +
+ <%= settings_fields.number_field opacity_key, label: t(".#{opacity_key}"), step: 0.1, min: 0, max: 1 %> +
+ <% end %> +
<% end %> <% form.fields_for :images, form.object.images do |images_fields| %> <%= images_fields.upload :background_image, label: t(".background_image") %> diff --git a/app/cells/decidim/alternative_landing/content_blocks/latest_blog_posts_cell.rb b/app/cells/decidim/alternative_landing/content_blocks/latest_blog_posts_cell.rb index 17cbafb..9d3e378 100644 --- a/app/cells/decidim/alternative_landing/content_blocks/latest_blog_posts_cell.rb +++ b/app/cells/decidim/alternative_landing/content_blocks/latest_blog_posts_cell.rb @@ -21,12 +21,16 @@ def post_path(post) def posts @posts ||= Blogs::Post.where( - component: blog_components.find_by(id: model.settings.component_id) || blog_components + component: component || components ).limit(posts_to_show).order(created_at: :desc) end private + def manifest_name + "blogs" + end + # A MD5 hash of model attributes because is needed because # it ensures the cache version value will always be the same size def cache_hash @@ -39,10 +43,6 @@ def cache_hash hash.join("/") end - def blog_components - @blog_components ||= Component.published.where(manifest_name: "blogs") - end - def posts_to_show model.settings.count || 3 end diff --git a/app/cells/decidim/alternative_landing/content_blocks/latest_blog_posts_settings_form/show.erb b/app/cells/decidim/alternative_landing/content_blocks/latest_blog_posts_settings_form/show.erb index 9931fdb..dccf4a2 100644 --- a/app/cells/decidim/alternative_landing/content_blocks/latest_blog_posts_settings_form/show.erb +++ b/app/cells/decidim/alternative_landing/content_blocks/latest_blog_posts_settings_form/show.erb @@ -3,12 +3,10 @@ <%= settings_fields.translated :text_field, :link_text, label: t(".link_text") %> <%= settings_fields.translated :text_field, :link_url, label: t(".link_url") %> <%= settings_fields.number_field :count, label: t(".count") %> - <% if component %>

<%= t(".info", component: translated_attribute(component.name), space: translated_attribute(component.participatory_space.title)) %>

<% end %> - - <%= settings_fields.select :component_id, available_components("blogs") %> + <%= settings_fields.select :component_id, available_components %> <% end %> diff --git a/app/cells/decidim/alternative_landing/content_blocks/latest_blog_posts_settings_form_cell.rb b/app/cells/decidim/alternative_landing/content_blocks/latest_blog_posts_settings_form_cell.rb index 3487b59..d929daf 100644 --- a/app/cells/decidim/alternative_landing/content_blocks/latest_blog_posts_settings_form_cell.rb +++ b/app/cells/decidim/alternative_landing/content_blocks/latest_blog_posts_settings_form_cell.rb @@ -5,6 +5,10 @@ module AlternativeLanding module ContentBlocks class LatestBlogPostsSettingsFormCell < BaseCell alias form model + + def manifest_name + "blogs" + end end end end diff --git a/app/cells/decidim/alternative_landing/content_blocks/stack_horizontal/show.erb b/app/cells/decidim/alternative_landing/content_blocks/stack_horizontal/show.erb index b14a7b6..fcb459c 100644 --- a/app/cells/decidim/alternative_landing/content_blocks/stack_horizontal/show.erb +++ b/app/cells/decidim/alternative_landing/content_blocks/stack_horizontal/show.erb @@ -8,7 +8,7 @@ <% 1.upto(3) do |item_number| %>
- <%= image_tag image(item_number) %> + <%= image_tag image(item_number) if image(item_number).present? %>
diff --git a/app/cells/decidim/alternative_landing/content_blocks/tiles_cell.rb b/app/cells/decidim/alternative_landing/content_blocks/tiles_cell.rb index c9db3ac..188f426 100644 --- a/app/cells/decidim/alternative_landing/content_blocks/tiles_cell.rb +++ b/app/cells/decidim/alternative_landing/content_blocks/tiles_cell.rb @@ -7,13 +7,21 @@ class TilesCell < BaseCell def translated_title(item_number = nil) return translated_attribute(model.settings.title) if item_number.blank? - translated_attribute(model.settings.send("title_#{item_number}")) + if translated_url(item_number).blank? + translated_attribute(model.settings.send("title_#{item_number}")) + else + link_to translated_attribute(model.settings.send("title_#{item_number}")), translated_url(item_number) + end end def translated_body(item_number) translated_attribute(model.settings.send("body_#{item_number}")) end + def translated_url(item_number) + translated_attribute(model.settings.send("link_url_#{item_number}")) + end + def background_image(item_number) model.images_container.attached_uploader("background_image_#{item_number}".to_sym).path(variant: :landscape) end diff --git a/app/cells/decidim/alternative_landing/content_blocks/tiles_settings_form/show.erb b/app/cells/decidim/alternative_landing/content_blocks/tiles_settings_form/show.erb index 6c8e60e..e56ae2e 100644 --- a/app/cells/decidim/alternative_landing/content_blocks/tiles_settings_form/show.erb +++ b/app/cells/decidim/alternative_landing/content_blocks/tiles_settings_form/show.erb @@ -6,6 +6,7 @@
<%= settings_fields.translated :text_field, :"title_#{item_number}", label: t(".title_n", item_number: item_number) %> <%= settings_fields.translated :text_area, :"body_#{item_number}", rows: 3, label: t(".body_n", item_number: item_number) %> + <%= settings_fields.translated :text_field, :"link_url_#{item_number}", label: t(".link_url_n", item_number: item_number) %>
<% end %>
diff --git a/app/packs/stylesheets/decidim/alternative_landing/content_blocks/cover.scss b/app/packs/stylesheets/decidim/alternative_landing/content_blocks/cover.scss index 684a689..6ca9f84 100644 --- a/app/packs/stylesheets/decidim/alternative_landing/content_blocks/cover.scss +++ b/app/packs/stylesheets/decidim/alternative_landing/content_blocks/cover.scss @@ -1,10 +1,10 @@ @import "stylesheets/decidim/alternative_landing/content_blocks/variables"; -@mixin cover-background-image($color) { +@mixin cover-background-image($color, $opacity) { background-size: cover; background-position: center; background-blend-mode: luminosity; - background-color: $color; + background-color: rgba($color, $opacity); min-height: 100vh; } @@ -16,12 +16,12 @@ } .cover-full { - color: var(--color_text); + color: rgba(var(--color_text-rgb), var(--opacity_text)); - @include cover-background-image(var(--color_background_image)); + @include cover-background-image(var(--color_background_image-rgb), var(--opacity_background_image)); .cover-text { - background-color: rgba(var(--color_background_text-rgb), 0.7); + background-color: rgba(var(--color_background_text-rgb), var(--opacity_background_text)); padding: $gap * 2; } } @@ -33,7 +33,7 @@ .cover-image { grid-column: 2; - @include cover-background-image(var(--color_background_image)); + @include cover-background-image(var(--color_background_image-rgb), var(--opacity_background_image)); } .cover-text { @@ -54,7 +54,7 @@ } .navbar.transparent { - background-color: rgba(var(--color_navbar-rgb), 0.3); + background-color: rgba(var(--color_navbar-rgb), var(--opacity_navbar)); position: relative; margin-bottom: -50px; } diff --git a/app/packs/stylesheets/decidim/alternative_landing/content_blocks/tiles.scss b/app/packs/stylesheets/decidim/alternative_landing/content_blocks/tiles.scss index 637d631..ed5dcba 100644 --- a/app/packs/stylesheets/decidim/alternative_landing/content_blocks/tiles.scss +++ b/app/packs/stylesheets/decidim/alternative_landing/content_blocks/tiles.scss @@ -23,6 +23,12 @@ max-width: 20em; background: $alternative-color-1; padding: $gap; + + h2 { + a { + color: $header-color; + } + } } &-heading { diff --git a/babel.config.json b/babel.config.json new file mode 100644 index 0000000..3314d8d --- /dev/null +++ b/babel.config.json @@ -0,0 +1,25 @@ +{ + "presets": [ + [ + "@babel/preset-env", { + "forceAllTransforms": true, + "useBuiltIns": "entry", + "corejs": 3, + "modules": false + } + ], + ["@babel/preset-react"] + ], + "plugins": [ + "@babel/plugin-transform-classes", + [ + "@babel/plugin-transform-runtime", + { + "helpers": false, + "regenerator": true, + "corejs": false + } + ], + ["@babel/plugin-transform-regenerator", { "async": false }] + ] +} diff --git a/config/locales/en.yml b/config/locales/en.yml index 090ebd7..8c239de 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -31,6 +31,10 @@ en: color_background_text: Text background color color_navbar: Navbar color color_text: Text color + opacity_background_image: Image tint opacity + opacity_background_text: Text background opacity + opacity_navbar: Navbar opacity + opacity_text: Text opacity title: Title cover_half: name: Cover (Half screen) @@ -43,6 +47,10 @@ en: color_text: Text color link_text: Link text link_url: Link url + opacity_background_image: Image tint opacity + opacity_background_text: Text background opacity + opacity_navbar: Navbar opacity + opacity_text: Text opacity title: Title extra_information: name: Process Group Information @@ -99,6 +107,7 @@ en: tiles_settings_form: background_image_n: 'Background image for item #%{item_number}' body_n: 'Body #%{item_number}' + link_url_n: 'Link URL for item #%{item_number}' title: Title title_n: 'Title #%{item_number}' upcoming_meetings: diff --git a/lib/decidim/alternative_landing.rb b/lib/decidim/alternative_landing.rb index 8a3dba4..def90a0 100644 --- a/lib/decidim/alternative_landing.rb +++ b/lib/decidim/alternative_landing.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require "decidim/alternative_landing/default_colors" +require "decidim/alternative_landing/default_opacities" require "decidim/alternative_landing/engine" module Decidim diff --git a/lib/decidim/alternative_landing/content_blocks/content_blocks_shared.rb b/lib/decidim/alternative_landing/content_blocks/content_blocks_shared.rb index 438aa77..ed23d73 100644 --- a/lib/decidim/alternative_landing/content_blocks/content_blocks_shared.rb +++ b/lib/decidim/alternative_landing/content_blocks/content_blocks_shared.rb @@ -15,6 +15,10 @@ Decidim::AlternativeLanding::DefaultColors.cover_full.each do |k, v| settings.attribute :"color_#{k}", type: :text, default: v end + + Decidim::AlternativeLanding::DefaultOpacities.cover_full.each do |k, v| + settings.attribute :"opacity_#{k}", type: :text, default: v + end end content_block.images = [{ name: :background_image, uploader: "Decidim::AlternativeLanding::CoverImageUploader" }] @@ -34,6 +38,10 @@ Decidim::AlternativeLanding::DefaultColors.cover_half.each do |k, v| settings.attribute :"color_#{k}", type: :text, default: v end + + Decidim::AlternativeLanding::DefaultOpacities.cover_half.each do |k, v| + settings.attribute :"opacity_#{k}", type: :text, default: v + end end content_block.images = [{ name: :background_image, uploader: "Decidim::AlternativeLanding::CoverImageUploader" }] @@ -88,6 +96,7 @@ 1.upto(4).map do |item_number| settings.attribute :"title_#{item_number}", type: :text, translated: true settings.attribute :"body_#{item_number}", type: :text, translated: true + settings.attribute :"link_url_#{item_number}", type: :text, translated: true end end diff --git a/lib/decidim/alternative_landing/default_opacities.rb b/lib/decidim/alternative_landing/default_opacities.rb new file mode 100644 index 0000000..3718a5e --- /dev/null +++ b/lib/decidim/alternative_landing/default_opacities.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module Decidim + module AlternativeLanding + module DefaultOpacities + class << self + def cover_full + { + background_text: 0.7, + background_image: 1, + text: 1, + navbar: 0.3 + } + end + + def cover_half + { + background_text: 0.7, + background_image: 1, + text: 1, + navbar: 0.3 + } + end + end + end + end +end diff --git a/lib/decidim/alternative_landing/test/factories.rb b/lib/decidim/alternative_landing/test/factories.rb index 9e76848..2858677 100644 --- a/lib/decidim/alternative_landing/test/factories.rb +++ b/lib/decidim/alternative_landing/test/factories.rb @@ -134,11 +134,18 @@ factory :latest_blog_posts_block, parent: :alternative_landing_content_block do manifest_name { :latest_blog_posts } + transient do + component_id { nil } + count { 3 } + end + settings do { title: generate_localized_title, link_text: Decidim::Faker::Localized.word, - link_url: Decidim::Faker::Localized.literal("https://decidim.org") + link_url: Decidim::Faker::Localized.literal("https://decidim.org"), + component_id: component_id, + count: count } end end @@ -146,11 +153,18 @@ factory :alternative_upcoming_meetings_block, parent: :alternative_landing_content_block do manifest_name { :alternative_upcoming_meetings } + transient do + component_id { nil } + count { 3 } + end + settings do { title: generate_localized_title, link_text: Decidim::Faker::Localized.word, - link_url: Decidim::Faker::Localized.literal("https://decidim.org") + link_url: Decidim::Faker::Localized.literal("https://decidim.org"), + component_id: component_id, + count: count } end end diff --git a/spec/shared/system_admin_homepage_examples.rb b/spec/shared/system_admin_homepage_examples.rb new file mode 100644 index 0000000..824b159 --- /dev/null +++ b/spec/shared/system_admin_homepage_examples.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +shared_examples "increase number of content blocks" do |text| + it "increases the number of active content blocks" do + content_block = find("ul.js-list-availables li", text: text) + active_blocks_list = find("ul.js-list-actives") + content_block.drag_to(active_blocks_list) + sleep(2) + expect(Decidim::ContentBlock.count).to eq 1 + end +end + +shared_examples "updates the content block" do |manifest_name| + it "updates the settings of the content block" do + visit decidim_admin.edit_organization_homepage_content_block_path(manifest_name.to_sym) + + fill_in( + :content_block_settings_title_en, + with: "Custom #{manifest_name} title text!" + ) + + click_button "Update" + visit decidim.root_path + expect(page).to have_content(/Custom #{manifest_name} title text!/i) + end +end diff --git a/spec/shared/system_admin_process_group_landing_examples.rb b/spec/shared/system_admin_process_group_landing_examples.rb new file mode 100644 index 0000000..764937a --- /dev/null +++ b/spec/shared/system_admin_process_group_landing_examples.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +shared_examples "updates the content block extra title" do + it "updates the settings of the content block" do + visit "/admin/participatory_process_groups/#{participatory_process_group.id}/landing_page/content_blocks/extra_title/edit" + + fill_in( + :content_block_settings_link_text_1_en, + with: "Custom extra title link text!" + ) + # rubocop:disable Naming/VariableNumber + fill_in( + :content_block_settings_link_url_1, + with: "https://google.es" + ) + # rubocop:enable Naming/VariableNumber + click_button "Update" + visit decidim_participatory_processes.participatory_process_group_path(participatory_process_group) + expect(page).to have_content(/Custom extra title link text!/i) + end +end + +shared_examples "updates the content block extra information" do + it "updates the settings of the content block" do + visit "/admin/participatory_process_groups/#{participatory_process_group.id}/landing_page/content_blocks/extra_information/edit" + + editor = find(".ql-editor") + editor.set("Custom extra information body text!") + + fill_in( + :content_block_settings_columns, + with: 2 + ) + + click_button "Update" + visit decidim_participatory_processes.participatory_process_group_path(participatory_process_group) + expect(page).to have_content(/Custom extra information body text!/i) + expect(page).to have_css(".columns.large-2") + end +end diff --git a/spec/shared/system_homepage_examples.rb b/spec/shared/system_homepage_examples.rb index 33a8ebc..9fa3459 100644 --- a/spec/shared/system_homepage_examples.rb +++ b/spec/shared/system_homepage_examples.rb @@ -1,8 +1,9 @@ # frozen_string_literal: true shared_examples "render all stack block elements" do |type| - let!(:content_block) do - type == "stack-horizontal" ? create(:stack_horizontal_block, organization: organization) : create(:stack_vertical_block, organization: organization) + let(:manifest_name) { type.gsub("-", "_") } + let(:content_block) do + Decidim::ContentBlock.find_by(organization: organization, manifest_name: manifest_name) end it "renders all elements" do @@ -18,7 +19,7 @@ # /rails/active_storage/representation/redirect/same-hash--different-token/same-file-name # That's why we are splitting by "--" and comparing the first img_path = content_block.images_container.attached_uploader("image_#{item_number}".to_sym).path(variant: :landscape) - [img_path.split("--").first, img_path.split("/").last].each do |regex| + img_path && [img_path.split("--").first, img_path.split("/").last].each do |regex| expect(page.find("img")[:src]).to match(/#{regex}/) end end @@ -54,8 +55,9 @@ end shared_examples "render all cover block elements" do |type| - let!(:content_block) do - type == "cover-full" ? create(:cover_full_block, organization: organization) : create(:cover_half_block, organization: organization) + let(:manifest_name) { type.gsub("-", "_") } + let(:content_block) do + Decidim::ContentBlock.find_by(organization: organization, manifest_name: manifest_name) end it "renders all elements" do @@ -108,6 +110,7 @@ within ".tile-body" do expect(page).to have_i18n_content(tiles_block.settings.send(:"title_#{item_number}")) expect(page).to have_i18n_content(tiles_block.settings.send(:"body_#{item_number}")) + expect(page).to have_link(tiles_block.settings.send(:"body_#{item_number}")[I18n.locale]) if tiles_block.settings.send(:"link_url_#{item_number}").present? end end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index e74c150..2883ffc 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -3,7 +3,10 @@ require "decidim/dev" require "simplecov" -SimpleCov.start "rails" +SimpleCov.start "rails" do + add_filter "lib/decidim/alternative_landing/version.rb" + add_filter "lib/tasks" +end if ENV["CODECOV"] require "codecov" SimpleCov.formatter = SimpleCov::Formatter::Codecov diff --git a/spec/system/admin_homepage_spec.rb b/spec/system/admin_homepage_spec.rb new file mode 100644 index 0000000..4f2efa1 --- /dev/null +++ b/spec/system/admin_homepage_spec.rb @@ -0,0 +1,83 @@ +# frozen_string_literal: true + +require "spec_helper" +require "shared/system_admin_homepage_examples" + +describe "Admin visits homepage settings", type: :system do + include ActionView::Helpers::SanitizeHelper + + let(:organization) { create(:organization) } + let(:user) { create(:user, :admin, :confirmed, organization: organization) } + let(:blogs_component) { create(:component, manifest_name: "blogs", organization: organization) } + let(:meetings_component) { create(:component, manifest_name: "meetings", organization: organization) } + let!(:post) { create(:post, component: blogs_component) } + let!(:meeting) { create :meeting, component: meetings_component } + + before do + switch_to_host(organization.host) + login_as user, scope: :user + end + + context "when visiting homepage settings" do + before do + visit decidim_admin.edit_organization_homepage_path + end + + it "renders active and inactive content blocks headers" do + expect(page).to have_content("Active content blocks") + expect(page).to have_content("Inactive content blocks") + end + + it "renders all alternative landing content blocks" do + expect(page).to have_content("Upcoming meetings (Alternative)") + expect(page).to have_content("Stack of 3 custom items (Horizontal)") + expect(page).to have_content("Stack of 3 custom items (Vertical)") + expect(page).to have_content("Latest blog posts") + expect(page).to have_content("Cover (Full screen)") + expect(page).to have_content("Cover (Half screen)") + expect(page).to have_content("Tiles") + end + + it "has initial active content blocks equal to 0" do + expect(Decidim::ContentBlock.count).to eq 0 + end + + context "when dragging the content block from inactive to active panel" do + it_behaves_like "increase number of content blocks", "Upcoming meetings (Alternative)" + it_behaves_like "increase number of content blocks", "Stack of 3 custom items (Horizontal)" + it_behaves_like "increase number of content blocks", "Stack of 3 custom items (Vertical)" + it_behaves_like "increase number of content blocks", "Latest blog posts" + it_behaves_like "increase number of content blocks", "Cover (Full screen)" + it_behaves_like "increase number of content blocks", "Cover (Half screen)" + it_behaves_like "increase number of content blocks", "Tiles" + end + + context "when editing a persisted content block" do + let!(:alternative_upcoming_meetings_block) { create :alternative_upcoming_meetings_block, organization: organization, scope_name: :homepage, component_id: meetings_component.id } + let!(:cover_full_block) { create :content_block, organization: organization, manifest_name: "cover_full", scope_name: :homepage } + let!(:cover_half_block) { create :cover_half_block, organization: organization, scope_name: :homepage } + let!(:latest_blog_posts_block) { create :latest_blog_posts_block, organization: organization, scope_name: :homepage, component_id: blogs_component.id } + let!(:stack_horizontal_block) { create :stack_horizontal_block, organization: organization, scope_name: :homepage } + let!(:stack_vertical_block) { create :stack_vertical_block, organization: organization, scope_name: :homepage } + let!(:tiles_block) { create :tiles_block, organization: organization, scope_name: :homepage } + + it_behaves_like "updates the content block", "alternative_upcoming_meetings" + it_behaves_like "updates the content block", "cover_full" + it_behaves_like "updates the content block", "cover_half" + it_behaves_like "updates the content block", "latest_blog_posts" + it_behaves_like "updates the content block", "stack_horizontal" + it_behaves_like "updates the content block", "stack_vertical" + it_behaves_like "updates the content block", "tiles" + + it "updates the images of the content block" do + visit decidim_admin.edit_organization_homepage_content_block_path(:cover_full) + + dynamically_attach_file(:content_block_images_background_image, Decidim::Dev.asset("city2.jpeg")) + + click_button "Update" + visit decidim.root_path + expect(page.html).to include("city2.jpeg") + end + end + end +end diff --git a/spec/system/admin_spec.rb b/spec/system/admin_spec.rb new file mode 100644 index 0000000..82710e8 --- /dev/null +++ b/spec/system/admin_spec.rb @@ -0,0 +1,187 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe "Admin manages organization homepage", type: :system do + let(:organization) { create(:organization) } + let(:user) { create(:user, :admin, :confirmed, organization: organization) } + + before do + switch_to_host(organization.host) + login_as user, scope: :user + end + + context "when editing a cover_full content block" do + let!(:cover_full_block) { create :cover_full_block, organization: organization } + + before do + visit decidim_admin.edit_organization_homepage_content_block_path(:cover_full) + end + + it "updates the settings of the content block" do + fill_in( + :content_block_settings_title_en, + with: "Custom welcome text!" + ) + click_button "Update" + visit decidim.root_path + expect(page).to have_content("Custom welcome text!") + end + + it "updates the images of the content block" do + dynamically_attach_file(:content_block_images_background_image, Decidim::Dev.asset("city2.jpeg"), remove_before: true) + + click_button "Update" + visit decidim.root_path + expect(page.html).to include("city2.jpeg") + end + end + + context "when editing a cover_half content block" do + let!(:cover_half_block) { create :cover_half_block, organization: organization } + + before do + visit decidim_admin.edit_organization_homepage_content_block_path(:cover_half) + end + + it "updates the settings of the content block" do + fill_in( + :content_block_settings_title_en, + with: "Hello there people!" + ) + click_button "Update" + visit decidim.root_path + expect(page).to have_content("Hello there people!") + end + + it "updates the images of the content block" do + dynamically_attach_file(:content_block_images_background_image, Decidim::Dev.asset("city3.jpeg"), remove_before: true) + + click_button "Update" + visit decidim.root_path + expect(page.html).to include("city3.jpeg") + end + end + + context "when editing a latest_blog_posts content block" do + let!(:latest_blog_posts_block) { create(:latest_blog_posts_block, organization: organization) } + let!(:blogs_component) { create(:component, manifest_name: "blogs", organization: organization) } + let!(:other_blogs_component) { create(:component, manifest_name: "blogs", organization: organization) } + let!(:other_organization_blogs_component) { create(:component, manifest_name: "blogs") } + let!(:blog_posts) { create_list(:post, 2, component: blogs_component) } + let!(:other_blog_posts) { create_list(:post, 2, component: other_blogs_component) } + let!(:other_organization_blog_posts) { create_list(:post, 2, component: other_organization_blogs_component) } + + before do + visit decidim_admin.edit_organization_homepage_content_block_path(:latest_blog_posts) + end + + it "updates the settings of the content block" do + fill_in :content_block_settings_title_en, with: "Latest blog posts" + fill_in :content_block_settings_link_text_en, with: "See all" + fill_in :content_block_settings_link_url_en, with: "example.org/example-path" + fill_in :content_block_settings_count, with: 4 + + click_button "Update" + visit decidim.root_path + + within ".alternative-landing.latest-blog-posts" do + expect(page).to have_content "LATEST BLOG POSTS" + expect(page).to have_link "See all", href: "example.org/example-path" + + blog_posts.each do |blog_post| + expect(page).to have_i18n_content(blog_post.title) + end + + other_blog_posts.each do |blog_post| + expect(page).to have_i18n_content(blog_post.title) + end + + other_organization_blog_posts.each do |blog_post| + expect(page).not_to have_i18n_content(blog_post.title) + end + end + + visit decidim_admin.edit_organization_homepage_content_block_path(:latest_blog_posts) + select blogs_component.name["en"], from: "Component" + + click_button "Update" + visit decidim.root_path + + within ".alternative-landing.latest-blog-posts" do + blog_posts.each do |blog_post| + expect(page).to have_i18n_content(blog_post.title) + end + + other_blog_posts.each do |blog_post| + expect(page).not_to have_i18n_content(blog_post.title) + end + + other_organization_blog_posts.each do |blog_post| + expect(page).not_to have_i18n_content(blog_post.title) + end + end + end + end + + context "when editing a upcoming_meetings content block" do + let!(:alternative_upcoming_meetings_block) { create(:alternative_upcoming_meetings_block, organization: organization) } + let!(:meetings_component) { create(:component, manifest_name: "meetings", organization: organization) } + let!(:other_meetings_component) { create(:component, manifest_name: "meetings", organization: organization) } + let!(:other_organization_meetings_component) { create(:component, manifest_name: "meetings") } + let!(:meetings) { create_list(:meeting, 2, :upcoming, component: meetings_component) } + let!(:other_meetings) { create_list(:meeting, 2, :upcoming, component: other_meetings_component) } + let!(:other_organization_meetings) { create_list(:meeting, 2, :upcoming, component: other_organization_meetings_component) } + + before do + visit decidim_admin.edit_organization_homepage_content_block_path(:alternative_upcoming_meetings) + end + + it "updates the settings of the content block" do + fill_in :content_block_settings_title_en, with: "Upcoming meetings" + fill_in :content_block_settings_link_text_en, with: "See all" + fill_in :content_block_settings_link_url_en, with: "example.org/example-path" + fill_in :content_block_settings_count, with: 4 + + click_button "Update" + visit decidim.root_path + + within ".alternative-landing.upcoming-meetings" do + expect(page).to have_content "UPCOMING MEETINGS" + expect(page).to have_link "See all", href: "example.org/example-path" + + meetings.each do |meeting| + expect(page).to have_i18n_content(meeting.title) + end + + other_meetings.each do |meeting| + expect(page).to have_i18n_content(meeting.title) + end + + other_organization_meetings.each do |meeting| + expect(page).not_to have_i18n_content(meeting.title) + end + end + + visit decidim_admin.edit_organization_homepage_content_block_path(:alternative_upcoming_meetings) + select meetings_component.name["en"], from: "Component" + + click_button "Update" + visit decidim.root_path + + within ".alternative-landing.upcoming-meetings" do + meetings.each do |meeting| + expect(page).to have_i18n_content(meeting.title) + end + + other_meetings.each do |meeting| + expect(page).not_to have_i18n_content(meeting.title) + end + + other_organization_meetings.each do |meeting| + expect(page).not_to have_i18n_content(meeting.title) + end + end + end + end +end diff --git a/spec/system/custom_colors_spec.rb b/spec/system/custom_colors_spec.rb index a0a61bc..d8fdf1f 100644 --- a/spec/system/custom_colors_spec.rb +++ b/spec/system/custom_colors_spec.rb @@ -20,14 +20,16 @@ { color_background_image: "#ff0000", color_background_text: "#00ff00", - color_text: "#0000ff" + color_text: "#0000ff", + color_navbar: "#ffff00" } end - it "renders default colors" do + it "renders custom colors" do expect(get_property_value(selector, "--color_background_image")).to eq("#ff0000") expect(get_property_value(selector, "--color_background_text")).to eq("#00ff00") expect(get_property_value(selector, "--color_text")).to eq("#0000ff") + expect(get_property_value(selector, "--color_navbar")).to eq("#ffff00") end end end @@ -37,11 +39,13 @@ let(:default_image_background_color) { Decidim::AlternativeLanding::DefaultColors.cover_full[:background_image] } let(:default_text_background_color) { Decidim::AlternativeLanding::DefaultColors.cover_full[:background_text] } let(:default_text_color) { Decidim::AlternativeLanding::DefaultColors.cover_full[:text] } + let(:default_navbar_color) { Decidim::AlternativeLanding::DefaultColors.cover_full[:navbar] } it "renders default colors" do expect(get_property_value(selector, "--color_background_image")).to eq(get_property_value(selector, "--secondary")) expect(get_property_value(selector, "--color_background_text")).to eq(get_property_value(selector, "--secondary")) expect(get_property_value(selector, "--color_text")).to eq(default_text_color) + expect(get_property_value(selector, "--color_navbar")).to eq(default_navbar_color) end it_behaves_like "custom colors defined" @@ -52,11 +56,13 @@ let(:default_image_background_color) { Decidim::AlternativeLanding::DefaultColors.cover_half[:background_image] } let(:default_text_background_color) { Decidim::AlternativeLanding::DefaultColors.cover_half[:background_text] } let(:default_text_color) { Decidim::AlternativeLanding::DefaultColors.cover_half[:text] } + let(:default_navbar_color) { Decidim::AlternativeLanding::DefaultColors.cover_full[:navbar] } it "renders default colors" do expect(get_property_value(selector, "--color_background_image")).to eq(get_property_value(selector, "--primary")) expect(get_property_value(selector, "--color_background_text")).to eq(get_property_value(selector, "--primary")) expect(get_property_value(selector, "--color_text")).to eq(default_text_color) + expect(get_property_value(selector, "--color_navbar")).to eq(default_navbar_color) end it_behaves_like "custom colors defined" diff --git a/spec/system/custom_opacities_spec.rb b/spec/system/custom_opacities_spec.rb new file mode 100644 index 0000000..c172e5a --- /dev/null +++ b/spec/system/custom_opacities_spec.rb @@ -0,0 +1,77 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe "Custom opacities", type: :system, perform_enqueued: true do + let(:organization) { create :organization, available_locales: [:en] } + + let(:settings) { {} } + let!(:cover_full_block) { create(:cover_full_block, organization: organization, settings: settings) } + let!(:cover_half_block) { create(:cover_half_block, organization: organization, settings: settings) } + + before do + switch_to_host(organization.host) + visit decidim.root_path + end + + shared_examples_for "default opacities" do + context "when no custom opacities defined" do + it "renders default opacities" do + expect(get_property_value(selector, "--opacity_background_image")).to eq(default_image_background_opacity) + expect(get_property_value(selector, "--opacity_background_text")).to eq(default_text_background_opacity) + expect(get_property_value(selector, "--opacity_text")).to eq(default_text_opacity) + expect(get_property_value(selector, "--opacity_navbar")).to eq(default_navbar_opacity) + end + end + end + + shared_examples_for "custom opacities defined" do + context "when custom opacities defined" do + let(:settings) do + { + opacity_background_image: 0.33, + opacity_background_text: 0.44, + opacity_text: 0.55, + opacity_navbar: 0.66 + } + end + + it "renders custom opacities" do + expect(get_property_value(selector, "--opacity_background_image")).to eq("0.33") + expect(get_property_value(selector, "--opacity_background_text")).to eq("0.44") + expect(get_property_value(selector, "--opacity_text")).to eq("0.55") + expect(get_property_value(selector, "--opacity_navbar")).to eq("0.66") + end + end + end + + describe "cover_full block" do + let(:selector) { ".cover-full" } + let(:default_image_background_opacity) { Decidim::AlternativeLanding::DefaultOpacities.cover_full[:background_image].to_s } + let(:default_text_background_opacity) { Decidim::AlternativeLanding::DefaultOpacities.cover_full[:background_text].to_s } + let(:default_text_opacity) { Decidim::AlternativeLanding::DefaultOpacities.cover_full[:text].to_s } + let(:default_navbar_opacity) { Decidim::AlternativeLanding::DefaultOpacities.cover_full[:navbar].to_s } + + it_behaves_like "default opacities" + it_behaves_like "custom opacities defined" + end + + describe "cover_half block" do + let(:selector) { ".cover-half" } + let(:default_image_background_opacity) { Decidim::AlternativeLanding::DefaultOpacities.cover_half[:background_image].to_s } + let(:default_text_background_opacity) { Decidim::AlternativeLanding::DefaultOpacities.cover_half[:background_text].to_s } + let(:default_text_opacity) { Decidim::AlternativeLanding::DefaultOpacities.cover_half[:text].to_s } + let(:default_navbar_opacity) { Decidim::AlternativeLanding::DefaultOpacities.cover_full[:navbar].to_s } + + it_behaves_like "default opacities" + it_behaves_like "custom opacities defined" + end + + def get_computed_style(selector) + page.execute_script("return window.getComputedStyle($('#{selector}')[0])").strip + end + + def get_property_value(selector, variable_name) + page.execute_script("return window.getComputedStyle($('#{selector}')[0]).getPropertyValue('#{variable_name}')").strip + end +end diff --git a/spec/system/homepage_spec.rb b/spec/system/homepage_spec.rb index 64394ba..4985fd8 100644 --- a/spec/system/homepage_spec.rb +++ b/spec/system/homepage_spec.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true require "spec_helper" -require "shared/system_homepage_examples" describe "Visit the home page", type: :system, perform_enqueued: true do let(:organization) { create :organization, available_locales: [:en] } @@ -16,8 +15,13 @@ end context "when there are active alternative landing content blocks" do + let!(:cover_full_block) { create(:cover_full_block, organization: organization) } + let!(:cover_half_block) { create(:cover_half_block, organization: organization) } + let!(:stack_horizontal_block) { create(:stack_horizontal_block, organization: organization) } + let!(:stack_vertical_block) { create(:stack_vertical_block, organization: organization) } + let!(:tiles_block) { create(:tiles_block, organization: organization) } let!(:latest_blog_posts_block) { create(:latest_blog_posts_block, organization: organization) } - let!(:alternative_upcoming_meetings_block) { create(:alternative_upcoming_meetings_block, organization: organization) } + let!(:alternative_upcoming_meetings_block) { create(:alternative_upcoming_meetings_block, organization: organization, component_id: meetings_component.id) } let!(:blogs_component) { create(:component, manifest_name: "blogs", organization: organization) } let!(:meetings_component) { create(:component, manifest_name: "meetings", organization: organization) } let!(:blog_posts) { create_list(:post, 6, component: blogs_component) } @@ -30,32 +34,48 @@ it "renders them" do expect(page).to have_selector(".alternative-landing") + expect(page).to have_selector(".alternative-landing.cover-full") + expect(page).to have_selector(".alternative-landing.cover-half") + expect(page).to have_selector(".alternative-landing.stack-horizontal") + expect(page).to have_selector(".alternative-landing.stack-vertical") + expect(page).to have_selector(".alternative-landing.tiles-4") expect(page).to have_selector(".alternative-landing.latest-blog-posts") expect(page).to have_selector(".alternative-landing.upcoming-meetings") end describe "cover blocks" do - context "with cover half block" do - it_behaves_like "render all cover block elements", "cover-half" - end - - context "with cover full block" do - it_behaves_like "render all cover block elements", "cover-full" - end + it_behaves_like "render all cover block elements", "cover-half" + it_behaves_like "render all cover block elements", "cover-full" end describe "stack blocks" do - context "with stack horizontal block" do - it_behaves_like "render all stack block elements", "stack-horizontal" - end + it_behaves_like "render all stack block elements", "stack-horizontal" + it_behaves_like "render all stack block elements", "stack-vertical" + + context "without images" do + before do + stack_horizontal_block.attachments.destroy_all + stack_vertical_block.attachments.destroy_all + end - context "with stack vertical block" do + it_behaves_like "render all stack block elements", "stack-horizontal" it_behaves_like "render all stack block elements", "stack-vertical" end end describe "tiles block" do it_behaves_like "render tiles block elements" + + context "with link" do + before do + settings = tiles_block.settings + settings.link_url_1 = Decidim::Faker::Localized.literal(Faker::Internet.url) + tiles_block.settings = settings + tiles_block.save + end + + it_behaves_like "render tiles block elements" + end end describe "latest_blog_posts block" do diff --git a/spec/system/latest_blog_posts_spec.rb b/spec/system/latest_blog_posts_spec.rb new file mode 100644 index 0000000..219c8eb --- /dev/null +++ b/spec/system/latest_blog_posts_spec.rb @@ -0,0 +1,61 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe "Visit the home page", type: :system, perform_enqueued: true do + let(:organization) { create :organization, available_locales: [:en] } + + before do + switch_to_host(organization.host) + end + + context "when there is an active 'latest_blog_posts' content block" do + let(:settings) do + { + title: Decidim::Faker::Localized.sentence, + link_text: Decidim::Faker::Localized.sentence, + link_url: { en: "https://url-en.org" } + } + end + let!(:latest_blog_posts_block) { create(:latest_blog_posts_block, organization: organization, settings: settings) } + let!(:blogs_component) { create(:component, manifest_name: "blogs", organization: organization) } + let!(:blog_posts) { create_list(:post, 6, component: blogs_component) } + + describe "latest_blog_posts block" do + before do + visit decidim.root_path + end + + it "renders it" do + expect(page).to have_selector(".alternative-landing.latest-blog-posts") + end + + it "renders all elements" do + within ".alternative-landing.latest-blog-posts" do + expect(page).to have_i18n_content(latest_blog_posts_block.settings.title, upcase: true) + expect(page).to have_i18n_content(latest_blog_posts_block.settings.link_text, upcase: true) + blog_posts.last(3).each do |blog_post| + expect(page).to have_i18n_content(blog_post.title) + end + blog_posts.first(3).each do |blog_post| + expect(page).not_to have_i18n_content(blog_post.title) + end + end + end + + context "when 'component_id' option is set" do + let(:settings) { { component_id: blogs_component.id } } + let!(:other_blogs_component) { create(:component, manifest_name: "blogs", organization: organization) } + let!(:other_blog_posts) { create_list(:post, 6, component: other_blogs_component) } + + it "renders only posts from that component" do + within ".alternative-landing.latest-blog-posts" do + other_blog_posts.each do |blog_post| + expect(page).not_to have_i18n_content(blog_post.title) + end + end + end + end + end + end +end diff --git a/spec/system/process_group_landing_spec.rb b/spec/system/process_group_landing_spec.rb index 833e576..ac01257 100644 --- a/spec/system/process_group_landing_spec.rb +++ b/spec/system/process_group_landing_spec.rb @@ -1,9 +1,11 @@ # frozen_string_literal: true require "spec_helper" +require "shared/system_admin_process_group_landing_examples" describe "Visit a process group's landing page", type: :system, perform_enqueued: true do let!(:organization) { create :organization, available_locales: [:en] } + let(:user) { create(:user, :admin, :confirmed, organization: organization) } let!(:participatory_process_group) { create :participatory_process_group, :with_participatory_processes, organization: organization } let!(:processes) { participatory_process_group.participatory_processes } @@ -16,6 +18,7 @@ before do switch_to_host(organization.host) + login_as user, scope: :user visit decidim_participatory_processes.participatory_process_group_path(participatory_process_group) end @@ -31,6 +34,8 @@ expect(page).to have_selector(".icon--instagram") end end + + it_behaves_like "updates the content block extra title", "extra_title" end describe "extra information block" do @@ -39,6 +44,8 @@ expect(page).to have_i18n_content(extra_information_block.settings.body) end end + + it_behaves_like "updates the content block extra information", "extra_information" end describe "calendar block" do