From 765ab758dc3b12406219c999d7ec5dd6f99d10a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Mon, 22 Apr 2024 03:46:04 +0200 Subject: [PATCH] Extract component to render a poll in the poll index This is consistent with the way we've got partials to render debates, proposals and legislation processes on their index pages. Note that, while adding the tests for the status icon, we're keeping one system test because it also tests the process of voting. We're adding a new, similar component test, where the voter is created in the database, so all possible statuses are tested in the component. --- app/assets/stylesheets/application.scss | 1 + app/assets/stylesheets/participation.scss | 88 -------------------- app/assets/stylesheets/polls/poll.scss | 86 +++++++++++++++++++ app/components/polls/poll_component.html.erb | 66 +++++++++++++++ app/components/polls/poll_component.rb | 18 ++++ app/helpers/polls_helper.rb | 8 -- app/views/polls/_poll_group.html.erb | 67 +-------------- spec/components/polls/poll_component_spec.rb | 73 ++++++++++++++++ spec/system/polls/polls_spec.rb | 60 ------------- 9 files changed, 245 insertions(+), 222 deletions(-) create mode 100644 app/assets/stylesheets/polls/poll.scss create mode 100644 app/components/polls/poll_component.html.erb create mode 100644 app/components/polls/poll_component.rb create mode 100644 spec/components/polls/poll_component_spec.rb diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index ae376c155..d97ec39f1 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -47,6 +47,7 @@ @import "layout/**/*"; @import "machine_learning/**/*"; @import "moderation/**/*"; +@import "polls/**/*"; @import "proposals/**/*"; @import "relationable/**/*"; @import "sdg/**/*"; diff --git a/app/assets/stylesheets/participation.scss b/app/assets/stylesheets/participation.scss index b3127f0c3..83c53f89a 100644 --- a/app/assets/stylesheets/participation.scss +++ b/app/assets/stylesheets/participation.scss @@ -1515,100 +1515,12 @@ position: relative; } -.poll { - - &.with-image { - - @include breakpoint(medium) { - padding: 0 calc(#{$line-height} / 2) 0 0; - } - - .image-container img { - height: 100%; - max-width: none; - position: absolute; - } - } -} - .poll, .poll-question { border: 1px solid $border; margin-bottom: calc(#{$line-height} / 2); padding: calc(#{$line-height} / 2); position: relative; - - .icon-poll-answer { - border-top: 0; - border-bottom: 60px solid transparent; - height: 0; - position: absolute; - right: 0; - top: 0; - width: 0; - - &.cant-answer::after, - &.not-logged-in::after, - &.already-answer::after, - &.unverified::after { - font-family: "icons" !important; - left: 34px; - position: absolute; - top: 5px; - } - - &.cant-answer { - border-right: 60px solid $alert-bg; - - &::after { - color: $color-alert; - content: "\74"; - } - } - - &.not-logged-in { - border-right: 60px solid $info-bg; - - &::after { - color: $color-info; - content: "\6f"; - } - } - - &.unverified { - border-right: 60px solid $warning-bg; - - &::after { - color: $color-warning; - content: "\6f"; - } - } - - &.already-answer { - border-right: 60px solid $success-bg; - - &::after { - color: $color-success; - content: "\59"; - } - } - } - - .dates { - color: $text-medium; - font-size: $small-font-size; - margin-bottom: calc(#{$line-height} / 2); - } - - h4 { - font-size: rem-calc(30); - line-height: $line-height * 1.5; - - a { - color: inherit; - display: inline-block; - } - } } .questions-callout { diff --git a/app/assets/stylesheets/polls/poll.scss b/app/assets/stylesheets/polls/poll.scss new file mode 100644 index 000000000..501ec968d --- /dev/null +++ b/app/assets/stylesheets/polls/poll.scss @@ -0,0 +1,86 @@ +.poll { + &.with-image { + + @include breakpoint(medium) { + padding: 0 calc(#{$line-height} / 2) 0 0; + } + + .image-container img { + height: 100%; + max-width: none; + position: absolute; + } + } + + .icon-poll-answer { + border-top: 0; + border-bottom: 60px solid transparent; + height: 0; + position: absolute; + right: 0; + top: 0; + width: 0; + + &.cant-answer::after, + &.not-logged-in::after, + &.already-answer::after, + &.unverified::after { + font-family: "icons" !important; + left: 34px; + position: absolute; + top: 5px; + } + + &.cant-answer { + border-right: 60px solid $alert-bg; + + &::after { + color: $color-alert; + content: "\74"; + } + } + + &.not-logged-in { + border-right: 60px solid $info-bg; + + &::after { + color: $color-info; + content: "\6f"; + } + } + + &.unverified { + border-right: 60px solid $warning-bg; + + &::after { + color: $color-warning; + content: "\6f"; + } + } + + &.already-answer { + border-right: 60px solid $success-bg; + + &::after { + color: $color-success; + content: "\59"; + } + } + } + + .dates { + color: $text-medium; + font-size: $small-font-size; + margin-bottom: calc(#{$line-height} / 2); + } + + h4 { + font-size: rem-calc(30); + line-height: $line-height * 1.5; + + a { + color: inherit; + display: inline-block; + } + } +} diff --git a/app/components/polls/poll_component.html.erb b/app/components/polls/poll_component.html.erb new file mode 100644 index 000000000..6e52866ce --- /dev/null +++ b/app/components/polls/poll_component.html.erb @@ -0,0 +1,66 @@ +
+ <% if !user_signed_in? %> +
"> + <%= t("polls.index.not_logged_in") %> +
+ <% elsif user_signed_in? %> + <% if current_user.unverified? %> +
"> + <%= t("polls.index.unverified") %> +
+ <% elsif cannot?(:answer, poll) %> +
"> + <%= t("polls.index.cant_answer") %> +
+ <% elsif !poll.votable_by?(current_user) %> +
"> + <%= t("polls.index.already_answer") %> +
+ <% end %> + <% end %> +
+
+
+ <% if poll.image.present? %> + <%= image_tag poll.image.variant(:large), alt: poll.image.title.unicode_normalize %> + <% end %> +
+
+
+
+ <% if poll.questions.one? %> +

