Listing Liquid Tag (#3250)
* init wip still needs html, styling, and specs * add more html similar to singleListing.jsx * add generate_new_liquid method to spec * remove unexpected period * add error handling for invalid slugs and expired listings * initial styling * styling for author and tags * import variables to sass * more styling and swithc tags and author info * add specs * handle error for slug with no category * add listing tag to editor guide * reduce complexity * render expired listing rather than raise error * update expired listing spec * import mixins to classified listing tag styling
This commit is contained in:
parent
3c1d51b836
commit
42cb72d185
6 changed files with 275 additions and 0 deletions
81
app/assets/stylesheets/ltags/ClassifiedListingTag.scss
Normal file
81
app/assets/stylesheets/ltags/ClassifiedListingTag.scss
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
@import 'variables';
|
||||
@import 'mixins';
|
||||
|
||||
.ltag__listing {
|
||||
font-family: $helvetica;
|
||||
border:1px solid $light-medium-gray;
|
||||
box-shadow: $shadow;
|
||||
border-radius:3px;
|
||||
display:block;
|
||||
margin:0.95em 0 1.20em;
|
||||
max-width: 100%;
|
||||
@media screen and (min-width: 760px) {
|
||||
margin: 0.95em auto;
|
||||
width:620px;
|
||||
}
|
||||
|
||||
.ltag__listing-content {
|
||||
padding: calc(0.5vw + 6px);
|
||||
h3 {
|
||||
margin:0;
|
||||
padding:0;
|
||||
font-weight: 500;
|
||||
font-size: 1.5em;
|
||||
a {
|
||||
color: var(--theme-color, $black);
|
||||
}
|
||||
}
|
||||
|
||||
.ltag__listing-body {
|
||||
max-width: 100%;
|
||||
margin: 5px 0 15px 0;
|
||||
font-size: 0.9em;
|
||||
a {
|
||||
color: var(--theme-color, $black);
|
||||
}
|
||||
}
|
||||
|
||||
.ltag__listing-tags {
|
||||
font-size: 0.7em;
|
||||
line-height: 1.3em;
|
||||
|
||||
.ltag__listing-tag {
|
||||
margin-right: calc(0.4vw + 4px);
|
||||
margin-left: 1px;
|
||||
|
||||
a {
|
||||
@include themeable(
|
||||
color,
|
||||
theme-secondary-color,
|
||||
$dark-medium-gray
|
||||
);
|
||||
@include themeable(
|
||||
background,
|
||||
theme-container-accent-background,
|
||||
$light-medium-gray
|
||||
);
|
||||
display: inline-block;
|
||||
margin: 2px;
|
||||
padding: 2px 7px;
|
||||
border-radius: 3px;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ltag__listing-author-info {
|
||||
color: #666666;
|
||||
color: var(--theme-secondary-color, #666666);
|
||||
line-height: 1.3em;
|
||||
a {
|
||||
margin: 0.1vw 0;
|
||||
padding: 0;
|
||||
font-size: 0.7em;
|
||||
margin-bottom: 0;
|
||||
font-weight: bold;
|
||||
color: #666666;
|
||||
color: var(--theme-secondary-color, #666666);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -11,3 +11,4 @@
|
|||
@import 'GistTag';
|
||||
@import 'GithubReadmeTag';
|
||||
@import 'PollTag';
|
||||
@import 'ClassifiedListingTag';
|
||||
34
app/liquid_tags/classified_listing_tag.rb
Normal file
34
app/liquid_tags/classified_listing_tag.rb
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
class ClassifiedListingTag < LiquidTagBase
|
||||
PARTIAL = "classified_listings/liquid".freeze
|
||||
|
||||
def initialize(_tag_name, slug_path_url, _tokens)
|
||||
striped_path = ActionController::Base.helpers.strip_tags(slug_path_url).strip
|
||||
@listing = get_listing(striped_path)
|
||||
end
|
||||
|
||||
def render(_context)
|
||||
ActionController::Base.new.render_to_string(
|
||||
partial: PARTIAL,
|
||||
locals: { listing: @listing },
|
||||
)
|
||||
end
|
||||
|
||||
def get_hash(url)
|
||||
path = Addressable::URI.parse(url).path
|
||||
path.slice!(0, 10) if path.starts_with?("/listings/") # remove leading slash if present
|
||||
path.slice!(-1) if path.ends_with?("/") # remove trailing slash if present
|
||||
Addressable::Template.new("{category}/{slug}").extract(path)&.symbolize_keys
|
||||
end
|
||||
|
||||
def get_listing(url)
|
||||
hash = get_hash(url)
|
||||
raise StandardError, "Invalid URL or slug. Listing not found." if hash.nil?
|
||||
|
||||
listing = ClassifiedListing.find_by(hash)
|
||||
raise StandardError, "Invalid URL or slug. Listing not found." unless listing
|
||||
|
||||
listing
|
||||
end
|
||||
end
|
||||
|
||||
Liquid::Template.register_tag("listing", ClassifiedListingTag)
|
||||
36
app/views/classified_listings/_liquid.html.erb
Normal file
36
app/views/classified_listings/_liquid.html.erb
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
<% if listing.bumped_at < (Time.zone.today - 30) %>
|
||||
<div class="ltag__listing">
|
||||
<div class="ltag__listing-content">
|
||||
<h3>
|
||||
<a href="/listings">
|
||||
This listing has expired.
|
||||
</a>
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
<% else %>
|
||||
<div class="ltag__listing">
|
||||
<div class="ltag__listing-content">
|
||||
<h3>
|
||||
<a href="/listings/<%= listing.category %>/<%= listing.slug %>">
|
||||
<%= listing.title %>
|
||||
</a>
|
||||
</h3>
|
||||
<div class="ltag__listing-body">
|
||||
<a href="/listings/<%= listing.category %>/<%= listing.slug %>">
|
||||
<%= listing.processed_html.html_safe %>
|
||||
</a>
|
||||
</div>
|
||||
<div class="ltag__listing-tags">
|
||||
<% listing.tag_list.each do |tag| %>
|
||||
<span class='ltag__listing-tag'><a href='/listings?t=<%= tag %>'><%= tag %></a></span>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="ltag__listing-author-info">
|
||||
<a href="/listings/<%= listing.category %>"><%= listing.category %></a>
|
||||
・
|
||||
<a href="/<%= listing.author.username %>"><%= listing.author.name %></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
|
@ -72,6 +72,12 @@
|
|||
<h3><strong>dev.to Podcast Episode Embed</strong></h3>
|
||||
<p>All you need is the full link of the podcast episode:</p>
|
||||
<code>{% podcast https://dev.to/basecspodcast/s2e2--queues-irl %}</code>
|
||||
<h3><strong>dev.to Listing Embed</strong></h3>
|
||||
<p>All you need is the full link of the listing:</p>
|
||||
<code>{% listing https://dev.to/listings/collabs/dev-is-open-source-823 %}</code>
|
||||
<p>You can also use the category and slug like this:</p>
|
||||
<code>{% listing collabs/dev-is-open-source-823 %}</code>
|
||||
<p>Note: Expired listings will raise an error. Make sure the listing is published or recently bumped.</p>
|
||||
<h3><strong>Twitter Embed</strong></h3>
|
||||
<p>Using the Twitter Liquid tag will allow the tweet to pre-render from the server, providing your reader with a better experience. All you need is the tweet
|
||||
<code>id</code> from the url.</p>
|
||||
|
|
|
|||
117
spec/liquid_tags/classified_listing_tag_spec.rb
Normal file
117
spec/liquid_tags/classified_listing_tag_spec.rb
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
require "rails_helper"
|
||||
|
||||
RSpec.describe ClassifiedListingTag, type: :liquid_template do
|
||||
let(:user) { create(:user, username: "dariamorgendorffer", name: "Daria Morgendorffer") }
|
||||
let(:user_listing) do
|
||||
create(
|
||||
:classified_listing,
|
||||
user_id: user.id,
|
||||
title: "save me pls",
|
||||
body_markdown: "sigh sigh sigh",
|
||||
processed_html: "<p>sigh sigh sigh</p>",
|
||||
category: "cfp",
|
||||
tag_list: %w[a b c],
|
||||
organization_id: nil,
|
||||
)
|
||||
end
|
||||
let(:expired_listing) do
|
||||
create(
|
||||
:classified_listing,
|
||||
user_id: user.id,
|
||||
title: "this old af",
|
||||
body_markdown: "exxpired",
|
||||
processed_html: "<p>exxpired</p>",
|
||||
category: "cfp",
|
||||
tag_list: %w[x y z],
|
||||
organization_id: nil,
|
||||
bumped_at: Time.zone.today - 40,
|
||||
created_at: Time.zone.today - 40,
|
||||
updated_at: Time.zone.today - 40,
|
||||
)
|
||||
end
|
||||
let(:org) { create(:organization) }
|
||||
let(:org_user) { create(:user, organization_id: org.id) }
|
||||
let(:org_listing) do
|
||||
create(
|
||||
:classified_listing,
|
||||
user_id: org_user.id,
|
||||
title: "this is a job posting",
|
||||
body_markdown: "wow code lots get not only money but satisfaction from work",
|
||||
processed_html: "<p>wow code lots get not only money but satisfaction from work</p>",
|
||||
category: "misc",
|
||||
tag_list: %w[a b c],
|
||||
organization_id: org.id,
|
||||
)
|
||||
end
|
||||
|
||||
def generate_new_liquid(slug)
|
||||
Liquid::Template.register_tag("listing", ClassifiedListingTag)
|
||||
Liquid::Template.parse("{% listing #{slug} %}")
|
||||
end
|
||||
|
||||
def correct_link_html(listing)
|
||||
tags = ""
|
||||
listing.tag_list.each do |tag|
|
||||
tags += "<span class='ltag__listing-tag'><a href='/listings?t=#{tag}'>#{tag}</a></span>\n "
|
||||
end
|
||||
tags = tags.rstrip
|
||||
<<~HTML
|
||||
<div class="ltag__listing">
|
||||
<div class="ltag__listing-content">
|
||||
<h3>
|
||||
<a href="/listings/#{listing.category}/#{listing.slug}">
|
||||
#{listing.title}
|
||||
</a>
|
||||
</h3>
|
||||
<div class="ltag__listing-body">
|
||||
<a href="/listings/#{listing.category}/#{listing.slug}">
|
||||
#{listing.processed_html}
|
||||
</a>
|
||||
</div>
|
||||
<div class="ltag__listing-tags">
|
||||
#{tags}
|
||||
</div>
|
||||
<div class="ltag__listing-author-info">
|
||||
<a href="/listings/#{listing.category}">#{listing.category}</a>
|
||||
・
|
||||
<a href="/#{listing.author.username}">#{listing.author.name}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
HTML
|
||||
end
|
||||
|
||||
def render_expired_listing
|
||||
<<~HTML
|
||||
<div class="ltag__listing">
|
||||
<div class="ltag__listing-content">
|
||||
<h3>
|
||||
<a href="/listings">
|
||||
This listing has expired.
|
||||
</a>
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
HTML
|
||||
end
|
||||
|
||||
it "raises an error when invalid" do
|
||||
expect { generate_new_liquid("/listings/fakecategory/fakeslug") }.
|
||||
to raise_error("Invalid URL or slug. Listing not found.")
|
||||
end
|
||||
|
||||
it "displays expired message when listing is expired" do
|
||||
liquid = generate_new_liquid("#{expired_listing.category}/#{expired_listing.slug}")
|
||||
expect(liquid.render).to eq(render_expired_listing)
|
||||
end
|
||||
|
||||
it "renders a proper listing tag from user listing" do
|
||||
liquid = generate_new_liquid("#{user_listing.category}/#{user_listing.slug}")
|
||||
expect(liquid.render).to eq(correct_link_html(user_listing))
|
||||
end
|
||||
|
||||
it "renders a proper listing tag from org listing" do
|
||||
liquid = generate_new_liquid("#{org_listing.category}/#{org_listing.slug}")
|
||||
expect(liquid.render).to eq(correct_link_html(org_listing))
|
||||
end
|
||||
end
|
||||
Loading…
Add table
Reference in a new issue