Merge pull request #5481 from consuldemocracy/remove_initialjs
Replace initialjs-rails with custom avatar code
This commit is contained in:
1
Gemfile
1
Gemfile
@@ -30,7 +30,6 @@ gem "graphiql-rails", "~> 1.8.0"
|
||||
gem "graphql", "~> 1.13.22"
|
||||
gem "groupdate", "~> 6.4.0"
|
||||
gem "image_processing", "~> 1.12.2"
|
||||
gem "initialjs-rails", "~> 0.2.0.9"
|
||||
gem "invisible_captcha", "~> 2.3.0"
|
||||
gem "kaminari", "~> 1.2.2"
|
||||
gem "mini_magick", "~> 4.12.0"
|
||||
|
||||
@@ -280,8 +280,6 @@ GEM
|
||||
image_processing (1.12.2)
|
||||
mini_magick (>= 4.9.5, < 5)
|
||||
ruby-vips (>= 2.0.17, < 3)
|
||||
initialjs-rails (0.2.0.9)
|
||||
railties (>= 3.1, < 7.0)
|
||||
invisible_captcha (2.3.0)
|
||||
rails (>= 5.2)
|
||||
json (2.7.1)
|
||||
@@ -719,7 +717,6 @@ DEPENDENCIES
|
||||
groupdate (~> 6.4.0)
|
||||
i18n-tasks (~> 0.9.37)
|
||||
image_processing (~> 1.12.2)
|
||||
initialjs-rails (~> 0.2.0.9)
|
||||
invisible_captcha (~> 2.3.0)
|
||||
kaminari (~> 1.2.2)
|
||||
knapsack_pro (~> 7.0.1)
|
||||
|
||||
@@ -58,7 +58,6 @@
|
||||
//= require ckeditor/loader
|
||||
//= require_directory ./ckeditor
|
||||
//= require social-share-button
|
||||
//= require initial
|
||||
//= require ahoy
|
||||
//= require app
|
||||
//= require check_all_none
|
||||
@@ -75,7 +74,6 @@
|
||||
//= require annotator
|
||||
//= require jquery.amsify.suggestags
|
||||
//= require tags
|
||||
//= require users
|
||||
//= require participation_not_allowed
|
||||
//= require advanced_search
|
||||
//= require registration_form
|
||||
@@ -136,7 +134,6 @@ var initialize_modules = function() {
|
||||
App.Answers.initialize();
|
||||
App.Questions.initialize();
|
||||
App.Comments.initialize();
|
||||
App.Users.initialize();
|
||||
App.ParticipationNotAllowed.initialize();
|
||||
App.Tags.initialize();
|
||||
App.FoundationExtras.initialize();
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
(function() {
|
||||
"use strict";
|
||||
App.Users = {
|
||||
initialize: function() {
|
||||
var observer;
|
||||
$(".initialjs-avatar").initial();
|
||||
observer = new MutationObserver(function(mutations) {
|
||||
$.each(mutations, function(index, mutation) {
|
||||
$(mutation.addedNodes).find(".initialjs-avatar").initial();
|
||||
});
|
||||
});
|
||||
observer.observe(document.body, { childList: true, subtree: true });
|
||||
}
|
||||
};
|
||||
}).call(this);
|
||||
@@ -25,6 +25,7 @@
|
||||
@import "advanced_search";
|
||||
@import "annotator_overrides";
|
||||
@import "autocomplete_overrides";
|
||||
@import "avatar";
|
||||
@import "banner";
|
||||
@import "comments_count";
|
||||
@import "datepicker_overrides";
|
||||
@@ -48,6 +49,7 @@
|
||||
@import "account/**/*";
|
||||
@import "admin/**/*";
|
||||
@import "budgets/**/*";
|
||||
@import "comments/**/*";
|
||||
@import "debates/**/*";
|
||||
@import "documents/**/*";
|
||||
@import "layout/**/*";
|
||||
|
||||
4
app/assets/stylesheets/avatar.scss
Normal file
4
app/assets/stylesheets/avatar.scss
Normal file
@@ -0,0 +1,4 @@
|
||||
.initialjs-avatar {
|
||||
font-family: HelveticaNeue-Light, "Helvetica Neue Light", "Helvetica Neue",
|
||||
Helvetica, Arial, "Lucida Grande", sans-serif;
|
||||
}
|
||||
6
app/assets/stylesheets/comments/avatar.scss
Normal file
6
app/assets/stylesheets/comments/avatar.scss
Normal file
@@ -0,0 +1,6 @@
|
||||
.comment-avatar {
|
||||
img,
|
||||
svg {
|
||||
float: $global-left;
|
||||
}
|
||||
}
|
||||
@@ -1558,8 +1558,9 @@ table {
|
||||
.comment-body,
|
||||
.notification-body {
|
||||
|
||||
img {
|
||||
margin-right: calc(#{$line-height} / 2);
|
||||
img,
|
||||
svg {
|
||||
margin-#{$global-right}: calc(#{$line-height} / 2);
|
||||
}
|
||||
|
||||
.reply {
|
||||
|
||||
3
app/components/comments/avatar_component.html.erb
Normal file
3
app/components/comments/avatar_component.html.erb
Normal file
@@ -0,0 +1,3 @@
|
||||
<span class="comment-avatar">
|
||||
<%= avatar %>
|
||||
</span>
|
||||
27
app/components/comments/avatar_component.rb
Normal file
27
app/components/comments/avatar_component.rb
Normal file
@@ -0,0 +1,27 @@
|
||||
class Comments::AvatarComponent < ApplicationComponent
|
||||
attr_reader :comment
|
||||
|
||||
def initialize(comment)
|
||||
@comment = comment
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def avatar
|
||||
if comment.as_administrator?
|
||||
special_avatar("avatar_admin.png", class: "admin-avatar")
|
||||
elsif comment.as_moderator?
|
||||
special_avatar("avatar_moderator.png", class: "moderator-avatar")
|
||||
elsif comment.user.hidden? || comment.user.erased?
|
||||
tag.span(class: "icon-deleted user-deleted")
|
||||
elsif comment.user.organization?
|
||||
special_avatar("avatar_collective.png", class: "avatar")
|
||||
else
|
||||
render Shared::AvatarComponent.new(comment.user, size: 32)
|
||||
end
|
||||
end
|
||||
|
||||
def special_avatar(image_name, options = {})
|
||||
image_tag(image_name, { size: 32, alt: "" }.merge(options))
|
||||
end
|
||||
end
|
||||
@@ -1 +1 @@
|
||||
<%= avatar_image(record, options) %>
|
||||
<%= avatar_image %>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
class Shared::AvatarComponent < ApplicationComponent
|
||||
attr_reader :record, :given_options
|
||||
use_helpers :avatar_image
|
||||
|
||||
def initialize(record, **given_options)
|
||||
@record = record
|
||||
@@ -10,7 +9,7 @@ class Shared::AvatarComponent < ApplicationComponent
|
||||
private
|
||||
|
||||
def default_options
|
||||
{ background_color: colors[seed % colors.size] }
|
||||
{ background_color: colors[seed % colors.size], size: 100, color: "white" }
|
||||
end
|
||||
|
||||
def options
|
||||
@@ -27,4 +26,53 @@ class Shared::AvatarComponent < ApplicationComponent
|
||||
def seed
|
||||
record.id
|
||||
end
|
||||
|
||||
def size
|
||||
options[:size]
|
||||
end
|
||||
|
||||
def font_size
|
||||
(size * 0.6).round
|
||||
end
|
||||
|
||||
def background_color
|
||||
options[:background_color]
|
||||
end
|
||||
|
||||
def color
|
||||
options[:color]
|
||||
end
|
||||
|
||||
def svg_options
|
||||
{
|
||||
xmlns: "http://www.w3.org/2000/svg",
|
||||
width: size,
|
||||
height: size,
|
||||
role: "img",
|
||||
"aria-label": "",
|
||||
style: "background-color: #{background_color}",
|
||||
class: "initialjs-avatar #{options[:class]}".strip
|
||||
}
|
||||
end
|
||||
|
||||
def text_options
|
||||
{
|
||||
x: "50%",
|
||||
y: "50%",
|
||||
dy: "0.35em",
|
||||
"text-anchor": "middle",
|
||||
fill: color,
|
||||
style: "font-size: #{font_size}px"
|
||||
}
|
||||
end
|
||||
|
||||
def initial
|
||||
record.name.first.upcase
|
||||
end
|
||||
|
||||
def avatar_image
|
||||
tag.svg(**svg_options) do
|
||||
tag.text(initial, **text_options)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -9,19 +9,7 @@
|
||||
</div>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<% if comment.as_administrator? %>
|
||||
<%= image_tag("avatar_admin.png", size: 32, class: "admin-avatar float-left") %>
|
||||
<% elsif comment.as_moderator? %>
|
||||
<%= image_tag("avatar_moderator.png", size: 32, class: "moderator-avatar float-left") %>
|
||||
<% else %>
|
||||
<% if comment.user.hidden? || comment.user.erased? %>
|
||||
<span class="icon-deleted user-deleted"></span>
|
||||
<% elsif comment.user.organization? %>
|
||||
<%= image_tag("avatar_collective.png", size: 32, class: "avatar float-left") %>
|
||||
<% else %>
|
||||
<%= render Shared::AvatarComponent.new(comment.user, size: 32, class: "float-left") %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<%= render Comments::AvatarComponent.new(comment) %>
|
||||
|
||||
<div class="comment-info">
|
||||
|
||||
|
||||
56
spec/components/comments/avatar_component_spec.rb
Normal file
56
spec/components/comments/avatar_component_spec.rb
Normal file
@@ -0,0 +1,56 @@
|
||||
require "rails_helper"
|
||||
|
||||
describe Comments::AvatarComponent do
|
||||
it "displays a regular avatar for regular comments" do
|
||||
comment = create(:comment, user: create(:user, username: "Oscar Wilde"))
|
||||
|
||||
render_inline Comments::AvatarComponent.new(comment)
|
||||
|
||||
expect(page).to have_avatar "O"
|
||||
expect(page).not_to have_css "img"
|
||||
end
|
||||
|
||||
it "displays the admin avatar with an empty alt attribute for admin comments" do
|
||||
admin = create(:administrator)
|
||||
comment = create(:comment, user: admin.user, administrator_id: admin.id)
|
||||
|
||||
render_inline Comments::AvatarComponent.new(comment)
|
||||
|
||||
expect(page).to have_css "img.admin-avatar[alt='']"
|
||||
end
|
||||
|
||||
it "displays the moderator avatar with an empty alt attribute for moderator comments" do
|
||||
moderator = create(:moderator)
|
||||
comment = create(:comment, user: moderator.user, moderator_id: moderator.id)
|
||||
|
||||
render_inline Comments::AvatarComponent.new(comment)
|
||||
|
||||
expect(page).to have_css "img.moderator-avatar[alt='']"
|
||||
end
|
||||
|
||||
it "displays the organization avatar with an empty alt attribute for organization comments" do
|
||||
comment = create(:comment, user: create(:organization).user)
|
||||
|
||||
render_inline Comments::AvatarComponent.new(comment)
|
||||
|
||||
expect(page).to have_css "img.avatar[alt='']"
|
||||
end
|
||||
|
||||
it "displays an empty icon for comments by hidden users" do
|
||||
comment = create(:comment, user: create(:user, :hidden))
|
||||
|
||||
render_inline Comments::AvatarComponent.new(comment)
|
||||
|
||||
expect(page).to have_css ".user-deleted"
|
||||
expect(page).not_to have_css "img"
|
||||
end
|
||||
|
||||
it "displays an empty icon for comments by erased users" do
|
||||
comment = create(:comment, user: create(:user, erased_at: Time.current))
|
||||
|
||||
render_inline Comments::AvatarComponent.new(comment)
|
||||
|
||||
expect(page).to have_css ".user-deleted"
|
||||
expect(page).not_to have_css "img"
|
||||
end
|
||||
end
|
||||
22
spec/components/shared/avatar_component_spec.rb
Normal file
22
spec/components/shared/avatar_component_spec.rb
Normal file
@@ -0,0 +1,22 @@
|
||||
require "rails_helper"
|
||||
|
||||
describe Shared::AvatarComponent do
|
||||
let(:user) { double(id: 1, name: "Johnny") }
|
||||
let(:component) { Shared::AvatarComponent.new(user) }
|
||||
|
||||
it "does not contain redundant text already present around it" do
|
||||
render_inline component
|
||||
|
||||
expect(page).to have_css "svg", count: 1
|
||||
expect(page).to have_css "svg[role='img'][aria-label='']"
|
||||
end
|
||||
|
||||
it "shows the initial letter of the name" do
|
||||
render_inline component
|
||||
|
||||
page.find("svg") do |avatar|
|
||||
expect(avatar).to have_text "J"
|
||||
expect(avatar).not_to have_text "Johnny"
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -18,8 +18,4 @@ module Comments
|
||||
end
|
||||
expect(page).to have_content "It will be done next week."
|
||||
end
|
||||
|
||||
def avatar(name)
|
||||
"img.initialjs-avatar[data-name='#{name}']"
|
||||
end
|
||||
end
|
||||
|
||||
9
spec/support/matchers/have_avatar.rb
Normal file
9
spec/support/matchers/have_avatar.rb
Normal file
@@ -0,0 +1,9 @@
|
||||
RSpec::Matchers.define :have_avatar do |text, **options|
|
||||
match do |page|
|
||||
page.has_css?("svg.initialjs-avatar", **{ exact_text: text }.merge(options))
|
||||
end
|
||||
|
||||
failure_message do
|
||||
"expected to find avatar with text #{text} but there were no matches."
|
||||
end
|
||||
end
|
||||
@@ -15,7 +15,7 @@ describe "Account" do
|
||||
expect(page).to have_current_path(account_path, ignore_query: true)
|
||||
|
||||
expect(page).to have_css "input[value='Manuela Colau']"
|
||||
expect(page).to have_css avatar("Manuela Colau"), count: 1
|
||||
expect(page).to have_avatar "M", count: 1
|
||||
end
|
||||
|
||||
scenario "Show organization" do
|
||||
@@ -26,7 +26,7 @@ describe "Account" do
|
||||
expect(page).to have_css "input[value='Manuela Corp']"
|
||||
expect(page).not_to have_css "input[value='Manuela Colau']"
|
||||
|
||||
expect(page).to have_css avatar("Manuela Corp"), count: 1
|
||||
expect(page).to have_avatar "M", count: 1
|
||||
end
|
||||
|
||||
scenario "Edit" do
|
||||
|
||||
@@ -72,15 +72,15 @@ describe "Debates" do
|
||||
end
|
||||
|
||||
scenario "Show" do
|
||||
debate = create(:debate)
|
||||
debate = create(:debate, author: create(:user, username: "Charles Dickens"))
|
||||
|
||||
visit debate_path(debate)
|
||||
|
||||
expect(page).to have_content debate.title
|
||||
expect(page).to have_content "Debate description"
|
||||
expect(page).to have_content debate.author.name
|
||||
expect(page).to have_content "Charles Dickens"
|
||||
expect(page).to have_content I18n.l(debate.created_at.to_date)
|
||||
expect(page).to have_css avatar(debate.author.name)
|
||||
expect(page).to have_avatar "C"
|
||||
expect(page.html).to include "<title>#{debate.title}</title>"
|
||||
end
|
||||
|
||||
|
||||
@@ -114,16 +114,16 @@ describe "Proposals" do
|
||||
end
|
||||
|
||||
scenario "Show" do
|
||||
proposal = create(:proposal)
|
||||
proposal = create(:proposal, author: create(:user, username: "Mark Twain"))
|
||||
|
||||
visit proposal_path(proposal)
|
||||
|
||||
expect(page).to have_content proposal.title
|
||||
expect(page).to have_content proposal.code
|
||||
expect(page).to have_content "Proposal description"
|
||||
expect(page).to have_content proposal.author.name
|
||||
expect(page).to have_content "Mark Twain"
|
||||
expect(page).to have_content I18n.l(proposal.created_at.to_date)
|
||||
expect(page).to have_css avatar(proposal.author.name)
|
||||
expect(page).to have_avatar "M"
|
||||
expect(page.html).to include "<title>#{proposal.title}</title>"
|
||||
expect(page).not_to have_css ".js-flag-actions"
|
||||
expect(page).not_to have_css ".js-follow"
|
||||
|
||||
@@ -485,18 +485,4 @@ describe "Users" do
|
||||
expect(page).not_to have_content("Sport")
|
||||
end
|
||||
end
|
||||
|
||||
describe "Initials" do
|
||||
scenario "display SVG avatars when loaded into the DOM" do
|
||||
login_as(create(:user))
|
||||
visit debate_path(create(:debate))
|
||||
|
||||
fill_in "Leave your comment", with: "I'm awesome"
|
||||
click_button "Publish comment"
|
||||
|
||||
within ".comment", text: "I'm awesome" do
|
||||
expect(page).to have_css "img.initialjs-avatar[src^='data:image/svg']"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user