<%= link_to_poll poll.questions.first.title, poll %>

+ <%= dates %> + <% else %> +

<%= link_to_poll poll.name, poll %>

+ <%= dates %> + +
    + <% poll.questions.sort_for_list.each do |question| %> +
  • <%= question.title %>
  • + <% end %> +
+ <% end %> + <% if poll.geozones.any? %> +

+ <%= t("polls.index.geozone_info") %> +

+ <% end %> +
    + <% poll.geozones.each do |g| %> +
  • <%= g.name %>
  • + <% end %> +
+ <%= render SDG::TagListComponent.new(poll, limit: 5, linkable: false) %> +
+
+
+ <% if poll.expired? %> + <%= link_to_poll t("polls.index.participate_button_expired"), poll, class: "button hollow expanded" %> + <% else %> + <%= link_to_poll t("polls.index.participate_button"), poll, class: "button hollow expanded" %> + <% end %> +
+
+
+
diff --git a/app/components/polls/poll_component.rb b/app/components/polls/poll_component.rb new file mode 100644 index 000000000..45aae8da8 --- /dev/null +++ b/app/components/polls/poll_component.rb @@ -0,0 +1,18 @@ +class Polls::PollComponent < ApplicationComponent + attr_reader :poll + use_helpers :cannot?, :user_signed_in?, :current_user, :link_to_poll + + def initialize(poll) + @poll = poll + end + + private + + def dates + if poll.starts_at.blank? || poll.ends_at.blank? + I18n.t("polls.no_dates") + else + I18n.t("polls.dates", open_at: l(poll.starts_at.to_date), closed_at: l(poll.ends_at.to_date)) + end + end +end diff --git a/app/helpers/polls_helper.rb b/app/helpers/polls_helper.rb index 21ae9208e..b6cadd423 100644 --- a/app/helpers/polls_helper.rb +++ b/app/helpers/polls_helper.rb @@ -1,12 +1,4 @@ module PollsHelper - def poll_dates(poll) - if poll.starts_at.blank? || poll.ends_at.blank? - I18n.t("polls.no_dates") - else - I18n.t("polls.dates", open_at: l(poll.starts_at.to_date), closed_at: l(poll.ends_at.to_date)) - end - end - def booth_name_with_location(booth) location = booth.location.blank? ? "" : " (#{booth.location})" booth.name + location diff --git a/app/views/polls/_poll_group.html.erb b/app/views/polls/_poll_group.html.erb index 9798527b9..167edb831 100644 --- a/app/views/polls/_poll_group.html.erb +++ b/app/views/polls/_poll_group.html.erb @@ -1,68 +1,3 @@ <% poll_group.each do |poll| %> -
- <% if !user_signed_in? %> -
"> - <%= t("polls.index.not_logged_in") %> -
- <% elsif user_signed_in? %> - <% if current_user.unverified? %> -
"> - <%= t("polls.index.unverified") %> -
- <% elsif cannot?(:answer, poll) %> -
"> - <%= t("polls.index.cant_answer") %> -
- <% elsif !poll.votable_by?(current_user) %> -
"> - <%= t("polls.index.already_answer") %> -
- <% end %> - <% end %> -
-
-
- <% if poll.image.present? %> - <%= image_tag poll.image.variant(:large), alt: poll.image.title.unicode_normalize %> - <% end %> -
-
-
-
- <% if poll.questions.one? %> -

<%= link_to_poll poll.questions.first.title, poll %>

- <%= poll_dates(poll) %> - <% else %> -

<%= link_to_poll poll.name, poll %>

- <%= poll_dates(poll) %> - -
    - <% poll.questions.sort_for_list.each do |question| %> -
  • <%= question.title %>
  • - <% end %> -
- <% end %> - <% if poll.geozones.any? %> -

- <%= t("polls.index.geozone_info") %> -

- <% end %> -
    - <% poll.geozones.each do |g| %> -
  • <%= g.name %>
  • - <% end %> -
- <%= render SDG::TagListComponent.new(poll, limit: 5, linkable: false) %> -
-
-
- <% if poll.expired? %> - <%= link_to_poll t("polls.index.participate_button_expired"), poll, class: "button hollow expanded" %> - <% else %> - <%= link_to_poll t("polls.index.participate_button"), poll, class: "button hollow expanded" %> - <% end %> -
-
-
-
+ <%= render Polls::PollComponent.new(poll) %> <% end %> diff --git a/spec/components/polls/poll_component_spec.rb b/spec/components/polls/poll_component_spec.rb new file mode 100644 index 000000000..ca4ea271e --- /dev/null +++ b/spec/components/polls/poll_component_spec.rb @@ -0,0 +1,73 @@ +require "rails_helper" + +describe Polls::PollComponent do + include Rails.application.routes.url_helpers + + describe "status message" do + it "asks anonymous users to sign in" do + render_inline Polls::PollComponent.new(create(:poll)) + + expect(page).to have_css ".not-logged-in", count: 1 + expect(page).to have_content "You must sign in or sign up to participate" + end + + it "asks unverified users to verify their account" do + sign_in(create(:user)) + + render_inline Polls::PollComponent.new(create(:poll)) + + expect(page).to have_css ".unverified", count: 1 + expect(page).to have_content "You must verify your account to participate" + end + + it "tell users from different geozones that the poll isn't available" do + sign_in(create(:user, :level_two)) + + render_inline Polls::PollComponent.new(create(:poll, geozone_restricted: true)) + + expect(page).to have_css ".cant-answer", count: 1 + expect(page).to have_content "This poll is not available on your geozone" + end + + it "informs users when they've already participated" do + user = create(:user, :level_two) + poll = create(:poll) + create(:poll_voter, user: user, poll: poll) + + sign_in(user) + render_inline Polls::PollComponent.new(poll) + + expect(page).to have_css ".already-answer", count: 1 + expect(page).to have_content "You already have participated in this poll" + end + end + + it "shows a link to poll stats if enabled" do + poll = create(:poll, :expired, name: "Poll with stats", stats_enabled: true) + + render_inline Polls::PollComponent.new(poll) + + expect(page).to have_link "Poll with stats", href: stats_poll_path(poll.slug) + expect(page).to have_link "Poll ended", href: stats_poll_path(poll.slug) + end + + it "shows a link to poll results if enabled" do + poll = create(:poll, :expired, name: "Poll with results", stats_enabled: true, results_enabled: true) + + render_inline Polls::PollComponent.new(poll) + + expect(page).to have_link "Poll with results", href: results_poll_path(poll.slug) + expect(page).to have_link "Poll ended", href: results_poll_path(poll.slug) + end + + it "shows SDG tags when that feature is enabled" do + Setting["feature.sdg"] = true + Setting["sdg.process.polls"] = true + poll = create(:poll, sdg_goals: [SDG::Goal[1]], sdg_targets: [SDG::Target["1.1"]]) + + render_inline Polls::PollComponent.new(poll) + + expect(page).to have_css "img[alt='1. No Poverty']" + expect(page).to have_content "target 1.1" + end +end diff --git a/spec/system/polls/polls_spec.rb b/spec/system/polls/polls_spec.rb index 19dad5252..c8b286c9a 100644 --- a/spec/system/polls/polls_spec.rb +++ b/spec/system/polls/polls_spec.rb @@ -87,36 +87,6 @@ describe "Polls" do expect(page).not_to have_link("Expired") end - scenario "Displays a message asking anonymous users to sign in" do - create_list(:poll, 3) - - visit polls_path - - expect(page).to have_css(".not-logged-in", count: 3) - expect(page).to have_content("You must sign in or sign up to participate") - end - - scenario "Displays a message asking unverified users to verify their account" do - create_list(:poll, 3) - user = create(:user) - login_as(user) - - visit polls_path - - expect(page).to have_css(".unverified", count: 3) - expect(page).to have_content("You must verify your account to participate") - end - - scenario "Geozone poll" do - create(:poll, geozone_restricted: true) - - login_as(create(:user, :level_two)) - visit polls_path - - expect(page).to have_css(".cant-answer", count: 1) - expect(page).to have_content("This poll is not available on your geozone") - end - scenario "Already participated in a poll" do poll_with_question = create(:poll) question = create(:poll_question, :yes_no, poll: poll_with_question) @@ -133,36 +103,6 @@ describe "Polls" do expect(page).to have_css(".already-answer", count: 1) expect(page).to have_content("You already have participated in this poll") end - - scenario "Poll title and button link to stats if enabled" do - poll = create(:poll, :expired, name: "Poll with stats", stats_enabled: true) - - visit polls_path(filter: "expired") - - expect(page).to have_link("Poll with stats", href: stats_poll_path(poll.slug)) - expect(page).to have_link("Poll ended", href: stats_poll_path(poll.slug)) - end - - scenario "Poll title and button link to results if enabled" do - poll = create(:poll, :expired, name: "Poll with results", stats_enabled: true, results_enabled: true) - - visit polls_path(filter: "expired") - - expect(page).to have_link("Poll with results", href: results_poll_path(poll.slug)) - expect(page).to have_link("Poll ended", href: results_poll_path(poll.slug)) - end - - scenario "Shows SDG tags when feature is enabled" do - Setting["feature.sdg"] = true - Setting["sdg.process.polls"] = true - - create(:poll, sdg_goals: [SDG::Goal[1]], sdg_targets: [SDG::Target["1.1"]]) - - visit polls_path - - expect(page).to have_css "img[alt='1. No Poverty']" - expect(page).to have_content "target 1.1" - end end context "Show" do