From b2a49cd2918dd4094c6b1675bc64344ef7060285 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Mon, 22 Apr 2024 03:16:40 +0200 Subject: [PATCH 01/26] Extract component to render the status of a poll We're renaming the existing HTML class in order to be consistent with the name of the component. --- .../stylesheets/polls/access_status.scss | 55 ++++++++++++++++++ app/assets/stylesheets/polls/poll.scss | 56 ------------------- .../polls/access_status_component.html.erb | 19 +++++++ .../polls/access_status_component.rb | 8 +++ app/components/polls/poll_component.html.erb | 21 +------ app/components/polls/poll_component.rb | 2 +- .../polls/access_status_component_spec.rb | 40 +++++++++++++ spec/components/polls/poll_component_spec.rb | 39 ------------- 8 files changed, 125 insertions(+), 115 deletions(-) create mode 100644 app/assets/stylesheets/polls/access_status.scss create mode 100644 app/components/polls/access_status_component.html.erb create mode 100644 app/components/polls/access_status_component.rb create mode 100644 spec/components/polls/access_status_component_spec.rb diff --git a/app/assets/stylesheets/polls/access_status.scss b/app/assets/stylesheets/polls/access_status.scss new file mode 100644 index 000000000..24391e76e --- /dev/null +++ b/app/assets/stylesheets/polls/access_status.scss @@ -0,0 +1,55 @@ +.poll .access-status { + border-bottom: 60px solid transparent; + border-top: 0; + 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"; + } + } +} diff --git a/app/assets/stylesheets/polls/poll.scss b/app/assets/stylesheets/polls/poll.scss index 8e3446ef7..e97264af5 100644 --- a/app/assets/stylesheets/polls/poll.scss +++ b/app/assets/stylesheets/polls/poll.scss @@ -12,62 +12,6 @@ } } - .icon-poll-answer { - border-bottom: 60px solid transparent; - border-top: 0; - 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; diff --git a/app/components/polls/access_status_component.html.erb b/app/components/polls/access_status_component.html.erb new file mode 100644 index 000000000..046094db9 --- /dev/null +++ b/app/components/polls/access_status_component.html.erb @@ -0,0 +1,19 @@ +<% if current_user %> + <% 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 %> +<% else %> +
"> + <%= t("polls.index.not_logged_in") %> +
+<% end %> diff --git a/app/components/polls/access_status_component.rb b/app/components/polls/access_status_component.rb new file mode 100644 index 000000000..faf10512e --- /dev/null +++ b/app/components/polls/access_status_component.rb @@ -0,0 +1,8 @@ +class Polls::AccessStatusComponent < ApplicationComponent + attr_reader :poll + use_helpers :cannot?, :current_user + + def initialize(poll) + @poll = poll + end +end diff --git a/app/components/polls/poll_component.html.erb b/app/components/polls/poll_component.html.erb index 110bef076..e290c9434 100644 --- a/app/components/polls/poll_component.html.erb +++ b/app/components/polls/poll_component.html.erb @@ -1,23 +1,6 @@
- <% if current_user %> - <% 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 %> - <% else %> -
"> - <%= t("polls.index.not_logged_in") %> -
- <% end %> + <%= render Polls::AccessStatusComponent.new(poll) %> +
diff --git a/app/components/polls/poll_component.rb b/app/components/polls/poll_component.rb index a1cda60ea..32b8b0b17 100644 --- a/app/components/polls/poll_component.rb +++ b/app/components/polls/poll_component.rb @@ -1,6 +1,6 @@ class Polls::PollComponent < ApplicationComponent attr_reader :poll - use_helpers :cannot?, :current_user, :link_to_poll + use_helpers :link_to_poll def initialize(poll) @poll = poll diff --git a/spec/components/polls/access_status_component_spec.rb b/spec/components/polls/access_status_component_spec.rb new file mode 100644 index 000000000..405f06049 --- /dev/null +++ b/spec/components/polls/access_status_component_spec.rb @@ -0,0 +1,40 @@ +require "rails_helper" + +describe Polls::AccessStatusComponent do + it "asks anonymous users to sign in" do + render_inline Polls::AccessStatusComponent.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::AccessStatusComponent.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::AccessStatusComponent.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::AccessStatusComponent.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 diff --git a/spec/components/polls/poll_component_spec.rb b/spec/components/polls/poll_component_spec.rb index a12041ff8..4317ad2a8 100644 --- a/spec/components/polls/poll_component_spec.rb +++ b/spec/components/polls/poll_component_spec.rb @@ -3,45 +3,6 @@ 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 - describe "dates" do it "renders the dates inside an HTML tag" do poll = create(:poll, starts_at: "2015-07-15", ends_at: "2015-07-22") From 1b9d321c4e291e56ee782876ef21e1e465fbc71b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Mon, 22 Apr 2024 03:28:33 +0200 Subject: [PATCH 02/26] Extract methods in poll status component This way we remove duplication in the HTML. We're also adding a test checking what happens when users can vote in order to test the `render?` method we've added. --- .../polls/access_status_component.html.erb | 22 ++------------- .../polls/access_status_component.rb | 28 +++++++++++++++++++ .../polls/access_status_component_spec.rb | 8 ++++++ 3 files changed, 39 insertions(+), 19 deletions(-) diff --git a/app/components/polls/access_status_component.html.erb b/app/components/polls/access_status_component.html.erb index 046094db9..d82f5d669 100644 --- a/app/components/polls/access_status_component.html.erb +++ b/app/components/polls/access_status_component.html.erb @@ -1,19 +1,3 @@ -<% if current_user %> - <% 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 %> -<% else %> -
"> - <%= t("polls.index.not_logged_in") %> -
-<% end %> +
+ <%= text %> +
diff --git a/app/components/polls/access_status_component.rb b/app/components/polls/access_status_component.rb index faf10512e..f93c38a97 100644 --- a/app/components/polls/access_status_component.rb +++ b/app/components/polls/access_status_component.rb @@ -5,4 +5,32 @@ class Polls::AccessStatusComponent < ApplicationComponent def initialize(poll) @poll = poll end + + def render? + attributes.present? + end + + private + + def text + attributes[:text] + end + + def html_class + attributes[:class] + end + + def attributes + if current_user + if current_user.unverified? + { text: t("polls.index.unverified"), class: "unverified" } + elsif cannot?(:answer, poll) + { text: t("polls.index.cant_answer"), class: "cant-answer" } + elsif !poll.votable_by?(current_user) + { text: t("polls.index.already_answer"), class: "already-answer" } + end + else + { text: t("polls.index.not_logged_in"), class: "not-logged-in" } + end + end end diff --git a/spec/components/polls/access_status_component_spec.rb b/spec/components/polls/access_status_component_spec.rb index 405f06049..9ac1b77cc 100644 --- a/spec/components/polls/access_status_component_spec.rb +++ b/spec/components/polls/access_status_component_spec.rb @@ -37,4 +37,12 @@ describe Polls::AccessStatusComponent do expect(page).to have_css ".already-answer", count: 1 expect(page).to have_content "You already have participated in this poll" end + + it "is not rendered when users can vote" do + sign_in(create(:user, :level_two)) + + render_inline Polls::AccessStatusComponent.new(create(:poll)) + + expect(page).not_to be_rendered + end end From edfea8f06bdd7b8f16c06bc492696057b4f56cbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Tue, 22 Oct 2024 23:18:14 +0200 Subject: [PATCH 03/26] Move image-container CSS to the polls component This is the only place where we use the `image-container` HTML class. --- app/assets/stylesheets/participation.scss | 6 ------ app/assets/stylesheets/polls/poll.scss | 8 +++++++- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/assets/stylesheets/participation.scss b/app/assets/stylesheets/participation.scss index 09b22bd4f..d89822de3 100644 --- a/app/assets/stylesheets/participation.scss +++ b/app/assets/stylesheets/participation.scss @@ -1390,12 +1390,6 @@ } } -.image-container { - background: #fafafa; - overflow: hidden; - position: relative; -} - .public .poll { border: 1px solid $border; margin-bottom: calc($line-height / 2); diff --git a/app/assets/stylesheets/polls/poll.scss b/app/assets/stylesheets/polls/poll.scss index e97264af5..338bdbc8c 100644 --- a/app/assets/stylesheets/polls/poll.scss +++ b/app/assets/stylesheets/polls/poll.scss @@ -4,8 +4,14 @@ @include breakpoint(medium) { padding: 0 calc($line-height / 2) 0 0; } + } - .image-container img { + .image-container { + background: #fafafa; + overflow: hidden; + position: relative; + + img { height: 100%; max-width: none; position: absolute; From b00ddfc931a2dbb64f7c4c5270565bb9af54e03f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Tue, 22 Oct 2024 23:23:10 +0200 Subject: [PATCH 04/26] Move helper method to the component --- app/components/polls/poll_component.rb | 12 +++++++++++- app/helpers/polls_helper.rb | 10 ---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/app/components/polls/poll_component.rb b/app/components/polls/poll_component.rb index 32b8b0b17..af70041d7 100644 --- a/app/components/polls/poll_component.rb +++ b/app/components/polls/poll_component.rb @@ -1,6 +1,6 @@ class Polls::PollComponent < ApplicationComponent attr_reader :poll - use_helpers :link_to_poll + use_helpers :can? def initialize(poll) @poll = poll @@ -11,4 +11,14 @@ class Polls::PollComponent < ApplicationComponent def dates t("polls.dates", open_at: l(poll.starts_at.to_date), closed_at: l(poll.ends_at.to_date)) end + + def link_to_poll(text, poll, options = {}) + if can?(:results, poll) + link_to text, results_poll_path(id: poll.slug || poll.id), options + elsif can?(:stats, poll) + link_to text, stats_poll_path(id: poll.slug || poll.id), options + else + link_to text, poll_path(id: poll.slug || poll.id), options + end + end end diff --git a/app/helpers/polls_helper.rb b/app/helpers/polls_helper.rb index b6cadd423..9705c3fda 100644 --- a/app/helpers/polls_helper.rb +++ b/app/helpers/polls_helper.rb @@ -4,16 +4,6 @@ module PollsHelper booth.name + location end - def link_to_poll(text, poll, options = {}) - if can?(:results, poll) - link_to text, results_poll_path(id: poll.slug || poll.id), options - elsif can?(:stats, poll) - link_to text, stats_poll_path(id: poll.slug || poll.id), options - else - link_to text, poll_path(id: poll.slug || poll.id), options - end - end - def results_menu? controller_name == "polls" && action_name == "results" end From 8ca941ab02bb7f85b3b194642f53b0c35b7ec5a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Tue, 22 Oct 2024 23:57:02 +0200 Subject: [PATCH 05/26] Use flex in a poll in the polls index Note we aren't using flex-with-gap because currently (until we decide to use the `gap` property) this mixin sets a negative margin that would move the border of this element to the left. --- app/assets/stylesheets/layout.scss | 8 --- app/assets/stylesheets/polls/poll.scss | 18 ++++++ app/components/polls/poll_component.html.erb | 62 +++++++++----------- 3 files changed, 46 insertions(+), 42 deletions(-) diff --git a/app/assets/stylesheets/layout.scss b/app/assets/stylesheets/layout.scss index b3eae06c4..073dcde3e 100644 --- a/app/assets/stylesheets/layout.scss +++ b/app/assets/stylesheets/layout.scss @@ -403,18 +403,10 @@ button, vertical-align: top; } -.align-middle { - vertical-align: middle; -} - .table { display: table; } -.table-cell { - display: table-cell; -} - .uppercase { text-transform: uppercase; } diff --git a/app/assets/stylesheets/polls/poll.scss b/app/assets/stylesheets/polls/poll.scss index 338bdbc8c..3cf046f2d 100644 --- a/app/assets/stylesheets/polls/poll.scss +++ b/app/assets/stylesheets/polls/poll.scss @@ -2,7 +2,25 @@ &.with-image { @include breakpoint(medium) { + display: flex; padding: 0 calc($line-height / 2) 0 0; + + > * { + flex-basis: 25%; + } + + .poll-info { + flex-basis: 50%; + } + + > a { + align-self: center; + } + + > .poll-info, + > a { + margin-#{$global-left}: rem-calc(map-get($grid-column-gutter, medium)); + } } } diff --git a/app/components/polls/poll_component.html.erb b/app/components/polls/poll_component.html.erb index e290c9434..17002ba0c 100644 --- a/app/components/polls/poll_component.html.erb +++ b/app/components/polls/poll_component.html.erb @@ -1,39 +1,33 @@
<%= render Polls::AccessStatusComponent.new(poll) %> -
-
-
- <% 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 %> - <%= render Polls::GeozonesComponent.new(poll) %> - <%= 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 %> -
-
+
+ <% 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 %> + <%= render Polls::GeozonesComponent.new(poll) %> + <%= 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 %>
From 22b0c04cf4107b80f737c86287d357bf68c200cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Wed, 23 Oct 2024 00:22:23 +0200 Subject: [PATCH 06/26] Use a component to render poll results We're also renaming the `poll-results-stats` class to `poll-results`. The former name was confusing because it had nothing to do with stats. --- app/assets/stylesheets/participation.scss | 3 ++- .../polls/results_component.html.erb | 22 ++++++++++++++++++ app/components/polls/results_component.rb | 7 ++++++ app/views/polls/results.html.erb | 23 +------------------ app/views/polls/stats.html.erb | 2 +- 5 files changed, 33 insertions(+), 24 deletions(-) create mode 100644 app/components/polls/results_component.html.erb create mode 100644 app/components/polls/results_component.rb diff --git a/app/assets/stylesheets/participation.scss b/app/assets/stylesheets/participation.scss index d89822de3..73ad32cdd 100644 --- a/app/assets/stylesheets/participation.scss +++ b/app/assets/stylesheets/participation.scss @@ -1409,7 +1409,8 @@ // 09. Polls results and stats // --------------------------- -.polls-results-stats { +.polls-results, +.polls-stats { table { table-layout: fixed; diff --git a/app/components/polls/results_component.html.erb b/app/components/polls/results_component.html.erb new file mode 100644 index 000000000..a112b9dc2 --- /dev/null +++ b/app/components/polls/results_component.html.erb @@ -0,0 +1,22 @@ +<% provide :title, poll.name %> + +
+ <%= render Polls::PollHeaderComponent.new(poll) %> + + <%= render "poll_subnav" %> + +
+ + +
+ <%= render Polls::Results::QuestionComponent.with_collection(poll.questions) %> +
+
+
diff --git a/app/components/polls/results_component.rb b/app/components/polls/results_component.rb new file mode 100644 index 000000000..9da3eb130 --- /dev/null +++ b/app/components/polls/results_component.rb @@ -0,0 +1,7 @@ +class Polls::ResultsComponent < ApplicationComponent + attr_reader :poll + + def initialize(poll) + @poll = poll + end +end diff --git a/app/views/polls/results.html.erb b/app/views/polls/results.html.erb index 8f6cddd44..44af3685a 100644 --- a/app/views/polls/results.html.erb +++ b/app/views/polls/results.html.erb @@ -1,22 +1 @@ -<% provide :title, @poll.name %> - -
- <%= render Polls::PollHeaderComponent.new(@poll) %> - - <%= render "poll_subnav" %> - -
- - -
- <%= render Polls::Results::QuestionComponent.with_collection(@poll.questions) %> -
-
-
+<%= render Polls::ResultsComponent.new(@poll) %> diff --git a/app/views/polls/stats.html.erb b/app/views/polls/stats.html.erb index b99052f48..8c1766181 100644 --- a/app/views/polls/stats.html.erb +++ b/app/views/polls/stats.html.erb @@ -1,6 +1,6 @@ <% provide :title, @poll.name %> -
+
<%= render Polls::PollHeaderComponent.new(@poll) %> <%= render "poll_subnav" %> From c4d69416ca34efb5c99ba58e599c472005b9bc30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Wed, 23 Oct 2024 00:27:39 +0200 Subject: [PATCH 07/26] Use flex instead of equalizer in polls results Note that the sidebar class isn't used since commit b91b766e9, so we're removing it. --- app/assets/stylesheets/polls/results.scss | 22 +++++++++++++++++++ .../polls/results_component.html.erb | 6 ++--- 2 files changed, 25 insertions(+), 3 deletions(-) create mode 100644 app/assets/stylesheets/polls/results.scss diff --git a/app/assets/stylesheets/polls/results.scss b/app/assets/stylesheets/polls/results.scss new file mode 100644 index 000000000..769199149 --- /dev/null +++ b/app/assets/stylesheets/polls/results.scss @@ -0,0 +1,22 @@ +.polls-results { + .polls-results-content { + margin-bottom: $line-height; + margin-top: $line-height; + + @include breakpoint(medium) { + display: flex; + + > :first-child { + flex-basis: 25%; + } + + > :last-child { + flex-basis: 75%; + } + } + + > * { + @include grid-column-gutter; + } + } +} diff --git a/app/components/polls/results_component.html.erb b/app/components/polls/results_component.html.erb index a112b9dc2..28e33ad82 100644 --- a/app/components/polls/results_component.html.erb +++ b/app/components/polls/results_component.html.erb @@ -5,8 +5,8 @@ <%= render "poll_subnav" %> -
-