Unify code in debates/legislation vote links
We were using the same code to render links to agree and disagree, so we can extract a new component for this code. We're also adding component tests to make it easier to test whether we're breaking anything while refactoring, although the code is probably already covered by system tests. Since the votes mixin was only used in one place, we're removing it and moving most of its code to a new CSS file for the shared component.
This commit is contained in:
@@ -27,6 +27,7 @@
|
||||
@import "milestones";
|
||||
@import "pages";
|
||||
@import "dashboard";
|
||||
@import "in_favor_against";
|
||||
@import "legislation";
|
||||
@import "legislation_process";
|
||||
@import "legislation_process_form";
|
||||
|
||||
97
app/assets/stylesheets/in_favor_against.scss
Normal file
97
app/assets/stylesheets/in_favor_against.scss
Normal file
@@ -0,0 +1,97 @@
|
||||
.in-favor-against {
|
||||
display: inline-block;
|
||||
|
||||
.icon-like,
|
||||
.icon-unlike {
|
||||
background: #fff;
|
||||
border: 2px solid;
|
||||
border-radius: rem-calc(3);
|
||||
color: $text-light;
|
||||
display: inline-block;
|
||||
font-size: rem-calc(30);
|
||||
line-height: rem-calc(30);
|
||||
padding: rem-calc(3) rem-calc(6);
|
||||
position: relative;
|
||||
|
||||
&:hover,
|
||||
&:active {
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
opacity: 1 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.icon-like {
|
||||
|
||||
&:hover,
|
||||
&:active {
|
||||
background: $like;
|
||||
border: 2px solid $like;
|
||||
}
|
||||
}
|
||||
|
||||
.icon-unlike {
|
||||
|
||||
&:hover,
|
||||
&:active {
|
||||
background: $unlike;
|
||||
border: 2px solid $unlike;
|
||||
}
|
||||
}
|
||||
|
||||
.like,
|
||||
.unlike {
|
||||
line-height: rem-calc(48);
|
||||
vertical-align: super;
|
||||
text-decoration: none;
|
||||
|
||||
.percentage {
|
||||
color: $text;
|
||||
display: inline-block;
|
||||
font-size: $small-font-size;
|
||||
line-height: $line-height * 2;
|
||||
padding-right: $line-height / 2;
|
||||
vertical-align: top;
|
||||
|
||||
@include breakpoint(medium) {
|
||||
display: block;
|
||||
line-height: $line-height;
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.voted {
|
||||
|
||||
.icon-like,
|
||||
.icon-unlike {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.icon-like {
|
||||
background: $like;
|
||||
border: 2px solid $like;
|
||||
}
|
||||
|
||||
.icon-unlike {
|
||||
background: $unlike;
|
||||
border: 2px solid $unlike;
|
||||
}
|
||||
}
|
||||
|
||||
.no-voted {
|
||||
|
||||
.icon-like,
|
||||
.icon-unlike {
|
||||
opacity: 0.3;
|
||||
}
|
||||
}
|
||||
|
||||
.against {
|
||||
margin-left: $line-height / 4;
|
||||
}
|
||||
|
||||
.divider {
|
||||
margin: 0 rem-calc(6);
|
||||
}
|
||||
}
|
||||
@@ -14,120 +14,6 @@
|
||||
// 01. Votes and supports
|
||||
// ----------------------
|
||||
|
||||
@mixin votes {
|
||||
border-top: 1px solid $border;
|
||||
margin-top: $line-height;
|
||||
padding: $line-height / 2 0;
|
||||
position: relative;
|
||||
|
||||
@include breakpoint(medium) {
|
||||
border-left: 1px solid $border;
|
||||
border-top: 0;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.icon-like,
|
||||
.icon-unlike {
|
||||
background: #fff;
|
||||
border: 2px solid;
|
||||
border-radius: rem-calc(3);
|
||||
color: $text-light;
|
||||
display: inline-block;
|
||||
font-size: rem-calc(30);
|
||||
line-height: rem-calc(30);
|
||||
padding: rem-calc(3) rem-calc(6);
|
||||
position: relative;
|
||||
|
||||
&:hover,
|
||||
&:active {
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
opacity: 1 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.icon-like {
|
||||
|
||||
&:hover,
|
||||
&:active {
|
||||
background: $like;
|
||||
border: 2px solid $like;
|
||||
}
|
||||
}
|
||||
|
||||
.icon-unlike {
|
||||
|
||||
&:hover,
|
||||
&:active {
|
||||
background: $unlike;
|
||||
border: 2px solid $unlike;
|
||||
}
|
||||
}
|
||||
|
||||
.like,
|
||||
.unlike {
|
||||
line-height: rem-calc(48);
|
||||
vertical-align: super;
|
||||
text-decoration: none;
|
||||
|
||||
.percentage {
|
||||
color: $text;
|
||||
display: inline-block;
|
||||
font-size: $small-font-size;
|
||||
line-height: $line-height * 2;
|
||||
padding-right: $line-height / 2;
|
||||
vertical-align: top;
|
||||
|
||||
@include breakpoint(medium) {
|
||||
display: block;
|
||||
line-height: $line-height;
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.voted {
|
||||
|
||||
.icon-like,
|
||||
.icon-unlike {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.icon-like {
|
||||
background: $like;
|
||||
border: 2px solid $like;
|
||||
}
|
||||
|
||||
.icon-unlike {
|
||||
background: $unlike;
|
||||
border: 2px solid $unlike;
|
||||
}
|
||||
}
|
||||
|
||||
.no-voted {
|
||||
|
||||
.icon-like,
|
||||
.icon-unlike {
|
||||
opacity: 0.3;
|
||||
}
|
||||
}
|
||||
|
||||
.total-votes {
|
||||
font-weight: bold;
|
||||
float: right;
|
||||
line-height: $line-height * 2;
|
||||
|
||||
@include breakpoint(medium) {
|
||||
display: block;
|
||||
float: none;
|
||||
}
|
||||
}
|
||||
|
||||
.divider {
|
||||
margin: 0 rem-calc(6);
|
||||
}
|
||||
}
|
||||
|
||||
@mixin supports {
|
||||
padding: $line-height 0;
|
||||
position: relative;
|
||||
@@ -713,14 +599,27 @@
|
||||
.legislation-proposals {
|
||||
|
||||
.votes {
|
||||
@include votes;
|
||||
border-top: 1px solid $border;
|
||||
margin-top: $line-height;
|
||||
padding: $line-height / 2 0;
|
||||
position: relative;
|
||||
|
||||
@include breakpoint(medium) {
|
||||
border-left: 1px solid $border;
|
||||
border-top: 0;
|
||||
margin-top: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.against {
|
||||
margin-left: $line-height / 4;
|
||||
.total-votes {
|
||||
font-weight: bold;
|
||||
float: right;
|
||||
line-height: $line-height * 2;
|
||||
|
||||
@include breakpoint(medium) {
|
||||
display: block;
|
||||
float: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,42 +1,5 @@
|
||||
<div class="votes">
|
||||
<div class="in-favor inline-block">
|
||||
<% if current_user %>
|
||||
<%= link_to vote_debate_path(debate, value: "yes"),
|
||||
class: "like #{voted_classes[:in_favor]}", title: t("votes.agree"), method: "post", remote: true do %>
|
||||
<span class="icon-like">
|
||||
<span class="show-for-sr"><%= t("votes.agree") %></span>
|
||||
</span>
|
||||
<span class="percentage"><%= votes_percentage("likes", debate) %></span>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<div class="like">
|
||||
<span class="icon-like">
|
||||
<span class="show-for-sr"><%= t("votes.agree") %></span>
|
||||
</span>
|
||||
<span class="percentage"><%= votes_percentage("likes", debate) %></span>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<span class="divider"></span>
|
||||
|
||||
<div class="against inline-block">
|
||||
<% if current_user %>
|
||||
<%= link_to vote_debate_path(debate, value: "no"), class: "unlike #{voted_classes[:against]}", title: t("votes.disagree"), method: "post", remote: true do %>
|
||||
<span class="icon-unlike">
|
||||
<span class="show-for-sr"><%= t("votes.disagree") %></span>
|
||||
</span>
|
||||
<span class="percentage"><%= votes_percentage("dislikes", debate) %></span>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<div class="unlike">
|
||||
<span class="icon-unlike">
|
||||
<span class="show-for-sr"><%= t("votes.disagree") %></span>
|
||||
</span>
|
||||
<span class="percentage"><%= votes_percentage("dislikes", debate) %></span>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
<%= render Shared::InFavorAgainstComponent.new(debate) %>
|
||||
|
||||
<span class="total-votes">
|
||||
<%= t("debates.debate.votes", count: debate.votes_score) %>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
class Debates::VotesComponent < ApplicationComponent
|
||||
attr_reader :debate
|
||||
delegate :css_classes_for_vote, :current_user, :link_to_verify_account, :votes_percentage, to: :helpers
|
||||
delegate :current_user, :link_to_verify_account, to: :helpers
|
||||
|
||||
def initialize(debate)
|
||||
@debate = debate
|
||||
@@ -8,10 +8,6 @@ class Debates::VotesComponent < ApplicationComponent
|
||||
|
||||
private
|
||||
|
||||
def voted_classes
|
||||
@voted_classes ||= css_classes_for_vote(debate)
|
||||
end
|
||||
|
||||
def can_vote?
|
||||
debate.votable_by?(current_user)
|
||||
end
|
||||
|
||||
@@ -1,43 +1,6 @@
|
||||
<div class="votes">
|
||||
<% if proposal.process.proposals_phase.open? %>
|
||||
<div class="in-favor inline-block">
|
||||
<% if current_user %>
|
||||
<%= link_to vote_legislation_process_proposal_path(process_id: proposal.process, id: proposal, value: "yes"),
|
||||
class: "like #{voted_classes[:in_favor]}", title: t("votes.agree"), method: "post", remote: true do %>
|
||||
<span class="icon-like">
|
||||
<span class="show-for-sr"><%= t("votes.agree") %></span>
|
||||
</span>
|
||||
<span class="percentage"><%= votes_percentage("likes", proposal) %></span>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<div class="like">
|
||||
<span class="icon-like">
|
||||
<span class="show-for-sr"><%= t("votes.agree") %></span>
|
||||
</span>
|
||||
<span class="percentage"><%= votes_percentage("likes", proposal) %></span>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<span class="divider"></span>
|
||||
|
||||
<div class="against inline-block">
|
||||
<% if current_user %>
|
||||
<%= link_to vote_legislation_process_proposal_path(process_id: proposal.process, id: proposal, value: "no"), class: "unlike #{voted_classes[:against]}", title: t("votes.disagree"), method: "post", remote: true do %>
|
||||
<span class="icon-unlike">
|
||||
<span class="show-for-sr"><%= t("votes.disagree") %></span>
|
||||
</span>
|
||||
<span class="percentage"><%= votes_percentage("dislikes", proposal) %></span>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<div class="unlike">
|
||||
<span class="icon-unlike">
|
||||
<span class="show-for-sr"><%= t("votes.disagree") %></span>
|
||||
</span>
|
||||
<span class="percentage"><%= votes_percentage("dislikes", proposal) %></span>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
<%= render Shared::InFavorAgainstComponent.new(proposal) %>
|
||||
<% end %>
|
||||
|
||||
<span class="total-votes">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
class Legislation::Proposals::VotesComponent < ApplicationComponent
|
||||
attr_reader :proposal
|
||||
delegate :css_classes_for_vote, :current_user, :link_to_verify_account, :votes_percentage, to: :helpers
|
||||
delegate :current_user, :link_to_verify_account, to: :helpers
|
||||
|
||||
def initialize(proposal)
|
||||
@proposal = proposal
|
||||
@@ -8,10 +8,6 @@ class Legislation::Proposals::VotesComponent < ApplicationComponent
|
||||
|
||||
private
|
||||
|
||||
def voted_classes
|
||||
@voted_classes ||= css_classes_for_vote(proposal)
|
||||
end
|
||||
|
||||
def can_vote?
|
||||
proposal.votable_by?(current_user)
|
||||
end
|
||||
|
||||
40
app/components/shared/in_favor_against_component.html.erb
Normal file
40
app/components/shared/in_favor_against_component.html.erb
Normal file
@@ -0,0 +1,40 @@
|
||||
<div class="in-favor-against">
|
||||
<div class="in-favor inline-block">
|
||||
<% if current_user %>
|
||||
<%= link_to polymorphic_path(votable, action: :vote, value: "yes"),
|
||||
class: "like #{voted_classes[:in_favor]}", title: t("votes.agree"), method: "post", remote: true do %>
|
||||
<span class="icon-like">
|
||||
<span class="show-for-sr"><%= t("votes.agree") %></span>
|
||||
</span>
|
||||
<span class="percentage"><%= votes_percentage("likes", votable) %></span>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<div class="like">
|
||||
<span class="icon-like">
|
||||
<span class="show-for-sr"><%= t("votes.agree") %></span>
|
||||
</span>
|
||||
<span class="percentage"><%= votes_percentage("likes", votable) %></span>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<span class="divider"></span>
|
||||
|
||||
<div class="against inline-block">
|
||||
<% if current_user %>
|
||||
<%= link_to polymorphic_path(votable, action: :vote, value: "no"), class: "unlike #{voted_classes[:against]}", title: t("votes.disagree"), method: "post", remote: true do %>
|
||||
<span class="icon-unlike">
|
||||
<span class="show-for-sr"><%= t("votes.disagree") %></span>
|
||||
</span>
|
||||
<span class="percentage"><%= votes_percentage("dislikes", votable) %></span>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<div class="unlike">
|
||||
<span class="icon-unlike">
|
||||
<span class="show-for-sr"><%= t("votes.disagree") %></span>
|
||||
</span>
|
||||
<span class="percentage"><%= votes_percentage("dislikes", votable) %></span>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
25
app/components/shared/in_favor_against_component.rb
Normal file
25
app/components/shared/in_favor_against_component.rb
Normal file
@@ -0,0 +1,25 @@
|
||||
class Shared::InFavorAgainstComponent < ApplicationComponent
|
||||
attr_reader :votable
|
||||
delegate :current_user, :votes_percentage, to: :helpers
|
||||
|
||||
def initialize(votable)
|
||||
@votable = votable
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def voted_classes
|
||||
@voted_classes ||= css_classes_for_vote
|
||||
end
|
||||
|
||||
def css_classes_for_vote
|
||||
case current_user&.voted_as_when_voted_for(votable)
|
||||
when true
|
||||
{ in_favor: "voted", against: "no-voted" }
|
||||
when false
|
||||
{ in_favor: "no-voted", against: "voted" }
|
||||
else
|
||||
{ in_favor: "", against: "" }
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -12,15 +12,4 @@ module VotesHelper
|
||||
"#{100 - debate_percentage_of_likes(debate)}%"
|
||||
end
|
||||
end
|
||||
|
||||
def css_classes_for_vote(votable)
|
||||
case current_user&.voted_as_when_voted_for(votable)
|
||||
when true
|
||||
{ in_favor: "voted", against: "no-voted" }
|
||||
when false
|
||||
{ in_favor: "no-voted", against: "voted" }
|
||||
else
|
||||
{ in_favor: "", against: "" }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
29
spec/components/debates/votes_component_spec.rb
Normal file
29
spec/components/debates/votes_component_spec.rb
Normal file
@@ -0,0 +1,29 @@
|
||||
require "rails_helper"
|
||||
|
||||
describe Debates::VotesComponent do
|
||||
let(:debate) { create(:debate, title: "What about the 2030 agenda?") }
|
||||
let(:component) { Debates::VotesComponent.new(debate) }
|
||||
|
||||
describe "Agree and disagree links" do
|
||||
it "is shown as plain text to anonymous users" do
|
||||
render_inline component
|
||||
|
||||
expect(page).to have_content "I agree"
|
||||
expect(page).to have_content "I disagree"
|
||||
expect(page).to have_content "You must sign in or sign up to continue."
|
||||
expect(page).not_to have_link "I agree"
|
||||
expect(page).not_to have_link "I disagree"
|
||||
end
|
||||
|
||||
it "is shown to identified users" do
|
||||
sign_in(create(:user))
|
||||
|
||||
render_inline component
|
||||
|
||||
expect(page).to have_link count: 2
|
||||
expect(page).to have_link "I agree", title: "I agree"
|
||||
expect(page).to have_link "I disagree", title: "I disagree"
|
||||
expect(page).not_to have_content "You must sign in or sign up to continue."
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,42 @@
|
||||
require "rails_helper"
|
||||
|
||||
describe Legislation::Proposals::VotesComponent do
|
||||
let(:proposal) { create(:legislation_proposal, title: "Require wearing masks at home") }
|
||||
let(:component) { Legislation::Proposals::VotesComponent.new(proposal) }
|
||||
|
||||
describe "Agree and disagree links" do
|
||||
it "is not shown when the proposals phase isn't open" do
|
||||
proposal.process.update!(
|
||||
proposals_phase_start_date: 2.days.ago,
|
||||
proposals_phase_end_date: Date.yesterday
|
||||
)
|
||||
|
||||
sign_in(create(:user))
|
||||
render_inline component
|
||||
|
||||
expect(page).not_to have_content "I agree"
|
||||
expect(page).not_to have_content "I disagree"
|
||||
end
|
||||
|
||||
it "is shown as plain text to anonymous users" do
|
||||
render_inline component
|
||||
|
||||
expect(page).to have_content "I agree"
|
||||
expect(page).to have_content "I disagree"
|
||||
expect(page).to have_content "You must sign in or sign up to continue."
|
||||
expect(page).not_to have_link "I agree"
|
||||
expect(page).not_to have_link "I disagree"
|
||||
end
|
||||
|
||||
it "is shown to identified users" do
|
||||
sign_in(create(:user))
|
||||
|
||||
render_inline component
|
||||
|
||||
expect(page).to have_link count: 2
|
||||
expect(page).to have_link "I agree", title: "I agree"
|
||||
expect(page).to have_link "I disagree", title: "I disagree"
|
||||
expect(page).not_to have_content "You must sign in or sign up to continue."
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user