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:
parent
bb472728d4
commit
5f92ef7b8f
12 changed files with 171 additions and 196 deletions
3
app/assets/images/website.svg
Normal file
3
app/assets/images/website.svg
Normal 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 |
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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" %>
|
||||
|
|
|
|||
|
|
@ -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 %>
|
||||
|
|
|
|||
|
|
@ -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 %>
|
||||
|
||||
|
|
|
|||
|
|
@ -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 %>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue