Fix profile field production display area functionality and remove SiteConfig.dev_to? hacks for better generalization (#12015)

* Initial work

* Add special exceptions

* Clean up and progress

* Update github url

Co-authored-by: Ridhwana <Ridhwana.Khan16@gmail.com>

* Add tests and finalize data update script

* Fix test

* Add some tests

* Fix display_area test

* Adjust spec

* Fix svg title

Co-authored-by: Molly Struve <mollylbs@gmail.com>
Co-authored-by: Ridhwana <Ridhwana.Khan16@gmail.com>
This commit is contained in:
Ben Halpern 2021-02-11 10:05:06 -05:00 committed by GitHub
parent bb472728d4
commit 5f92ef7b8f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 171 additions and 196 deletions

View file

@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path d="M10 6v2H5v11h11v-5h2v6a1 1 0 01-1 1H4a1 1 0 01-1-1V7a1 1 0 011-1h6zm11-3v8h-2V6.413l-7.793 7.794-1.414-1.414L17.585 5H13V3h8z"/>
</svg>

After

Width:  |  Height:  |  Size: 233 B

View file

@ -1,14 +1,8 @@
class ProfileDecorator < ApplicationDecorator
DEV_HEADER_FIELDS = %w[employment_title employer_name].freeze
# Return a Hash of the profile fields that should be rendered for a given
# display area, e.g. :left_sidebar
def ui_attributes_for(area:)
names = ProfileField.public_send(area).pluck(:attribute_name)
# Temporary workaround: DEV specific header fields are hardcoded in the view
if SiteConfig.dev_to?
names -= DEV_HEADER_FIELDS
end
data.slice(*names).select { |_, v| v.present? }
end
end

View file

@ -9,6 +9,30 @@ class Profile < ApplicationRecord
store_attribute :data, :custom_attributes, :json, default: {}
SPECIAL_DISPLAY_ATTRIBUTES = %w[
summary
employment_title
employer_name
employer_url
location
].freeze
SPECIAL_SOCIAL_LINK_ATTRIBUTES = %w[
twitter_url
github_url
facebook_url
linkedin_url
youtube_url
instagram_url
behance_url
medium_url
stackoverflow_url
gitlab_url
twitch_url
mastodon_url
website_url
].freeze
# NOTE: @citizen428 This is a temporary mapping so we don't break DEV during
# profile migration/generalization work.
MAPPED_ATTRIBUTES = {
@ -41,6 +65,14 @@ class Profile < ApplicationRecord
(stored_attributes[:data] || []).map(&:to_s)
end
def self.special_attributes
SPECIAL_DISPLAY_ATTRIBUTES + SPECIAL_SOCIAL_LINK_ATTRIBUTES
end
def self.special_social_link_attributes
SPECIAL_SOCIAL_LINK_ATTRIBUTES.freeze
end
def custom_profile_attributes
custom_profile_fields.pluck(:attribute_name)
end

View file

@ -286,6 +286,14 @@ class User < ApplicationRecord
summary
end
def twitter_url
"https://twitter.com/#{twitter_username}" if twitter_username.present?
end
def github_url
"https://github.com/#{github_username}" if github_username.present?
end
def set_remember_fields
self.remember_token ||= self.class.remember_token if respond_to?(:remember_token)
self.remember_created_at ||= Time.now.utc

View file

@ -1,4 +1,9 @@
<main id="profileFields">
<div class="crayons-notice mb-4">
<p>
Instructions on how to properly use this feature at this time.
</p>
</div>
<%= render "add_group_modal" %>
<%= render "grouped_profile_fields" %>
<%= render "ungrouped_profile_fields" %>

View file

@ -1,63 +1,31 @@
<% if FeatureFlag.enabled?(:profile_admin) %>
<% if (header_fields = @profile.ui_attributes_for(area: :header)).present? %>
<div class="profile-header__bottom fs-base">
<% header_fields.each do |title, value| %>
<div class="crayons-definition">
<strong class="crayons-definition__title">
<%= sanitized_sidebar title.titleize %>
</strong>
<p class="crayons-definition__value">
<%= sanitized_sidebar value %>
</p>
</div>
<% end %>
<%# As we migrate to more generalized profiles, we don't want to break DEV %>
<% if SiteConfig.dev_to? %>
<% if @user.employment_title.present? %>
<div class="crayons-definition">
<strong class="crayons-definition__title">Work</strong>
<p class="crayons-definition__value">
<%= @user.employment_title %>
<% if @user.employer_name.present? %>
<span class="opacity-50"> at </span>
<% if @user.employer_url.present? %>
<a href="<%= @user.employer_url %>" class="crayons-link crayons-link--brand" target="_blank" rel="noopener"><%= @user.employer_name %></a>
<% else %>
<%= @user.employer_name %>
<% end %>
<% end %>
</p>
</div>
<% end %>
<% end %>
</div>
<% end %>
<% else %>
<% if @user.employment_title.present? || @user.education.present? %>
<div class="profile-header__bottom fs-base">
<% if @user.education.present? %>
<div class="crayons-definition">
<strong class="crayons-definition__title">Education</strong>
<p class="crayons-definition__value"><%= @user.education %></p>
</div>
<% end %>
<% if @user.employment_title.present? %>
<div class="crayons-definition">
<strong class="crayons-definition__title">Work</strong>
<p class="crayons-definition__value">
<%= @user.employment_title %>
<% if @user.employer_name.present? %>
<span class="opacity-50"> at </span>
<% if @user.employer_url.present? %>
<a href="<%= @user.employer_url %>" class="crayons-link crayons-link--brand" target="_blank" rel="noopener"><%= @user.employer_name %></a>
<% else %>
<%= @user.employer_name %>
<% end %>
<% if (header_fields = @profile.ui_attributes_for(area: :header)).present? %>
<div class="profile-header__bottom fs-base">
<% header_fields.each do |title, value| %>
<% next if Profile.special_attributes.include?(title) %>
<div class="crayons-definition">
<strong class="crayons-definition__title">
<%= sanitized_sidebar title.titleize %>
</strong>
<p class="crayons-definition__value">
<%= sanitized_sidebar value %>
</p>
</div>
<% end %>
<% if @user.employment_title.present? %>
<div class="crayons-definition">
<strong class="crayons-definition__title">Work</strong>
<p class="crayons-definition__value">
<%= @user.employment_title %>
<% if @user.employer_name.present? %>
<span class="opacity-50"> at </span>
<% if @user.employer_url.present? %>
<a href="<%= @user.employer_url %>" class="crayons-link crayons-link--brand" target="_blank" rel="noopener"><%= @user.employer_name %></a>
<% else %>
<%= @user.employer_name %>
<% end %>
</p>
</div>
<% end %>
</div>
<% end %>
<% end %>
</p>
</div>
<% end %>
</div>
<% end %>

View file

@ -32,68 +32,18 @@
<% end %>
<% cache "user-profile-sidebar-profile-details-#{@user.id}-#{@user.profile_updated_at}", expires_in: 10.days do %>
<% if FeatureFlag.enabled?(:profile_admin) %>
<% @profile.ui_attributes_for(area: :left_sidebar).each do |title, value| %>
<div class="crayons-card crayons-card--secondary">
<header class="crayons-card__header">
<h3 class="crayons-subtitle-3">
<%= sanitized_sidebar title.titleize %>
</h3>
</header>
<% @profile.ui_attributes_for(area: :left_sidebar).each do |title, value| %>
<div class="crayons-card crayons-card--secondary">
<header class="crayons-card__header">
<h3 class="crayons-subtitle-3">
<%= sanitized_sidebar title.titleize %>
</h3>
</header>
<div class="crayons-card__body">
<%= sanitized_sidebar value %>
</div>
<div class="crayons-card__body">
<%= sanitized_sidebar value %>
</div>
<% end %>
<% else %>
<% if @user.mostly_work_with.present? %>
<div class="crayons-card crayons-card--secondary">
<header class="crayons-card__header">
<h3 class="crayons-subtitle-3">Skills/languages</h3>
</header>
<div class="crayons-card__body">
<%= sanitized_sidebar @user.mostly_work_with %>
</div>
</div>
<% end %>
<% if @user.currently_learning.present? %>
<div class="crayons-card crayons-card--secondary">
<header class="crayons-card__header">
<h3 class="crayons-subtitle-3">Learning/trying</h3>
</header>
<div class="crayons-card__body">
<%= sanitized_sidebar @user.currently_learning %>
</div>
</div>
<% end %>
<% if @user.currently_hacking_on.present? %>
<div class="crayons-card crayons-card--secondary">
<header class="crayons-card__header">
<h3 class="crayons-subtitle-3">Projects</h3>
</header>
<div class="crayons-card__body">
<%= sanitized_sidebar @user.currently_hacking_on %>
</div>
</div>
<% end %>
<% if @user.available_for.present? %>
<div class="crayons-card crayons-card--secondary">
<header class="crayons-card__header">
<h3 class="crayons-subtitle-3">Available for...</h3>
</header>
<div class="crayons-card__body">
<%= sanitized_sidebar @user.available_for %>
</div>
</div>
<% end %>
</div>
<% end %>
<% end %>

View file

@ -74,75 +74,10 @@
</a>
<% end %>
<span class="profile-header__meta__item -ml-1">
<% if @user.twitter_username? %>
<a href="https://twitter.com/<%= @user.twitter_username %>" target="_blank" rel="noopener me" class="px-1 align-middle inline-block">
<%= inline_svg_tag("twitter.svg", class: "crayons-icon", aria: true, title: "Twitter logo") %>
</a>
<% end %>
<% if @user.github_username? %>
<a href="https://github.com/<%= @user.github_username %>" target="_blank" rel="noopener me" class="px-1 align-middle inline-block">
<%= inline_svg_tag("github.svg", class: "crayons-icon", aria: true, title: "GitHub logo") %>
</a>
<% end %>
<% if SiteConfig.dev_to? %>
<% if @user.mastodon_url.present? %>
<a href="<%= @user.mastodon_url %>" target="_blank" rel="noopener me" class="px-1 align-middle inline-block">
<%= inline_svg_tag("mastodon.svg", class: "crayons-icon", aria: true, title: "Mastodon logo") %>
</a>
<% end %>
<% if @user.facebook_url.present? %>
<a href="<%= @user.facebook_url %>" target="_blank" rel="noopener me" class="px-1 align-middle inline-block">
<%= inline_svg_tag("facebook.svg", class: "crayons-icon", aria: true, title: "Facebook logo") %>
</a>
<% end %>
<% if @user.youtube_url.present? %>
<a href="<%= @user.youtube_url %>" target="_blank" rel="noopener me" class="px-1 align-middle inline-block">
<%= inline_svg_tag("youtube.svg", class: "crayons-icon", aria: true, title: "Youtube logo") %>
</a>
<% end %>
<% if @user.linkedin_url.present? %>
<a href="<%= @user.linkedin_url %>" target="_blank" rel="noopener me" class="px-1 align-middle inline-block">
<%= inline_svg_tag("linkedin.svg", class: "crayons-icon", aria: true, title: "LinkedIn logo") %>
</a>
<% end %>
<% if @user.behance_url.present? %>
<a href="<%= @user.behance_url %>" target="_blank" rel="noopener me" class="px-1 align-middle inline-block">
<%= inline_svg_tag("behance.svg", class: "crayons-icon", aria: true, title: "Behance logo") %>
</a>
<% end %>
<% if @user.stackoverflow_url.present? %>
<a href="<%= @user.stackoverflow_url %>" target="_blank" rel="noopener me" class="px-1 align-middle inline-block">
<%= inline_svg_tag("stackoverflow.svg", class: "crayons-icon", aria: true, title: "StackOverflow logo") %>
</a>
<% end %>
<% if @user.dribbble_url.present? %>
<a href="<%= @user.dribbble_url %>" target="_blank" rel="noopener me" class="px-1 align-middle inline-block">
<%= inline_svg_tag("dribbble.svg", class: "crayons-icon", aria: true, title: "Dribbble logo") %>
</a>
<% end %>
<% if @user.medium_url.present? %>
<a href="<%= @user.medium_url %>" target="_blank" rel="noopener nofollow me" class="px-1 align-middle inline-block">
<%= inline_svg_tag("medium.svg", class: "crayons-icon", aria: true, title: "Medium logo") %>
</a>
<% end %>
<% if @user.gitlab_url.present? %>
<a href="<%= @user.gitlab_url %>" target="_blank" rel="noopener nofollow me" class="px-1 align-middle inline-block">
<%= inline_svg_tag("gitlab.svg", class: "crayons-icon", aria: true, title: "GitLab logo") %>
</a>
<% end %>
<% if @user.instagram_url.present? %>
<a href="<%= @user.instagram_url %>" target="_blank" rel="noopener nofollow me" class="px-1 align-middle inline-block">
<%= inline_svg_tag("instagram.svg", class: "crayons-icon", aria: true, title: "Instagram logo") %>
</a>
<% end %>
<% if @user.twitch_url.present? %>
<a href="<%= @user.twitch_url %>" target="_blank" rel="noopener nofollow me" class="px-1 align-middle inline-block">
<%= inline_svg_tag("twitch.svg", class: "crayons-icon", aria: true, title: "Twitch logo") %>
</a>
<% end %>
<% if @user.website_url.present? %>
<a href="<%= @user.website_url %>" target="_blank" rel="noopener nofollow me" class="px-1 align-middle inline-block">
<%= inline_svg_tag("external.svg", class: "crayons-icon", aria: true, title: "External link icon") %>
<% Profile.special_social_link_attributes.each do |attribute| %>
<% if @user.send(attribute).present? %>
<a href="<%= @user.send(attribute) %>" target="_blank" rel="noopener me" class="px-1 align-middle inline-block">
<%= inline_svg_tag("#{attribute.gsub("_url","")}.svg", class: "crayons-icon", aria: true, title: "#{attribute.split("_").first.titleize} logo") %>
</a>
<% end %>
<% end %>

View file

@ -0,0 +1,28 @@
module DataUpdateScripts
class SetProperDisplayAreaForProfileFields
def run
ProfileField.where(attribute_name: %w[brand_color1
brand_color2
recruiters_can_contact_me_about_job_opportunities
display_email_on_profile
twitter_url
github_url
facebook_url
linkedin_url
youtube_url
instagram_url
behance_url
medium_url
stackoverflow_url
gitlab_url
twitch_url
mastodon_url
website_url])
.update_all(display_area: "settings_only")
ProfileField.where.not(display_area: "settings_only").update_all(display_area: "header")
ProfileField.where(attribute_name:
%w[currently_hacking_on currently_learning mostly_work_with available_for])
.update_all(display_area: "left_sidebar")
end
end
end

View file

@ -0,0 +1,15 @@
require "rails_helper"
require Rails.root.join(
"lib/data_update_scripts/20201223013903_set_proper_display_area_for_profile_fields.rb",
)
describe DataUpdateScripts::SetProperDisplayAreaForProfileFields do
it "migrates profile fields to proper areas" do
# Run the script
described_class.new.run
expect(ProfileField.find_by(attribute_name: "instagram_url").display_area)
.to eq("settings_only")
expect(ProfileField.find_by(attribute_name: "education").display_area).to eq("header")
expect(ProfileField.find_by(attribute_name: "currently_learning").display_area).to eq("left_sidebar")
end
end

View file

@ -91,6 +91,40 @@ RSpec.describe "UserProfiles", type: :request do
expect(response.body).not_to include "author-payment-pointer"
end
it "renders sidebar profile field elements in sidebar" do
create(:profile_field, label: "whoaaaa", display_area: "left_sidebar")
get "/#{user.username}"
# Ensure this comes after the start of the sidebar element
expect(response.body.split("Whoaaaa").first).to include "crayons-layout__sidebar-left"
end
it "does not render settings_only on page" do
create(:profile_field, label: "whoaaaa", display_area: "settings_only")
get "/#{user.username}"
expect(response.body).not_to include "Whoaaaa"
end
it "does not render special display header elements naively" do
user.location = "hawaii"
user.save
get "/#{user.username}"
# Does not include the word, but does include the SVG
expect(response.body).not_to include "<p>Location</p>"
expect(response.body).to include user.location
expect(response.body).to include "M18.364 17.364L12 23.728l-6.364-6.364a9 9 0 1112.728 0zM12 13a2 2 0 100-4 2 2 0"
end
it "does not render special display social link elements naively" do
user.instagram_url = "https://instagram.com/whoa"
user.save
get "/#{user.username}"
# Does not include the word, but does include the SVG
expect(response.body).not_to include "<p>Instagram"
expect(response.body).to include "Instagram logo</title>"
expect(response.body).to include user.instagram_url
expect(response.body).to include "M12 2c2.717 0 3.056.01 4.122.06 1.065.05 1.79.217 2.428.465.66.254"
end
context "when organization" do
it "renders organization page if org" do
get organization.path

View file

@ -16,6 +16,7 @@ RSpec.describe ProfileFields::ImportFromCsv do
expect(field.placeholder_text).to eq "John Doe"
expect(field.description).to be_nil
expect(field.profile_field_group.name).to eq "Basic"
expect(field.display_area).to eq "header"
end
it "handles missing placeholder_texts", :aggregate_failures do
@ -24,6 +25,7 @@ RSpec.describe ProfileFields::ImportFromCsv do
expect(field.placeholder_text).to be_nil
expect(field.description).to eq "Programming languages"
expect(field.profile_field_group.name).to eq "Coding"
expect(field.display_area).to eq "left_sidebar"
end
it "handles commas in correctly quoted fields", :aggregate_failures do
@ -32,6 +34,7 @@ RSpec.describe ProfileFields::ImportFromCsv do
expect(field.placeholder_text).to eq "#000000"
expect(field.description).to eq "Used for backgrounds, borders etc."
expect(field.profile_field_group.name).to eq "Branding"
expect(field.display_area).to eq "settings_only"
end
end
end